2000-01-30 09:03:00 +00:00
|
|
|
/* 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>
|
2000-02-27 23:18:38 +00:00
|
|
|
#include <string.h>
|
2000-01-30 09:03:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* global list of registered types */
|
|
|
|
GList *_gst_types;
|
|
|
|
guint16 _gst_maxtype;
|
|
|
|
|
2000-08-22 21:18:18 +00:00
|
|
|
#define MAX_COST 999999
|
|
|
|
|
2000-08-21 21:20:38 +00:00
|
|
|
struct _gst_type_node
|
|
|
|
{
|
|
|
|
int iNode;
|
|
|
|
int iDist;
|
|
|
|
int iPrev;
|
|
|
|
};
|
|
|
|
typedef struct _gst_type_node gst_type_node;
|
|
|
|
|
2000-08-28 20:20:55 +00:00
|
|
|
static gboolean gst_type_typefind_dummy(GstBuffer *buffer, gpointer priv);
|
|
|
|
|
2000-08-21 21:20:38 +00:00
|
|
|
/* 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.
|
|
|
|
*
|
|
|
|
**/
|
2000-01-30 09:03:00 +00:00
|
|
|
|
|
|
|
void _gst_type_initialize() {
|
2000-08-28 20:20:55 +00:00
|
|
|
|
2000-01-30 09:03:00 +00:00
|
|
|
_gst_types = NULL;
|
|
|
|
_gst_maxtype = 1; /* type 0 is undefined */
|
|
|
|
|
|
|
|
// gst_type_audio_register();
|
|
|
|
}
|
|
|
|
|
|
|
|
guint16 gst_type_register(GstTypeFactory *factory) {
|
|
|
|
guint16 id;
|
|
|
|
GstType *type;
|
|
|
|
|
2000-02-27 23:18:38 +00:00
|
|
|
g_return_val_if_fail(factory != NULL, 0);
|
2000-01-30 09:03:00 +00:00
|
|
|
|
2000-08-28 20:20:55 +00:00
|
|
|
id = gst_type_find_by_mime(factory->mime);
|
2000-01-30 09:03:00 +00:00
|
|
|
if (!id) {
|
2000-08-28 20:20:55 +00:00
|
|
|
type = g_new0(GstType, 1);
|
2000-01-30 09:03:00 +00:00
|
|
|
|
|
|
|
type->id = _gst_maxtype++;
|
|
|
|
type->mime = factory->mime;
|
|
|
|
type->exts = factory->exts;
|
|
|
|
type->typefindfunc = factory->typefindfunc;
|
|
|
|
type->srcs = NULL;
|
|
|
|
type->sinks = NULL;
|
2000-08-21 21:20:38 +00:00
|
|
|
type->converters = g_hash_table_new(NULL, NULL);
|
2000-01-30 09:03:00 +00:00
|
|
|
_gst_types = g_list_prepend(_gst_types,type);
|
|
|
|
|
|
|
|
id = type->id;
|
2000-08-28 20:20:55 +00:00
|
|
|
|
2000-01-30 09:03:00 +00:00
|
|
|
} 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 */
|
2000-08-28 20:20:55 +00:00
|
|
|
if ((type->typefindfunc == gst_type_typefind_dummy ||
|
|
|
|
type->typefindfunc == NULL) && factory->typefindfunc)
|
2000-01-30 09:03:00 +00:00
|
|
|
type->typefindfunc = factory->typefindfunc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2000-08-28 20:20:55 +00:00
|
|
|
static guint16 gst_type_find_by_mime_func(gchar *mime) {
|
|
|
|
GList *walk;
|
2000-01-30 09:03:00 +00:00
|
|
|
GstType *type;
|
|
|
|
gint typelen,mimelen;
|
|
|
|
gchar *search, *found;
|
|
|
|
|
2000-08-28 20:20:55 +00:00
|
|
|
|
|
|
|
walk = _gst_types;
|
2000-01-30 09:03:00 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2000-08-28 20:20:55 +00:00
|
|
|
guint16 gst_type_find_by_mime(gchar *mime) {
|
|
|
|
guint16 typeid;
|
|
|
|
|
|
|
|
typeid = gst_type_find_by_mime_func(mime);
|
|
|
|
|
|
|
|
if (!typeid) {
|
|
|
|
gst_plugin_load_typefactory(mime);
|
|
|
|
}
|
|
|
|
|
|
|
|
return gst_type_find_by_mime_func(mime);
|
|
|
|
}
|
|
|
|
|
2000-01-30 09:03:00 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2000-08-21 21:20:38 +00:00
|
|
|
static void gst_type_dump_converter(gpointer key, gpointer value, gpointer data) {
|
|
|
|
GList *walk = (GList *)value;
|
|
|
|
GstElementFactory *factory;
|
|
|
|
|
2000-08-28 20:20:55 +00:00
|
|
|
g_print("gsttype: %u, (", GPOINTER_TO_UINT(key));
|
2000-08-21 21:20:38 +00:00
|
|
|
|
|
|
|
while (walk) {
|
|
|
|
factory = (GstElementFactory *) walk->data;
|
|
|
|
g_print("%s, ", factory->name);
|
|
|
|
walk = g_list_next(walk);
|
|
|
|
}
|
|
|
|
g_print("NULL)), ");
|
|
|
|
}
|
|
|
|
|
|
|
|
void gst_type_dump() {
|
|
|
|
GList *walk = _gst_types;
|
|
|
|
GstType *type;
|
|
|
|
|
|
|
|
g_print("gst_type_dump() : \n");
|
|
|
|
|
|
|
|
while (walk) {
|
|
|
|
type = (GstType *)walk->data;
|
|
|
|
|
|
|
|
g_print("gst_type: %d (%s) -> (", type->id, type->mime);
|
|
|
|
g_hash_table_foreach(type->converters, gst_type_dump_converter, NULL);
|
|
|
|
g_print("NULL)\n");
|
|
|
|
|
|
|
|
walk = g_list_next(walk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-01-30 09:03:00 +00:00
|
|
|
void gst_type_add_src(guint16 id,GstElementFactory *src) {
|
2000-08-21 21:20:38 +00:00
|
|
|
GList *walk;
|
2000-01-30 09:03:00 +00:00
|
|
|
GstType *type = gst_type_find_by_id(id);
|
|
|
|
|
|
|
|
g_return_if_fail(type != NULL);
|
|
|
|
g_return_if_fail(src != NULL);
|
|
|
|
|
|
|
|
type->srcs = g_list_prepend(type->srcs,src);
|
2000-08-21 21:20:38 +00:00
|
|
|
gst_elementfactory_add_src(src, id);
|
|
|
|
|
|
|
|
// find out if the element has to be indexed in the matrix
|
|
|
|
walk = src->sink_types;
|
|
|
|
|
|
|
|
while (walk) {
|
|
|
|
GstType *type2 = gst_type_find_by_id(GPOINTER_TO_UINT(walk->data));
|
|
|
|
GList *converters = (GList *)g_hash_table_lookup(type2->converters, GUINT_TO_POINTER((guint)id));
|
|
|
|
GList *orig = converters;
|
|
|
|
|
|
|
|
while (converters) {
|
|
|
|
if (converters->data == src) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
converters = g_list_next(converters);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!converters) {
|
|
|
|
orig = g_list_prepend(orig, src);
|
|
|
|
g_hash_table_insert(type2->converters, GUINT_TO_POINTER((guint)id), orig);
|
|
|
|
}
|
|
|
|
|
|
|
|
walk = g_list_next(walk);
|
|
|
|
}
|
2000-01-30 09:03:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void gst_type_add_sink(guint16 id,GstElementFactory *sink) {
|
2000-08-21 21:20:38 +00:00
|
|
|
GList *walk;
|
2000-01-30 09:03:00 +00:00
|
|
|
GstType *type = gst_type_find_by_id(id);
|
|
|
|
|
|
|
|
g_return_if_fail(type != NULL);
|
|
|
|
g_return_if_fail(sink != NULL);
|
|
|
|
|
|
|
|
type->sinks = g_list_prepend(type->sinks,sink);
|
2000-08-21 21:20:38 +00:00
|
|
|
gst_elementfactory_add_sink(sink, id);
|
|
|
|
|
|
|
|
// find out if the element has to be indexed in the matrix
|
|
|
|
walk = sink->src_types;
|
|
|
|
|
|
|
|
while (walk) {
|
|
|
|
GList *converters = (GList *)g_hash_table_lookup(type->converters, walk->data);
|
|
|
|
GList *orig = converters;
|
|
|
|
|
|
|
|
while (converters) {
|
|
|
|
if (converters->data == sink) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
converters = g_list_next(converters);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!converters) {
|
|
|
|
orig = g_list_prepend(orig, sink);
|
|
|
|
g_hash_table_insert(type->converters, walk->data, orig);
|
|
|
|
}
|
|
|
|
|
|
|
|
walk = g_list_next(walk);
|
|
|
|
}
|
2000-01-30 09:03:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GList *gst_type_get_srcs(guint16 id) {
|
|
|
|
GstType *type = gst_type_find_by_id(id);
|
|
|
|
|
2000-02-27 23:18:38 +00:00
|
|
|
g_return_val_if_fail(type != NULL, NULL);
|
2000-01-30 09:03:00 +00:00
|
|
|
|
|
|
|
return type->srcs;
|
|
|
|
}
|
|
|
|
|
|
|
|
GList *gst_type_get_sinks(guint16 id) {
|
|
|
|
GstType *type = gst_type_find_by_id(id);
|
|
|
|
|
2000-02-27 23:18:38 +00:00
|
|
|
g_return_val_if_fail(type != 0, NULL);
|
2000-01-30 09:03:00 +00:00
|
|
|
|
|
|
|
return type->sinks;
|
|
|
|
}
|
|
|
|
|
2000-08-21 21:20:38 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2000-09-16 10:58:23 +00:00
|
|
|
g_print("gsttype: constructed mime path ");
|
2000-08-22 21:18:18 +00:00
|
|
|
while (current != MAX_COST)
|
2000-08-21 21:20:38 +00:00
|
|
|
{
|
|
|
|
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));
|
|
|
|
|
2000-08-28 20:20:55 +00:00
|
|
|
// FIXME do something very clever here...
|
2000-08-21 21:20:38 +00:00
|
|
|
if (converters) return 1;
|
2000-08-22 21:18:18 +00:00
|
|
|
return MAX_COST;
|
2000-08-21 21:20:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2000-08-22 21:18:18 +00:00
|
|
|
rgnNodes[i].iDist = MAX_COST;
|
|
|
|
rgnNodes[i].iPrev = MAX_COST;
|
2000-08-21 21:20:38 +00:00
|
|
|
}
|
|
|
|
rgnNodes[sinkid].iDist = 0;
|
2000-08-22 21:18:18 +00:00
|
|
|
rgnNodes[sinkid].iPrev = MAX_COST;
|
2000-08-21 21:20:38 +00:00
|
|
|
|
2000-08-22 21:18:18 +00:00
|
|
|
queue = gst_type_enqueue(queue, sinkid, 0, MAX_COST);
|
2000-08-21 21:20:38 +00:00
|
|
|
|
|
|
|
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);
|
2000-08-22 21:18:18 +00:00
|
|
|
if (iCost != MAX_COST) {
|
|
|
|
if((MAX_COST == rgnNodes[i].iDist) ||
|
2000-08-21 21:20:38 +00:00
|
|
|
(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);
|
|
|
|
}
|
|
|
|
|
2000-01-30 09:03:00 +00:00
|
|
|
GList *gst_type_get_list() {
|
|
|
|
return _gst_types;
|
|
|
|
}
|
2000-08-28 20:20:55 +00:00
|
|
|
|
|
|
|
xmlNodePtr gst_type_save_thyself(GstType *type, xmlNodePtr parent) {
|
|
|
|
xmlNodePtr tree;
|
|
|
|
|
|
|
|
tree = xmlNewChild(parent, NULL, "type", NULL);
|
|
|
|
|
|
|
|
xmlNewChild(tree, NULL, "mime", type->mime);
|
|
|
|
|
|
|
|
return tree;
|
|
|
|
}
|
|
|
|
|
|
|
|
guint16 gst_type_load_thyself(xmlNodePtr parent) {
|
|
|
|
xmlNodePtr children = parent->childs;
|
|
|
|
guint16 typeid = 0;
|
|
|
|
|
|
|
|
while (children) {
|
|
|
|
if (!strcmp(children->name, "type")) {
|
|
|
|
xmlNodePtr field = children->childs;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
children = children->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return typeid;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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->typefindfunc = 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|