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 GType gst_static_autoplug_render_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_render_class_init,
62 (GInstanceInitFunc)gst_static_autoplug_render_init,
64 static_autoplug_type = g_type_register_static (GST_TYPE_AUTOPLUG, "GstStaticAutoplugRender", &static_autoplug_info, 0);
66 return static_autoplug_type;
70 gst_static_autoplug_render_class_init(GstStaticAutoplugRenderClass *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_renderers = gst_static_autoplug_to_render;
81 static void gst_static_autoplug_render_init(GstStaticAutoplugRender *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 ("staticrender",
92 "A static autoplugger, it constructs the complete element before running it",
93 gst_static_autoplug_render_get_type ());
95 if (factory != NULL) {
96 gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
99 g_warning ("could not register autoplugger: staticrender");
104 GstPluginDesc plugin_desc = {
107 "gststaticautoplugrender",
111 static GstPadTemplate*
112 gst_autoplug_match_caps (GstElementFactory *factory, GstPadDirection direction, GstCaps *caps)
116 templates = factory->padtemplates;
119 GstPadTemplate *template = (GstPadTemplate *)templates->data;
121 if (template->direction == direction && direction == GST_PAD_SRC) {
122 if (gst_caps_check_compatibility (GST_PAD_TEMPLATE_CAPS (template), caps))
125 else if (template->direction == direction && direction == GST_PAD_SINK) {
126 if (gst_caps_check_compatibility (caps, GST_PAD_TEMPLATE_CAPS (template)))
129 templates = g_list_next (templates);
135 gst_autoplug_can_match (GstElementFactory *src, GstElementFactory *dest)
137 GList *srctemps, *desttemps;
139 srctemps = src->padtemplates;
142 GstPadTemplate *srctemp = (GstPadTemplate *)srctemps->data;
143 srctemps = g_list_next (srctemps);
145 if (srctemp->direction != GST_PAD_SRC)
148 desttemps = dest->padtemplates;
151 GstPadTemplate *desttemp = (GstPadTemplate *)desttemps->data;
152 desttemps = g_list_next (desttemps);
154 if (desttemp->direction == GST_PAD_SINK && desttemp->presence != GST_PAD_REQUEST) {
155 if (gst_caps_check_compatibility (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));
165 GST_DEBUG (GST_CAT_AUTOPLUG_ATTEMPT,
166 "factory \"%s\" cannot connect with factory \"%s\"",
167 GST_OBJECT_NAME (src), GST_OBJECT_NAME (dest));
172 gst_autoplug_pads_autoplug_func (GstElement *src, GstPad *pad, GstElement *sink)
174 const GList *sinkpads;
175 gboolean connected = FALSE;
176 GstElementState state = GST_STATE (gst_element_get_parent (src));
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));
181 if (state == GST_STATE_PLAYING)
182 gst_element_set_state (GST_ELEMENT (gst_element_get_parent (src)), GST_STATE_PAUSED);
184 sinkpads = gst_element_get_pad_list(sink);
186 GstPad *sinkpad = (GstPad *)sinkpads->data;
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))
193 if ((connected = gst_pad_connect (pad, sinkpad))) {
197 GST_DEBUG (0,"pads incompatible %s, %s", GST_PAD_NAME (pad), GST_PAD_NAME (sinkpad));
200 sinkpads = g_list_next(sinkpads);
203 if (state == GST_STATE_PLAYING)
204 gst_element_set_state (GST_ELEMENT (gst_element_get_parent (src)), GST_STATE_PLAYING);
207 GST_DEBUG (0,"gstpipeline: no path to sinks for type");
213 gst_autoplug_pads_autoplug (GstElement *src, GstElement *sink)
215 const GList *srcpads;
216 gboolean connected = FALSE;
218 srcpads = gst_element_get_pad_list(src);
220 while (srcpads && !connected) {
221 GstPad *srcpad = (GstPad *)srcpads->data;
223 if (gst_pad_get_direction(srcpad) == GST_PAD_SRC) {
224 connected = gst_autoplug_pads_autoplug_func (src, srcpad, sink);
229 srcpads = g_list_next(srcpads);
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);
241 gst_autoplug_element_factory_get_list (gpointer data)
243 return (GList *) gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY);
251 #define IS_CAPS(cap) (((cap) == caps->src) || (cap) == caps->sink)
253 gst_autoplug_caps_find_cost (gpointer src, gpointer dest, gpointer data)
255 caps_struct *caps = (caps_struct *)data;
258 if (IS_CAPS (src) && IS_CAPS (dest)) {
259 res = gst_caps_check_compatibility ((GstCaps *)src, (GstCaps *)dest);
260 /*GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"caps %d to caps %d %d", ((GstCaps *)src)->id, ((GstCaps *)dest)->id, res); */
262 else if (IS_CAPS (src)) {
263 GstPadTemplate *templ;
265 templ = gst_autoplug_match_caps ((GstElementFactory *)dest, GST_PAD_SINK, (GstCaps *)src);
267 if (templ && templ->presence != GST_PAD_REQUEST)
272 /*GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factory %s to src caps %d %d", ((GstElementFactory *)dest)->name, ((GstCaps *)src)->id, res);*/
274 else if (IS_CAPS (dest)) {
275 GstPadTemplate *templ;
277 templ = gst_autoplug_match_caps ((GstElementFactory *)src, GST_PAD_SRC, (GstCaps *)dest);
279 if (templ && templ->presence != GST_PAD_REQUEST)
283 /*GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factory %s to sink caps %d %d", ((GstElementFactory *)src)->name, ((GstCaps *)dest)->id, res);*/
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);
294 return GST_AUTOPLUG_MAX_COST;
298 gst_static_autoplug_to_render (GstAutoplug *autoplug, GstCaps *srccaps, GstElement *target, va_list args)
301 GstElement *targetelement;
302 GstElement *result = NULL, *srcelement = NULL;
304 GList *chains = NULL;
305 GList *endelements = NULL;
306 guint numsinks = 0, i;
307 gboolean have_common = FALSE;
309 targetelement = target;
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).
318 while (targetelement) {
321 GstPadTemplate *templ;
323 pad = GST_PAD_REALIZE (gst_element_get_pad_list (targetelement)->data);
324 templ = GST_PAD_PAD_TEMPLATE (pad);
327 caps.sink = GST_PAD_TEMPLATE_CAPS (templ);
331 GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"autoplugging two caps structures");
333 elements = gst_autoplug_func (caps.src, caps.sink,
334 gst_autoplug_element_factory_get_list,
335 gst_autoplug_caps_find_cost,
339 chains = g_list_append (chains, elements);
340 endelements = g_list_append (endelements, targetelement);
346 targetelement = va_arg (args, GstElement *);
350 * If no list could be found the pipeline cannot be autoplugged and
351 * we return a NULL element
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
361 factories = g_new0 (GList *, numsinks);
363 for (i = 0; chains; i++) {
364 GList *elements = (GList *) chains->data;
366 factories[i] = elements;
368 chains = g_list_next (chains);
370 /*FIXME, free the list */
372 result = gst_bin_new ("autoplug_bin");
375 * We now hav a list of lists that is probably like:
382 * we now try to find the common elements (A) and add them to
383 * the bin. We remove them from both lists too.
385 while (factories[0]) {
386 GstElementFactory *factory;
389 /* fase 3: add common elements */
390 factory = (GstElementFactory *) (factories[0]->data);
392 /* check to other paths for matching elements (factories) */
393 for (i=1; i<numsinks; i++) {
394 if (factory != (GstElementFactory *) (factories[i]->data)) {
399 GST_DEBUG (0,"common factory \"%s\"", GST_OBJECT_NAME (factory));
401 element = gst_element_factory_create (factory, g_strdup (GST_OBJECT_NAME (factory)));
402 gst_bin_add (GST_BIN(result), element);
404 if (srcelement != NULL) {
405 gst_autoplug_pads_autoplug (srcelement, element);
407 /* this is the first element, find a good ghostpad */
411 pads = gst_element_get_pad_list (element);
414 GstPad *pad = GST_PAD (pads->data);
415 GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (pad);
417 if (gst_caps_check_compatibility (srccaps, GST_PAD_TEMPLATE_CAPS (templ))) {
418 gst_element_add_ghost_pad (result, pad, "sink");
422 pads = g_list_next (pads);
425 gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (element));
427 srcelement = element;
429 /* advance the pointer in all lists */
430 for (i=0; i<numsinks; i++) {
431 factories[i] = g_list_next (factories[i]);
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;
446 sinkelement = GST_ELEMENT (endelements->data);
447 endelements = g_list_next (endelements);
449 use_thread = have_common;
451 while (factories[i] || sinkelement) {
452 /* fase 4: add other elements... */
453 GstElementFactory *factory;
457 factory = (GstElementFactory *)(factories[i]->data);
459 GST_DEBUG (0,"factory \"%s\"", GST_OBJECT_NAME (factory));
460 element = gst_element_factory_create(factory, g_strdup (GST_OBJECT_NAME (factory)));
463 element = sinkelement;
467 /* this element suggests the use of a thread, so we set one up... */
468 if (GST_ELEMENT_IS_THREAD_SUGGESTED(element) || use_thread) {
471 GstElement *current_bin = thebin;
475 GST_DEBUG (0,"sugest new thread for \"%s\" %08x", GST_ELEMENT_NAME (element), GST_FLAGS(element));
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));
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));
484 gst_bin_add(GST_BIN(thebin), queue);
485 gst_autoplug_signal_new_object (GST_AUTOPLUG (autoplug), GST_OBJECT (queue));
487 srcpad = gst_element_get_pad(queue, "src");
489 gst_autoplug_pads_autoplug(thesrcelement, queue);
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;
499 /* no thread needed, easy case */
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));
505 gst_autoplug_pads_autoplug(thesrcelement, element);
507 /* this element is now the new source element */
508 thesrcelement = element;
510 factories[i] = g_list_next(factories[i]);
518 * shortest path algorithm
521 struct _gst_autoplug_node
528 typedef struct _gst_autoplug_node gst_autoplug_node;
531 find_factory (gst_autoplug_node *rgnNodes, gpointer factory)
535 while (rgnNodes[i].iNode) {
536 if (rgnNodes[i].iNode == factory) return i;
543 construct_path (gst_autoplug_node *rgnNodes, gpointer factory)
545 GstElementFactory *current;
546 GList *factories = NULL;
548 current = rgnNodes[find_factory(rgnNodes, factory)].iPrev;
550 GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factories found in autoplugging (reversed order)");
552 while (current != NULL)
554 gpointer next = NULL;
556 next = rgnNodes[find_factory(rgnNodes, current)].iPrev;
558 factories = g_list_prepend (factories, current);
559 GST_INFO (GST_CAT_AUTOPLUG_ATTEMPT,"factory: \"%s\"", GST_OBJECT_NAME (current));
567 gst_autoplug_enqueue (GList *queue, gpointer iNode, gint iDist, gpointer iPrev)
569 gst_autoplug_node *node = g_malloc (sizeof (gst_autoplug_node));
575 queue = g_list_append (queue, node);
581 gst_autoplug_dequeue (GList *queue, gpointer *iNode, gint *iDist, gpointer *iPrev)
584 gst_autoplug_node *node;
586 head = g_list_first (queue);
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);
600 gst_autoplug_func (gpointer src, gpointer sink,
601 GstAutoplugListFunction list_function,
602 GstAutoplugCostFunction cost_function,
605 gst_autoplug_node *rgnNodes;
607 gpointer iNode, iPrev;
608 gint iDist, i, iCost;
610 GList *elements = g_list_copy (list_function(data));
614 elements = g_list_append (elements, sink);
615 elements = g_list_append (elements, src);
617 factories = elements;
619 num_factories = g_list_length (factories);
621 rgnNodes = g_new0 (gst_autoplug_node, num_factories+1);
623 for (i=0; i< num_factories; i++) {
624 gpointer fact = factories->data;
626 rgnNodes[i].iNode = fact;
627 rgnNodes[i].iPrev = NULL;
630 rgnNodes[i].iDist = 0;
633 rgnNodes[i].iDist = GST_AUTOPLUG_MAX_COST;
636 factories = g_list_next (factories);
638 rgnNodes[num_factories].iNode = NULL;
640 queue = gst_autoplug_enqueue (queue, src, 0, NULL);
642 while (g_list_length (queue) > 0) {
643 GList *factories2 = elements;
645 queue = gst_autoplug_dequeue (queue, &iNode, &iDist, &iPrev);
647 for (i=0; i< num_factories; i++) {
648 gpointer current = factories2->data;
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;
657 queue = gst_autoplug_enqueue (queue, current, iDist + iCost, iNode);
661 factories2 = g_list_next (factories2);
665 return construct_path (rgnNodes, sink);