gstreamer/gst/gsttype.c
Wim Taymans d78215be9d More Caps changes, this change introduces the padfactory. the GstPadFactory will be used to describe the pads that an...
Original commit message from CVS:
More Caps changes, this change introduces the padfactory. the GstPadFactory
will be used to describe the pads that an element will contain during
its lifetime.
mpg123 and mp3parse serve as an example.
More XML changes to incorporate the padfactories.
2000-12-12 19:29:43 +00:00

740 lines
16 KiB
C

/* Gnome-Streamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* TODO:
* probably should set up a hash table for the type id's, since currently
* it's a rather pathetic linear search. Eventually there may be dozens
* of id's, but in reality there are only so many instances of lookup, so
* I'm not overly worried yet...
*/
#include <gst/gst.h>
#include <string.h>
/* global list of registered types */
GList *_gst_types;
guint16 _gst_maxtype;
struct _GstTypeFindInfo {
GstTypeFindFunc typefindfunc; /* typefind function */
GstPlugin *plugin; /* the plugin with this typefind function */
};
#define MAX_COST 999999
struct _gst_type_node
{
int iNode;
int iDist;
int iPrev;
};
typedef struct _gst_type_node gst_type_node;
static gboolean gst_type_typefind_dummy (GstBuffer *buffer, gpointer priv);
/* we keep a (spase) matrix in the hashtable like:
*
* type_id list of factories hashed by src type_id
*
* 1 -> (1, factory1, factory2), (3, factory3)
* 2 -> NULL
* 3 -> (4, factory4)
* 4 -> NULL
*
* That way, we can quickly find all factories that convert
* 1 to 2.
*
**/
void
_gst_type_initialize (void)
{
_gst_types = NULL;
_gst_maxtype = 1; /* type 0 is undefined */
}
/**
* gst_type_register:
* @factory: the type factory to register
*
* register a new type factory to the system
*
* Returns: the new type id
*/
guint16
gst_type_register (GstTypeFactory *factory)
{
guint16 id;
GstType *type;
g_return_val_if_fail (factory != NULL, 0);
//g_print("gsttype: type register %s\n", factory->mime);
id = gst_type_find_by_mime (factory->mime);
if (!id) {
type = g_new0 (GstType, 1);
type->id = _gst_maxtype++;
type->mime = factory->mime;
type->exts = factory->exts;
//type->typefindfunc = factory->typefindfunc;
type->srcs = NULL;
type->sinks = NULL;
type->converters = g_hash_table_new (NULL, NULL);
_gst_types = g_list_prepend (_gst_types, type);
id = type->id;
} else {
type = gst_type_find_by_id (id);
/* now we want to try to merge the types and return the original */
/* FIXME: do extension merging here, not that easy */
/* if there is no existing typefind function, try to use new one */
/*
if ((type->typefindfunc == gst_type_typefind_dummy ||
type->typefindfunc == NULL) && factory->typefindfunc)
type->typefindfunc = factory->typefindfunc;
*/
}
return id;
}
static
guint16 gst_type_find_by_mime_func (gchar *mime)
{
GList *walk;
GstType *type;
gint typelen,mimelen;
gchar *search, *found;
g_return_val_if_fail (mime != NULL, 0);
walk = _gst_types;
// DEBUG("searching for '%s'\n",mime);
mimelen = strlen (mime);
while (walk) {
type = (GstType *)walk->data;
search = type->mime;
// DEBUG("checking against '%s'\n",search);
typelen = strlen (search);
while ((search - type->mime) < typelen) {
found = strstr (search, mime);
/* if the requested mime is in the list */
if (found) {
if ((*(found + mimelen) == ' ') ||
(*(found + mimelen) == ',') ||
(*(found + mimelen) == '\0')) {
return type->id;
} else {
search = found + mimelen;
}
} else
search += mimelen;
}
walk = g_list_next (walk);
}
return 0;
}
/**
* gst_type_find_by_mime:
* @mime: the mime type to find
*
* find the type id of a given mime type
*
* Returns: the type id
*/
guint16
gst_type_find_by_mime (gchar *mime)
{
return gst_type_find_by_mime_func (mime);
}
/**
* gst_type_find_by_ext:
* @ext: the extension to find
*
* find the type id of a given extention
*
* Returns: the type id
*/
guint16
gst_type_find_by_ext (gchar *ext)
{
//FIXME
g_warning ("gsttype: find_by_ext not implemented");
return 0;
}
/**
* gst_type_find_by_id:
* @id: the type id to lookup
*
* find the type of a given type id
*
* Returns: the type
*/
GstType*
gst_type_find_by_id (guint16 id)
{
GList *walk = _gst_types;
GstType *type;
while (walk) {
type = (GstType *)walk->data;
if (type->id == id)
return type;
walk = g_list_next (walk);
}
return NULL;
}
static void
gst_type_dump_converter (gpointer key,
gpointer value,
gpointer data)
{
GList *walk = (GList *)value;
GstElementFactory *factory;
guint16 id = GPOINTER_TO_UINT (key);
GstType *type = gst_type_find_by_id (id);
g_print ("\ngsttype: %u (%s), ", type->id, type->mime);
while (walk) {
factory = (GstElementFactory *) walk->data;
g_print("\"%s\" ", factory->name);
walk = g_list_next (walk);
}
}
/**
* gst_type_dump:
*
* dumps the current type system
*/
void
gst_type_dump(void)
{
GList *walk = _gst_types;
GstType *type;
g_print ("gst_type_dump() : \n");
while (walk) {
type = (GstType *)walk->data;
g_print ("gsttype: %d (%s)", type->id, type->mime);
g_hash_table_foreach (type->converters, gst_type_dump_converter, NULL);
g_print ("\n");
walk = g_list_next (walk);
}
}
static void
gst_type_handle_src (guint16 id, GstElementFactory *src, gboolean remove)
{
GList *walk;
GstType *type = gst_type_find_by_id (id);
g_return_if_fail (type != NULL);
g_return_if_fail (src != NULL);
g_print ("gsttype: handle src \"%s\" %d\n", src->name, remove);
if (remove)
type->srcs = g_list_remove (type->srcs, src);
else
type->srcs = g_list_prepend (type->srcs, src);
// find out if the element has to be indexed in the matrix
walk = src->padfactories;
while (walk) {
GstPadFactory *factory;
factory = (GstPadFactory *) walk->data;
if (factory->direction == GST_PAD_SINK) {
GstType *type2;
GList *converters;
GList *orig;
GstCaps *caps;
caps = gst_padfactory_get_caps (factory);
if (caps)
type2 = gst_type_find_by_id (caps->id);
else
goto next;
converters = (GList *)g_hash_table_lookup (type2->converters, GUINT_TO_POINTER ((guint)id));
orig = converters;
while (converters) {
if (converters->data == src) {
break;
}
converters = g_list_next (converters);
}
if (remove)
orig = g_list_remove (orig, src);
else if (!converters)
orig = g_list_prepend (orig, src);
g_hash_table_insert (type2->converters, GUINT_TO_POINTER ((guint)id), orig);
}
next:
walk = g_list_next (walk);
}
}
/**
* gst_type_add_src:
* @id: the type id to add the source factory to
* @src: the source factory for the type
*
* register the src factory as being a source for the
* given type id
*/
void
_gst_type_add_src (guint16 id, GstElementFactory *src)
{
gst_type_handle_src (id, src, FALSE);
}
/**
* gst_type_remove_src:
* @id: the type id to add the source factory to
* @src: the source factory for the type
*
* register the src factory as being a source for the
* given type id
*/
void
_gst_type_remove_src (guint16 id, GstElementFactory *src)
{
gst_type_handle_src (id, src, TRUE);
}
static void
gst_type_handle_sink (guint16 id, GstElementFactory *sink, gboolean remove)
{
GList *walk;
GstType *type = gst_type_find_by_id (id);
g_return_if_fail (type != NULL);
g_return_if_fail (sink != NULL);
g_print ("gsttype: handle sink \"%s\" %d\n", sink->name, remove);
if (remove)
type->sinks = g_list_remove (type->sinks, sink);
else
type->sinks = g_list_prepend (type->sinks, sink);
// find out if the element has to be indexed in the matrix
walk = sink->padfactories;
while (walk) {
GstPadFactory *factory;
factory = (GstPadFactory *) walk->data;
if (factory->direction == GST_PAD_SRC) {
guint16 id2;
GList *converters;
GList *orig;
GstCaps *caps;
caps = gst_padfactory_get_caps (factory);
if (caps)
id2 = caps->id;
else
goto next;
converters = (GList *)g_hash_table_lookup (type->converters, GUINT_TO_POINTER ((guint)id2));
orig = converters;
while (converters) {
if (converters->data == sink) {
break;
}
converters = g_list_next (converters);
}
if (remove)
orig = g_list_remove (orig, sink);
else if (!converters)
orig = g_list_prepend (orig, sink);
g_hash_table_insert (type->converters, GUINT_TO_POINTER ((guint)id2), orig);
}
next:
walk = g_list_next (walk);
}
}
/**
* gst_type_add_sink:
* @id: the type id to add the sink factory to
* @sink: the sink factory for the type
*
* register the sink factory as being a sink for the
* given type id
*/
void
_gst_type_add_sink (guint16 id, GstElementFactory *sink)
{
gst_type_handle_sink (id, sink, FALSE);
}
/**
* gst_type_remove_sink:
* @id: the type id to remove the sink factory from
* @sink: the sink factory for the type
*
* remove the sink factory as being a sink for the
* given type id
*/
void
_gst_type_remove_sink (guint16 id, GstElementFactory *sink)
{
gst_type_handle_sink (id, sink, TRUE);
}
/**
* gst_type_get_srcs:
* @id: the id to fetch the source factories for
*
* return a list of elementfactories that source
* the given type id
*
* Returns: a list of elementfactories
*/
GList*
gst_type_get_srcs (guint16 id)
{
GstType *type = gst_type_find_by_id (id);
g_return_val_if_fail (type != NULL, NULL);
return type->srcs;
}
/**
* gst_type_get_sinks:
* @id: the id to fetch the sink factories for
*
* return a list of elementfactories that sink
* the given type id
*
* Returns: a list of elementfactories
*/
GList*
gst_type_get_sinks (guint16 id)
{
GstType *type = gst_type_find_by_id (id);
g_return_val_if_fail (type != 0, NULL);
return type->sinks;
}
/*
* An implementation of Dijkstra's shortest path
* algorithm to find the best set of GstElementFactories
* to connnect two GstTypes
*
**/
static GList*
gst_type_enqueue (GList *queue, gint iNode, gint iDist, gint iPrev)
{
gst_type_node *node = g_malloc (sizeof (gst_type_node));
node->iNode = iNode;
node->iDist = iDist;
node->iPrev = iPrev;
queue = g_list_append (queue, node);
return queue;
}
static GList*
gst_type_dequeue (GList *queue, gint *iNode, gint *iDist, gint *iPrev)
{
GList *head;
gst_type_node *node;
head = g_list_first (queue);
if (head) {
node = (gst_type_node *)head->data;
*iNode = node->iNode;
*iPrev = node->iPrev;
*iDist = node->iDist;
head = g_list_remove (queue, node);
}
return head;
}
static GList*
construct_path (gst_type_node *rgnNodes, gint chNode)
{
guint src = chNode;
guint current = rgnNodes[chNode].iPrev;
GList *factories = NULL;
GstType *type;
GList *converters;
g_print ("gsttype: constructed mime path ");
while (current != MAX_COST)
{
type = gst_type_find_by_id (current);
converters = (GList *)g_hash_table_lookup (type->converters, GUINT_TO_POINTER (src));
g_print ("(%d %d)", src, current);
factories = g_list_prepend (factories, converters->data);
src = current;
current = rgnNodes[current].iPrev;
}
g_print("\n");
return factories;
}
static guint
gst_type_find_cost (gint src, gint dest)
{
GstType *type = gst_type_find_by_id (src);
GList *converters = (GList *)g_hash_table_lookup (type->converters, GUINT_TO_POINTER (dest));
// FIXME do something very clever here...
if (converters) return 1;
return MAX_COST;
}
/**
* gst_type_get_sink_to_src:
* @sinkid: the id of the sink
* @srcid: the id of the source
*
* return a list of elementfactories that convert the source
* type id to the sink type id
*
* Returns: a list of elementfactories
*/
GList*
gst_type_get_sink_to_src (guint16 sinkid, guint16 srcid)
{
gst_type_node *rgnNodes;
GList *queue = NULL;
gint iNode, iDist, iPrev, i, iCost;
if (sinkid == srcid) {
//FIXME return an identity element
return NULL;
}
else {
rgnNodes = g_malloc (sizeof (gst_type_node) * _gst_maxtype);
for (i=0; i< _gst_maxtype; i++) {
rgnNodes[i].iNode = i;
rgnNodes[i].iDist = MAX_COST;
rgnNodes[i].iPrev = MAX_COST;
}
rgnNodes[sinkid].iDist = 0;
rgnNodes[sinkid].iPrev = MAX_COST;
queue = gst_type_enqueue (queue, sinkid, 0, MAX_COST);
while (g_list_length (queue) > 0) {
queue = gst_type_dequeue (queue, &iNode, &iDist, &iPrev);
for (i=0; i< _gst_maxtype; i++) {
iCost = gst_type_find_cost (iNode, i);
if (iCost != MAX_COST) {
if((MAX_COST == rgnNodes[i].iDist) ||
(rgnNodes[i].iDist > (iCost + iDist))) {
rgnNodes[i].iDist = iDist + iCost;
rgnNodes[i].iPrev = iNode;
queue = gst_type_enqueue (queue, i, iDist + iCost, iNode);
}
}
}
}
}
return construct_path (rgnNodes, srcid);
}
/**
* gst_type_get_list:
*
* return a list of all registered types
*
* Returns: a list of GstTypes
*/
GList*
gst_type_get_list (void)
{
return _gst_types;
}
/**
* gst_type_save_thyself:
* @type: the type to save
* @parent: the parent node to save into
*
* save a type into an XML representation
*
* Returns: the new xmlNodePtr
*/
xmlNodePtr
gst_type_save_thyself (GstType *type, xmlNodePtr parent)
{
xmlNewChild (parent, NULL, "mime", type->mime);
return parent;
}
/**
* gst_type_load_thyself:
* @parent: the parent node with the xml information
*
* load a type from an XML representation
*
* Returns: the loaded type id
*/
guint16
gst_type_load_thyself (xmlNodePtr parent)
{
xmlNodePtr field = parent->childs;
guint16 typeid = 0;
while (field) {
if (!strcmp (field->name, "mime")) {
typeid = gst_type_find_by_mime (xmlNodeGetContent (field));
if (!typeid) {
GstTypeFactory *factory = g_new0 (GstTypeFactory, 1);
factory->mime = g_strdup (xmlNodeGetContent (field));
typeid = gst_type_register (factory);
}
return typeid;
}
field = field->next;
}
return typeid;
}
/**
* gst_typefactory_save_thyself:
* @factory: the type factory to save
* @parent: the parent node to save into
*
* save a typefactory into an XML representation
*
* Returns: the new xmlNodePtr
*/
xmlNodePtr
gst_typefactory_save_thyself (GstTypeFactory *factory, xmlNodePtr parent)
{
xmlNewChild (parent, NULL, "mime", factory->mime);
if (factory->exts) {
xmlNewChild (parent, NULL, "extensions", factory->exts);
}
if (factory->typefindfunc) {
xmlNewChild (parent, NULL, "typefind", NULL);
}
return parent;
}
static gboolean
gst_type_typefind_dummy (GstBuffer *buffer, gpointer priv)
{
GstType *type = (GstType *)priv;
guint16 typeid;
g_print ("gsttype: need to load typefind function\n");
type->typefindfuncs = NULL;
gst_plugin_load_typefactory (type->mime);
typeid = gst_type_find_by_mime (type->mime);
type = gst_type_find_by_id (typeid);
/*
if (type->typefindfunc) {
return type->typefindfunc (buffer, type);
}
*/
return FALSE;
}
/**
* gst_typefactory_load_thyself:
* @parent: the parent node to load from
*
* load a typefactory from an XML representation
*
* Returns: the new typefactory
*/
GstTypeFactory*
gst_typefactory_load_thyself (xmlNodePtr parent)
{
GstTypeFactory *factory = g_new0 (GstTypeFactory, 1);
xmlNodePtr field = parent->childs;
factory->typefindfunc = NULL;
while (field) {
if (!strcmp (field->name, "mime")) {
factory->mime = g_strdup (xmlNodeGetContent (field));
}
else if (!strcmp (field->name, "extensions")) {
factory->exts = g_strdup (xmlNodeGetContent (field));
}
else if (!strcmp (field->name, "typefind")) {
//factory->typefindfunc = gst_type_typefind_dummy;
}
field = field->next;
}
return factory;
}