base: Improve negotiation with new getcaps() filter

This commit is contained in:
Sebastian Dröge 2011-05-11 15:12:04 +02:00
parent 3fa1594aaf
commit bdf9022861
7 changed files with 173 additions and 109 deletions

View file

@ -227,6 +227,9 @@ The 0.11 porting guide
gst_collect_pads_read() removed, use _read_buffer() or _take_buffer() and
then use the memory API to get to the memory.
* GstBaseSrc, GstBaseTransform, GstBaseSink
GstBaseSrc::get_caps(), GstBaseTransform::transform_caps() and
GstBaseSink::get_caps() now take a filter GstCaps* parameter to
filter the caps and allow better negotiation decisions.

View file

@ -363,7 +363,7 @@ static gboolean gst_base_sink_send_event (GstElement * element,
static gboolean gst_base_sink_query (GstElement * element, GstQuery ** query);
static const GstQueryType *gst_base_sink_get_query_types (GstElement * element);
static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink);
static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink, GstCaps * caps);
static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps);
static void gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
GstClockTime * start, GstClockTime * end);
@ -390,7 +390,7 @@ static gboolean gst_base_sink_pad_activate_pull (GstPad * pad, gboolean active);
static gboolean gst_base_sink_event (GstPad * pad, GstEvent * event);
static gboolean gst_base_sink_negotiate_pull (GstBaseSink * basesink);
static GstCaps *gst_base_sink_pad_getcaps (GstPad * pad);
static GstCaps *gst_base_sink_pad_getcaps (GstPad * pad, GstCaps * filter);
static void gst_base_sink_pad_fixate (GstPad * pad, GstCaps * caps);
/* check if an object was too late */
@ -562,7 +562,7 @@ gst_base_sink_class_init (GstBaseSinkClass * klass)
}
static GstCaps *
gst_base_sink_pad_getcaps (GstPad * pad)
gst_base_sink_pad_getcaps (GstPad * pad, GstCaps * filter)
{
GstBaseSinkClass *bclass;
GstBaseSink *bsink;
@ -577,7 +577,7 @@ gst_base_sink_pad_getcaps (GstPad * pad)
}
if (caps == NULL) {
if (bclass->get_caps)
caps = bclass->get_caps (bsink);
caps = bclass->get_caps (bsink, filter);
if (caps == NULL) {
GstPadTemplate *pad_template;
@ -587,6 +587,15 @@ gst_base_sink_pad_getcaps (GstPad * pad)
"sink");
if (pad_template != NULL) {
caps = gst_caps_ref (gst_pad_template_get_caps (pad_template));
if (filter) {
GstCaps *intersection;
intersection =
gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (caps);
caps = intersection;
}
}
}
}
@ -1393,7 +1402,7 @@ gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value,
static GstCaps *
gst_base_sink_get_caps (GstBaseSink * sink)
gst_base_sink_get_caps (GstBaseSink * sink, GstCaps * filter)
{
return NULL;
}

View file

@ -151,7 +151,7 @@ struct _GstBaseSinkClass {
GstElementClass parent_class;
/* get caps from subclass */
GstCaps* (*get_caps) (GstBaseSink *sink);
GstCaps* (*get_caps) (GstBaseSink *sink, GstCaps *filter);
/* notify subclass of new caps */
gboolean (*set_caps) (GstBaseSink *sink, GstCaps *caps);

View file

@ -280,7 +280,7 @@ gst_base_src_get_type (void)
return base_src_type;
}
static GstCaps *gst_base_src_getcaps (GstPad * pad);
static GstCaps *gst_base_src_getcaps (GstPad * pad, GstCaps * filter);
static gboolean gst_base_src_setcaps (GstPad * pad, GstCaps * caps);
static void gst_base_src_fixate (GstPad * pad, GstCaps * caps);
@ -786,7 +786,7 @@ gst_base_src_setcaps (GstPad * pad, GstCaps * caps)
}
static GstCaps *
gst_base_src_getcaps (GstPad * pad)
gst_base_src_getcaps (GstPad * pad, GstCaps * filter)
{
GstBaseSrcClass *bclass;
GstBaseSrc *bsrc;
@ -795,7 +795,7 @@ gst_base_src_getcaps (GstPad * pad)
bsrc = GST_BASE_SRC (GST_PAD_PARENT (pad));
bclass = GST_BASE_SRC_GET_CLASS (bsrc);
if (bclass->get_caps)
caps = bclass->get_caps (bsrc);
caps = bclass->get_caps (bsrc, filter);
if (caps == NULL) {
GstPadTemplate *pad_template;
@ -804,6 +804,15 @@ gst_base_src_getcaps (GstPad * pad)
gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src");
if (pad_template != NULL) {
caps = gst_caps_ref (gst_pad_template_get_caps (pad_template));
if (filter) {
GstCaps *intersection;
intersection =
gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (caps);
caps = intersection;
}
}
}
return caps;
@ -2551,7 +2560,7 @@ gst_base_src_default_negotiate (GstBaseSrc * basesrc)
gboolean result = FALSE;
/* first see what is possible on our source pad */
thiscaps = gst_pad_get_caps (GST_BASE_SRC_PAD (basesrc));
thiscaps = gst_pad_get_caps (GST_BASE_SRC_PAD (basesrc), NULL);
GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps);
/* nothing or anything is allowed, we're done */
if (thiscaps == NULL || gst_caps_is_any (thiscaps))
@ -2561,38 +2570,35 @@ gst_base_src_default_negotiate (GstBaseSrc * basesrc)
goto no_caps;
/* get the peer caps */
peercaps = gst_pad_peer_get_caps (GST_BASE_SRC_PAD (basesrc));
peercaps = gst_pad_peer_get_caps (GST_BASE_SRC_PAD (basesrc), thiscaps);
GST_DEBUG_OBJECT (basesrc, "caps of peer: %" GST_PTR_FORMAT, peercaps);
if (peercaps) {
/* get intersection */
caps =
gst_caps_intersect_full (peercaps, thiscaps, GST_CAPS_INTERSECT_FIRST);
GST_DEBUG_OBJECT (basesrc, "intersect: %" GST_PTR_FORMAT, caps);
gst_caps_unref (peercaps);
/* The result is already a subset of our caps */
caps = peercaps;
gst_caps_unref (thiscaps);
} else {
/* no peer, work with our own caps then */
caps = gst_caps_copy (thiscaps);
caps = thiscaps;
}
gst_caps_unref (thiscaps);
if (caps) {
if (caps && !gst_caps_is_empty (caps)) {
caps = gst_caps_make_writable (caps);
/* take first (and best, since they are sorted) possibility */
gst_caps_truncate (caps);
/* now fixate */
if (!gst_caps_is_empty (caps)) {
GST_DEBUG_OBJECT (basesrc, "have caps: %" GST_PTR_FORMAT, caps);
if (gst_caps_is_any (caps)) {
/* hmm, still anything, so element can do anything and
* nego is not needed */
result = TRUE;
} else {
gst_pad_fixate_caps (GST_BASE_SRC_PAD (basesrc), caps);
GST_DEBUG_OBJECT (basesrc, "fixated to: %" GST_PTR_FORMAT, caps);
if (gst_caps_is_fixed (caps)) {
/* yay, fixed caps, use those then, it's possible that the subclass does
* not accept this caps after all and we have to fail. */
result = gst_pad_set_caps (GST_BASE_SRC_PAD (basesrc), caps);
}
GST_DEBUG_OBJECT (basesrc, "have caps: %" GST_PTR_FORMAT, caps);
if (gst_caps_is_any (caps)) {
/* hmm, still anything, so element can do anything and
* nego is not needed */
result = TRUE;
} else {
gst_pad_fixate_caps (GST_BASE_SRC_PAD (basesrc), caps);
GST_DEBUG_OBJECT (basesrc, "fixated to: %" GST_PTR_FORMAT, caps);
if (gst_caps_is_fixed (caps)) {
/* yay, fixed caps, use those then, it's possible that the subclass does
* not accept this caps after all and we have to fail. */
result = gst_pad_set_caps (GST_BASE_SRC_PAD (basesrc), caps);
}
}
gst_caps_unref (caps);

View file

@ -168,7 +168,7 @@ struct _GstBaseSrcClass {
/* virtual methods for subclasses */
/* get caps from subclass */
GstCaps* (*get_caps) (GstBaseSrc *src);
GstCaps* (*get_caps) (GstBaseSrc *src, GstCaps *filter);
/* notify the subclass of new caps */
gboolean (*set_caps) (GstBaseSrc *src, GstCaps *caps);

View file

@ -312,12 +312,12 @@ static GstFlowReturn gst_base_transform_getrange (GstPad * pad, guint64 offset,
guint length, GstBuffer ** buffer);
static GstFlowReturn gst_base_transform_chain (GstPad * pad,
GstBuffer * buffer);
static GstCaps *gst_base_transform_getcaps (GstPad * pad);
static GstCaps *gst_base_transform_getcaps (GstPad * pad, GstCaps * filter);
static gboolean gst_base_transform_acceptcaps (GstPad * pad, GstCaps * caps);
static gboolean gst_base_transform_acceptcaps_default (GstBaseTransform * trans,
GstPadDirection direction, GstCaps * caps);
static gboolean gst_base_transform_setcaps (GstBaseTransform * trans,
GstPad * pad, GstCaps * caps, gboolean reconfigure);
GstPad * pad, GstCaps * caps);
static gboolean gst_base_transform_query (GstPad * pad, GstQuery ** query);
static const GstQueryType *gst_base_transform_query_type (GstPad * pad);
@ -450,7 +450,7 @@ gst_base_transform_init (GstBaseTransform * trans,
*/
static GstCaps *
gst_base_transform_transform_caps (GstBaseTransform * trans,
GstPadDirection direction, GstCaps * caps)
GstPadDirection direction, GstCaps * caps, GstCaps * filter)
{
GstCaps *ret;
GstBaseTransformClass *klass;
@ -472,9 +472,8 @@ gst_base_transform_transform_caps (GstBaseTransform * trans,
if (gst_caps_is_any (caps)) {
/* for any caps we still have to call the transform function */
GST_DEBUG_OBJECT (trans, "from: ANY");
temp = klass->transform_caps (trans, direction, caps);
temp = klass->transform_caps (trans, direction, caps, filter);
GST_DEBUG_OBJECT (trans, " to: %" GST_PTR_FORMAT, temp);
temp = gst_caps_make_writable (temp);
gst_caps_append (ret, temp);
} else {
@ -486,8 +485,9 @@ gst_base_transform_transform_caps (GstBaseTransform * trans,
nth = gst_caps_copy_nth (caps, i);
GST_LOG_OBJECT (trans, "from[%d]: %" GST_PTR_FORMAT, i, nth);
temp = klass->transform_caps (trans, direction, nth);
temp = klass->transform_caps (trans, direction, nth, filter);
gst_caps_unref (nth);
GST_LOG_OBJECT (trans, " to[%d]: %" GST_PTR_FORMAT, i, temp);
temp = gst_caps_make_writable (temp);
@ -505,10 +505,31 @@ gst_base_transform_transform_caps (GstBaseTransform * trans,
GST_DEBUG_OBJECT (trans, "simplified: (%d)", gst_caps_get_size (ret));
*/
}
#ifndef G_DISABLE_ASSERT
if (filter) {
if (!gst_caps_is_subset (ret, filter)) {
GST_ERROR_OBJECT (trans,
"transform_caps returned caps %" GST_PTR_FORMAT
" which are not a real subset of the filter caps %"
GST_PTR_FORMAT, ret, filter);
g_warning ("%s: transform_caps returned caps which are not a real "
"subset of the filter caps", GST_ELEMENT_NAME (trans));
temp = gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (ret);
ret = temp;
}
}
#endif
} else {
GST_DEBUG_OBJECT (trans, "identity from: %" GST_PTR_FORMAT, caps);
/* no transform function, use the identity transform */
ret = gst_caps_ref (caps);
if (filter) {
ret = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
} else {
ret = gst_caps_ref (caps);
}
}
GST_DEBUG_OBJECT (trans, "to: (%d) %" GST_PTR_FORMAT, gst_caps_get_size (ret),
@ -613,19 +634,52 @@ no_out_size:
* If there is no peer, we simply return the caps of the padtemplate of pad.
*/
static GstCaps *
gst_base_transform_getcaps (GstPad * pad)
gst_base_transform_getcaps (GstPad * pad, GstCaps * filter)
{
GstBaseTransform *trans;
GstPad *otherpad;
GstCaps *peercaps, *caps;
GstCaps *peercaps, *caps, *peerfilter = NULL;
trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad));
otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad;
/* we can do what the peer can */
peercaps = gst_pad_peer_get_caps (otherpad);
if (peercaps) {
if (filter) {
GstCaps *temp;
const GstCaps *templ;
GST_DEBUG_OBJECT (pad, "filter caps %" GST_PTR_FORMAT, filter);
/* filtered against our padtemplate on the other side */
templ = gst_pad_get_pad_template_caps (pad);
GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
temp = gst_caps_intersect_full (filter, templ, GST_CAPS_INTERSECT_FIRST);
GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
/* then see what we can transform this to */
peerfilter = gst_base_transform_transform_caps (trans,
GST_PAD_DIRECTION (pad), temp, NULL);
GST_DEBUG_OBJECT (pad, "transformed %" GST_PTR_FORMAT, peerfilter);
gst_caps_unref (temp);
/* and filter against the template of this pad */
templ = gst_pad_get_pad_template_caps (otherpad);
GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ);
/* We keep the caps sorted like the returned caps */
temp =
gst_caps_intersect_full (peerfilter, templ, GST_CAPS_INTERSECT_FIRST);
GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
gst_caps_unref (peerfilter);
peerfilter = temp;
}
peercaps = gst_pad_peer_get_caps (otherpad, peerfilter);
if (peerfilter)
gst_caps_unref (peerfilter);
if (peercaps && !gst_caps_is_any (peercaps)) {
GstCaps *temp;
const GstCaps *templ;
@ -639,7 +693,7 @@ gst_base_transform_getcaps (GstPad * pad)
/* then see what we can transform this to */
caps = gst_base_transform_transform_caps (trans,
GST_PAD_DIRECTION (otherpad), temp);
GST_PAD_DIRECTION (otherpad), temp, filter);
GST_DEBUG_OBJECT (pad, "transformed %" GST_PTR_FORMAT, caps);
gst_caps_unref (temp);
if (caps == NULL)
@ -665,6 +719,14 @@ gst_base_transform_getcaps (GstPad * pad)
} else {
/* no peer or the peer can do anything, our padtemplate is enough then */
caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
if (filter) {
GstCaps *temp;
temp = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (caps);
caps = temp;
}
}
done:
@ -740,7 +802,7 @@ gst_base_transform_configure_caps (GstBaseTransform * trans, GstCaps * in,
*/
static GstCaps *
gst_base_transform_find_transform (GstBaseTransform * trans, GstPad * pad,
GstCaps * caps, gboolean reconfigure)
GstCaps * caps)
{
GstBaseTransformClass *klass;
GstPad *otherpad, *otherpeer;
@ -760,7 +822,7 @@ gst_base_transform_find_transform (GstBaseTransform * trans, GstPad * pad,
* passthrough because it might be possible that this element cannot support
* passthrough at all. */
othercaps = gst_base_transform_transform_caps (trans,
GST_PAD_DIRECTION (pad), caps);
GST_PAD_DIRECTION (pad), caps, NULL);
/* The caps we can actually output is the intersection of the transformed
* caps with the pad template for the pad */
@ -789,71 +851,55 @@ gst_base_transform_find_transform (GstBaseTransform * trans, GstPad * pad,
/* FIXME. maybe the caps is not fixed because it has multiple structures of
* fixed caps */
is_fixed = gst_caps_is_fixed (othercaps);
if (!is_fixed && !reconfigure) {
if (!is_fixed) {
GST_DEBUG_OBJECT (trans,
"transform returned non fixed %" GST_PTR_FORMAT, othercaps);
/* see if the target caps are a superset of the source caps, in this
* case we can try to perform passthrough */
if (gst_caps_can_intersect (othercaps, caps)) {
GST_DEBUG_OBJECT (trans, "try passthrough with %" GST_PTR_FORMAT, caps);
if (otherpeer) {
/* try passthrough. we know it's fixed, because caps is fixed */
if (gst_pad_accept_caps (otherpeer, caps)) {
GST_DEBUG_OBJECT (trans, "peer accepted %" GST_PTR_FORMAT, caps);
/* peer accepted unmodified caps, we free the original non-fixed
* caps and work with the passthrough caps */
gst_caps_unref (othercaps);
othercaps = gst_caps_ref (caps);
is_fixed = TRUE;
/* mark that we checked othercaps with the peer, this
* makes sure we don't call accept_caps again with these same
* caps */
peer_checked = TRUE;
} else {
GST_DEBUG_OBJECT (trans,
"peer did not accept %" GST_PTR_FORMAT, caps);
}
} else {
GST_DEBUG_OBJECT (trans, "no peer, doing passthrough");
gst_caps_unref (othercaps);
othercaps = gst_caps_ref (caps);
is_fixed = TRUE;
}
}
}
/* Now let's see what the peer suggests based on our transformed caps */
if (otherpeer) {
GstCaps *peercaps, *intersection;
const GstCaps *templ_caps;
/* second attempt at fixation is done by intersecting with
* the peer caps */
if (!is_fixed && otherpeer) {
/* intersect against what the peer can do */
GstCaps *peercaps;
GstCaps *intersect;
GST_DEBUG_OBJECT (trans,
"Checking peer caps with filter %" GST_PTR_FORMAT, othercaps);
GST_DEBUG_OBJECT (trans, "othercaps now %" GST_PTR_FORMAT, othercaps);
peercaps = gst_pad_get_caps (otherpeer, othercaps);
GST_DEBUG_OBJECT (trans, "Resulted in %" GST_PTR_FORMAT, peercaps);
peercaps = gst_pad_get_caps (otherpeer);
if (!reconfigure)
intersect = gst_caps_intersect (peercaps, othercaps);
else
intersect =
templ_caps = gst_pad_get_pad_template_caps (otherpad);
GST_DEBUG_OBJECT (trans,
"Intersecting with template caps %" GST_PTR_FORMAT, templ_caps);
intersection =
gst_caps_intersect_full (peercaps, templ_caps,
GST_CAPS_INTERSECT_FIRST);
GST_DEBUG_OBJECT (trans, "Intersection: %" GST_PTR_FORMAT, intersection);
gst_caps_unref (peercaps);
peercaps = intersection;
GST_DEBUG_OBJECT (trans,
"Intersecting with transformed caps %" GST_PTR_FORMAT, othercaps);
intersection =
gst_caps_intersect_full (peercaps, othercaps,
GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (peercaps);
gst_caps_unref (othercaps);
othercaps = intersect;
peer_checked = FALSE;
is_fixed = gst_caps_is_fixed (othercaps);
GST_DEBUG_OBJECT (trans,
"filtering against peer yields %" GST_PTR_FORMAT, othercaps);
GST_DEBUG_OBJECT (trans, "Intersection: %" GST_PTR_FORMAT, intersection);
gst_caps_unref (peercaps);
gst_caps_unref (othercaps);
othercaps = intersection;
is_fixed = gst_caps_is_fixed (othercaps);
peer_checked = TRUE;
} else {
GST_DEBUG_OBJECT (trans, "no peer, doing passthrough");
gst_caps_unref (othercaps);
othercaps = gst_caps_ref (caps);
is_fixed = TRUE;
}
}
if (gst_caps_is_empty (othercaps))
goto no_transform_possible;
/* third attempt at fixation, call the fixate vmethod and
/* second attempt at fixation, call the fixate vmethod and
* ultimately call the pad fixate function. */
if (!is_fixed) {
GST_DEBUG_OBJECT (trans,
@ -978,9 +1024,9 @@ gst_base_transform_acceptcaps_default (GstBaseTransform * trans,
/* get all the formats we can handle on this pad */
if (direction == GST_PAD_SRC)
allowed = gst_pad_get_caps (trans->srcpad);
allowed = gst_pad_get_caps (trans->srcpad, NULL);
else
allowed = gst_pad_get_caps (trans->sinkpad);
allowed = gst_pad_get_caps (trans->sinkpad, NULL);
if (!allowed) {
GST_DEBUG_OBJECT (trans, "gst_pad_get_caps() failed");
@ -1058,7 +1104,7 @@ gst_base_transform_acceptcaps (GstPad * pad, GstCaps * caps)
*/
static gboolean
gst_base_transform_setcaps (GstBaseTransform * trans, GstPad * pad,
GstCaps * caps, gboolean reconfigure)
GstCaps * caps)
{
GstPad *otherpad, *otherpeer;
GstCaps *othercaps = NULL;
@ -1076,7 +1122,7 @@ gst_base_transform_setcaps (GstBaseTransform * trans, GstPad * pad,
GST_DEBUG_OBJECT (pad, "have new caps %p %" GST_PTR_FORMAT, caps, caps);
/* find best possible caps for the other pad */
othercaps = gst_base_transform_find_transform (trans, pad, caps, reconfigure);
othercaps = gst_base_transform_find_transform (trans, pad, caps);
if (!othercaps || gst_caps_is_empty (othercaps))
goto no_transform_possible;
@ -1528,7 +1574,7 @@ gst_base_transform_sink_eventfunc (GstBaseTransform * trans, GstEvent * event)
GstCaps *caps;
gst_event_parse_caps (event, &caps);
gst_base_transform_setcaps (trans, trans->sinkpad, caps, FALSE);
gst_base_transform_setcaps (trans, trans->sinkpad, caps);
forward = FALSE;
break;
@ -1643,7 +1689,7 @@ gst_base_transform_handle_buffer (GstBaseTransform * trans, GstBuffer * inbuf,
/* if we need to reconfigure we pretend a buffer with new caps arrived. This
* will reconfigure the transform with the new output format. We can only
* do this if the buffer actually has caps. */
if (!gst_base_transform_setcaps (trans, trans->sinkpad, incaps, TRUE)) {
if (!gst_base_transform_setcaps (trans, trans->sinkpad, incaps)) {
gst_caps_unref (incaps);
goto not_negotiated;
}

View file

@ -205,7 +205,7 @@ struct _GstBaseTransformClass {
/* virtual methods for subclasses */
GstCaps* (*transform_caps) (GstBaseTransform *trans,
GstPadDirection direction,
GstCaps *caps);
GstCaps *caps, GstCaps *filter);
void (*fixate_caps) (GstBaseTransform *trans,
GstPadDirection direction, GstCaps *caps,