Commit b2ee3857 authored by Wim Taymans's avatar Wim Taymans

Landed the new improved capsnegotiation system.

Original commit message from CVS:
Landed the new improved capsnegotiation system.
The main idea is to keep track of the possible data types that can
pass through a connection. plugins can at any time inspect, adjust and
refine these caps. plugins also get notified when something changes to
the types so that they can reconfigure themselves.
Look at the updated plugins and the soon to be finished doc.
parent 0a0598f9
......@@ -187,10 +187,10 @@ gst_autoplug_pads_autoplug_func (GstElement *src, GstPad *pad, GstElement *sink)
/* if we have a match, connect the pads */
if (gst_pad_get_direction(sinkpad) == GST_PAD_SINK &&
!GST_PAD_CONNECTED (pad) && !GST_PAD_CONNECTED(sinkpad))
!GST_PAD_IS_CONNECTED (pad) && !GST_PAD_IS_CONNECTED(sinkpad))
{
if ((connected = gst_pad_try_connect (pad, sinkpad))) {
if ((connected = gst_pad_connect (pad, sinkpad))) {
break;
}
else {
......
......@@ -133,26 +133,6 @@ gst_identity_get_bufferpool (GstPad *pad)
return gst_pad_get_bufferpool (identity->srcpad);
}
static GstPadNegotiateReturn
gst_identity_negotiate_src (GstPad *pad, GstCaps **caps, gpointer *data)
{
GstIdentity *identity;
identity = GST_IDENTITY (gst_pad_get_parent (pad));
return gst_pad_negotiate_proxy (pad, identity->sinkpad, caps);
}
static GstPadNegotiateReturn
gst_identity_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data)
{
GstIdentity *identity;
identity = GST_IDENTITY (gst_pad_get_parent (pad));
return gst_pad_negotiate_proxy (pad, identity->srcpad, caps);
}
static void
gst_identity_init (GstIdentity *identity)
{
......@@ -160,11 +140,9 @@ gst_identity_init (GstIdentity *identity)
gst_element_add_pad (GST_ELEMENT (identity), identity->sinkpad);
gst_pad_set_chain_function (identity->sinkpad, GST_DEBUG_FUNCPTR (gst_identity_chain));
gst_pad_set_bufferpool_function (identity->sinkpad, gst_identity_get_bufferpool);
gst_pad_set_negotiate_function (identity->sinkpad, gst_identity_negotiate_sink);
identity->srcpad = gst_pad_new ("src", GST_PAD_SRC);
gst_element_add_pad (GST_ELEMENT (identity), identity->srcpad);
gst_pad_set_negotiate_function (identity->srcpad, gst_identity_negotiate_src);
identity->loop_based = FALSE;
identity->sleep_time = 0;
......
......@@ -146,26 +146,6 @@ gst_statistics_get_bufferpool (GstPad *pad)
return gst_pad_get_bufferpool (statistics->srcpad);
}
static GstPadNegotiateReturn
gst_statistics_negotiate_src (GstPad *pad, GstCaps **caps, gpointer *data)
{
GstStatistics *statistics;
statistics = GST_STATISTICS (gst_pad_get_parent (pad));
return gst_pad_negotiate_proxy (pad, statistics->sinkpad, caps);
}
static GstPadNegotiateReturn
gst_statistics_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data)
{
GstStatistics *statistics;
statistics = GST_STATISTICS (gst_pad_get_parent (pad));
return gst_pad_negotiate_proxy (pad, statistics->srcpad, caps);
}
static void
gst_statistics_init (GstStatistics *statistics)
{
......@@ -173,11 +153,9 @@ gst_statistics_init (GstStatistics *statistics)
gst_element_add_pad (GST_ELEMENT (statistics), statistics->sinkpad);
gst_pad_set_chain_function (statistics->sinkpad, GST_DEBUG_FUNCPTR (gst_statistics_chain));
gst_pad_set_bufferpool_function (statistics->sinkpad, GST_DEBUG_FUNCPTR (gst_statistics_get_bufferpool));
gst_pad_set_negotiate_function (statistics->sinkpad, GST_DEBUG_FUNCPTR (gst_statistics_negotiate_sink));
statistics->srcpad = gst_pad_new ("src", GST_PAD_SRC);
gst_element_add_pad (GST_ELEMENT (statistics), statistics->srcpad);
gst_pad_set_negotiate_function (statistics->srcpad, GST_DEBUG_FUNCPTR (gst_statistics_negotiate_src));
statistics->timer = NULL;
statistics->last_timer = NULL;
......
......@@ -66,7 +66,6 @@ static void gst_tee_get_property (GObject *object, guint prop_id,
static void gst_tee_chain (GstPad *pad, GstBuffer *buf);
static GstPadNegotiateReturn gst_tee_handle_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data);
static GstElementClass *parent_class = NULL;
/*static guint gst_tee_signals[LAST_SIGNAL] = { 0 };*/
......@@ -110,20 +109,44 @@ gst_tee_class_init (GstTeeClass *klass)
FALSE, G_PARAM_READWRITE));
gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_tee_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_tee_get_property);
gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR(gst_tee_request_new_pad);
}
static gboolean
gst_tee_sinkconnect (GstPad *pad, GstCaps *caps)
{
GstTee *tee;
GList *pads;
tee = GST_TEE (gst_pad_get_parent (pad));
/* go through all the src pads */
pads = gst_element_get_pad_list (GST_ELEMENT (tee));
while (pads) {
GstPad *outpad = GST_PAD (pads->data);
pads = g_list_next (pads);
if (GST_PAD_DIRECTION (outpad) != GST_PAD_SRC || !GST_PAD_IS_CONNECTED (outpad))
continue;
if (!(gst_pad_try_set_caps (outpad, caps))) {
return FALSE;
}
}
return TRUE;
}
static void
gst_tee_init (GstTee *tee)
{
tee->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
gst_element_add_pad (GST_ELEMENT (tee), tee->sinkpad);
gst_pad_set_chain_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_chain));
gst_pad_set_negotiate_function (tee->sinkpad, GST_DEBUG_FUNCPTR(gst_tee_handle_negotiate_sink));
gst_pad_set_connect_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_sinkconnect));
tee->silent = FALSE;
}
......@@ -152,7 +175,7 @@ gst_tee_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar
GST_PAD_ELEMENT_PRIVATE (srcpad) = NULL;
if (GST_PAD_CAPS (tee->sinkpad)) {
gst_pad_set_caps (srcpad, GST_PAD_CAPS (tee->sinkpad));
gst_pad_try_set_caps (srcpad, GST_PAD_CAPS (tee->sinkpad));
}
return srcpad;
......@@ -253,18 +276,18 @@ gst_tee_chain (GstPad *pad, GstBuffer *buf)
GstEvent *event = GST_EVENT (GST_PAD_ELEMENT_PRIVATE (outpad));
GST_PAD_ELEMENT_PRIVATE (outpad) = NULL;
if (GST_PAD_CONNECTED (outpad))
if (GST_PAD_IS_CONNECTED (outpad))
gst_pad_push (outpad, GST_BUFFER (event));
else
gst_event_free (event);
}
if (!tee->silent) {
gst_element_info (GST_ELEMENT (tee), "chain ******* (%s:%s)t (%d bytes, %llu) \n",
gst_element_info (GST_ELEMENT (tee), "chain ******* (%s:%s)t (%d bytes, %llu)",
GST_DEBUG_PAD_NAME (outpad), GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf));
}
if (GST_PAD_CONNECTED (outpad))
if (GST_PAD_IS_CONNECTED (outpad))
gst_pad_push (outpad, buf);
else
gst_buffer_unref (buf);
......@@ -279,30 +302,3 @@ gst_tee_factory_init (GstElementFactory *factory)
return TRUE;
}
static GstPadNegotiateReturn
gst_tee_handle_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer* data)
{
GstCaps* tempcaps;
gint i;
GstTee* tee = GST_TEE (GST_OBJECT_PARENT (pad));
GList *pads;
if (*caps==NULL)
return GST_PAD_NEGOTIATE_FAIL;
/* go through all the src pads */
pads = gst_element_get_pad_list (GST_ELEMENT (tee));
while (pads) {
GstPad *outpad = GST_PAD (pads->data);
pads = g_list_next (pads);
if (GST_PAD_DIRECTION (outpad) != GST_PAD_SRC || !GST_PAD_CONNECTED (outpad))
continue;
if (!(gst_pad_set_caps (outpad, *caps))) {
return GST_PAD_NEGOTIATE_FAIL;
}
}
return GST_PAD_NEGOTIATE_AGREE;
}
......@@ -75,20 +75,40 @@ get_type_for_mime (const gchar *mime)
GstCaps*
gst_caps_new (const gchar *name, const gchar *mime, GstProps *props)
{
GstCaps *caps;
g_return_val_if_fail (mime != NULL, NULL);
return gst_caps_new_id (name, get_type_for_mime (mime), props);
}
/**
* gst_caps_new_id:
* @name: the name of this capability
* @id: the id of the mime type
* @props: the properties to add to this capability
*
* Create a new capability with the given mime typeid and properties.
*
* Returns: a new capability
*/
GstCaps*
gst_caps_new_id (const gchar *name, const guint16 id, GstProps *props)
{
GstCaps *caps;
g_mutex_lock (_gst_caps_chunk_lock);
caps = g_mem_chunk_alloc (_gst_caps_chunk);
g_mutex_unlock (_gst_caps_chunk_lock);
caps->name = g_strdup (name);
caps->id = get_type_for_mime (mime);
caps->id = id;
caps->properties = props;
caps->next = NULL;
caps->refcount = 1;
caps->lock = g_mutex_new ();
if (props)
caps->fixed = props->fixed;
else
caps->fixed = TRUE;
return caps;
}
......@@ -105,18 +125,43 @@ gst_caps_destroy (GstCaps *caps)
{
GstCaps *next;
g_return_if_fail (caps != NULL);
if (caps == NULL)
return;
GST_CAPS_LOCK (caps);
next = caps->next;
g_free (caps->name);
g_free (caps);
GST_CAPS_UNLOCK (caps);
g_mutex_free (caps->lock);
gst_props_unref (caps->properties);
g_free (caps->name);
g_mutex_lock (_gst_caps_chunk_lock);
g_mem_chunk_free (_gst_caps_chunk, caps);
g_mutex_unlock (_gst_caps_chunk_lock);
if (next)
gst_caps_unref (next);
}
void
gst_caps_debug (GstCaps *caps)
{
GST_DEBUG_ENTER ("caps debug");
while (caps) {
GST_DEBUG (GST_CAT_CAPS, "caps: %p %s %s\n", caps, caps->name, gst_caps_get_mime (caps));
if (caps->properties) {
gst_props_debug (caps->properties);
}
else {
GST_DEBUG (GST_CAT_CAPS, "no properties\n");
}
caps = caps->next;
}
GST_DEBUG_LEAVE ("caps debug");
}
/**
* gst_caps_unref:
* @caps: the caps to unref
......@@ -132,7 +177,9 @@ gst_caps_unref (GstCaps *caps)
gboolean zero;
GstCaps **next;
g_return_val_if_fail (caps != NULL, NULL);
if (caps == NULL)
return NULL;
g_return_val_if_fail (caps->refcount > 0, NULL);
GST_CAPS_LOCK (caps);
......@@ -182,16 +229,24 @@ gst_caps_ref (GstCaps *caps)
GstCaps*
gst_caps_copy (GstCaps *caps)
{
GstCaps *new = caps;;
GstCaps *new = NULL, *walk = NULL;
g_return_val_if_fail (caps != NULL, NULL);
while (caps) {
GstCaps *newcaps;
GST_CAPS_LOCK (caps);
new = gst_caps_new (
newcaps = gst_caps_new_id (
caps->name,
(gst_type_find_by_id (caps->id))->mime,
caps->id,
gst_props_copy (caps->properties));
GST_CAPS_UNLOCK (caps);
if (new == NULL) {
new = walk = newcaps;
}
else {
walk = walk->next = newcaps;
}
caps = caps->next;
}
return new;
}
......@@ -437,11 +492,11 @@ gst_caps_prepend (GstCaps *caps, GstCaps *capstoadd)
{
GstCaps *orig = capstoadd;
g_return_val_if_fail (caps != capstoadd, caps);
if (capstoadd == NULL)
return caps;
g_return_val_if_fail (caps != capstoadd, caps);
while (capstoadd->next) {
capstoadd = capstoadd->next;
}
......@@ -545,6 +600,118 @@ gst_caps_check_compatibility (GstCaps *fromcaps, GstCaps *tocaps)
return FALSE;
}
static GstCaps*
gst_caps_intersect_func (GstCaps *caps1, GstCaps *caps2)
{
GstCaps *result = NULL;
GstProps *props;
if (caps1->id != caps2->id) {
GST_DEBUG (GST_CAT_CAPS,"mime types differ (%s to %s)\n",
gst_type_find_by_id (caps1->id)->mime,
gst_type_find_by_id (caps2->id)->mime);
return NULL;
}
if (caps1->properties == NULL) {
return gst_caps_ref (caps2);
}
if (caps2->properties == NULL) {
return gst_caps_ref (caps1);
}
props = gst_props_intersect (caps1->properties, caps2->properties);
if (props) {
result = gst_caps_new_id ("intersect", caps1->id, props);
}
return result;
}
/**
* gst_caps_intersect:
* @caps1: a capabilty
* @caps2: a capabilty
*
* Make the intersection between two caps.
*
* Returns: The intersection of the two caps or NULL if the intersection
* is empty.
*/
GstCaps*
gst_caps_intersect (GstCaps *caps1, GstCaps *caps2)
{
GstCaps *result = NULL, *walk = NULL;
if (caps1 == NULL) {
GST_DEBUG (GST_CAT_CAPS, "first caps is NULL, return other caps\n");
return gst_caps_copy (caps2);
}
if (caps2 == NULL) {
GST_DEBUG (GST_CAT_CAPS, "second caps is NULL, return other caps\n");
return gst_caps_copy (caps1);
}
while (caps1) {
GstCaps *othercaps = caps2;
while (othercaps) {
GstCaps *intersection = gst_caps_intersect_func (caps1, othercaps);
if (intersection) {
if (!result) {
walk = result = intersection;
}
else {
walk = walk->next = intersection;
}
}
othercaps = othercaps->next;
}
caps1 = caps1->next;
}
return result;
}
GstCaps*
gst_caps_normalize (GstCaps *caps)
{
GstCaps *result = NULL, *walk = caps;
if (caps == NULL)
return caps;
while (caps) {
GList *proplist;
proplist = gst_props_normalize (caps->properties);
if (proplist && g_list_next (proplist) == NULL) {
if (result == NULL)
walk = result = caps;
else {
walk = walk->next = caps;
}
goto next;
}
while (proplist) {
GstProps *props = (GstProps *) proplist->data;
GstCaps *newcaps = gst_caps_new_id (caps->name, caps->id, props);
if (result == NULL)
walk = result = newcaps;
else {
walk = walk->next = newcaps;
}
proplist = g_list_next (proplist);
}
next:
caps = caps->next;
}
return result;
}
#ifndef GST_DISABLE_LOADSAVE_REGISTRY
/**
* gst_caps_save_thyself:
......@@ -605,6 +772,7 @@ gst_caps_load_thyself (xmlNodePtr parent)
caps->refcount = 1;
caps->lock = g_mutex_new ();
caps->next = NULL;
caps->fixed = TRUE;
while (subfield) {
if (!strcmp (subfield->name, "name")) {
......
......@@ -37,16 +37,20 @@ typedef struct _GstCaps GstCaps;
#define GST_CAPS_TRYLOCK(caps) (g_mutex_trylock(GST_CAPS(caps)->lock))
#define GST_CAPS_UNLOCK(caps) (g_mutex_unlock(GST_CAPS(caps)->lock))
#define GST_CAPS_IS_FIXED(caps) ((caps)->fixed)
#define GST_CAPS_IS_CHAINED(caps) ((caps)->next)
struct _GstCaps {
gchar *name; /* the name of this caps */
guint16 id; /* type id (major type) */
gchar *name; /* the name of this caps */
guint16 id; /* type id (major type) */
guint refcount;
GMutex *lock; /* global lock for this capability */
guint refcount;
GMutex *lock; /* global lock for this capability */
gboolean fixed; /* this caps doesn't contain variable properties */
GstProps *properties; /* properties for this capability */
GstProps *properties; /* properties for this capability */
GstCaps *next;
GstCaps *next;
};
#define GST_CAPS_NEW(name, type, a...) \
......@@ -75,11 +79,14 @@ factoryname (void) \
void _gst_caps_initialize (void);
GstCaps* gst_caps_new (const gchar *name, const gchar *mime, GstProps *props);
GstCaps* gst_caps_new_id (const gchar *name, const guint16 id, GstProps *props);
GstCaps* gst_caps_unref (GstCaps *caps);
GstCaps* gst_caps_ref (GstCaps *caps);
void gst_caps_destroy (GstCaps *caps);
void gst_caps_debug (GstCaps *caps);
GstCaps* gst_caps_copy (GstCaps *caps);
GstCaps* gst_caps_copy_on_write (GstCaps *caps);
......@@ -102,6 +109,7 @@ GstProps* gst_caps_get_props (GstCaps *caps);
#define gst_caps_get_fourcc_int(caps, name) gst_props_get_fourcc_int ((caps)->properties, name)
#define gst_caps_get_boolean(caps, name) gst_props_get_boolean ((caps)->properties, name)
#define gst_caps_get_string(caps, name) gst_props_get_string ((caps)->properties, name)
#define gst_caps_has_property(caps, name) gst_props_has_property ((caps)->properties, name)
GstCaps* gst_caps_get_by_name (GstCaps *caps, const gchar *name);
......@@ -110,6 +118,8 @@ GstCaps* gst_caps_append (GstCaps *caps, GstCaps *capstoadd);
GstCaps* gst_caps_prepend (GstCaps *caps, GstCaps *capstoadd);
gboolean gst_caps_check_compatibility (GstCaps *fromcaps, GstCaps *tocaps);
GstCaps* gst_caps_intersect (GstCaps *caps1, GstCaps *caps2);
GstCaps* gst_caps_normalize (GstCaps *caps);
#ifndef GST_DISABLE_LOADSAVE
xmlNodePtr gst_caps_save_thyself (GstCaps *caps, xmlNodePtr parent);
......
......@@ -691,44 +691,69 @@ gst_element_request_pad_by_name (GstElement *element, const gchar *name)
}
/**
* gst_element_connect:
* gst_element_connect_filtered:
* @src: element containing source pad
* @srcpadname: name of pad in source element
* @dest: element containing destination pad
* @destpadname: name of pad in destination element
* @filtercaps: the caps to use as a filter
*
* Connect the two named pads of the source and destination elements.
* Side effect is that if one of the pads has no parent, it becomes a
* child of the parent of the other element. If they have different
* parents, the connection fails.
*
* Return: TRUE if the pads could be connected.
*/
void
gst_element_connect (GstElement *src, const gchar *srcpadname,
GstElement *dest, const gchar *destpadname)
gboolean
gst_element_connect_filtered (GstElement *src, const gchar *srcpadname,
GstElement *dest, const gchar *destpadname,
GstCaps *filtercaps)
{
GstPad *srcpad,*destpad;
g_return_if_fail (src != NULL);
g_return_if_fail (GST_IS_ELEMENT(src));
g_return_if_fail (srcpadname != NULL);
g_return_if_fail (dest != NULL);
g_return_if_fail (GST_IS_ELEMENT(dest));
g_return_if_fail (destpadname != NULL);
g_return_val_if_fail (src != NULL, FALSE);
g_return_val_if_fail (GST_IS_ELEMENT(src), FALSE);
g_return_val_if_fail (srcpadname != NULL, FALSE);
g_return_val_if_fail (dest != NULL, FALSE);
g_return_val_if_fail (GST_IS_ELEMENT(dest), FALSE);
g_return_val_if_fail (destpadname != NULL, FALSE);
/* obtain the pads requested */
srcpad = gst_element_get_pad (src, srcpadname);
if (srcpad == NULL) {
GST_ERROR(src,"source element has no pad \"%s\"",srcpadname);
return;
GST_ERROR (src, "source element has no pad \"%s\"", srcpadname);
return FALSE;
}
destpad = gst_element_get_pad (dest, destpadname);
if (srcpad == NULL) {
GST_ERROR(dest,"destination element has no pad \"%s\"",destpadname);
return;
GST_ERROR (dest, "destination element has no pad \"%s\"", destpadname);
return FALSE;
}
/* we're satisified they can be connected, let's do it */
gst_pad_connect(srcpad,destpad);
return gst_pad_connect_filtered (srcpad, destpad, filtercaps);
}
/**
* gst_element_connect:
* @src: element containing source pad
* @srcpadname: name of pad in source element
* @dest: element containing destination pad
* @destpadname: name of pad in destination element
*
* Connect the two named pads of the source and destination elements.
* Side effect is that if one of the pads has no parent, it becomes a
* child of the parent of the other element. If they have different
* parents, the connection fails.
*
* Return: TRUE if the pads could be connected.
*/
gboolean
gst_element_connect (GstElement *src, const gchar *srcpadname,
GstElement *dest, const gchar *destpadname)
{
return gst_element_connect_filtered (src, srcpadname, dest, destpadname, NULL);
}
/**
......
......@@ -200,8 +200,11 @@ void gst_element_remove_ghost_pad (GstElement *element, GstPad *pad);