diff --git a/ChangeLog b/ChangeLog index 73b95afdc1..a86662967f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,35 @@ +2008-08-05 Wim Taymans + + * docs/libs/gstreamer-libs-sections.txt: + Add new function: + API: GstBaseTransform::gst_base_transform_suggest() + + * libs/gst/base/gstbasetransform.c: (gst_base_transform_finalize), + (gst_base_transform_init), (gst_base_transform_transform_caps), + (gst_base_transform_transform_size), + (gst_base_transform_configure_caps), + (gst_base_transform_can_transform), + (gst_base_transform_find_transform), (gst_base_transform_setcaps), + (gst_base_transform_prepare_output_buffer), + (gst_base_transform_buffer_alloc), + (gst_base_transform_handle_buffer), (gst_base_transform_getrange), + (gst_base_transform_chain), (gst_base_transform_activate), + (gst_base_transform_set_passthrough), + (gst_base_transform_is_passthrough), + (gst_base_transform_set_in_place), + (gst_base_transform_is_in_place), (gst_base_transform_update_qos), + (gst_base_transform_set_qos_enabled), + (gst_base_transform_is_qos_enabled), + (gst_base_transform_set_gap_aware), (gst_base_transform_suggest), + (gst_base_transform_reconfigure): + * libs/gst/base/gstbasetransform.h: + Rewrite of basetransform to perform negotiation outside of the + buffer_alloc functions. Fixes #545853. + + * tests/check/libs/transform1.c: (GST_START_TEST), + (buffer_alloc_ct2): + Update unit test. + 2008-08-05 Stefan Kost * tests/check/gst/gstpreset.c: diff --git a/docs/libs/gstreamer-libs-sections.txt b/docs/libs/gstreamer-libs-sections.txt index 655dee1d1a..1f08d57820 100644 --- a/docs/libs/gstreamer-libs-sections.txt +++ b/docs/libs/gstreamer-libs-sections.txt @@ -305,6 +305,7 @@ gst_base_transform_is_qos_enabled gst_base_transform_set_qos_enabled gst_base_transform_update_qos gst_base_transform_set_gap_aware +gst_base_transform_suggest GST_BASE_TRANSFORM_SINK_NAME GST_BASE_TRANSFORM_SRC_NAME diff --git a/libs/gst/base/gstbasetransform.c b/libs/gst/base/gstbasetransform.c index ee26a0585b..2bb7681157 100644 --- a/libs/gst/base/gstbasetransform.c +++ b/libs/gst/base/gstbasetransform.c @@ -1,9 +1,9 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans * 2005 Wim Taymans * 2005 Andy Wingo * 2005 Thomas Vander Stichele + * 2008 Wim Taymans * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -236,6 +236,14 @@ struct _GstBaseTransformPrivate GstActivateMode pad_mode; gboolean gap_aware; + + /* caps used for allocating buffers */ + gboolean proxy_alloc; + GstCaps *sink_alloc; + GstCaps *src_alloc; + /* upstream caps and size suggestions */ + GstCaps *sink_suggest; + guint size_suggest; }; static GstElementClass *parent_class = NULL; @@ -244,7 +252,7 @@ static void gst_base_transform_class_init (GstBaseTransformClass * klass); static void gst_base_transform_init (GstBaseTransform * trans, GstBaseTransformClass * klass); static GstFlowReturn gst_base_transform_prepare_output_buffer (GstBaseTransform - * trans, GstBuffer * input, gint size, GstCaps * caps, GstBuffer ** buf); + * trans, GstBuffer * input, GstBuffer ** buf); GType gst_base_transform_get_type (void) @@ -309,6 +317,7 @@ gst_base_transform_finalize (GObject * object) trans = GST_BASE_TRANSFORM (object); + gst_caps_replace (&trans->priv->sink_suggest, NULL); g_mutex_free (trans->transform_lock); G_OBJECT_CLASS (parent_class)->finalize (object); @@ -380,8 +389,6 @@ gst_base_transform_init (GstBaseTransform * trans, trans->srcpad = gst_pad_new_from_template (pad_template, "src"); gst_pad_set_getcaps_function (trans->srcpad, GST_DEBUG_FUNCPTR (gst_base_transform_getcaps)); - gst_pad_set_setcaps_function (trans->srcpad, - GST_DEBUG_FUNCPTR (gst_base_transform_setcaps)); gst_pad_set_event_function (trans->srcpad, GST_DEBUG_FUNCPTR (gst_base_transform_src_event)); gst_pad_set_checkgetrange_function (trans->srcpad, @@ -393,7 +400,6 @@ gst_base_transform_init (GstBaseTransform * trans, gst_element_add_pad (GST_ELEMENT (trans), trans->srcpad); trans->transform_lock = g_mutex_new (); - trans->delay_configure = FALSE; trans->pending_configure = FALSE; trans->priv->qos_enabled = DEFAULT_PROP_QOS; trans->cache_caps1 = NULL; @@ -426,6 +432,9 @@ gst_base_transform_transform_caps (GstBaseTransform * trans, GstCaps *ret; GstBaseTransformClass *klass; + if (caps == NULL) + return NULL; + klass = GST_BASE_TRANSFORM_GET_CLASS (trans); /* if there is a custom transform function, use this */ @@ -452,28 +461,25 @@ gst_base_transform_transform_caps (GstBaseTransform * trans, GstCaps *nth; nth = gst_caps_copy_nth (caps, i); - GST_DEBUG_OBJECT (trans, "from[%d]: %" GST_PTR_FORMAT, i, nth); + GST_LOG_OBJECT (trans, "from[%d]: %" GST_PTR_FORMAT, i, nth); temp = klass->transform_caps (trans, direction, nth); gst_caps_unref (nth); - GST_DEBUG_OBJECT (trans, " to[%d]: %" GST_PTR_FORMAT, i, temp); + GST_LOG_OBJECT (trans, " to[%d]: %" GST_PTR_FORMAT, i, temp); temp = gst_caps_make_writable (temp); - /* FIXME: here we need to only append those structures, that are not yet - * in there - * gst_caps_append (ret, temp); - */ + /* here we need to only append those structures, that are not yet + * in there, we use the merge function for this */ gst_caps_merge (ret, temp); - GST_DEBUG_OBJECT (trans, " merged[%d]: %" GST_PTR_FORMAT, i, ret); + GST_LOG_OBJECT (trans, " merged[%d]: %" GST_PTR_FORMAT, i, ret); } - GST_DEBUG_OBJECT (trans, "merged: (%d)", gst_caps_get_size (ret)); - /* now simplify caps - gst_caps_do_simplify (ret); - GST_DEBUG_OBJECT (trans, "simplified: (%d)", gst_caps_get_size (ret)); - */ + GST_LOG_OBJECT (trans, "merged: (%d)", gst_caps_get_size (ret)); + /* we can't do much simplification here because we don't really want to + * change the caps order */ } } else { - /* else use the identity transform */ + GST_DEBUG_OBJECT (trans, "identity from: %" GST_PTR_FORMAT, caps); + /* no transform function, use the identity transform */ ret = gst_caps_ref (caps); } @@ -483,6 +489,15 @@ gst_base_transform_transform_caps (GstBaseTransform * trans, return ret; } +/* transform a buffer of @size with @caps on the pad with @direction to + * the size of a buffer with @othercaps and store the result in @othersize + * + * We have two ways of doing this: + * 1) use a custom transform size function, this is for complicated custom + * cases with no fixed unit_size. + * 2) use the unit_size functions where there is a relationship between the + * caps and the size of a buffer. + */ static gboolean gst_base_transform_transform_size (GstBaseTransform * trans, GstPadDirection direction, GstCaps * caps, @@ -498,24 +513,33 @@ gst_base_transform_transform_size (GstBaseTransform * trans, GST_PTR_FORMAT " to size for caps %" GST_PTR_FORMAT " in direction %s", size, caps, othercaps, direction == GST_PAD_SRC ? "SRC" : "SINK"); - /* if there is a custom transform function, use this */ if (klass->transform_size) { + /* if there is a custom transform function, use this */ ret = klass->transform_size (trans, direction, caps, size, othercaps, othersize); } else { + /* there is no transform_size function, we have to use the unit_size + * functions. This method assumes there is a fixed unit_size associated with + * each caps. We provide the same amount of units on both sides. */ if (!gst_base_transform_get_unit_size (trans, caps, &inunitsize)) goto no_in_size; GST_DEBUG_OBJECT (trans, "input size %d, input unit size %d", size, inunitsize); + /* input size must be a multiple of the unit_size of the input caps */ if (inunitsize == 0 || (size % inunitsize != 0)) goto no_multiple; + /* get the amount of units */ units = size / inunitsize; + + /* now get the unit size of the output */ if (!gst_base_transform_get_unit_size (trans, othercaps, &outunitsize)) goto no_out_size; + /* the output size is the unit_size times the amount of units on the + * input */ *othersize = units * outunitsize; GST_DEBUG_OBJECT (trans, "transformed size to %d", *othersize); @@ -546,6 +570,15 @@ no_out_size: } } +/* get the caps that can be handled by @pad. We perforn: + * + * - take the caps of peer of otherpad, + * - filter against the padtemplate of otherpad, + * - calculate all transforms of remaining caps + * - filter against template of @pad + * + * If there is no peer, we simply return the caps of the padtemplate of pad. + */ static GstCaps * gst_base_transform_getcaps (GstPad * pad) { @@ -611,10 +644,17 @@ gst_base_transform_configure_caps (GstBaseTransform * trans, GstCaps * in, klass = GST_BASE_TRANSFORM_GET_CLASS (trans); + GST_DEBUG_OBJECT (trans, "in caps: %" GST_PTR_FORMAT, in); + GST_DEBUG_OBJECT (trans, "out caps: %" GST_PTR_FORMAT, out); + /* clear the cache */ gst_caps_replace (&trans->cache_caps1, NULL); gst_caps_replace (&trans->cache_caps2, NULL); + /* figure out same caps state */ + trans->have_same_caps = gst_caps_is_equal (in, out); + GST_DEBUG_OBJECT (trans, "have_same_caps: %d", trans->have_same_caps); + /* If we've a transform_ip method and same input/output caps, set in_place * by default. If for some reason the sub-class prefers using a transform * function, it can clear the in place flag in the set_caps */ @@ -637,33 +677,90 @@ gst_base_transform_configure_caps (GstBaseTransform * trans, GstCaps * in, return ret; } -/* called when new caps arrive on the sink or source pad */ +/* check if caps @in on @pad can be transformed to @out on the other pad. + * We don't have a vmethod to test this yet so we have to do a somewhat less + * efficient check for this. + */ static gboolean -gst_base_transform_setcaps (GstPad * pad, GstCaps * caps) +gst_base_transform_can_transform (GstBaseTransform * trans, GstPad * pad, + GstCaps * in, GstCaps * out) +{ + GstCaps *othercaps; + + /* convert the in caps to all possible out caps */ + othercaps = + gst_base_transform_transform_caps (trans, GST_PAD_DIRECTION (pad), in); + + /* check if transform is empty */ + if (!othercaps || gst_caps_is_empty (othercaps)) + goto no_transform; + + /* check if the out caps is a subset of the othercaps */ + if (!gst_caps_is_subset (out, othercaps)) + goto no_subset; + + if (othercaps) + gst_caps_unref (othercaps); + + GST_DEBUG_OBJECT (trans, "from %" GST_PTR_FORMAT, in); + GST_DEBUG_OBJECT (trans, "to %" GST_PTR_FORMAT, out); + + return TRUE; + + /* ERRORS */ +no_transform: + { + GST_DEBUG_OBJECT (trans, + "transform returned useless %" GST_PTR_FORMAT, othercaps); + if (othercaps) + gst_caps_unref (othercaps); + return FALSE; + } +no_subset: + { + GST_DEBUG_OBJECT (trans, "no subset"); + if (othercaps) + gst_caps_unref (othercaps); + return FALSE; + } +} + +/* given a fixed @caps on @pad, create the best possible caps for the + * other pad. + * @caps must be fixed when calling this function. + * + * This function calls the transform caps vmethod of the basetransform to figure + * out the possible target formats. It then tries to select the best format from + * this list by: + * + * - attempt passthrough if the target caps is a superset of the input caps + * - fixating by using peer caps + * - fixating with transform fixate function + * - fixating with pad fixate functions. + * + * this function returns a caps that can be transformed into and is accepted by + * the peer element. + */ +static GstCaps * +gst_base_transform_find_transform (GstBaseTransform * trans, GstPad * pad, + GstCaps * caps) { - GstBaseTransform *trans; GstBaseTransformClass *klass; GstPad *otherpad, *otherpeer; - GstCaps *othercaps = NULL; - gboolean ret = TRUE; + GstCaps *othercaps; gboolean peer_checked = FALSE; - trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); + /* caps must be fixed here, this is a programming error if it's not */ + g_return_val_if_fail (gst_caps_is_fixed (caps), NULL); + klass = GST_BASE_TRANSFORM_GET_CLASS (trans); otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad; otherpeer = gst_pad_get_peer (otherpad); - /* if we get called recursively, we bail out now to avoid an - * infinite loop. */ - if (GST_PAD_IS_IN_SETCAPS (otherpad)) - goto done; - - /* caps must be fixed here */ - if (!gst_caps_is_fixed (caps)) - goto unfixed_caps; - - /* see how we can transform the input caps. */ + /* see how we can transform the input caps. We need to do this even for + * 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); @@ -674,6 +771,9 @@ gst_base_transform_setcaps (GstPad * pad, GstCaps * caps) const GstCaps *templ_caps; templ_caps = gst_pad_get_pad_template_caps (otherpad); + GST_DEBUG_OBJECT (trans, + "intersecting against padtemplate %" GST_PTR_FORMAT, templ_caps); + intersect = gst_caps_intersect (othercaps, templ_caps); gst_caps_unref (othercaps); @@ -686,6 +786,8 @@ gst_base_transform_setcaps (GstPad * pad, GstCaps * caps) /* if the othercaps are not fixed, we need to fixate them, first attempt * is by attempting passthrough if the othercaps are a superset of caps. */ + /* FIXME. maybe the caps is not fixed because it has multiple structures of + * fixed caps */ if (!gst_caps_is_fixed (othercaps)) { GstCaps *temp; @@ -697,22 +799,28 @@ gst_base_transform_setcaps (GstPad * pad, GstCaps * caps) temp = gst_caps_intersect (othercaps, caps); GST_DEBUG_OBJECT (trans, "intersect returned %" GST_PTR_FORMAT, temp); if (temp) { - if (!gst_caps_is_empty (temp) && otherpeer) { + if (!gst_caps_is_empty (temp)) { GST_DEBUG_OBJECT (trans, "try passthrough with %" GST_PTR_FORMAT, caps); - /* 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 */ + 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); + /* 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); - /* 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); } } gst_caps_unref (temp); @@ -791,34 +899,110 @@ gst_base_transform_setcaps (GstPad * pad, GstCaps * caps) GST_DEBUG_OBJECT (trans, "Input caps were %" GST_PTR_FORMAT ", and got final caps %" GST_PTR_FORMAT, caps, othercaps); - trans->have_same_caps = gst_caps_is_equal (caps, othercaps); - GST_DEBUG_OBJECT (trans, "have_same_caps: %d", trans->have_same_caps); + if (otherpeer) + gst_object_unref (otherpeer); - /* see if we have to configure the element now */ - if (!trans->delay_configure) { - GstCaps *incaps, *outcaps; + return othercaps; - /* make sure in and out caps are correct */ - if (pad == trans->sinkpad) { - incaps = caps; - outcaps = othercaps; - } else { - incaps = othercaps; - outcaps = caps; - } - /* call configure now */ - if (!(ret = gst_base_transform_configure_caps (trans, incaps, outcaps))) - goto failed_configure; - } else { - /* set pending configure, the configure will happen later with the - * caps we set on the pads above. */ - trans->pending_configure = TRUE; + /* ERRORS */ +no_transform: + { + GST_DEBUG_OBJECT (trans, + "transform returned useless %" GST_PTR_FORMAT, othercaps); + goto error_cleanup; } +no_transform_possible: + { + GST_DEBUG_OBJECT (trans, + "transform could not transform %" GST_PTR_FORMAT + " in anything we support", caps); + goto error_cleanup; + } +could_not_fixate: + { + GST_DEBUG_OBJECT (trans, "FAILED to fixate %" GST_PTR_FORMAT, othercaps); + goto error_cleanup; + } +peer_no_accept: + { + GST_DEBUG_OBJECT (trans, "FAILED to get peer of %" GST_PTR_FORMAT + " to accept %" GST_PTR_FORMAT, otherpad, othercaps); + goto error_cleanup; + } +error_cleanup: + { + if (otherpeer) + gst_object_unref (otherpeer); + if (othercaps) + gst_caps_unref (othercaps); + return NULL; + } +} + +/* called when new caps arrive on the sink or source pad, + * We try to find the best caps for the other side using our _find_transform() + * function. If there are caps, we configure the transform for this new + * transformation. + * + * FIXME, this function is currently commutative but this should not really be + * because we never set caps starting from the srcpad. + */ +static gboolean +gst_base_transform_setcaps (GstPad * pad, GstCaps * caps) +{ + GstBaseTransform *trans; + GstBaseTransformClass *klass; + GstPad *otherpad, *otherpeer; + GstCaps *othercaps = NULL; + gboolean ret = TRUE; + GstCaps *incaps, *outcaps; + + trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); + klass = GST_BASE_TRANSFORM_GET_CLASS (trans); + + otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad; + otherpeer = gst_pad_get_peer (otherpad); + + /* if we get called recursively, we bail out now to avoid an + * infinite loop. */ + if (GST_PAD_IS_IN_SETCAPS (otherpad)) + goto done; + + GST_DEBUG_OBJECT (pad, "have new caps %" GST_PTR_FORMAT, caps); + + /* find best possible caps for the other pad */ + othercaps = gst_base_transform_find_transform (trans, pad, caps); + if (!othercaps || gst_caps_is_empty (othercaps)) + goto no_transform_possible; + + /* configure the element now */ + /* make sure in and out caps are correct */ + if (pad == trans->sinkpad) { + incaps = caps; + outcaps = othercaps; + } else { + incaps = othercaps; + outcaps = caps; + } + +#if 0 + /* if we have the same caps, we can optimize and reuse the input caps */ + if (gst_caps_is_equal (incaps, outcaps)) { + GST_INFO_OBJECT (trans, "reuse caps"); + gst_caps_unref (outcaps); + outcaps = gst_caps_ref (incaps); + } +#endif + + /* call configure now */ + if (!(ret = gst_base_transform_configure_caps (trans, incaps, outcaps))) + goto failed_configure; /* we know this will work, we implement the setcaps */ gst_pad_set_caps (otherpad, othercaps); if (pad == trans->srcpad && trans->priv->pad_mode == GST_ACTIVATE_PULL) { + /* FIXME hm? */ ret &= gst_pad_set_caps (otherpeer, othercaps); if (!ret) { GST_INFO_OBJECT (trans, "otherpeer setcaps(%" GST_PTR_FORMAT ") failed", @@ -839,43 +1023,17 @@ done: return ret; /* ERRORS */ -unfixed_caps: - { - GST_DEBUG_OBJECT (trans, "caps are not fixed %" GST_PTR_FORMAT, caps); - ret = FALSE; - goto done; - } -no_transform: - { - GST_DEBUG_OBJECT (trans, - "transform returned useless %" GST_PTR_FORMAT, othercaps); - ret = FALSE; - goto done; - } no_transform_possible: { - GST_DEBUG_OBJECT (trans, + GST_WARNING_OBJECT (trans, "transform could not transform %" GST_PTR_FORMAT " in anything we support", caps); ret = FALSE; goto done; } -could_not_fixate: - { - GST_ERROR_OBJECT (trans, "FAILED to fixate %" GST_PTR_FORMAT, othercaps); - ret = FALSE; - goto done; - } -peer_no_accept: - { - GST_DEBUG_OBJECT (trans, "FAILED to get peer of %" GST_PTR_FORMAT - " to accept %" GST_PTR_FORMAT, otherpad, othercaps); - ret = FALSE; - goto done; - } failed_configure: { - GST_DEBUG_OBJECT (trans, "FAILED to configure caps %" GST_PTR_FORMAT + GST_WARNING_OBJECT (trans, "FAILED to configure caps %" GST_PTR_FORMAT " to accept %" GST_PTR_FORMAT, otherpad, othercaps); ret = FALSE; goto done; @@ -884,44 +1042,69 @@ failed_configure: /* Allocate a buffer using gst_pad_alloc_buffer * - * This function does not do renegotiation on the source pad + * This function can do renegotiation on the source pad * * The output buffer is always writable. outbuf can be equal to * inbuf, the caller should be prepared for this and perform * appropriate refcounting. */ - -/* FIXME 0.11: out_size should be unsigned */ static GstFlowReturn gst_base_transform_prepare_output_buffer (GstBaseTransform * trans, - GstBuffer * in_buf, gint out_size, GstCaps * out_caps, GstBuffer ** out_buf) + GstBuffer * in_buf, GstBuffer ** out_buf) { GstBaseTransformClass *bclass; + GstBaseTransformPrivate *priv; GstFlowReturn ret = GST_FLOW_OK; - gboolean copy_inbuf = FALSE; - - g_return_val_if_fail (out_size >= 0, GST_FLOW_ERROR); + guint outsize, newsize; + gboolean discard; + GstCaps *incaps, *oldcaps, *newcaps; bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - /* we cannot reconfigure the element yet as we are still processing - * the old buffer. We will therefore delay the reconfiguration of the - * element until we have processed this last buffer. */ - trans->delay_configure = TRUE; + priv = trans->priv; - /* out_caps is the caps of the src pad gathered through the GST_PAD_CAPS - macro. If a set_caps occurs during this function this caps will become - invalid. We want to keep them during preparation of the output buffer. */ - if (out_caps) - gst_caps_ref (out_caps); + /* figure out how to allocate a buffer based on the current configuration */ + if (trans->passthrough) { + GST_DEBUG_OBJECT (trans, "doing passthrough alloc"); + /* passthrough, we don't really need to call pad alloc but we still need to + * in order to get upstream negotiation. The output size is the same as the + * input size. */ + outsize = GST_BUFFER_SIZE (in_buf); + /* we always alloc and discard here */ + discard = TRUE; + } else { + gboolean want_in_place = (bclass->transform_ip != NULL) + && trans->always_in_place; + + if (want_in_place) { + GST_DEBUG_OBJECT (trans, "doing inplace alloc"); + /* we alloc a buffer of the same size as the input */ + outsize = GST_BUFFER_SIZE (in_buf); + /* only discard it when the input was not writable, otherwise, we reuse + * the input buffer. */ + discard = gst_buffer_is_writable (in_buf); + } else { + GST_DEBUG_OBJECT (trans, "getting output size for copy transform"); + /* copy transform, figure out the output size */ + if (!gst_base_transform_transform_size (trans, + GST_PAD_SINK, GST_PAD_CAPS (trans->sinkpad), + GST_BUFFER_SIZE (in_buf), GST_PAD_CAPS (trans->srcpad), + &outsize)) { + goto unknown_size; + } + /* never discard this buffer, we need it for storing the output */ + discard = FALSE; + } + } + + oldcaps = GST_PAD_CAPS (trans->srcpad); - /* see if the subclass wants to alloc a buffer */ if (bclass->prepare_output_buffer) { + GST_DEBUG_OBJECT (trans, + "calling prepare buffer with caps %" GST_PTR_FORMAT, oldcaps); ret = - bclass->prepare_output_buffer (trans, in_buf, out_size, out_caps, + bclass->prepare_output_buffer (trans, in_buf, outsize, oldcaps, out_buf); - if (ret != GST_FLOW_OK) - goto done; /* FIXME 0.11: * decrease refcount again if vmethod returned refcounted in_buf. This @@ -930,75 +1113,151 @@ gst_base_transform_prepare_output_buffer (GstBaseTransform * trans, * a reffed inbuf, which is exactly what we don't want :), oh well.. */ if (in_buf == *out_buf) gst_buffer_unref (in_buf); + + /* never discard the buffer from the prepare_buffer method */ + discard = FALSE; + } else { + GST_DEBUG_OBJECT (trans, "doing alloc with caps %" GST_PTR_FORMAT, oldcaps); + + ret = gst_pad_alloc_buffer (trans->srcpad, + GST_BUFFER_OFFSET (in_buf), outsize, oldcaps, out_buf); } - /* See if we want to prepare the buffer for in place output */ - if (*out_buf == NULL && GST_BUFFER_SIZE (in_buf) == out_size - && bclass->transform_ip) { - if (gst_buffer_is_writable (in_buf)) { - *out_buf = in_buf; - GST_LOG_OBJECT (trans, "reuse input buffer"); + if (ret != GST_FLOW_OK) + goto alloc_failed; - /* the buffer is writable, this also means the metadata is writable and we - * can update the caps and flags when we need to. */ - if (!trans->have_same_caps) - /* we need to change the caps */ - gst_caps_replace (&GST_BUFFER_CAPS (*out_buf), out_caps); + /* must always return a buffer */ + g_assert (*out_buf != NULL); - /* clear the GAP flag when the subclass does not understand it */ - if (!trans->priv->gap_aware) - GST_BUFFER_FLAG_UNSET (*out_buf, GST_BUFFER_FLAG_GAP); + /* check if we got different caps on this new output buffer */ + newcaps = GST_BUFFER_CAPS (*out_buf); + newsize = GST_BUFFER_SIZE (*out_buf); + if (!gst_caps_is_equal (newcaps, oldcaps)) { + GstCaps *othercaps; + gboolean can_convert; - /* we are done now */ - goto done; + GST_DEBUG_OBJECT (trans, "received new caps %" GST_PTR_FORMAT, newcaps); + + incaps = GST_PAD_CAPS (trans->sinkpad); + + /* it's possible that the buffer we got is of the wrong size, get the + * expected size here */ + gst_base_transform_transform_size (trans, + GST_PAD_SINK, incaps, GST_BUFFER_SIZE (in_buf), newcaps, &outsize); + + /* check if we can convert the current incaps to the new target caps */ + can_convert = + gst_base_transform_can_transform (trans, trans->sinkpad, incaps, + newcaps); + + if (can_convert) { + GST_DEBUG_OBJECT (trans, "reconfigure transform for current buffer"); + /* caps not empty, try to renegotiate to the new format */ + if (!gst_base_transform_configure_caps (trans, incaps, newcaps)) { + /* not sure we need to fail hard here, we can simply continue our + * conversion with what we negotiated before */ + goto failed_configure; + } + /* new format configure, and use the new output buffer */ + gst_pad_set_caps (trans->srcpad, newcaps); + discard = FALSE; } else { - /* Make a writable buffer below and copy the data */ - copy_inbuf = TRUE; - GST_LOG_OBJECT (trans, "need to copy input buffer to new output buffer"); + GST_DEBUG_OBJECT (trans, "cannot perform transform on current buffer"); + + /* we cannot convert the current buffer but we might be able to suggest a + * new format upstream, try to find what the best format is. */ + othercaps = + gst_base_transform_find_transform (trans, trans->srcpad, newcaps); + + if (!othercaps) { + GST_DEBUG_OBJECT (trans, "incompatible caps, ignoring"); + /* we received caps that we cannot transform. Upstream is behaving badly + * because it should have checked if we could handle these caps. We can + * simply ignore these caps and produce a buffer with our original caps. */ + } else { + guint size_suggest; + + GST_DEBUG_OBJECT (trans, "getting size of suggestion"); + + /* not a subset, we have a new upstream suggestion, remember it and + * allocate a default buffer. First we try to convert the size */ + if (gst_base_transform_transform_size (trans, + GST_PAD_SRC, newcaps, outsize, othercaps, &size_suggest)) { + + /* ok, remember the suggestions now */ + GST_DEBUG_OBJECT (trans, + "storing new caps and size suggestion of %u and %" GST_PTR_FORMAT, + size_suggest, othercaps); + + GST_OBJECT_LOCK (trans->sinkpad); + if (priv->sink_suggest) + gst_caps_unref (priv->sink_suggest); + priv->sink_suggest = gst_caps_ref (othercaps); + priv->size_suggest = size_suggest; + GST_OBJECT_UNLOCK (trans->sinkpad); + } + gst_caps_unref (othercaps); + } + if (in_buf != *out_buf) + gst_buffer_unref (*out_buf); + *out_buf = NULL; + } + + /* if we got a buffer of the wrong size, discard it now */ + if (newsize != outsize) { + if (in_buf != *out_buf) + gst_buffer_unref (*out_buf); + *out_buf = NULL; + discard = FALSE; } } if (*out_buf == NULL) { - /* Sub-class didn't already provide a buffer for us. Make one */ - ret = gst_pad_alloc_buffer (trans->srcpad, - GST_BUFFER_OFFSET (in_buf), out_size, out_caps, out_buf); - if (ret != GST_FLOW_OK || *out_buf == NULL) - goto done; - - /* allocated buffer could be of different caps than what we requested */ - if (G_UNLIKELY (!gst_caps_is_equal (out_caps, GST_BUFFER_CAPS (*out_buf)))) { - /* FIXME, it is possible we can reconfigure the transform with new caps at this - * point but for now we just create a buffer ourselves */ + if (!discard) { + GST_DEBUG_OBJECT (trans, "make default output buffer"); + /* no valid buffer yet, make one */ + *out_buf = gst_buffer_new_and_alloc (outsize); + gst_buffer_copy_metadata (*out_buf, in_buf, + GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS); + } else { + GST_DEBUG_OBJECT (trans, "reuse input buffer"); + *out_buf = in_buf; + } + } else { + if (discard) { + GST_DEBUG_OBJECT (trans, "discard buffer, reuse input buffer"); gst_buffer_unref (*out_buf); - *out_buf = gst_buffer_new_and_alloc (out_size); - gst_buffer_set_caps (*out_buf, out_caps); + *out_buf = in_buf; + } else { + GST_DEBUG_OBJECT (trans, "using pad-alloc buffer"); + gst_buffer_copy_metadata (*out_buf, in_buf, + GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS); } } + gst_buffer_set_caps (*out_buf, GST_PAD_CAPS (trans->srcpad)); - /* If the output buffer metadata is modifiable, copy timestamps and - * buffer flags */ - if (*out_buf != in_buf && gst_buffer_is_metadata_writable (*out_buf)) { - - if (copy_inbuf && gst_buffer_is_writable (*out_buf)) - memcpy (GST_BUFFER_DATA (*out_buf), GST_BUFFER_DATA (in_buf), out_size); - - gst_buffer_copy_metadata (*out_buf, in_buf, - GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS); - - /* Unset the GAP flag if the element is _not_ GAP aware. Otherwise - * it might create an output buffer that does not contain neutral data - * but still has the GAP flag on it! */ - if (!trans->priv->gap_aware) - GST_BUFFER_FLAG_UNSET (*out_buf, GST_BUFFER_FLAG_GAP); - } - -done: - if (out_caps) - gst_caps_unref (out_caps); - - trans->delay_configure = FALSE; + /* clear the GAP flag when the subclass does not understand it */ + if (!trans->priv->gap_aware) + GST_BUFFER_FLAG_UNSET (*out_buf, GST_BUFFER_FLAG_GAP); return ret; + + /* ERRORS */ +alloc_failed: + { + GST_WARNING_OBJECT (trans, "pad-alloc failed: %s", gst_flow_get_name (ret)); + return ret; + } +unknown_size: + { + GST_ERROR_OBJECT (trans, "unknown output size"); + return GST_FLOW_ERROR; + } +failed_configure: + { + GST_WARNING_OBJECT (trans, "failed to configure caps"); + return GST_FLOW_NOT_NEGOTIATED; + } } /* Given @caps calcultate the size of one unit. @@ -1068,176 +1327,157 @@ gst_base_transform_buffer_alloc (GstPad * pad, guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf) { GstBaseTransform *trans; + GstBaseTransformPrivate *priv; GstFlowReturn res; - guint new_size; - gboolean issinkcaps = TRUE; + gboolean proxy = FALSE, suggest = FALSE; + GstCaps *sink_suggest; + guint size_suggest = 0; trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); + priv = trans->priv; - /* we cannot run this when we are transforming data and as such doing - * another negotiation in the transform method. */ - g_mutex_lock (trans->transform_lock); + GST_DEBUG_OBJECT (pad, "alloc with caps %" GST_PTR_FORMAT ", size %u", caps, + size); *buf = NULL; - GST_DEBUG_OBJECT (trans, "allocating a buffer of size %d ...", size); - if (offset == GST_BUFFER_OFFSET_NONE) - GST_DEBUG_OBJECT (trans, "... and offset NONE"); - else - GST_DEBUG_OBJECT (trans, "... and offset %" G_GUINT64_FORMAT, offset); - - /* if have_same_caps was previously set to TRUE we need to double check if it - * hasn't changed */ - if (trans->have_same_caps) { - GstCaps *sinkcaps; - - GST_OBJECT_LOCK (trans->sinkpad); - sinkcaps = GST_PAD_CAPS (trans->sinkpad); - issinkcaps = sinkcaps && (gst_caps_is_equal (sinkcaps, caps)); - GST_OBJECT_UNLOCK (trans->sinkpad); - } - - /* before any buffers are pushed, have_same_caps is TRUE; allocating can trigger - * a renegotiation and change that to FALSE */ - - /* bilboed: This seems wrong, from all debug logs, have_same_caps is - * initialized to FALSE */ - - /* checking against trans->have_same_caps is not enough !! It should also - * check to see if the requested caps are equal to the sink caps */ - if (trans->have_same_caps && issinkcaps) { - /* request a buffer with the same caps */ - GST_DEBUG_OBJECT (trans, "requesting buffer with same caps, size %d", size); - - res = - gst_pad_alloc_buffer_and_set_caps (trans->srcpad, offset, size, caps, - buf); + /* we remember our previous alloc request to quickly see if we can proxy or + * not. */ + if (caps && gst_caps_is_equal (priv->sink_alloc, caps)) { + /* we have seen this before, see if we need to proxy */ + GST_DEBUG_OBJECT (trans, "have old caps"); + sink_suggest = caps; + suggest = FALSE; + res = GST_FLOW_OK; } else { - /* if we are configured, request a buffer with the src caps */ - GstCaps *srccaps; - GstCaps *sinkcaps; - gboolean configured; + GstCaps *temp, *othercaps; + const GstCaps *templ; + gboolean empty; - /* take lock, peek if the caps are ok */ - GST_OBJECT_LOCK (trans->sinkpad); - sinkcaps = GST_PAD_CAPS (trans->sinkpad); - configured = (sinkcaps == NULL || gst_caps_is_equal (sinkcaps, caps)); - GST_OBJECT_UNLOCK (trans->sinkpad); - if (!configured) - goto not_configured; + GST_DEBUG_OBJECT (trans, "new format %" GST_PTR_FORMAT, caps); - /* take lock on srcpad to grab the caps, caps can change when pushing a - * buffer. */ - GST_OBJECT_LOCK (trans->srcpad); - if ((srccaps = GST_PAD_CAPS (trans->srcpad))) - gst_caps_ref (srccaps); - GST_OBJECT_UNLOCK (trans->srcpad); - if (!srccaps) - goto not_configured; + /* if we have a suggestion, pretend we got these as input */ + GST_OBJECT_LOCK (pad); + if ((priv->sink_suggest && !gst_caps_is_equal (caps, priv->sink_suggest))) { + sink_suggest = gst_caps_ref (priv->sink_suggest); + size_suggest = priv->size_suggest; + GST_DEBUG_OBJECT (trans, "have suggestion %" GST_PTR_FORMAT, + sink_suggest); + suggest = TRUE; + } else { + GST_DEBUG_OBJECT (trans, "using caps %" GST_PTR_FORMAT, caps); + sink_suggest = caps; + suggest = FALSE; + } + GST_OBJECT_UNLOCK (pad); - GST_DEBUG_OBJECT (trans, "calling transform_size"); - if (!gst_base_transform_transform_size (trans, - GST_PAD_DIRECTION (pad), caps, size, srccaps, &new_size)) { - gst_caps_unref (srccaps); - goto unknown_size; + /* check if we actually handle this format on the sinkpad */ + if (sink_suggest) { + templ = gst_pad_get_pad_template_caps (pad); + temp = gst_caps_intersect (sink_suggest, templ); + + empty = gst_caps_is_empty (temp); + gst_caps_unref (temp); + + if (empty) + goto not_supported; } - res = - gst_pad_alloc_buffer_and_set_caps (trans->srcpad, offset, new_size, - srccaps, buf); - - gst_caps_unref (srccaps); + /* find the best format for the other side */ + if (sink_suggest == NULL) { + /* always proxy when the caps are NULL. When this is a new format, see if + * we can proxy it downstream */ + GST_DEBUG_OBJECT (trans, "null caps, marking for proxy"); + priv->proxy_alloc = TRUE; + } else { + othercaps = gst_base_transform_find_transform (trans, pad, sink_suggest); + if (!othercaps || gst_caps_is_empty (othercaps)) { + /* no transform, can't proxy */ + GST_DEBUG_OBJECT (trans, "can't find transform, disable proxy"); + priv->proxy_alloc = FALSE; + } else { + if (gst_caps_is_equal (caps, othercaps)) { + GST_DEBUG_OBJECT (trans, + "best caps same as input, marking for proxy"); + priv->proxy_alloc = TRUE; + } else { + GST_DEBUG_OBJECT (trans, + "best caps different from input, disable proxy"); + priv->proxy_alloc = FALSE; + } + } + if (othercaps) + gst_caps_unref (othercaps); + } } + /* remember the new caps */ + gst_caps_replace (&priv->sink_alloc, sink_suggest); + proxy = priv->proxy_alloc; + GST_DEBUG_OBJECT (trans, "doing default alloc, proxy %d", proxy); - if (res == GST_FLOW_OK && !trans->have_same_caps) { - /* note that we might have had same caps before, but calling the - alloc_buffer caused setcaps to switch us out of in_place -- in any case - the alloc_buffer served to transmit caps information but we can't use the - buffer. fall through and allocate a buffer corresponding to our sink - caps, if any */ - GstCaps *sinkcaps; - GstCaps *srccaps; + if (proxy) { + GstCaps *newcaps; - GST_OBJECT_LOCK (trans->sinkpad); - if ((sinkcaps = GST_PAD_CAPS (trans->sinkpad))) - gst_caps_ref (sinkcaps); - GST_OBJECT_UNLOCK (trans->sinkpad); - if (!sinkcaps) - goto not_configured; + GST_DEBUG_OBJECT (trans, "proxy buffer-alloc with caps %" GST_PTR_FORMAT + ", size %u", caps, size); - GST_OBJECT_LOCK (trans->srcpad); - if ((srccaps = GST_PAD_CAPS (trans->srcpad))) - gst_caps_ref (srccaps); - GST_OBJECT_UNLOCK (trans->srcpad); - if (!srccaps) { - gst_caps_unref (sinkcaps); - goto not_configured; + res = gst_pad_alloc_buffer (trans->srcpad, offset, size, caps, buf); + if (res != GST_FLOW_OK) + goto alloc_failed; + + /* check if the caps changed */ + newcaps = GST_BUFFER_CAPS (*buf); + + GST_DEBUG_OBJECT (trans, "got caps %" GST_PTR_FORMAT, newcaps); + + if (newcaps != caps) { + GST_DEBUG_OBJECT (trans, "got new caps %" GST_PTR_FORMAT, newcaps); + if (!suggest) { + /* we have new caps, see if we can proxy downstream */ + if (gst_pad_peer_accept_caps (trans->sinkpad, newcaps)) { + /* peer accepts the caps, return a buffer in this format */ + GST_DEBUG_OBJECT (trans, "peer accepted new caps"); + /* remember the format */ + gst_caps_replace (&priv->sink_alloc, newcaps); + } else { + GST_DEBUG_OBJECT (trans, "peer did not accept new caps"); + /* peer does not accept the caps, create a buffer of the requested + * format. */ + gst_buffer_unref (*buf); + *buf = NULL; + } + } } - - if (!gst_base_transform_transform_size (trans, - GST_PAD_DIRECTION (trans->srcpad), srccaps, GST_BUFFER_SIZE (*buf), - sinkcaps, &new_size)) { - gst_caps_unref (srccaps); - gst_caps_unref (sinkcaps); - goto unknown_size; - } - /* don't need the caps anymore now */ - gst_caps_unref (srccaps); - - gst_buffer_unref (*buf); - - *buf = gst_buffer_new_and_alloc (new_size); - GST_BUFFER_OFFSET (*buf) = offset; - /* set caps, gives away the ref */ - GST_BUFFER_CAPS (*buf) = sinkcaps; - + } + if (suggest) { + *buf = gst_buffer_new_and_alloc (size_suggest); + GST_DEBUG_OBJECT (trans, + "doing suggestion of size %u, caps %" GST_PTR_FORMAT, size_suggest, + sink_suggest); + GST_BUFFER_CAPS (*buf) = sink_suggest; res = GST_FLOW_OK; } -done: - g_mutex_unlock (trans->transform_lock); gst_object_unref (trans); return res; -not_configured: + /* ERRORS */ +alloc_failed: { - /* let the default allocator handle it... */ - GST_DEBUG_OBJECT (trans, "not configured"); - gst_buffer_replace (buf, NULL); - - if (gst_pad_accept_caps (trans->srcpad, caps) - && gst_pad_peer_accept_caps (trans->srcpad, caps)) { - /* ...by seeing if the downstream elements can handle this */ - res = gst_pad_alloc_buffer (trans->srcpad, offset, size, caps, buf); - - if (res == GST_FLOW_OK - && !gst_caps_is_equal (caps, GST_BUFFER_CAPS (*buf))) { - /* In case downstream wants to see something different, ignore this - * buffer and leave it to the chain function to renegotiate */ - gst_buffer_replace (buf, NULL); - } - } else { - /* If not fall back on the default handler and let things be - * renegotiated in the chain function */ - res = GST_FLOW_OK; - } - goto done; + GST_DEBUG_OBJECT (trans, "pad alloc failed: %s", gst_flow_get_name (res)); + if (suggest) + gst_caps_unref (sink_suggest); + gst_object_unref (trans); + return res; } -unknown_size: +not_supported: { - /* let the default allocator handle it... */ - GST_DEBUG_OBJECT (trans, "unknown size"); - gst_buffer_replace (buf, NULL); - if (trans->passthrough) { - /* ...by calling alloc_buffer without setting caps on the src pad, which - * will force negotiation in the chain function. */ - res = gst_pad_alloc_buffer (trans->srcpad, offset, size, caps, buf); - } else { - /* ...by letting the default handler create a buffer */ - res = GST_FLOW_OK; - } - goto done; + GST_DEBUG_OBJECT (trans, "pad alloc with unsupported caps"); + if (suggest) + gst_caps_unref (sink_suggest); + gst_object_unref (trans); + return GST_FLOW_NOT_NEGOTIATED; } } @@ -1374,24 +1614,28 @@ gst_base_transform_src_eventfunc (GstBaseTransform * trans, GstEvent * event) return ret; } +/* perform a transform on @inbuf and put the result in @outbuf. + * + * This function is common to the push and pull-based operations. + * + * This function takes ownership of @inbuf */ static GstFlowReturn gst_base_transform_handle_buffer (GstBaseTransform * trans, GstBuffer * inbuf, GstBuffer ** outbuf) { GstBaseTransformClass *bclass; GstFlowReturn ret = GST_FLOW_OK; - guint out_size; gboolean want_in_place; GstClockTime qostime; bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); if (GST_BUFFER_OFFSET_IS_VALID (inbuf)) - GST_LOG_OBJECT (trans, "handling buffer %p of size %d and offset %" + GST_DEBUG_OBJECT (trans, "handling buffer %p of size %d and offset %" G_GUINT64_FORMAT, inbuf, GST_BUFFER_SIZE (inbuf), GST_BUFFER_OFFSET (inbuf)); else - GST_LOG_OBJECT (trans, "handling buffer %p of size %d and offset NONE", + GST_DEBUG_OBJECT (trans, "handling buffer %p of size %d and offset NONE", inbuf, GST_BUFFER_SIZE (inbuf)); /* Don't allow buffer handling before negotiation, except in passthrough mode @@ -1403,7 +1647,7 @@ gst_base_transform_handle_buffer (GstBaseTransform * trans, GstBuffer * inbuf, /* Set discont flag so we can mark the outgoing buffer */ if (GST_BUFFER_IS_DISCONT (inbuf)) { - GST_LOG_OBJECT (trans, "got DISCONT buffer %p", inbuf); + GST_DEBUG_OBJECT (trans, "got DISCONT buffer %p", inbuf); trans->priv->discont = TRUE; } @@ -1411,6 +1655,7 @@ gst_base_transform_handle_buffer (GstBaseTransform * trans, GstBuffer * inbuf, if (trans->segment.format != GST_FORMAT_TIME) goto no_qos; + /* QOS is done on the running time of the buffer, get it now */ qostime = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (inbuf)); @@ -1418,6 +1663,8 @@ gst_base_transform_handle_buffer (GstBaseTransform * trans, GstBuffer * inbuf, gboolean need_skip; GstClockTime earliest_time; + /* lock for getting the QoS parameters that are set (in a different thread) + * with the QOS events */ GST_OBJECT_LOCK (trans); earliest_time = trans->priv->earliest_time; /* check for QoS, don't perform conversion for buffers @@ -1437,76 +1684,48 @@ gst_base_transform_handle_buffer (GstBaseTransform * trans, GstBuffer * inbuf, } no_qos: + + /* first try to allocate an output buffer based on the currently negotiated + * format. While we call pad-alloc we could renegotiate the srcpad format or + * have a new suggestion for upstream buffer-alloc. + * In any case, outbuf will contain a buffer suitable for doing the configured + * transform after this function. */ + ret = gst_base_transform_prepare_output_buffer (trans, inbuf, outbuf); + if (G_UNLIKELY (ret != GST_FLOW_OK)) + goto no_buffer; + + /* now perform the needed transform */ if (trans->passthrough) { /* In passthrough mode, give transform_ip a look at the * buffer, without making it writable, or just push the * data through */ - GST_LOG_OBJECT (trans, "element is in passthrough mode"); - - if (bclass->transform_ip) - ret = bclass->transform_ip (trans, inbuf); - - *outbuf = inbuf; - - goto done; - } - - want_in_place = (bclass->transform_ip != NULL) && trans->always_in_place; - *outbuf = NULL; - - if (want_in_place) { - /* If want_in_place is TRUE, we may need to prepare a new output buffer - * Sub-classes can implement a prepare_output_buffer function as they - * wish. */ - GST_LOG_OBJECT (trans, "doing inplace transform"); - - ret = gst_base_transform_prepare_output_buffer (trans, inbuf, - GST_BUFFER_SIZE (inbuf), GST_PAD_CAPS (trans->srcpad), outbuf); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto no_buffer; - - ret = bclass->transform_ip (trans, *outbuf); - - } else { - GST_LOG_OBJECT (trans, "doing non-inplace transform"); - - /* not transforming inplace, figure out the output size */ - if (trans->always_in_place) { - out_size = GST_BUFFER_SIZE (inbuf); + if (bclass->transform_ip) { + GST_DEBUG_OBJECT (trans, "doing passthrough transform"); + ret = bclass->transform_ip (trans, *outbuf); } else { - if (!gst_base_transform_transform_size (trans, - GST_PAD_DIRECTION (trans->sinkpad), GST_PAD_CAPS (trans->sinkpad), - GST_BUFFER_SIZE (inbuf), GST_PAD_CAPS (trans->srcpad), - &out_size)) { - /* we have an error */ - goto no_size; - } + GST_DEBUG_OBJECT (trans, "element is in passthrough"); } + } else { + want_in_place = (bclass->transform_ip != NULL) && trans->always_in_place; - /* no in place transform, get buffer, this might renegotiate. */ - ret = gst_base_transform_prepare_output_buffer (trans, inbuf, out_size, - GST_PAD_CAPS (trans->srcpad), outbuf); - if (ret != GST_FLOW_OK) - goto no_buffer; + if (want_in_place) { + GST_DEBUG_OBJECT (trans, "doing inplace transform"); - if (bclass->transform) - ret = bclass->transform (trans, inbuf, *outbuf); - else - ret = GST_FLOW_NOT_SUPPORTED; - } + if (inbuf != *outbuf) { + /* different buffers, copy the input to the output first, we then do an + * in-place transform on the output buffer. */ + memcpy (GST_BUFFER_DATA (*outbuf), GST_BUFFER_DATA (inbuf), + GST_BUFFER_SIZE (inbuf)); + } + ret = bclass->transform_ip (trans, *outbuf); + } else { + GST_DEBUG_OBJECT (trans, "doing non-inplace transform"); - /* if we got renegotiated we can configure now */ - if (trans->pending_configure) { - gboolean success; - - success = - gst_base_transform_configure_caps (trans, - GST_PAD_CAPS (trans->sinkpad), GST_PAD_CAPS (trans->srcpad)); - - trans->pending_configure = FALSE; - - if (!success) - goto configure_failed; + if (bclass->transform) + ret = bclass->transform (trans, inbuf, *outbuf); + else + ret = GST_FLOW_NOT_SUPPORTED; + } } skip: @@ -1514,7 +1733,6 @@ skip: if (*outbuf != inbuf) gst_buffer_unref (inbuf); -done: return ret; /* ERRORS */ @@ -1525,28 +1743,13 @@ not_negotiated: ("not negotiated"), ("not negotiated")); return GST_FLOW_NOT_NEGOTIATED; } -no_size: - { - gst_buffer_unref (inbuf); - GST_ELEMENT_ERROR (trans, STREAM, NOT_IMPLEMENTED, - ("subclass did not specify output size"), - ("subclass did not specify output size")); - return GST_FLOW_ERROR; - } no_buffer: { gst_buffer_unref (inbuf); - GST_DEBUG_OBJECT (trans, "could not get buffer from pool: %s", + GST_WARNING_OBJECT (trans, "could not get buffer from pool: %s", gst_flow_get_name (ret)); return ret; } -configure_failed: - { - if (*outbuf != inbuf) - gst_buffer_unref (inbuf); - GST_DEBUG_OBJECT (trans, "could not negotiate"); - return GST_FLOW_NOT_NEGOTIATED; - } } static gboolean @@ -1579,9 +1782,7 @@ gst_base_transform_getrange (GstPad * pad, guint64 offset, ret = gst_pad_pull_range (trans->sinkpad, offset, length, &inbuf); if (ret == GST_FLOW_OK) { - g_mutex_lock (trans->transform_lock); ret = gst_base_transform_handle_buffer (trans, inbuf, buffer); - g_mutex_unlock (trans->transform_lock); } gst_object_unref (trans); @@ -1608,9 +1809,7 @@ gst_base_transform_chain (GstPad * pad, GstBuffer * buffer) } /* protect transform method and concurrent buffer alloc */ - g_mutex_lock (trans->transform_lock); ret = gst_base_transform_handle_buffer (trans, buffer, &outbuf); - g_mutex_unlock (trans->transform_lock); /* outbuf can be NULL, this means a dropped buffer, if we have a buffer but * GST_BASE_TRANSFORM_FLOW_DROPPED we will not push either. */ @@ -1707,6 +1906,7 @@ gst_base_transform_activate (GstBaseTransform * trans, gboolean active) trans->priv->proportion = 1.0; trans->priv->earliest_time = -1; trans->priv->discont = FALSE; + gst_caps_replace (&trans->priv->sink_suggest, NULL); GST_OBJECT_UNLOCK (trans); } else { @@ -1723,6 +1923,8 @@ gst_base_transform_activate (GstBaseTransform * trans, gboolean active) } gst_caps_replace (&trans->cache_caps1, NULL); gst_caps_replace (&trans->cache_caps2, NULL); + gst_caps_replace (&trans->priv->sink_alloc, NULL); + gst_caps_replace (&trans->priv->sink_suggest, NULL); if (trans->priv->pad_mode != GST_ACTIVATE_NONE && bclass->stop) result &= bclass->stop (trans); @@ -1789,7 +1991,7 @@ gst_base_transform_set_passthrough (GstBaseTransform * trans, { GstBaseTransformClass *bclass; - g_return_if_fail (trans != NULL); + g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); @@ -1820,7 +2022,7 @@ gst_base_transform_is_passthrough (GstBaseTransform * trans) { gboolean result; - g_return_val_if_fail (trans != NULL, FALSE); + g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE); GST_OBJECT_LOCK (trans); result = trans->passthrough; @@ -1849,7 +2051,7 @@ gst_base_transform_set_in_place (GstBaseTransform * trans, gboolean in_place) { GstBaseTransformClass *bclass; - g_return_if_fail (trans != NULL); + g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); @@ -1885,7 +2087,7 @@ gst_base_transform_is_in_place (GstBaseTransform * trans) { gboolean result; - g_return_val_if_fail (trans != NULL, FALSE); + g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE); GST_OBJECT_LOCK (trans); result = trans->always_in_place; @@ -1899,9 +2101,12 @@ gst_base_transform_is_in_place (GstBaseTransform * trans) * @trans: a #GstBaseTransform * @proportion: the proportion * @diff: the diff against the clock - * @timestamp: the timestamp of the buffer generating the QoS + * @timestamp: the timestamp of the buffer generating the QoS expressed in + * running_time. * - * Set the QoS parameters in the transform. + * Set the QoS parameters in the transform. This function is called internally + * when a QOS event is received but subclasses can provide custom information + * when needed. * * Since: 0.10.5 * @@ -1912,7 +2117,7 @@ gst_base_transform_update_qos (GstBaseTransform * trans, gdouble proportion, GstClockTimeDiff diff, GstClockTime timestamp) { - g_return_if_fail (trans != NULL); + g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, trans, "qos: proportion: %lf, diff %" G_GINT64_FORMAT ", timestamp %" @@ -1938,7 +2143,7 @@ gst_base_transform_update_qos (GstBaseTransform * trans, void gst_base_transform_set_qos_enabled (GstBaseTransform * trans, gboolean enabled) { - g_return_if_fail (trans != NULL); + g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, trans, "enabled: %d", enabled); @@ -1964,7 +2169,7 @@ gst_base_transform_is_qos_enabled (GstBaseTransform * trans) { gboolean result; - g_return_val_if_fail (trans != NULL, FALSE); + g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE); GST_OBJECT_LOCK (trans); result = trans->priv->qos_enabled; @@ -1992,10 +2197,53 @@ gst_base_transform_is_qos_enabled (GstBaseTransform * trans) void gst_base_transform_set_gap_aware (GstBaseTransform * trans, gboolean gap_aware) { - g_return_if_fail (trans != NULL); + g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); GST_OBJECT_LOCK (trans); trans->priv->gap_aware = gap_aware; GST_DEBUG_OBJECT (trans, "set gap aware %d", trans->priv->gap_aware); GST_OBJECT_UNLOCK (trans); } + +/** + * gst_base_transform_suggest: + * @trans: a #GstBaseTransform + * @gcaps: caps to suggest + * + * Instructs @trans to suggest new @caps upstream. + * + * Since: 0.10.21 + */ +void +gst_base_transform_suggest (GstBaseTransform * trans, GstCaps * caps, + guint size) +{ + g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); + + GST_OBJECT_LOCK (trans->sinkpad); + if (trans->priv->sink_suggest) + gst_caps_unref (trans->priv->sink_suggest); + trans->priv->sink_suggest = gst_caps_copy (caps); + trans->priv->size_suggest = size; + gst_caps_replace (&trans->priv->sink_alloc, NULL); + GST_DEBUG_OBJECT (trans, "new suggest %" GST_PTR_FORMAT, caps); + GST_OBJECT_UNLOCK (trans->sinkpad); +} + +#if 0 +/** + * gst_base_transform_reconfigure: + * @trans: a #GstBaseTransform + * + * Instructs @trans to renegotiate a new downstream transform on the next + * buffer. + * + * Note: Not yet implemented. + * + * Since: 0.10.21 + */ +void +gst_base_transform_reconfigure (GstBaseTransform * trans) +{ +} +#endif diff --git a/libs/gst/base/gstbasetransform.h b/libs/gst/base/gstbasetransform.h index 5562084444..5c26d292ff 100644 --- a/libs/gst/base/gstbasetransform.h +++ b/libs/gst/base/gstbasetransform.h @@ -256,6 +256,8 @@ gboolean gst_base_transform_is_qos_enabled (GstBaseTransform *trans); void gst_base_transform_set_gap_aware (GstBaseTransform *trans, gboolean gap_aware); +void gst_base_transform_suggest (GstBaseTransform *trans, + GstCaps *caps, guint size); G_END_DECLS #endif /* __GST_BASE_TRANSFORM_H__ */ diff --git a/tests/check/libs/transform1.c b/tests/check/libs/transform1.c index 28ced59dc3..1039858ca6 100644 --- a/tests/check/libs/transform1.c +++ b/tests/check/libs/transform1.c @@ -27,7 +27,7 @@ #include #include -#undef FAILING_TESTS +#define FAILING_TESTS #include "test_transform.c" @@ -203,7 +203,11 @@ GST_START_TEST (basetransform_chain_pt2) buffer = gst_test_trans_pop (trans); fail_unless (buffer != NULL); fail_unless (GST_BUFFER_SIZE (buffer) == 20); +#if OPTIMIZED fail_unless (GST_BUFFER_CAPS (buffer) == caps); +#else + fail_unless (gst_caps_is_equal (GST_BUFFER_CAPS (buffer), caps)); +#endif gst_buffer_unref (buffer); @@ -241,7 +245,11 @@ GST_START_TEST (basetransform_chain_pt2) buffer = gst_test_trans_pop (trans); fail_unless (buffer != NULL); fail_unless (GST_BUFFER_SIZE (buffer) == 10); +#if OPTIMIZED fail_unless (GST_BUFFER_CAPS (buffer) == caps); +#else + fail_unless (gst_caps_is_equal (GST_BUFFER_CAPS (buffer), caps)); +#endif gst_buffer_unref (buffer); @@ -468,7 +476,11 @@ GST_START_TEST (basetransform_chain_ip2) buffer = gst_test_trans_pop (trans); fail_unless (buffer != NULL); fail_unless (GST_BUFFER_SIZE (buffer) == 20); +#if OPTIMIZED fail_unless (GST_BUFFER_CAPS (buffer) == caps); +#else + fail_unless (gst_caps_is_equal (GST_BUFFER_CAPS (buffer), caps)); +#endif gst_buffer_unref (buffer); /* with caps buffer */ @@ -503,7 +515,11 @@ GST_START_TEST (basetransform_chain_ip2) buffer = gst_test_trans_pop (trans); fail_unless (buffer != NULL); fail_unless (GST_BUFFER_SIZE (buffer) == 20); +#if OPTIMIZED fail_unless (GST_BUFFER_CAPS (buffer) == caps); +#else + fail_unless (gst_caps_is_equal (GST_BUFFER_CAPS (buffer), caps)); +#endif /* output buffer has refcount 1 */ fail_unless (GST_MINI_OBJECT_REFCOUNT_VALUE (buffer) == 1); @@ -886,6 +902,7 @@ buffer_alloc_ct2 (GstPad * pad, guint64 offset, guint size, GstCaps * caps, } else outcaps = gst_caps_ref (incaps); } + GST_DEBUG_OBJECT (pad, "expect %" GST_PTR_FORMAT, incaps); fail_unless (gst_caps_is_equal (caps, incaps)); @@ -943,6 +960,7 @@ GST_START_TEST (basetransform_chain_ct2) res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, incaps, &buffer); fail_unless (res == GST_FLOW_OK); fail_unless (buffer_alloc_ct2_called == TRUE); + gst_buffer_unref (buffer); /* with caps buffer */ GST_DEBUG_OBJECT (trans, "alloc size 20, with wrong caps %" GST_PTR_FORMAT, @@ -1099,6 +1117,7 @@ GST_START_TEST (basetransform_chain_ct3) res = gst_pad_alloc_buffer (trans->srcpad, 0, 20, incaps, &buffer); fail_unless (res == GST_FLOW_OK); fail_unless (buffer_alloc_ct2_called == TRUE); + gst_buffer_unref (buffer); /* with caps buffer */ GST_DEBUG_OBJECT (trans, "alloc size 20, with wrong caps %" GST_PTR_FORMAT, @@ -1216,11 +1235,13 @@ GST_START_TEST (basetransform_chain_ct3) gst_buffer_set_caps (buffer, incaps); /* don't suggest anything else */ + buffer_alloc_ct2_case = 1; + set_caps_ct2_case = 2; transform_ct2_called = FALSE;; buffer_alloc_ct2_called = FALSE; res = gst_test_trans_push (trans, buffer); fail_unless (res == GST_FLOW_OK); - fail_unless (transform_ct2_called == FALSE); + fail_unless (transform_ct2_called == TRUE); #ifdef FAILING_TESTS /* FIXME, pad alloc must be called to get the new caps, because we don't call * pad alloc */ @@ -1231,9 +1252,11 @@ GST_START_TEST (basetransform_chain_ct3) fail_unless (buffer != NULL); #ifdef FAILING_TESTS /* FIXME changing src caps should produce converted buffer */ + GST_DEBUG_OBJECT (trans, "received caps %" GST_PTR_FORMAT, + GST_BUFFER_CAPS (buffer)); fail_unless (gst_caps_is_equal (GST_BUFFER_CAPS (buffer), outcaps)); + fail_unless (GST_BUFFER_SIZE (buffer) == 20); #endif - fail_unless (GST_BUFFER_SIZE (buffer) == 10); /* output buffer has refcount 1 */ fail_unless (GST_MINI_OBJECT_REFCOUNT_VALUE (buffer) == 1); @@ -1266,11 +1289,8 @@ GST_START_TEST (basetransform_chain_ct3) gst_buffer_set_caps (buffer, incaps); /* don't suggest anything else */ -#ifndef FAILING_TESTS - /* FIXME, above alloc reconfigured the transform */ buffer_alloc_ct2_suggest = FALSE; buffer_alloc_ct2_case = 0; -#endif transform_ct2_called = FALSE;; buffer_alloc_ct2_called = FALSE; res = gst_test_trans_push (trans, buffer);