gst/autoplug/: Die, spider, die.

Original commit message from CVS:
* gst/autoplug/.cvsignore:
* gst/autoplug/Makefile.am:
* gst/autoplug/gstsearchfuncs.c:
* gst/autoplug/gstsearchfuncs.h:
* gst/autoplug/gstspider.c:
* gst/autoplug/gstspider.h:
* gst/autoplug/gstspideridentity.c:
* gst/autoplug/gstspideridentity.h:
* gst/autoplug/spidertest.c:
Die, spider, die.
This commit is contained in:
Ronald S. Bultje 2005-04-25 09:51:06 +00:00
parent f494f49acd
commit 778981a34e
10 changed files with 13 additions and 2204 deletions

View file

@ -1,3 +1,16 @@
2005-04-25 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* gst/autoplug/.cvsignore:
* gst/autoplug/Makefile.am:
* gst/autoplug/gstsearchfuncs.c:
* gst/autoplug/gstsearchfuncs.h:
* gst/autoplug/gstspider.c:
* gst/autoplug/gstspider.h:
* gst/autoplug/gstspideridentity.c:
* gst/autoplug/gstspideridentity.h:
* gst/autoplug/spidertest.c:
Die, spider, die.
2005-04-25 Wim Taymans <wim@fluendo.com>
* gst/gstpad.c: (gst_pad_set_active), (gst_pad_peer_set_active),

View file

@ -1,6 +0,0 @@
autoplugtest
spidertest
*.bb
*.bbg
*.da
*.def

View file

@ -1,18 +0,0 @@
plugin_LTLIBRARIES = libgstspider.la
libgstspider_la_SOURCES = \
gstspider.c gstspideridentity.c \
gstsearchfuncs.c
libgstspider_la_CFLAGS = $(GST_OBJ_CFLAGS)
libgstspider_la_LIBADD = $(GST_OBJ_LIBS)
libgstspider_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
noinst_HEADERS = gstspider.h gstspideridentity.h gstsearchfuncs.h
noinst_PROGRAMS = spidertest
spidertest_SOURCES = spidertest.c
spidertest_CFLAGS = $(GST_OBJ_CFLAGS)
spidertest_LDADD = $(GST_OBJ_LIBS)

View file

@ -1,440 +0,0 @@
/* GStreamer
* Copyright (C) 1999-2002 Erik Walthinsen <omega@cse.ogi.edu>
* 2000-2002 Wim Taymans <wtay@chello.be>
*
* gstsearchfuncs.c: functions needed when doing searches while
* autoplugging
*
* 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.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gstsearchfuncs.h"
/* FIXME: "evil hack" alarm, we need a better way to get a category in here */
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_AUTOPLUG_ATTEMPT);
#define GST_CAT_DEFAULT GST_CAT_AUTOPLUG_ATTEMPT
/* function that really misses in GLib
* though the GLib version should take a function as argument...
*/
static void
g_list_free_list_and_elements (GList * list)
{
GList *walk = list;
while (walk) {
g_free (walk->data);
walk = g_list_next (walk);
}
g_list_free (list);
}
/**
* gst_autoplug_caps_intersect:
* @src: a source #GstCaps
* @sink: the sink #GstCaps
*
* Checks if the given caps have a non-null intersection.
*
* Returns: TRUE, if both caps intersect.
*/
gboolean
gst_autoplug_caps_intersect (const GstCaps * src, const GstCaps * sink)
{
GstCaps *caps;
/* get an intersection */
caps = gst_caps_intersect (src, sink);
/* if the caps can't link, there is no intersection */
if (gst_caps_is_empty (caps)) {
gst_caps_unref (caps);
return FALSE;
}
/* hurrah, we can link, now remove the intersection */
gst_caps_unref (caps);
return TRUE;
}
/**
* gst_autoplug_can_connect_src:
* @fac: factory to connect to
* @src: caps to check
*
* Checks if a factory's sink can connect to the given caps
*
* Returns: #GstPadTemplate that can connect to the given caps
*/
GstPadTemplate *
gst_autoplug_can_connect_src (GstElementFactory * fac, const GstCaps * src)
{
GList *templs;
templs = fac->padtemplates;
while (templs) {
if ((GST_PAD_TEMPLATE_DIRECTION (templs->data) == GST_PAD_SINK) &&
gst_autoplug_caps_intersect (src,
GST_PAD_TEMPLATE_CAPS (templs->data))) {
return GST_PAD_TEMPLATE (templs->data);
}
templs = g_list_next (templs);
}
return NULL;
}
/**
* gst_autoplug_can_connect_sink:
* @fac: factory to connect to
* @sink: caps to check
*
* Checks if a factory's src can connect to the given caps
*
* Returns: #GstPadTemplate that can connect to the given caps
*/
GstPadTemplate *
gst_autoplug_can_connect_sink (GstElementFactory * fac, const GstCaps * sink)
{
GList *templs;
templs = fac->padtemplates;
while (templs) {
GstCaps *caps = GST_PAD_TEMPLATE_CAPS (templs->data);
if ((GST_PAD_TEMPLATE_DIRECTION (templs->data) == GST_PAD_SRC) &&
gst_autoplug_caps_intersect (caps, sink)) {
return GST_PAD_TEMPLATE (templs->data);
}
templs = g_list_next (templs);
}
return NULL;
}
GstPadTemplate *
gst_autoplug_can_match (GstElementFactory * src, GstElementFactory * dest)
{
GList *srctemps, *desttemps;
srctemps = src->padtemplates;
while (srctemps) {
GstPadTemplate *srctemp = (GstPadTemplate *) srctemps->data;
desttemps = dest->padtemplates;
while (desttemps) {
GstPadTemplate *desttemp = (GstPadTemplate *) desttemps->data;
if (srctemp->direction == GST_PAD_SRC &&
desttemp->direction == GST_PAD_SINK) {
if (gst_autoplug_caps_intersect (gst_pad_template_get_caps (srctemp),
gst_pad_template_get_caps (desttemp))) {
GST_DEBUG ("factory \"%s\" can connect with factory \"%s\"",
GST_OBJECT_NAME (src), GST_OBJECT_NAME (dest));
return desttemp;
}
}
desttemps = g_list_next (desttemps);
}
srctemps = g_list_next (srctemps);
}
GST_DEBUG ("factory \"%s\" cannot connect with factory \"%s\"",
GST_OBJECT_NAME (src), GST_OBJECT_NAME (dest));
return NULL;
}
/* returns TRUE if the factory has padtemplates with the specified direction */
gboolean
gst_autoplug_factory_has_direction (GstElementFactory * fac,
GstPadDirection dir)
{
GList *templs = fac->padtemplates;
while (templs) {
if (GST_PAD_TEMPLATE_DIRECTION (templs->data) == dir) {
return TRUE;
}
templs = g_list_next (templs);
}
return FALSE;
}
/* Decisions are based on the padtemplates.
* These functions return a new list so be sure to free it.
*/
GList *
gst_autoplug_factories_sinks (GList * factories)
{
GList *ret = NULL;
while (factories) {
if (gst_autoplug_factory_has_sink (factories->data))
ret = g_list_prepend (ret, factories->data);
factories = g_list_next (factories);
}
return ret;
}
GList *
gst_autoplug_factories_srcs (GList * factories)
{
GList *ret = NULL;
while (factories) {
if (gst_autoplug_factory_has_src (factories->data))
ret = g_list_prepend (ret, factories->data);
factories = g_list_next (factories);
}
return ret;
}
GList *
gst_autoplug_factories_filters (GList * factories)
{
GList *ret = NULL;
while (factories) {
/* if you want it faster do src/sink check at once, don't call two functions */
if (gst_autoplug_factory_has_src (factories->data)
&& gst_autoplug_factory_has_sink (factories->data))
ret = g_list_prepend (ret, factories->data);
factories = g_list_next (factories);
}
return ret;
}
static gint
gst_autoplug_rank_compare (const GstElementFactory * a,
const GstElementFactory * b)
{
if (GST_PLUGIN_FEATURE (a)->rank > GST_PLUGIN_FEATURE (b)->rank)
return -1;
return (GST_PLUGIN_FEATURE (a)->rank < GST_PLUGIN_FEATURE (b)->rank) ? 1 : 0;
}
/* returns all factories which have sinks with non-NULL caps and srcs with
* any caps. also only returns factories with a non-zero rank, and sorts by
* rank descending.
*/
GList *
gst_autoplug_factories_filters_with_sink_caps (GList * factories)
{
GList *ret = NULL;
GstElementFactory *factory;
GList *templs;
while (factories) {
factory = (GstElementFactory *) factories->data;
templs = factory->padtemplates;
if (GST_PLUGIN_FEATURE (factory)->rank > 0) {
gboolean have_src = FALSE;
gboolean have_sink = FALSE;
while (templs) {
if (GST_PAD_TEMPLATE_DIRECTION (templs->data) == GST_PAD_SRC) {
have_src = TRUE;
}
if ((GST_PAD_TEMPLATE_DIRECTION (templs->data) == GST_PAD_SINK)
&& (GST_PAD_TEMPLATE_CAPS (templs->data) != NULL)) {
have_sink = TRUE;
}
if (have_src && have_sink) {
ret = g_list_prepend (ret, factory);
break;
}
templs = g_list_next (templs);
}
}
factories = g_list_next (factories);
}
return g_list_sort (ret, (GCompareFunc) gst_autoplug_rank_compare);
}
/* returns all factories which have a maximum of maxtemplates GstPadTemplates in direction dir
*/
GList *
gst_autoplug_factories_at_most_templates (GList * factories,
GstPadDirection dir, guint maxtemplates)
{
GList *ret = NULL;
while (factories) {
guint count = 0;
GList *templs = ((GstElementFactory *) factories->data)->padtemplates;
while (templs) {
if (GST_PAD_TEMPLATE_DIRECTION (templs->data) == dir) {
count++;
}
if (count > maxtemplates)
break;
templs = g_list_next (templs);
}
if (count <= maxtemplates)
ret = g_list_prepend (ret, factories->data);
factories = g_list_next (factories);
}
return ret;
}
/*********************************************************************
*
* SHORTEST PATH ALGORITHM
*/
/**
* gst_autoplug_sp:
* @src_caps: a #GstCaps to plug from.
* @sink_caps: the #GstCaps to plug to.
* @factories: a #GList containing all allowed #GstElementFactory entries.
*
* Finds the shortest path of elements that together make up a possible
* connection between the source and sink caps.
*
* Returns: a #GList of #GstElementFactory items which have to be connected
* to get the shortest path.
*/
GList *
gst_autoplug_sp (const GstCaps * srccaps, const GstCaps * sinkcaps,
GList * factories)
{
GList *factory_nodes = NULL;
guint curcost = GST_AUTOPLUG_MAX_COST; /* below this cost, there is no path */
GstAutoplugNode *bestnode = NULL; /* best (unconnected) endpoint currently */
g_return_val_if_fail (srccaps != NULL, NULL);
g_return_val_if_fail (sinkcaps != NULL, NULL);
GST_INFO ("attempting to autoplug via shortest path from %"
GST_PTR_FORMAT " to %" GST_PTR_FORMAT, srccaps, sinkcaps);
/* wrap all factories as GstAutoplugNode
* initialize the cost */
while (factories) {
GstAutoplugNode *node = g_new0 (GstAutoplugNode, 1);
node->prev = NULL;
node->fac = (GstElementFactory *) factories->data;
GST_DEBUG ("trying with %s", node->fac->details.longname);
node->templ = gst_autoplug_can_connect_src (node->fac, srccaps);
node->cost = (node->templ ? gst_autoplug_get_cost (node->fac)
: GST_AUTOPLUG_MAX_COST);
node->endpoint = gst_autoplug_can_connect_sink (node->fac, sinkcaps);
if (node->templ && node->endpoint)
GST_DEBUG ("%s makes connection possible", node->fac->details.longname);
else
GST_DEBUG ("direct connection with %s not possible",
node->fac->details.longname);
if ((node->endpoint != NULL) &&
((bestnode == NULL) || (node->cost < bestnode->cost))) {
bestnode = node;
}
factory_nodes = g_list_prepend (factory_nodes, node);
/* make curcost the minimum cost of any plugin */
curcost = node->cost < curcost ? node->cost : curcost;
factories = g_list_next (factories);
}
/* check if we even have possible endpoints */
if (bestnode == NULL) {
GST_DEBUG ("no factory found that could connect to sink caps");
g_list_free_list_and_elements (factory_nodes);
return NULL;
}
/* iterate until we found the best path */
while (curcost < GST_AUTOPLUG_MAX_COST) {
GList *nodes = factory_nodes;
guint nextcost = GST_AUTOPLUG_MAX_COST; /* next cost to check */
GST_DEBUG ("iterating at current cost %d, bestnode %s at %d", curcost,
GST_OBJECT_NAME (bestnode->fac), bestnode->cost);
/* check if we already have a valid best connection to the sink */
if (bestnode->cost <= curcost) {
GList *ret;
GST_DEBUG ("found a way to connect via %s",
GST_OBJECT_NAME ((GstObject *) bestnode->fac));
/* enter all factories into the return list */
ret = g_list_prepend (NULL, bestnode->fac);
bestnode = bestnode->prev;
while (bestnode != NULL) {
ret = g_list_prepend (ret, bestnode->fac);
bestnode = bestnode->prev;
}
g_list_free_list_and_elements (factory_nodes);
return ret;
}
/* iterate over all factories we have
* if they have the current cost, calculate if this
* factory supplies shorter paths to other elements
*/
while (nodes) {
if (((GstAutoplugNode *) nodes->data)->cost == curcost) {
/* now check all elements if we got a shorter path */
GList *sinknodes = factory_nodes;
GstAutoplugNode *srcnode = (GstAutoplugNode *) nodes->data;
while (sinknodes) {
GstAutoplugNode *sinknode = (GstAutoplugNode *) sinknodes->data;
GstPadTemplate *templ;
if ((sinknode->cost >
srcnode->cost + gst_autoplug_get_cost (sinknode->fac))
&& (templ = gst_autoplug_can_match (srcnode->fac, sinknode->fac))) {
/* we got a shorter path
* now enter that path to that node */
sinknode->prev = srcnode;
sinknode->templ = templ;
sinknode->cost =
srcnode->cost + gst_autoplug_get_cost (sinknode->fac);
/* make sure to set which cost to view next */
nextcost = (nextcost > sinknode->cost) ? sinknode->cost : nextcost;
/* did we get a new best node? */
if (sinknode->endpoint && (sinknode->cost < bestnode->cost)) {
bestnode = sinknode;
}
}
sinknodes = g_list_next (sinknodes);
}
/* FIXME: for speed remove the item we just iterated with from the factory_nodes
* but don't free it yet and don't forget to free it.
*/
}
nodes = g_list_next (nodes);
}
curcost = nextcost;
}
GST_DEBUG ("found no path from source caps to sink caps");
g_list_free_list_and_elements (factory_nodes);
return NULL;
}

View file

@ -1,64 +0,0 @@
/* GStreamer
* Copyright (C) 1999-2002 Erik Walthinsen <omega@cse.ogi.edu>
* 2000-2002 Wim Taymans <wtay@chello.be>
*
* gstsearchfuncs.h: Header for gstsearchfuncs.c
*
* 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.
*/
#ifndef __GST_SEARCHFUNCS_H__
#define __GST_SEARCHFUNCS_H__
#include <gst/gst.h>
/* placeholder for maximum cost when plugging */
#define GST_AUTOPLUG_MAX_COST 999999
/* struct for a node, in the search tree */
typedef struct _GstAutoplugNode GstAutoplugNode;
struct _GstAutoplugNode {
GstAutoplugNode *prev; /* previous node */
GstElementFactory *fac; /* factory of element to connect to */
GstPadTemplate *templ; /* template which can connect */
guint cost; /* total cost to get here */
GstPadTemplate *endpoint; /* pad template that can connect to sink caps */
};
/* helper functions */
gboolean gst_autoplug_caps_intersect (const GstCaps *src, const GstCaps *sink);
GstPadTemplate * gst_autoplug_can_connect_src (GstElementFactory *fac, const GstCaps *src);
GstPadTemplate * gst_autoplug_can_connect_sink (GstElementFactory *fac, const GstCaps *sink);
GstPadTemplate * gst_autoplug_can_match (GstElementFactory *src, GstElementFactory *dest);
gboolean gst_autoplug_factory_has_direction (GstElementFactory *fac, GstPadDirection dir);
#define gst_autoplug_factory_has_sink(fac) gst_autoplug_factory_has_direction((fac), GST_PAD_SINK)
#define gst_autoplug_factory_has_src(fac) gst_autoplug_factory_has_direction((fac), GST_PAD_SRC)
/* cost functions */
#define gst_autoplug_get_cost(fac) 1
/* factory selections */
GList * gst_autoplug_factories_sinks (GList *factories);
GList * gst_autoplug_factories_srcs (GList *factories);
GList * gst_autoplug_factories_filters (GList *factories);
GList * gst_autoplug_factories_filters_with_sink_caps(GList *factories);
GList * gst_autoplug_factories_at_most_templates(GList *factories, GstPadDirection dir, guint maxtemplates);
/* shortest path algorithm */
GList * gst_autoplug_sp (const GstCaps *src_caps, const GstCaps *sink_caps, GList *factories);
#endif /* __GST_SEARCHFUNCS_H__ */

View file

@ -1,765 +0,0 @@
/* GStreamer
* Copyright (C) 2002 Erik Walthinsen <omega@cse.ogi.edu>
* 2002 Wim Taymans <wtay@chello.be>
*
* gstspider.c: element to automatically link sinks and sources
*
* 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:
* - handle automatic removal of unneeded elements
* - make the spider handle and send events (esp. new media)
* - decide if we plug pads or elements, currently it's a mess
* - allow unlinking
* - implement proper saving/loading from xml
* - implement a way to allow merging/splitting (aka tee)
* - find ways to define which elements to use when plugging
* - remove pads
* - improve typefinding
* - react to errors inside the pipeline
* - implement more properties, change the current
* - emit signals (most important: "NOT PLUGGABLE")
* - implement something for reporting the state of the spider
* to allow easier debugging.
* (could be useful for bins in general)
* - fix bugs
* ...
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "../gst-i18n-lib.h"
#include "gstspider.h"
#include "gstspideridentity.h"
#include "gstsearchfuncs.h"
static GstElementDetails gst_spider_details = GST_ELEMENT_DETAILS ("Spider",
"Generic",
"Automatically link sinks and sources",
"Benjamin Otte <in7y118@public.uni-hamburg.de>");
GST_DEBUG_CATEGORY (gst_spider_debug);
#define GST_CAT_DEFAULT gst_spider_debug
/* signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
ARG_0,
ARG_FACTORIES,
/* FILL ME TOO */
};
/* generic templates */
static GstStaticPadTemplate spider_sink_factory =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate spider_src_factory =
GST_STATIC_PAD_TEMPLATE ("src_%d",
GST_PAD_SRC,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
/* standard GObject stuff */
static void gst_spider_class_init (GstSpiderClass * klass);
static void gst_spider_init (GstSpider * spider);
static void gst_spider_dispose (GObject * object);
/* element class functions */
static GstPad *gst_spider_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name);
static void gst_spider_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_spider_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
/* link functions */
static GstSpiderConnection *gst_spider_link_new (GstSpiderIdentity * src);
static void gst_spider_link_destroy (GstSpiderConnection * conn);
static void gst_spider_link_reset (GstSpiderConnection * conn, GstElement * to);
static void gst_spider_link_add (GstSpiderConnection * conn,
GstElement * element);
static GstSpiderConnection *gst_spider_link_find (GstSpiderIdentity * src);
static GstSpiderConnection *gst_spider_link_get (GstSpiderIdentity * src);
/* autoplugging functions */
static GstElement *gst_spider_find_element_to_plug (GstElement * src,
GstElementFactory * fac, GstPadDirection dir);
static GstPadLinkReturn gst_spider_plug (GstSpiderConnection * conn);
static GstPadLinkReturn gst_spider_plug_from_srcpad (GstSpiderConnection * conn,
GstPad * srcpad);
/*static GstPadLinkReturn gst_spider_plug_peers (GstSpider *spider, GstPad *srcpad, GstPad *sinkpad); */
static GstPadLinkReturn gst_spider_create_and_plug (GstSpiderConnection * conn,
GList * plugpath);
/* random functions */
static gchar *gst_spider_unused_elementname (GstBin * bin,
const gchar * startwith);
/* debugging stuff
static void print_spider_contents (GstSpider *spider);
static void print_spider_link (GstSpiderConnection *conn); */
/* === variables === */
static GstElementClass *parent_class = NULL;
/* no signals yet
static guint gst_spider_signals[LAST_SIGNAL] = { 0 };*/
/* GObject and GStreamer init functions */
GType
gst_spider_get_type (void)
{
static GType spider_type = 0;
if (!spider_type) {
static const GTypeInfo spider_info = {
sizeof (GstSpiderClass),
NULL,
NULL,
(GClassInitFunc) gst_spider_class_init,
NULL,
NULL,
sizeof (GstSpider),
0,
(GInstanceInitFunc) gst_spider_init,
};
spider_type =
g_type_register_static (GST_TYPE_BIN, "GstSpider", &spider_info, 0);
}
return spider_type;
}
static void
gst_spider_class_init (GstSpiderClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
parent_class = g_type_class_ref (GST_TYPE_BIN);
/* properties */
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FACTORIES,
g_param_spec_pointer ("factories", "allowed factories",
"allowed factories for autoplugging", G_PARAM_READWRITE));
gobject_class->set_property = gst_spider_set_property;
gobject_class->get_property = gst_spider_get_property;
gobject_class->dispose = gst_spider_dispose;
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&spider_sink_factory));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&spider_src_factory));
gst_element_class_set_details (gstelement_class, &gst_spider_details);
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_spider_request_new_pad);
}
static void
gst_spider_init (GstSpider * spider)
{
/* use only elements which have sources and sinks and where the sinks have caps */
/* FIXME: How do we handle factories that are added after the spider was constructed? */
GList *list = gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY);
spider->factories = gst_autoplug_factories_filters_with_sink_caps (list);
g_list_free (list);
spider->links = NULL;
spider->sink_ident = gst_spider_identity_new_sink ("sink_ident");
gst_bin_add (GST_BIN (spider), GST_ELEMENT (spider->sink_ident));
gst_element_add_ghost_pad (GST_ELEMENT (spider), spider->sink_ident->sink,
"sink");
}
static void
gst_spider_dispose (GObject * object)
{
GstSpider *spider;
GList *list;
spider = GST_SPIDER (object);
g_list_free (spider->factories);
spider->factories = NULL;
for (list = spider->links; list; list = list->next) {
GstSpiderConnection *conn = list->data;
g_list_free (conn->path);
g_free (conn);
}
g_list_free (spider->links);
spider->links = NULL;
((GObjectClass *) parent_class)->dispose (object);
}
static GstPad *
gst_spider_request_new_pad (GstElement * element, GstPadTemplate * templ,
const gchar * name)
{
GstPad *returnpad;
gchar *padname;
GstSpiderIdentity *identity;
GstSpider *spider;
g_return_val_if_fail (templ != NULL, NULL);
g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
g_return_val_if_fail (GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC,
NULL);
spider = GST_SPIDER (element);
/* create an identity object, so we have a pad */
padname = gst_spider_unused_elementname ((GstBin *) spider, "src_");
identity = gst_spider_identity_new_src (padname);
returnpad = identity->src;
/* FIXME: use the requested name for the pad */
gst_object_replace ((GstObject **) & returnpad->padtemplate,
(GstObject *) templ);
gst_bin_add (GST_BIN (element), GST_ELEMENT (identity));
returnpad = gst_element_add_ghost_pad (element, returnpad, padname);
g_free (padname);
gst_spider_link_new (identity);
GST_DEBUG ("successfully created requested pad %s:%s",
GST_DEBUG_PAD_NAME (returnpad));
return returnpad;
}
static void
gst_spider_set_property (GObject * object, guint prop_id, const GValue * value,
GParamSpec * pspec)
{
GstSpider *spider;
GList *list;
/* it's not null if we got it, but it might not be ours */
g_return_if_fail (GST_IS_SPIDER (object));
spider = GST_SPIDER (object);
switch (prop_id) {
case ARG_FACTORIES:
list = (GList *) g_value_get_pointer (value);
for (; list; list = list->next) {
g_return_if_fail (list->data != NULL);
g_return_if_fail (GST_IS_ELEMENT_FACTORY (list->data));
}
g_list_free (spider->factories);
spider->factories = (GList *) g_value_get_pointer (value);
break;
default:
break;
}
}
static void
gst_spider_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstSpider *spider;
/* it's not null if we got it, but it might not be ours */
spider = GST_SPIDER (object);
switch (prop_id) {
case ARG_FACTORIES:
g_value_set_pointer (value, spider->factories);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/* get a name for an element that isn't used yet */
static gchar *
gst_spider_unused_elementname (GstBin * bin, const gchar * startwith)
{
gchar *name = g_strdup_printf ("%s%d", startwith, 0);
guint i;
for (i = 0; gst_bin_get_by_name (bin, name) != NULL;) {
g_free (name);
name = g_strdup_printf ("%s%d", startwith, ++i);
}
return name;
}
static void
gst_spider_link_sometimes (GstElement * src, GstPad * pad,
GstSpiderConnection * conn)
{
gulong signal_id = conn->signal_id;
GST_INFO ("plugging from new sometimes pad %s:%s", GST_DEBUG_PAD_NAME (pad));
/* try to autoplug the elements */
if (gst_spider_plug_from_srcpad (conn, pad) != GST_PAD_LINK_REFUSED) {
GST_DEBUG ("%s:%s was autoplugged to %s:%s, removing callback",
GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (conn->src->sink));
g_signal_handler_disconnect (src, signal_id);
signal_id = 0;
}
}
/* create a new link from those two elements */
static GstSpiderConnection *
gst_spider_link_new (GstSpiderIdentity * src)
{
GstSpider *spider = GST_SPIDER (GST_OBJECT_PARENT (src));
GstSpiderConnection *conn = g_new0 (GstSpiderConnection, 1);
conn->src = src;
conn->path = NULL;
conn->current = (GstElement *) spider->sink_ident;
spider->links = g_list_prepend (spider->links, conn);
return conn;
}
static void
gst_spider_link_destroy (GstSpiderConnection * conn)
{
GstSpider *spider = GST_SPIDER (GST_OBJECT_PARENT (conn->src));
/* reset link to unplugged */
gst_spider_link_reset (conn, (GstElement *) spider->sink_ident);
g_free (conn);
}
static void
gst_spider_link_reset (GstSpiderConnection * conn, GstElement * to)
{
GstSpider *spider = GST_SPIDER (GST_OBJECT_PARENT (conn->src));
GST_DEBUG ("resetting link from %s to %s, currently at %s to %s",
GST_ELEMENT_NAME (spider->sink_ident), GST_ELEMENT_NAME (conn->src),
GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (to));
while ((conn->path != NULL) && ((GstElement *) conn->path->data != to)) {
gst_object_unref ((GstObject *) conn->path->data);
conn->path = g_list_delete_link (conn->path, conn->path);
}
if (conn->path == NULL) {
conn->current = (GstElement *) spider->sink_ident;
} else {
conn->current = to;
}
}
/* add an element to the link */
static void
gst_spider_link_add (GstSpiderConnection * conn, GstElement * element)
{
conn->path = g_list_prepend (conn->path, element);
conn->current = element;
}
/* find the link from those two elements */
static GstSpiderConnection *
gst_spider_link_find (GstSpiderIdentity * src)
{
GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (src);
GList *list;
for (list = spider->links; list; list = list->next) {
GstSpiderConnection *conn = (GstSpiderConnection *) list->data;
if (conn->src == src) {
return conn;
}
}
return NULL;
}
/* get a new link from those two elements
* search first; if none is found, create a new one */
static GstSpiderConnection *
gst_spider_link_get (GstSpiderIdentity * src)
{
GstSpiderConnection *ret;
if ((ret = gst_spider_link_find (src)) != NULL) {
return ret;
}
return gst_spider_link_new (src);
}
void
gst_spider_identity_plug (GstSpiderIdentity * ident)
{
GstSpider *spider;
const GList *padlist;
GstPadDirection dir;
GstSpiderConnection *conn;
/* checks */
g_return_if_fail (ident != NULL);
g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
spider = GST_SPIDER (GST_ELEMENT_PARENT (ident));
g_assert (spider != NULL);
g_assert (GST_IS_SPIDER (spider));
/* return if we're already plugged */
if (ident->plugged)
return;
/* check if there is at least one element factory that can handle the
identity's src caps */
{
GstCaps *src_caps = gst_pad_get_caps (ident->src);
if (!gst_caps_is_empty (src_caps) && !gst_caps_is_any (src_caps)) {
GList *factories;
GstPadTemplate *padtemp;
gboolean found = FALSE;
factories = spider->factories;
while (factories) {
if ((padtemp =
gst_autoplug_can_connect_src (factories->data, src_caps))) {
GST_DEBUG ("can connect src to %s pad template: %" GST_PTR_FORMAT,
GST_PLUGIN_FEATURE_NAME (factories->data),
gst_pad_template_get_caps (padtemp));
found = TRUE;
}
factories = factories->next;
}
if (!found) {
const char *mime;
mime = gst_structure_get_name (gst_caps_get_structure (src_caps, 0));
GST_ELEMENT_ERROR (spider, STREAM, CODEC_NOT_FOUND,
(_("There is no element present to handle the stream's mime type %s."), mime), (NULL));
gst_caps_unref (src_caps);
return;
}
}
gst_caps_unref (src_caps);
}
/* get the direction of our ident */
if (GST_PAD_PEER (ident->sink)) {
if (GST_PAD_PEER (ident->src)) {
/* Hey, the ident is linked on both sides */
g_warning ("Trying to autoplug a linked element. Aborting...");
return;
} else {
dir = GST_PAD_SINK;
}
} else {
if (GST_PAD_PEER (ident->src)) {
dir = GST_PAD_SRC;
} else {
/* the ident isn't linked on either side */
g_warning ("Trying to autoplug an unlinked element. Aborting...");
return;
}
}
/* now iterate all possible pads and link when needed */
padlist = GST_ELEMENT (spider)->pads;
for (; padlist; padlist = padlist->next) {
GstPad *otherpad;
GstSpiderIdentity *peer;
g_assert (GST_IS_PAD (padlist->data));
otherpad = (GstPad *) GST_GPAD_REALPAD (padlist->data);
peer = (GstSpiderIdentity *) GST_PAD_PARENT (otherpad);
/* we only want to link to the other side */
if (dir != GST_PAD_DIRECTION (otherpad)) {
/* we only link to plugged in elements */
if (peer->plugged == TRUE) {
/* plug in the right direction */
if (dir == GST_PAD_SINK) {
conn = gst_spider_link_get (peer);
} else {
conn = gst_spider_link_get (ident);
}
if ((GstElement *) spider->sink_ident == conn->current) {
gst_spider_plug (conn);
}
}
}
}
ident->plugged = TRUE;
}
void
gst_spider_identity_unplug (GstSpiderIdentity * ident)
{
GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (ident);
GList *list;
for (list = spider->links; list; list = list->next) {
GstSpiderConnection *conn = list->data;
if (conn->src == ident) {
g_list_delete_link (spider->links, list);
gst_spider_link_destroy (conn);
}
}
ident->plugged = FALSE;
}
/* links src to sink using the elementfactories in plugpath
* plugpath will be removed afterwards */
static GstPadLinkReturn
gst_spider_create_and_plug (GstSpiderConnection * conn, GList * plugpath)
{
GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (conn->src);
GList *endelements = NULL, *templist = NULL;
GstElement *element;
/* exit if plugging is already done */
if ((GstElement *) conn->src == conn->current)
return GST_PAD_LINK_DONE;
/* try to shorten the list at the end and not duplicate link code */
if (plugpath != NULL) {
templist = g_list_last (plugpath);
element = (GstElement *) conn->src;
while ((plugpath != NULL)
&& (element =
gst_spider_find_element_to_plug (element,
(GstElementFactory *) plugpath->data, GST_PAD_SINK))) {
GList *cur = templist;
endelements = g_list_prepend (endelements, element);
templist = g_list_previous (templist);
g_list_delete_link (cur, cur);
}
}
/* do the linking */
while (conn->current != (GstElement *) (endelements ==
NULL ? conn->src : endelements->data)) {
/* get sink element to plug, src is conn->current */
if (plugpath == NULL) {
element =
(GstElement *) (endelements == NULL ? conn->src : endelements->data);
} else {
element =
gst_element_factory_create ((GstElementFactory *) plugpath->data,
NULL);
GST_DEBUG
("Adding element %s of type %s and syncing state with autoplugger",
GST_ELEMENT_NAME (element), GST_PLUGIN_FEATURE_NAME (plugpath->data));
gst_bin_add (GST_BIN (spider), element);
}
/* insert and link new element */
if (gst_element_link (conn->current, element)) {
gst_element_sync_state_with_parent (element);
} else {
/* check if the src has SOMETIMES templates. If so, link a callback */
GList *templs = gst_element_get_pad_template_list (conn->current);
/* remove element that couldn't be linked, if it wasn't the endpoint */
if (element != (GstElement *) conn->src)
gst_bin_remove (GST_BIN (spider), element);
for (; templs; templs = templs->next) {
GstPadTemplate *templ = (GstPadTemplate *) templs->data;
if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC)
&& (GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_SOMETIMES)) {
GST_DEBUG ("adding callback to link element %s to %s",
GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
conn->signal_id =
g_signal_connect (G_OBJECT (conn->current), "new_pad",
G_CALLBACK (gst_spider_link_sometimes), conn);
g_list_free (plugpath);
return GST_PAD_LINK_DELAYED;
}
}
GST_DEBUG ("no chance to link element %s to %s",
GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
g_list_free (plugpath);
return GST_PAD_LINK_REFUSED;
}
GST_DEBUG ("coupling %s and %s",
GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (conn->current));
gst_spider_link_add (conn, element);
if (plugpath != NULL)
plugpath = g_list_delete_link (plugpath, plugpath);
}
/* ref all elements at the end */
while (endelements) {
gst_spider_link_add (conn, endelements->data);
endelements = g_list_delete_link (endelements, endelements);
}
return GST_PAD_LINK_DONE;
}
/* checks, if src is already linked to an element from factory fac on direction dir */
static GstElement *
gst_spider_find_element_to_plug (GstElement * src, GstElementFactory * fac,
GstPadDirection dir)
{
GList *padlist = GST_ELEMENT_PADS (src);
for (; padlist; padlist = padlist->next) {
GstPad *pad = (GstPad *) GST_PAD_REALIZE (padlist->data);
/* is the pad on the right side and is it linked? */
if ((GST_PAD_DIRECTION (pad) == dir)
&& (pad = (GstPad *) (GST_RPAD_PEER (pad)))) {
/* is the element the pad is linked to of the right type? */
GstElement *element = GST_PAD_PARENT (pad);
if (G_TYPE_FROM_INSTANCE (element) ==
gst_element_factory_get_element_type (fac)) {
return element;
}
}
}
return NULL;
}
/* try to establish the link */
static GstPadLinkReturn
gst_spider_plug (GstSpiderConnection * conn)
{
GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (conn->src);
if ((GstElement *) conn->src == conn->current)
return GST_PAD_LINK_DONE;
if ((GstElement *) spider->sink_ident == conn->current)
return gst_spider_plug_from_srcpad (conn, spider->sink_ident->src);
g_warning
("FIXME: autoplugging only possible from GstSpiderIdentity conn->sink yet (yep, that's technical)\n");
return GST_PAD_LINK_REFUSED;
}
/* try to establish the link using this pad */
static GstPadLinkReturn
gst_spider_plug_from_srcpad (GstSpiderConnection * conn, GstPad * srcpad)
{
GstElement *element;
GList *plugpath;
gboolean result = TRUE;
GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (conn->src);
GstElement *startelement = conn->current;
GstCaps *caps1;
GstCaps *caps2;
g_assert ((GstElement *) GST_OBJECT_PARENT (srcpad) == conn->current);
GST_DEBUG ("trying to plug from %s:%s to %s",
GST_DEBUG_PAD_NAME (srcpad), GST_ELEMENT_NAME (conn->src));
/* see if they match already */
if (gst_pad_link (srcpad, conn->src->sink)) {
GST_DEBUG ("%s:%s and %s:%s can link directly",
GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (conn->src->sink));
gst_pad_unlink (srcpad, conn->src->sink);
gst_spider_create_and_plug (conn, NULL);
return GST_PAD_LINK_OK;
}
/* find a path from src to sink */
caps1 = gst_pad_get_caps (srcpad);
caps2 = gst_pad_get_caps (conn->src->sink);
plugpath = gst_autoplug_sp (caps1, caps2, spider->factories);
gst_caps_unref (caps1);
gst_caps_unref (caps2);
/* prints out the path that was found for plugging */
/* g_print ("found path from %s to %s:\n", GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
templist = plugpath;
while (templist)
{
g_print("%s\n", GST_OBJECT_NAME (templist->data));
templist = g_list_next (templist);
} */
/* if there is no way to plug: return */
if (plugpath == NULL) {
GST_DEBUG ("no chance to plug from %s to %s",
GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src));
return GST_PAD_LINK_REFUSED;
}
GST_DEBUG ("found a link that needs %d elements", g_list_length (plugpath));
/* now remove non-needed elements from the beginning of the path
* alter src to point to the new element where we need to start
* plugging and alter the plugpath to represent the elements, that must be plugged
*/
element = conn->current;
while ((plugpath != NULL)
&& (element =
gst_spider_find_element_to_plug (element,
(GstElementFactory *) plugpath->data, GST_PAD_SRC))) {
gst_spider_link_add (conn, element);
plugpath = g_list_delete_link (plugpath, plugpath);
}
GST_DEBUG ("%d elements must be inserted to establish the link",
g_list_length (plugpath));
/* create the elements and plug them */
result = gst_spider_create_and_plug (conn, plugpath);
/* reset the "current" element */
if (result == GST_PAD_LINK_REFUSED) {
gst_spider_link_reset (conn, startelement);
}
return result;
}
static gboolean
plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (gst_spider_debug, "spider", 0,
"spider autoplugging element");
if (!gst_element_register (plugin, "spider", GST_RANK_NONE, GST_TYPE_SPIDER))
return FALSE;
if (!gst_element_register (plugin, "spideridentity", GST_RANK_NONE,
GST_TYPE_SPIDER_IDENTITY))
return FALSE;
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"gstspider",
"a 1:n autoplugger",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)

View file

@ -1,93 +0,0 @@
/* GStreamer
* Copyright (C) 2002 Erik Walthinsen <omega@cse.ogi.edu>
* 2002 Wim Taymans <wtay@chello.be>
*
* gstspider.h: Header for GstSpider object
*
* 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.
*/
#ifndef __GST_SPIDER_H__
#define __GST_SPIDER_H__
#include <gst/gst.h>
#include "gstspideridentity.h"
G_BEGIN_DECLS
GST_DEBUG_CATEGORY_EXTERN(gst_spider_debug);
/*
* Theory of operation:
* When connecting a sink to a source, GstSpiderConnections are used to keep track
* of the current status of the link. sink -> src is the path we intend to
* plug. current is how far we've come. If current equals
* - NULL, there is no possible path,
* - src, the link is established.
* - sink, it wasn't tried to establish a link.
* - something else, we have come that far while plugging.
* signal_id is used to remember the signal_id when we are waiting for a "new_pad"
* callback during link.
* When a path is established, the elements in the path (excluding sink and src)
* are refcounted once for every path.
* A GstSpider keeps a list of all GstSpiderConnections in it.
*/
typedef struct {
GstSpiderIdentity *src;
/* dunno if the path should stay here or if its too much load.
* it's at least easier then always searching it */
GList *path;
GstElement *current;
gulong signal_id;
} GstSpiderConnection;
#define GST_TYPE_SPIDER \
(gst_spider_get_type())
#define GST_SPIDER(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPIDER,GstSpider))
#define GST_SPIDER_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPIDER,GstSpiderClass))
#define GST_IS_SPIDER(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPIDER))
#define GST_IS_SPIDER_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPIDER))
typedef struct _GstSpider GstSpider;
typedef struct _GstSpiderClass GstSpiderClass;
struct _GstSpider {
GstBin parent;
GstSpiderIdentity *sink_ident;
GList * factories; /* factories to use for plugging */
GList * links; /* GStSpiderConnection list of all links */
};
struct _GstSpiderClass {
GstBinClass parent_class;
};
/* default initialization stuff */
GType gst_spider_get_type (void);
/* private link functions to be called by GstSpiderIdentity */
void gst_spider_identity_plug (GstSpiderIdentity *ident);
void gst_spider_identity_unplug (GstSpiderIdentity *ident);
G_END_DECLS
#endif /* __GST_SPIDER_H__ */

View file

@ -1,608 +0,0 @@
/* GStreamer
* Copyright (C) 2002 Erik Walthinsen <omega@cse.ogi.edu>
* 2002 Wim Taymans <wtay@chello.be>
*
* gstspideridentity.c: identity element for the spider autoplugger
*
* 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.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gstspideridentity.h"
#include "gstspider.h"
GST_DEBUG_CATEGORY_STATIC (gst_spider_identity_debug);
#define GST_CAT_DEFAULT gst_spider_identity_debug
static GstElementDetails gst_spider_identity_details =
GST_ELEMENT_DETAILS ("SpiderIdentity",
"Generic",
"Link between spider and outside elements",
"Benjamin Otte <in7y118@public.uni-hamburg.de>");
/* generic templates
* delete me when meging with spider.c
*/
static GstStaticPadTemplate spider_src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate spider_sink_factory =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
/* SpiderIdentity signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
ARG_0
/* FILL ME */
};
/* GObject stuff */
static void gst_spider_identity_class_init (GstSpiderIdentityClass * klass);
static void gst_spider_identity_init (GstSpiderIdentity * spider_identity);
/* functions set in pads, elements and stuff */
static void gst_spider_identity_chain (GstPad * pad, GstBuffer * buf);
static GstElementStateReturn gst_spider_identity_change_state (GstElement *
element);
static GstPadLinkReturn gst_spider_identity_link (GstPad * pad,
const GstCaps * caps);
static GstCaps *gst_spider_identity_getcaps (GstPad * pad);
/* loop functions */
static void gst_spider_identity_dumb_loop (GstSpiderIdentity * ident);
static void gst_spider_identity_src_loop (GstSpiderIdentity * ident);
static void gst_spider_identity_sink_loop_type_finding (GstSpiderIdentity *
ident);
static gboolean gst_spider_identity_handle_src_event (GstPad * pad,
GstEvent * event);
/* other functions */
static void gst_spider_identity_start_type_finding (GstSpiderIdentity * ident);
static GstElementClass *parent_class = NULL;
/* no signals
static guint gst_spider_identity_signals[LAST_SIGNAL] = { 0 }; */
GType
gst_spider_identity_get_type (void)
{
static GType spider_identity_type = 0;
if (!spider_identity_type) {
static const GTypeInfo spider_identity_info = {
sizeof (GstSpiderIdentityClass), NULL,
NULL,
(GClassInitFunc) gst_spider_identity_class_init,
NULL,
NULL,
sizeof (GstSpiderIdentity),
0,
(GInstanceInitFunc) gst_spider_identity_init,
};
spider_identity_type =
g_type_register_static (GST_TYPE_ELEMENT, "GstSpiderIdentity",
&spider_identity_info, 0);
GST_DEBUG_CATEGORY_INIT (gst_spider_identity_debug, "spideridentity", 0,
"spider autoplugging proxy element");
}
return spider_identity_type;
}
static void
gst_spider_identity_class_init (GstSpiderIdentityClass * klass)
{
GstElementClass *gstelement_class = (GstElementClass *) klass;
parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
/* add our two pad templates */
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&spider_src_factory));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&spider_sink_factory));
gst_element_class_set_details (gstelement_class,
&gst_spider_identity_details);
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_spider_identity_change_state);
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_spider_identity_request_new_pad);
}
static void
gst_spider_identity_init (GstSpiderIdentity * ident)
{
/* sink */
ident->sink =
gst_pad_new_from_template (gst_static_pad_template_get
(&spider_sink_factory), "sink");
gst_element_add_pad (GST_ELEMENT (ident), ident->sink);
gst_pad_set_link_function (ident->sink,
GST_DEBUG_FUNCPTR (gst_spider_identity_link));
gst_pad_set_getcaps_function (ident->sink,
GST_DEBUG_FUNCPTR (gst_spider_identity_getcaps));
/* src */
ident->src =
gst_pad_new_from_template (gst_static_pad_template_get
(&spider_src_factory), "src");
gst_element_add_pad (GST_ELEMENT (ident), ident->src);
gst_pad_set_link_function (ident->src,
GST_DEBUG_FUNCPTR (gst_spider_identity_link));
gst_pad_set_getcaps_function (ident->src,
GST_DEBUG_FUNCPTR (gst_spider_identity_getcaps));
gst_pad_set_event_function (ident->src,
GST_DEBUG_FUNCPTR (gst_spider_identity_handle_src_event));
/* variables */
ident->plugged = FALSE;
}
static void
gst_spider_identity_chain (GstPad * pad, GstBuffer * buf)
{
GstSpiderIdentity *ident;
g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
if (buf == NULL)
return;
ident = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
if (GST_IS_EVENT (buf)) {
GST_DEBUG_OBJECT (ident, "spider identity received event %p", buf);
/* start hack for current event stuff here */
/* check for unlinked elements and send them the EOS event, too */
if (GST_EVENT_TYPE (GST_EVENT (buf)) == GST_EVENT_EOS) {
GstSpider *spider = (GstSpider *) GST_OBJECT_PARENT (ident);
GList *list = spider->links;
while (list) {
GstSpiderConnection *conn = (GstSpiderConnection *) list->data;
list = g_list_next (list);
if (conn->current != (GstElement *) conn->src) {
GstEvent *event;
event = gst_event_new (GST_EVENT_EOS);
GST_DEBUG_OBJECT (ident,
"sending EOS event %p to unconnected element %s from %s",
event, GST_ELEMENT_NAME (conn->src), GST_ELEMENT_NAME (ident));
gst_pad_push (conn->src->src, GST_DATA (event));
gst_element_set_eos (GST_ELEMENT (conn->src));
}
}
}
/* end hack for current event stuff here */
GST_DEBUG_OBJECT (ident,
"calling default handler for event %p on pad %s:%s",
buf, GST_DEBUG_PAD_NAME (pad));
gst_pad_event_default (pad, GST_EVENT (buf));
return;
}
if ((ident->src != NULL) && (GST_PAD_PEER (ident->src) != NULL)) {
GST_LOG_OBJECT (ident, "pushing buffer %p "
"(refcount %d, size %u, offset %" G_GINT64_FORMAT ") ",
buf, GST_BUFFER_REFCOUNT_VALUE (buf),
GST_BUFFER_SIZE (buf), GST_BUFFER_OFFSET (buf));
gst_pad_push (ident->src, GST_DATA (buf));
} else if (GST_IS_BUFFER (buf)) {
gst_buffer_unref (buf);
}
}
GstSpiderIdentity *
gst_spider_identity_new_src (gchar * name)
{
GstSpiderIdentity *ret =
(GstSpiderIdentity *) gst_element_factory_make ("spideridentity", name);
/* set the right functions */
gst_element_set_loop_function (GST_ELEMENT (ret), (GstElementLoopFunction)
GST_DEBUG_FUNCPTR (gst_spider_identity_src_loop));
return ret;
}
GstSpiderIdentity *
gst_spider_identity_new_sink (gchar * name)
{
GstSpiderIdentity *ret =
(GstSpiderIdentity *) gst_element_factory_make ("spideridentity", name);
/* set the right functions */
gst_element_set_loop_function (GST_ELEMENT (ret), (GstElementLoopFunction)
GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop));
return ret;
}
/* shamelessly stolen from gstqueue.c to get proxy links */
static GstPadLinkReturn
gst_spider_identity_link (GstPad * pad, const GstCaps * caps)
{
GstSpiderIdentity *spider_identity =
GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
GstPad *otherpad;
if (pad == spider_identity->src) {
otherpad = spider_identity->sink;
if (GST_PAD_PEER (otherpad) == NULL)
return GST_PAD_LINK_DELAYED;
} else {
otherpad = spider_identity->src;
}
g_return_val_if_fail (otherpad != NULL, GST_PAD_LINK_REFUSED);
return gst_pad_try_set_caps (otherpad, caps);
}
static GstCaps *
gst_spider_identity_getcaps (GstPad * pad)
{
GstSpiderIdentity *ident = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
GstPad *otherpad;
if (pad == ident->src)
otherpad = ident->sink;
else
otherpad = ident->src;
if (otherpad != NULL) {
if (GST_PAD_PEER (otherpad)) {
GstCaps *ret = gst_pad_get_allowed_caps (otherpad);
if (ident->caps) {
GstCaps *ret2 = gst_caps_intersect (ident->caps, ret);
gst_caps_unref (ret);
ret = ret2;
}
return ret;
}
}
if (ident->caps)
return gst_caps_copy (ident->caps);
return gst_caps_new_any ();
}
GstPad *
gst_spider_identity_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name)
{
GstSpiderIdentity *ident;
/*checks */
g_return_val_if_fail (templ != NULL, NULL);
g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
ident = GST_SPIDER_IDENTITY (element);
g_return_val_if_fail (ident != NULL, NULL);
g_return_val_if_fail (GST_IS_SPIDER_IDENTITY (ident), NULL);
switch (GST_PAD_TEMPLATE_DIRECTION (templ)) {
case GST_PAD_SINK:
if (ident->sink != NULL)
break;
/* sink */
GST_DEBUG ("element %s requests new sink pad", GST_ELEMENT_NAME (ident));
ident->sink =
gst_pad_new_from_template (gst_static_pad_template_get
(&spider_sink_factory), "sink");
gst_element_add_pad (GST_ELEMENT (ident), ident->sink);
gst_pad_set_link_function (ident->sink,
GST_DEBUG_FUNCPTR (gst_spider_identity_link));
gst_pad_set_getcaps_function (ident->sink,
GST_DEBUG_FUNCPTR (gst_spider_identity_getcaps));
return ident->sink;
case GST_PAD_SRC:
/* src */
if (ident->src != NULL)
break;
GST_DEBUG ("element %s requests new src pad", GST_ELEMENT_NAME (ident));
ident->src =
gst_pad_new_from_template (gst_static_pad_template_get
(&spider_src_factory), "src");
gst_element_add_pad (GST_ELEMENT (ident), ident->src);
gst_pad_set_link_function (ident->src,
GST_DEBUG_FUNCPTR (gst_spider_identity_link));
gst_pad_set_getcaps_function (ident->src,
GST_DEBUG_FUNCPTR (gst_spider_identity_getcaps));
gst_pad_set_event_function (ident->src,
GST_DEBUG_FUNCPTR (gst_spider_identity_handle_src_event));
return ident->src;
default:
break;
}
GST_DEBUG ("element %s requested a new pad but none could be created",
GST_ELEMENT_NAME (ident));
return NULL;
}
/* this function has to
* - start the autoplugger
* - start type finding
* ...
*/
static GstElementStateReturn
gst_spider_identity_change_state (GstElement * element)
{
GstSpiderIdentity *ident;
GstSpider *spider;
GstElementStateReturn ret = GST_STATE_SUCCESS;
/* element check */
ident = GST_SPIDER_IDENTITY (element);
g_return_val_if_fail (ident != NULL, GST_STATE_FAILURE);
g_return_val_if_fail (GST_IS_SPIDER_IDENTITY (ident), GST_STATE_FAILURE);
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_PAUSED_TO_READY:
gst_caps_replace (&ident->caps, NULL);
break;
case GST_STATE_PAUSED_TO_PLAYING:
/* autoplugger check */
spider = GST_SPIDER (GST_ELEMENT_PARENT (ident));
g_return_val_if_fail (spider != NULL, GST_STATE_FAILURE);
g_return_val_if_fail (GST_IS_SPIDER (spider), GST_STATE_FAILURE);
/* start typefinding or plugging */
if ((GST_RPAD_PEER (ident->sink) != NULL)
&& (GST_RPAD_PEER (ident->src) == NULL)) {
GstCaps *caps =
gst_pad_get_caps ((GstPad *) GST_PAD_PEER (ident->sink));
if (gst_caps_is_any (caps) || gst_caps_is_empty (caps)) {
gst_spider_identity_start_type_finding (ident);
gst_caps_unref (caps);
break;
} else {
gst_spider_identity_plug (ident);
}
gst_caps_unref (caps);
}
/* autoplug on src */
if ((GST_RPAD_PEER (ident->src) != NULL)
&& (GST_RPAD_PEER (ident->sink) == NULL)) {
gst_spider_identity_plug (ident);
}
default:
break;
}
if ((ret != GST_STATE_FAILURE)
&& (GST_ELEMENT_CLASS (parent_class)->change_state))
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
return ret;
}
static void
gst_spider_identity_start_type_finding (GstSpiderIdentity * ident)
{
/* GstElement* typefind;
gchar *name;*/
gboolean restart = FALSE;
GST_DEBUG ("element %s starts typefinding", GST_ELEMENT_NAME (ident));
if (GST_STATE (GST_ELEMENT_PARENT (ident)) == GST_STATE_PLAYING) {
gst_element_set_state (GST_ELEMENT (GST_ELEMENT_PARENT (ident)),
GST_STATE_PAUSED);
restart = TRUE;
}
gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction)
GST_DEBUG_FUNCPTR (gst_spider_identity_sink_loop_type_finding));
if (restart) {
gst_element_set_state (GST_ELEMENT (GST_ELEMENT_PARENT (ident)),
GST_STATE_PLAYING);
}
}
/* since we can't set the loop function to NULL if there's a cothread for us,
* we have to use a dumb one
*/
static void
gst_spider_identity_dumb_loop (GstSpiderIdentity * ident)
{
GstBuffer *buf;
g_return_if_fail (ident != NULL);
g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
g_assert (ident->sink != NULL);
buf = GST_BUFFER (gst_pad_pull (ident->sink));
gst_spider_identity_chain (ident->sink, buf);
}
/* do nothing until we're linked - then disable yourself
*/
static void
gst_spider_identity_src_loop (GstSpiderIdentity * ident)
{
/* checks - disable for speed */
g_return_if_fail (ident != NULL);
g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
/* we don't want a loop function if we're plugged */
if (ident->sink && GST_PAD_PEER (ident->sink)) {
gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction)
GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop));
gst_spider_identity_dumb_loop (ident);
return;
}
gst_element_interrupt (GST_ELEMENT (ident));
}
/* This loop function is only needed when typefinding.
*/
typedef struct
{
GstBuffer *buffer;
guint best_probability;
GstCaps *caps;
}
SpiderTypeFind;
static guint8 *
spider_find_peek (gpointer data, gint64 offset, guint size)
{
SpiderTypeFind *find = (SpiderTypeFind *) data;
gint64 buffer_offset = GST_BUFFER_OFFSET_IS_VALID (find->buffer) ?
GST_BUFFER_OFFSET (find->buffer) : 0;
if (offset >= buffer_offset
&& offset + size <= buffer_offset + GST_BUFFER_SIZE (find->buffer)) {
GST_LOG ("peek %" G_GINT64_FORMAT ", %u successful", offset, size);
return GST_BUFFER_DATA (find->buffer) + offset - buffer_offset;
} else {
GST_LOG ("peek %" G_GINT64_FORMAT ", %u failed", offset, size);
return NULL;
}
}
static void
spider_find_suggest (gpointer data, guint probability, const GstCaps * caps)
{
SpiderTypeFind *find = (SpiderTypeFind *) data;
GST_INFO ("suggest %u, %" GST_PTR_FORMAT, probability, caps);
if (probability > find->best_probability) {
gst_caps_replace (&find->caps, gst_caps_copy (caps));
find->best_probability = probability;
}
}
static void
gst_spider_identity_sink_loop_type_finding (GstSpiderIdentity * ident)
{
GstData *data;
GstTypeFind gst_find;
SpiderTypeFind find;
GList *walk, *type_list = NULL;
g_return_if_fail (GST_IS_SPIDER_IDENTITY (ident));
data = gst_pad_pull (ident->sink);
if (!GST_IS_BUFFER (data)) {
gst_spider_identity_chain (ident->sink, GST_BUFFER (data));
return;
}
find.buffer = GST_BUFFER (data);
/* maybe there are already valid caps now? */
find.caps = gst_pad_get_allowed_caps (ident->sink);
if (!gst_caps_is_empty (find.caps) && !gst_caps_is_any (find.caps)) {
goto plug;
} else {
gst_caps_unref (find.caps);
find.caps = NULL;
}
/* now do the actual typefinding with the supplied buffer */
walk = type_list = gst_type_find_factory_get_list ();
find.best_probability = 0;
find.caps = NULL;
gst_find.data = &find;
gst_find.get_length = NULL;
gst_find.peek = spider_find_peek;
gst_find.suggest = spider_find_suggest;
while (walk) {
GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (walk->data);
GST_DEBUG ("trying typefind function %s",
GST_PLUGIN_FEATURE_NAME (factory));
gst_type_find_factory_call_function (factory, &gst_find);
if (find.best_probability >= GST_TYPE_FIND_MAXIMUM)
goto plug;
walk = g_list_next (walk);
}
if (find.best_probability > 0)
goto plug;
GST_ELEMENT_ERROR (ident, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
find.buffer = GST_BUFFER (gst_event_new (GST_EVENT_EOS));
end:
/* remove loop function */
gst_element_set_loop_function (GST_ELEMENT (ident), (GstElementLoopFunction)
GST_DEBUG_FUNCPTR (gst_spider_identity_dumb_loop));
/* push the buffer */
gst_spider_identity_chain (ident->sink, find.buffer);
return;
plug:
GST_INFO ("typefind function found caps");
ident->caps = find.caps;
if (GST_PAD_IS_LINKED (ident->src)) {
GstPadLinkReturn ret;
ret = gst_pad_try_set_caps (ident->src, find.caps);
if (GST_PAD_LINK_FAILED (ret)) {
g_critical ("could not set caps on spideridentity src pad\n");
}
}
GST_LOG_OBJECT (ident, "spider starting caps: %" GST_PTR_FORMAT, find.caps);
if (type_list)
g_list_free (type_list);
gst_spider_identity_plug (ident);
goto end;
}
static gboolean
gst_spider_identity_handle_src_event (GstPad * pad, GstEvent * event)
{
gboolean res = TRUE;
GstSpiderIdentity *ident;
GST_DEBUG ("spider_identity src_event");
ident = GST_SPIDER_IDENTITY (gst_pad_get_parent (pad));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH:
case GST_EVENT_SEEK:
default:
res = gst_pad_event_default (pad, event);
break;
}
return res;
}

View file

@ -1,72 +0,0 @@
/* GStreamer
* Copyright (C) 2002 Erik Walthinsen <omega@cse.ogi.edu>
* 2002 Wim Taymans <wtay@chello.be>
*
* gstspideridentity.h:
*
* 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.
*/
#ifndef __GST_SPIDER_IDENTITY_H__
#define __GST_SPIDER_IDENTITY_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_SPIDER_IDENTITY \
(gst_spider_identity_get_type())
#define GST_SPIDER_IDENTITY(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPIDER_IDENTITY,GstSpiderIdentity))
#define GST_SPIDER_IDENTITY_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPIDER_IDENTITY,GstSpiderIdentityClass))
#define GST_IS_SPIDER_IDENTITY(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPIDER_IDENTITY))
#define GST_IS_SPIDER_IDENTITY_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPIDER_IDENTITY))
typedef struct _GstSpiderIdentity GstSpiderIdentity;
typedef struct _GstSpiderIdentityClass GstSpiderIdentityClass;
struct _GstSpiderIdentity {
GstElement element;
/* sink and source */
GstPad *sink;
GstPad *src;
/* plugged into autoplugger yet? */
gboolean plugged;
/* Caps from typefinding */
GstCaps *caps;
};
struct _GstSpiderIdentityClass {
GstElementClass parent_class;
};
GType gst_spider_identity_get_type (void);
GstSpiderIdentity* gst_spider_identity_new_sink (gchar *name);
GstSpiderIdentity* gst_spider_identity_new_src (gchar *name);
GstPad* gst_spider_identity_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name);
G_END_DECLS
#endif /* __GST_SPIDER_IDENTITY_H__ */

View file

@ -1,138 +0,0 @@
#include <stdlib.h>
#include <gst/gst.h>
/* returns all factories which have a maximum of maxtemplates GstPadTemplates in direction dir
*/
GList *
gst_factories_at_most_templates (GList * factories, GstPadDirection dir,
guint maxtemplates)
{
GList *ret = NULL;
while (factories) {
guint count = 0;
GList *templs = ((GstElementFactory *) factories->data)->padtemplates;
while (templs) {
if (GST_PAD_TEMPLATE_DIRECTION (templs->data) == dir) {
count++;
}
if (count > maxtemplates)
break;
templs = g_list_next (templs);
}
if (count <= maxtemplates)
ret = g_list_prepend (ret, factories->data);
factories = g_list_next (factories);
}
return ret;
}
static void
property_change_callback (GObject * object, GstObject * orig,
GParamSpec * pspec)
{
GValue value = { 0, }; /* the important thing is that value.type = 0 */
gchar *str = 0;
if (pspec->flags & G_PARAM_READABLE) {
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
g_object_get_property (G_OBJECT (orig), pspec->name, &value);
if (G_IS_PARAM_SPEC_STRING (pspec))
str = g_value_dup_string (&value);
else if (G_IS_PARAM_SPEC_ENUM (pspec))
str = g_strdup_printf ("%d", g_value_get_enum (&value));
else if (G_IS_PARAM_SPEC_INT64 (pspec))
str = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (&value));
else
str = g_strdup_value_contents (&value);
g_print ("%s: %s = %s\n", GST_OBJECT_NAME (orig), pspec->name, str);
g_free (str);
g_value_unset (&value);
} else {
g_warning ("Parameter not readable. What's up with that?");
}
}
static void
error_callback (GObject * object, GstObject * orig, gchar * error)
{
g_print ("ERROR: %s: %s\n", GST_OBJECT_NAME (orig), error);
}
/*
* Test program for the autoplugger.
* Uses new API extensions (2002-01-28), too.
*
* USAGE: spidertest <mediafile>
* If mediafile can be recognized, xvideo and oss audio output are tried.
*/
int
main (int argc, char *argv[])
{
GstElement *bin, *filesrc, *decoder, *osssink, *videosink;
GList *facs;
if (argc < 2) {
g_print ("usage: %s <file>\n", argv[0]);
exit (-1);
}
gst_init (&argc, &argv);
/* create a new bin to hold the elements */
bin = gst_pipeline_new ("pipeline");
g_signal_connect (bin, "deep_notify", G_CALLBACK (property_change_callback),
NULL);
g_signal_connect (bin, "error", G_CALLBACK (error_callback), NULL);
/* create a disk reader */
filesrc = gst_element_factory_make ("filesrc", "disk_source");
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
/* now it's time to get the decoder */
decoder = gst_element_factory_make ("spider", "spider");
if (!decoder) {
g_print ("could not find plugin \"spider\"\n");
exit (-2);
}
/* only use decoding plugins */
g_object_get (decoder, "factories", &facs, NULL);
facs = gst_factories_at_most_templates (facs, GST_PAD_SINK, 1);
g_object_set (decoder, "factories", facs, NULL);
/* create video and audio sink */
osssink = gst_element_factory_make ("osssink", "audio");
videosink = gst_element_factory_make ("xvideosink", "video");
if ((!osssink) || (!videosink)) {
g_print ("could not create output plugins\n");
exit (-3);
}
/* add objects to the main pipeline */
gst_bin_add (GST_BIN (bin), filesrc);
gst_bin_add (GST_BIN (bin), decoder);
gst_bin_add (GST_BIN (bin), osssink);
gst_bin_add (GST_BIN (bin), videosink);
/* link objects */
if (!(gst_element_link (filesrc, decoder) &&
gst_element_link (decoder, osssink) &&
gst_element_link (decoder, videosink))) {
g_print ("the pipeline could not be linked\n");
exit (-4);
}
/* gst_bin_use_clock (GST_BIN (bin), gst_system_clock_obtain ());*/
/* start playing */
gst_element_set_state (bin, GST_STATE_PLAYING);
while (gst_bin_iterate (GST_BIN (bin)));
exit (0);
}