X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=libs%2Fgst%2Fbase%2Fgstbasetransform.c;h=5ddad78342dcc4c3d2da903cc23329fa994f7745;hb=5bf13cdd5314bc3c6c81bd620e712acdcab14eb2;hp=b0bae07092fac71b0bc5bc4cdbe06aef88d510ea;hpb=82eb275ef93ab8798a289c79fc2633289253fe83;p=platform%2Fupstream%2Fgstreamer.git diff --git a/libs/gst/base/gstbasetransform.c b/libs/gst/base/gstbasetransform.c index b0bae07..5ddad78 100644 --- a/libs/gst/base/gstbasetransform.c +++ b/libs/gst/base/gstbasetransform.c @@ -17,185 +17,120 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /** * SECTION:gstbasetransform + * @title: GstBaseTransform * @short_description: Base class for simple transform filters * @see_also: #GstBaseSrc, #GstBaseSink * - * This base class is for filter elements that process data. + * This base class is for filter elements that process data. Elements + * that are suitable for implementation using #GstBaseTransform are ones + * where the size and caps of the output is known entirely from the input + * caps and buffer sizes. These include elements that directly transform + * one buffer into another, modify the contents of a buffer in-place, as + * well as elements that collate multiple input buffers into one output buffer, + * or that expand one input buffer into multiple output buffers. See below + * for more concrete use cases. * * It provides for: - * - * one sinkpad and one srcpad - * - * Possible formats on sink and source pad implemented - * with custom transform_caps function. By default uses - * same format on sink and source. - * - * Handles state changes - * Does flushing - * Push mode - * - * Pull mode if the sub-class transform can operate on arbitrary data - * - * - * - * - * Use Cases - * - * - * - * Passthrough mode - * - * Element has no interest in modifying the buffer. It may want to inspect it, + * + * * one sinkpad and one srcpad + * * Possible formats on sink and source pad implemented + * with custom transform_caps function. By default uses + * same format on sink and source. + * + * * Handles state changes + * * Does flushing + * * Push mode + * * Pull mode if the sub-class transform can operate on arbitrary data + * + * # Use Cases + * + * ## Passthrough mode + * + * * Element has no interest in modifying the buffer. It may want to inspect it, * in which case the element should have a transform_ip function. If there * is no transform_ip function in passthrough mode, the buffer is pushed * intact. - * - * - * On the GstBaseTransformClass is the passthrough_on_same_caps variable - * which will automatically set/unset passthrough based on whether the + * + * * The #GstBaseTransformClass.passthrough_on_same_caps variable + * will automatically set/unset passthrough based on whether the * element negotiates the same caps on both pads. - * - * - * passthrough_on_same_caps on an element that doesn't implement a - * transform_caps function is useful for elements that only inspect data - * (such as level) - * - * - * - * Example elements - * Level - * Videoscale, audioconvert, ffmpegcolorspace, audioresample in - * certain modes. - * - * - * - * - * Modifications in-place - input buffer and output buffer are the - * same thing. - * - * The element must implement a transform_ip function. - * - * - * Output buffer size must <= input buffer size - * - * - * If the always_in_place flag is set, non-writable buffers will be copied - * and passed to the transform_ip function, otherwise a new buffer will be - * created and the transform function called. - * - * - * Incoming writable buffers will be passed to the transform_ip function - * immediately. - * - * only implementing transform_ip and not transform implies always_in_place - * = TRUE - * - * - * - * Example elements - * Volume - * Audioconvert in certain modes (signed/unsigned - * conversion) - * ffmpegcolorspace in certain modes (endianness - * swapping) - * - * - * - * - * Modifications only to the caps/metadata of a buffer - * - * The element does not require writable data, but non-writable buffers - * should be subbuffered so that the meta-information can be replaced. - * - * - * Elements wishing to operate in this mode should replace the - * prepare_output_buffer method to create subbuffers of the input buffer - * and set always_in_place to TRUE - * - * - * - * Example elements - * Capsfilter when setting caps on outgoing buffers that have - * none. - * identity when it is going to re-timestamp buffers by - * datarate. - * - * - * - * Normal mode - * - * always_in_place flag is not set, or there is no transform_ip function - * - * - * Element will receive an input buffer and output buffer to operate on. - * - * - * Output buffer is allocated by calling the prepare_output_buffer function. - * - * - * - * Example elements - * Videoscale, ffmpegcolorspace, audioconvert when doing - * scaling/conversions - * - * - * - * Special output buffer allocations - * - * Elements which need to do special allocation of their output buffers - * other than what gst_buffer_pad_alloc allows should implement a - * prepare_output_buffer method, which calls the parent implementation and - * passes the newly allocated buffer. - * - * - * - * Example elements - * efence - * - * - * - * - * - * - * Sub-class settable flags on GstBaseTransform - * - * - * - * passthrough - * - * Implies that in the current configuration, the sub-class is not - * interested in modifying the buffers. - * - * - * Elements which are always in passthrough mode whenever the same caps - * has been negotiated on both pads can set the class variable - * passthrough_on_same_caps to have this behaviour automatically. - * - * - * - * - * always_in_place - * - * Determines whether a non-writable buffer will be copied before passing - * to the transform_ip function. - * - * - * Implied TRUE if no transform function is implemented. - * - * - * Implied FALSE if ONLY transform function is implemented. - * - * - * - * - * - * + * + * * #GstBaseTransformClass.passthrough_on_same_caps on an element that + * doesn't implement a transform_caps function is useful for elements that + * only inspect data (such as level) + * + * * Example elements + * + * * Level + * * Videoscale, audioconvert, videoconvert, audioresample in certain modes. + * + * ## Modifications in-place - input buffer and output buffer are the same thing. + * + * * The element must implement a transform_ip function. + * * Output buffer size must <= input buffer size + * * If the always_in_place flag is set, non-writable buffers will be copied + * and passed to the transform_ip function, otherwise a new buffer will be + * created and the transform function called. + * + * * Incoming writable buffers will be passed to the transform_ip function + * immediately. + * * only implementing transform_ip and not transform implies always_in_place = %TRUE + * + * * Example elements: + * * Volume + * * Audioconvert in certain modes (signed/unsigned conversion) + * * videoconvert in certain modes (endianness swapping) + * + * ## Modifications only to the caps/metadata of a buffer + * + * * The element does not require writable data, but non-writable buffers + * should be subbuffered so that the meta-information can be replaced. + * + * * Elements wishing to operate in this mode should replace the + * prepare_output_buffer method to create subbuffers of the input buffer + * and set always_in_place to %TRUE + * + * * Example elements + * * Capsfilter when setting caps on outgoing buffers that have + * none. + * * identity when it is going to re-timestamp buffers by + * datarate. + * + * ## Normal mode + * * always_in_place flag is not set, or there is no transform_ip function + * * Element will receive an input buffer and output buffer to operate on. + * * Output buffer is allocated by calling the prepare_output_buffer function. + * * Example elements: + * * Videoscale, videoconvert, audioconvert when doing + * scaling/conversions + * + * ## Special output buffer allocations + * * Elements which need to do special allocation of their output buffers + * beyond allocating output buffers via the negotiated allocator or + * buffer pool should implement the prepare_output_buffer method. + * + * * Example elements: + * * efence + * + * # Sub-class settable flags on GstBaseTransform + * + * * passthrough + * + * * Implies that in the current configuration, the sub-class is not interested in modifying the buffers. + * * Elements which are always in passthrough mode whenever the same caps has been negotiated on both pads can set the class variable passthrough_on_same_caps to have this behaviour automatically. + * + * * always_in_place + * * Determines whether a non-writable buffer will be copied before passing + * to the transform_ip function. + * + * * Implied %TRUE if no transform function is implemented. + * * Implied %FALSE if ONLY transform function is implemented. */ #ifdef HAVE_CONFIG_H @@ -228,9 +163,6 @@ enum PROP_QOS }; -#define GST_BASE_TRANSFORM_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_TRANSFORM, GstBaseTransformPrivate)) - struct _GstBaseTransformPrivate { /* Set by sub-class */ @@ -255,6 +187,7 @@ struct _GstBaseTransformPrivate GstPadMode pad_mode; gboolean gap_aware; + gboolean prefer_passthrough; /* QoS stats */ guint64 processed; @@ -271,10 +204,15 @@ struct _GstBaseTransformPrivate static GstElementClass *parent_class = NULL; +static gint private_offset = 0; static void gst_base_transform_class_init (GstBaseTransformClass * klass); static void gst_base_transform_init (GstBaseTransform * trans, GstBaseTransformClass * klass); +static GstFlowReturn default_submit_input_buffer (GstBaseTransform * trans, + gboolean is_discont, GstBuffer * input); +static GstFlowReturn default_generate_output (GstBaseTransform * trans, + GstBuffer ** outbuf); /* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init * method to get to the padtemplates */ @@ -299,11 +237,21 @@ gst_base_transform_get_type (void) _type = g_type_register_static (GST_TYPE_ELEMENT, "GstBaseTransform", &base_transform_info, G_TYPE_FLAG_ABSTRACT); + + private_offset = + g_type_add_instance_private (_type, sizeof (GstBaseTransformPrivate)); + g_once_init_leave (&base_transform_type, _type); } return base_transform_type; } +static inline GstBaseTransformPrivate * +gst_base_transform_get_instance_private (GstBaseTransform * self) +{ + return (G_STRUCT_MEMBER_P (self, private_offset)); +} + static void gst_base_transform_finalize (GObject * object); static void gst_base_transform_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -356,6 +304,9 @@ static GstFlowReturn default_prepare_output_buffer (GstBaseTransform * trans, GstBuffer * inbuf, GstBuffer ** outbuf); static gboolean default_copy_metadata (GstBaseTransform * trans, GstBuffer * inbuf, GstBuffer * outbuf); +static gboolean +gst_base_transform_default_transform_meta (GstBaseTransform * trans, + GstBuffer * inbuf, GstMeta * meta, GstBuffer * outbuf); /* static guint gst_base_transform_signals[LAST_SIGNAL] = { 0 }; */ @@ -373,13 +324,14 @@ gst_base_transform_class_init (GstBaseTransformClass * klass) gobject_class = G_OBJECT_CLASS (klass); + if (private_offset != 0) + g_type_class_adjust_private_offset (klass, &private_offset); + GST_DEBUG_CATEGORY_INIT (gst_base_transform_debug, "basetransform", 0, "basetransform element"); GST_DEBUG ("gst_base_transform_class_init"); - g_type_class_add_private (klass, sizeof (GstBaseTransformPrivate)); - parent_class = g_type_class_peek_parent (klass); gobject_class->set_property = gst_base_transform_set_property; @@ -407,12 +359,16 @@ gst_base_transform_class_init (GstBaseTransformClass * klass) GST_DEBUG_FUNCPTR (gst_base_transform_default_propose_allocation); klass->transform_size = GST_DEBUG_FUNCPTR (gst_base_transform_default_transform_size); + klass->transform_meta = + GST_DEBUG_FUNCPTR (gst_base_transform_default_transform_meta); klass->sink_event = GST_DEBUG_FUNCPTR (gst_base_transform_sink_eventfunc); klass->src_event = GST_DEBUG_FUNCPTR (gst_base_transform_src_eventfunc); klass->prepare_output_buffer = GST_DEBUG_FUNCPTR (default_prepare_output_buffer); klass->copy_metadata = GST_DEBUG_FUNCPTR (default_copy_metadata); + klass->submit_input_buffer = GST_DEBUG_FUNCPTR (default_submit_input_buffer); + klass->generate_output = GST_DEBUG_FUNCPTR (default_generate_output); } static void @@ -424,7 +380,7 @@ gst_base_transform_init (GstBaseTransform * trans, GST_DEBUG ("gst_base_transform_init"); - priv = trans->priv = GST_BASE_TRANSFORM_GET_PRIVATE (trans); + priv = trans->priv = gst_base_transform_get_instance_private (trans); pad_template = gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink"); @@ -459,6 +415,7 @@ gst_base_transform_init (GstBaseTransform * trans, priv->cache_caps2 = NULL; priv->pad_mode = GST_PAD_MODE_NONE; priv->gap_aware = FALSE; + priv->prefer_passthrough = TRUE; priv->passthrough = FALSE; if (bclass->transform == NULL) { @@ -517,7 +474,7 @@ gst_base_transform_transform_caps (GstBaseTransform * trans, ret = klass->transform_caps (trans, direction, caps, filter); GST_LOG_OBJECT (trans, " to: %" GST_PTR_FORMAT, ret); -#ifndef G_DISABLE_ASSERT +#ifdef GST_ENABLE_EXTRA_CHECKS if (filter) { if (!gst_caps_is_subset (ret, filter)) { GstCaps *intersection; @@ -544,6 +501,21 @@ gst_base_transform_transform_caps (GstBaseTransform * trans, } static gboolean +gst_base_transform_default_transform_meta (GstBaseTransform * trans, + GstBuffer * inbuf, GstMeta * meta, GstBuffer * outbuf) +{ + const GstMetaInfo *info = meta->info; + const gchar *const *tags; + + tags = gst_meta_api_type_get_tags (info->api); + + if (!tags) + return TRUE; + + return FALSE; +} + +static gboolean gst_base_transform_default_transform_size (GstBaseTransform * trans, GstPadDirection direction, GstCaps * caps, gsize size, GstCaps * othercaps, gsize * othersize) @@ -645,7 +617,7 @@ gst_base_transform_transform_size (GstBaseTransform * trans, /* get the caps that can be handled by @pad. We perform: * * - take the caps of peer of otherpad, - * - filter against the padtemplate of otherpad, + * - filter against the padtemplate of otherpad, * - calculate all transforms of remaining caps * - filter against template of @pad * @@ -656,7 +628,7 @@ gst_base_transform_query_caps (GstBaseTransform * trans, GstPad * pad, GstCaps * filter) { GstPad *otherpad; - GstCaps *peercaps, *caps, *temp, *peerfilter = NULL; + GstCaps *peercaps = NULL, *caps, *temp, *peerfilter = NULL; GstCaps *templ, *otempl; otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad; @@ -664,11 +636,12 @@ gst_base_transform_query_caps (GstBaseTransform * trans, GstPad * pad, templ = gst_pad_get_pad_template_caps (pad); otempl = gst_pad_get_pad_template_caps (otherpad); - /* we can do what the peer can */ + /* first prepare the filter to be send onwards. We need to filter and + * transform it to valid caps for the otherpad. */ if (filter) { GST_DEBUG_OBJECT (pad, "filter caps %" GST_PTR_FORMAT, filter); - /* filtered against our padtemplate on the other side */ + /* filtered against our padtemplate of this 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); @@ -679,16 +652,29 @@ gst_base_transform_query_caps (GstBaseTransform * trans, GstPad * pad, GST_DEBUG_OBJECT (pad, "transformed %" GST_PTR_FORMAT, peerfilter); gst_caps_unref (temp); - /* and filter against the template of this pad */ - GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, otempl); - /* We keep the caps sorted like the returned caps */ - temp = - gst_caps_intersect_full (peerfilter, otempl, GST_CAPS_INTERSECT_FIRST); - GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp); - gst_caps_unref (peerfilter); - peerfilter = temp; + if (peerfilter && !gst_caps_is_empty (peerfilter)) { + /* and filter against the template of the other pad */ + GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, otempl); + /* We keep the caps sorted like the returned caps */ + temp = + gst_caps_intersect_full (peerfilter, otempl, + GST_CAPS_INTERSECT_FIRST); + GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp); + gst_caps_unref (peerfilter); + peerfilter = temp; + } + } + + GST_DEBUG_OBJECT (pad, "peer filter caps %" GST_PTR_FORMAT, peerfilter); + + if (peerfilter && gst_caps_is_empty (peerfilter)) { + GST_DEBUG_OBJECT (pad, "peer filter caps are empty"); + caps = peerfilter; + peerfilter = NULL; + goto done; } + /* query the peer with the transformed filter */ peercaps = gst_pad_peer_query_caps (otherpad, peerfilter); if (peerfilter) @@ -710,7 +696,7 @@ gst_base_transform_query_caps (GstBaseTransform * trans, GstPad * pad, GST_PAD_DIRECTION (otherpad), temp, filter); GST_DEBUG_OBJECT (pad, "transformed %" GST_PTR_FORMAT, caps); gst_caps_unref (temp); - if (caps == NULL) + if (caps == NULL || gst_caps_is_empty (caps)) goto done; if (peercaps) { @@ -722,12 +708,14 @@ gst_base_transform_query_caps (GstBaseTransform * trans, GstPad * pad, gst_caps_unref (caps); caps = temp; - /* Now try if we can put the untransformed downstream caps first */ - temp = gst_caps_intersect_full (peercaps, caps, GST_CAPS_INTERSECT_FIRST); - if (!gst_caps_is_empty (temp)) { - caps = gst_caps_merge (temp, caps); - } else { - gst_caps_unref (temp); + if (trans->priv->prefer_passthrough) { + /* Now try if we can put the untransformed downstream caps first */ + temp = gst_caps_intersect_full (peercaps, caps, GST_CAPS_INTERSECT_FIRST); + if (!gst_caps_is_empty (temp)) { + caps = gst_caps_merge (temp, caps); + } else { + gst_caps_unref (temp); + } } } else { gst_caps_unref (caps); @@ -791,7 +779,6 @@ gst_base_transform_set_allocation (GstBaseTransform * trans, gst_query_unref (oldquery); } return TRUE; - } static gboolean @@ -820,7 +807,7 @@ gst_base_transform_default_decide_allocation (GstBaseTransform * trans, /* by default we remove all metadata, subclasses should implement a * filter_meta function */ - if (gst_meta_api_type_has_tag (api, GST_META_TAG_MEMORY)) { + if (gst_meta_api_type_has_tag (api, _gst_meta_tag_memory)) { /* remove all memory dependent metadata because we are going to have to * allocate different memory for input and output. */ GST_LOG_OBJECT (trans, "removing memory specific metadata %s", @@ -875,7 +862,25 @@ gst_base_transform_default_decide_allocation (GstBaseTransform * trans, config = gst_buffer_pool_get_config (pool); gst_buffer_pool_config_set_params (config, outcaps, size, min, max); gst_buffer_pool_config_set_allocator (config, allocator, ¶ms); - gst_buffer_pool_set_config (pool, config); + + /* buffer pool may have to do some changes */ + if (!gst_buffer_pool_set_config (pool, config)) { + config = gst_buffer_pool_get_config (pool); + + /* If change are not acceptable, fallback to generic pool */ + if (!gst_buffer_pool_config_validate_params (config, outcaps, size, min, + max)) { + GST_DEBUG_OBJECT (trans, "unsupported pool, making new pool"); + + gst_object_unref (pool); + pool = gst_buffer_pool_new (); + gst_buffer_pool_config_set_params (config, outcaps, size, min, max); + gst_buffer_pool_config_set_allocator (config, allocator, ¶ms); + } + + if (!gst_buffer_pool_set_config (pool, config)) + goto config_failed; + } } if (update_allocator) @@ -891,6 +896,15 @@ gst_base_transform_default_decide_allocation (GstBaseTransform * trans, } return TRUE; + +config_failed: + if (pool) + gst_object_unref (pool); + + GST_ELEMENT_ERROR (trans, RESOURCE, SETTINGS, + ("Failed to configure the buffer pool"), + ("Configuration is most likely invalid, please report this issue.")); + return FALSE; } static gboolean @@ -944,6 +958,15 @@ gst_base_transform_do_bufferpool (GstBaseTransform * trans, GstCaps * outcaps) if (!result) goto no_decide_allocation; + /* check again in case the sub-class have switch to passthrough/in-place + * after looking at the meta APIs */ + if (priv->passthrough || priv->always_in_place) { + GST_DEBUG_OBJECT (trans, "no doing passthrough, delay bufferpool"); + gst_base_transform_set_allocation (trans, NULL, NULL, NULL, NULL); + gst_query_unref (query); + return TRUE; + } + /* we got configuration from our peer or the decide_allocation method, * parse them */ if (gst_query_get_n_allocation_params (query) > 0) { @@ -1061,7 +1084,7 @@ gst_base_transform_find_transform (GstBaseTransform * trans, GstPad * pad, /* The caps we can actually output is the intersection of the transformed * caps with the pad template for the pad */ - if (othercaps) { + if (othercaps && !gst_caps_is_empty (othercaps)) { GstCaps *intersect, *templ_caps; templ_caps = gst_pad_get_pad_template_caps (otherpad); @@ -1212,68 +1235,51 @@ static gboolean gst_base_transform_acceptcaps_default (GstBaseTransform * trans, GstPadDirection direction, GstCaps * caps) { -#if 0 - GstPad *otherpad; - GstCaps *othercaps = NULL; -#endif + GstPad *pad, *otherpad; + GstCaps *templ, *otempl, *ocaps = NULL; gboolean ret = TRUE; -#if 0 - otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad; - - /* we need fixed caps for the check, fall back to the default implementation - * if we don't */ - if (!gst_caps_is_fixed (caps)) -#endif - { - GstCaps *allowed; + pad = + (direction == + GST_PAD_SINK) ? GST_BASE_TRANSFORM_SINK_PAD (trans) : + GST_BASE_TRANSFORM_SRC_PAD (trans); + otherpad = + (direction == + GST_PAD_SINK) ? GST_BASE_TRANSFORM_SRC_PAD (trans) : + GST_BASE_TRANSFORM_SINK_PAD (trans); - GST_DEBUG_OBJECT (trans, "accept caps %" GST_PTR_FORMAT, caps); + GST_DEBUG_OBJECT (trans, "accept caps %" GST_PTR_FORMAT, caps); - /* get all the formats we can handle on this pad */ - if (direction == GST_PAD_SRC) - allowed = gst_pad_query_caps (trans->srcpad, NULL); - else - allowed = gst_pad_query_caps (trans->sinkpad, NULL); - - if (!allowed) { - GST_DEBUG_OBJECT (trans, "gst_pad_query_caps() failed"); - goto no_transform_possible; - } - - GST_DEBUG_OBJECT (trans, "allowed caps %" GST_PTR_FORMAT, allowed); - - /* intersect with the requested format */ - ret = gst_caps_is_subset (caps, allowed); - gst_caps_unref (allowed); - - if (!ret) - goto no_transform_possible; - } -#if 0 - else { - GST_DEBUG_OBJECT (pad, "accept caps %" GST_PTR_FORMAT, caps); + templ = gst_pad_get_pad_template_caps (pad); + otempl = gst_pad_get_pad_template_caps (otherpad); - /* find best possible caps for the other pad as a way to see if we can - * transform this caps. */ - othercaps = gst_base_transform_find_transform (trans, pad, caps, FALSE); - if (!othercaps || gst_caps_is_empty (othercaps)) - goto no_transform_possible; + /* get all the formats we can handle on this pad */ + GST_DEBUG_OBJECT (trans, "intersect with pad template: %" GST_PTR_FORMAT, + templ); + if (!gst_caps_can_intersect (caps, templ)) + goto reject_caps; - GST_DEBUG_OBJECT (pad, "we can transform to %" GST_PTR_FORMAT, othercaps); - } -#endif + GST_DEBUG_OBJECT (trans, "trying to transform with filter: %" + GST_PTR_FORMAT " (the other pad template)", otempl); + ocaps = gst_base_transform_transform_caps (trans, direction, caps, otempl); + if (!ocaps || gst_caps_is_empty (ocaps)) + goto no_transform_possible; done: -#if 0 - /* We know it's always NULL since we never use it */ - if (othercaps) - gst_caps_unref (othercaps); -#endif - + GST_DEBUG_OBJECT (trans, "accept-caps result: %d", ret); + if (ocaps) + gst_caps_unref (ocaps); + gst_caps_unref (templ); + gst_caps_unref (otempl); return ret; /* ERRORS */ +reject_caps: + { + GST_DEBUG_OBJECT (trans, "caps can't intersect with the template"); + ret = FALSE; + goto done; + } no_transform_possible: { GST_DEBUG_OBJECT (trans, @@ -1294,7 +1300,7 @@ gst_base_transform_setcaps (GstBaseTransform * trans, GstPad * pad, GstCaps * incaps) { GstBaseTransformPrivate *priv = trans->priv; - GstCaps *outcaps; + GstCaps *outcaps, *prev_incaps = NULL, *prev_outcaps = NULL; gboolean ret = TRUE; GST_DEBUG_OBJECT (pad, "have new caps %p %" GST_PTR_FORMAT, incaps, incaps); @@ -1313,12 +1319,23 @@ gst_base_transform_setcaps (GstBaseTransform * trans, GstPad * pad, outcaps = gst_caps_ref (incaps); } - /* call configure now */ - if (!(ret = gst_base_transform_configure_caps (trans, incaps, outcaps))) - goto failed_configure; + prev_incaps = gst_pad_get_current_caps (trans->sinkpad); + prev_outcaps = gst_pad_get_current_caps (trans->srcpad); + if (prev_incaps && prev_outcaps && gst_caps_is_equal (prev_incaps, incaps) + && gst_caps_is_equal (prev_outcaps, outcaps)) { + GST_DEBUG_OBJECT (trans, + "New caps equal to old ones: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT, + incaps, outcaps); + ret = TRUE; + } else { + /* call configure now */ + if (!(ret = gst_base_transform_configure_caps (trans, incaps, outcaps))) + goto failed_configure; - /* let downstream know about our caps */ - ret = gst_pad_set_caps (trans->srcpad, outcaps); + if (!prev_outcaps || !gst_caps_is_equal (outcaps, prev_outcaps)) + /* let downstream know about our caps */ + ret = gst_pad_set_caps (trans->srcpad, outcaps); + } if (ret) { /* try to get a pool when needed */ @@ -1328,6 +1345,10 @@ gst_base_transform_setcaps (GstBaseTransform * trans, GstPad * pad, done: if (outcaps) gst_caps_unref (outcaps); + if (prev_incaps) + gst_caps_unref (prev_incaps); + if (prev_outcaps) + gst_caps_unref (prev_outcaps); GST_OBJECT_LOCK (trans); priv->negotiated = ret; @@ -1381,6 +1402,41 @@ gst_base_transform_default_propose_allocation (GstBaseTransform * trans, } static gboolean +gst_base_transform_reconfigure (GstBaseTransform * trans) +{ + gboolean reconfigure, ret = TRUE; + + reconfigure = gst_pad_check_reconfigure (trans->srcpad); + + if (G_UNLIKELY (reconfigure)) { + GstCaps *incaps; + + GST_DEBUG_OBJECT (trans, "we had a pending reconfigure"); + + incaps = gst_pad_get_current_caps (trans->sinkpad); + if (incaps == NULL) + goto done; + + /* if we need to reconfigure we pretend new caps arrived. This + * will reconfigure the transform with the new output format. */ + if (!gst_base_transform_setcaps (trans, trans->sinkpad, incaps)) { + GST_ELEMENT_WARNING (trans, STREAM, FORMAT, + ("not negotiated"), ("not negotiated")); + ret = FALSE; + } + + gst_caps_unref (incaps); + } + +done: + + if (!ret) + gst_pad_mark_reconfigure (trans->srcpad); + + return ret; +} + +static gboolean gst_base_transform_default_query (GstBaseTransform * trans, GstPadDirection direction, GstQuery * query) { @@ -1403,21 +1459,25 @@ gst_base_transform_default_query (GstBaseTransform * trans, case GST_QUERY_ALLOCATION: { GstQuery *decide_query = NULL; - gboolean negotiated; /* can only be done on the sinkpad */ if (direction != GST_PAD_SINK) goto done; + ret = gst_base_transform_reconfigure (trans); + if (G_UNLIKELY (!ret)) + goto done; + GST_OBJECT_LOCK (trans); - if (G_UNLIKELY (!(negotiated = priv->negotiated))) { + if (!priv->negotiated && !priv->passthrough && (klass->set_caps != NULL)) { GST_DEBUG_OBJECT (trans, - "not negotiated yet, can't answer ALLOCATION query"); + "not negotiated yet but need negotiation, can't answer ALLOCATION query"); GST_OBJECT_UNLOCK (trans); goto done; } - if ((decide_query = trans->priv->query)) - gst_query_ref (decide_query); + + decide_query = trans->priv->query; + trans->priv->query = NULL; GST_OBJECT_UNLOCK (trans); GST_DEBUG_OBJECT (trans, @@ -1430,8 +1490,16 @@ gst_base_transform_default_query (GstBaseTransform * trans, else ret = FALSE; - if (decide_query) - gst_query_unref (decide_query); + if (decide_query) { + GST_OBJECT_LOCK (trans); + + if (trans->priv->query == NULL) + trans->priv->query = decide_query; + else + gst_query_unref (decide_query); + + GST_OBJECT_UNLOCK (trans); + } GST_DEBUG_OBJECT (trans, "ALLOCATION ret %d, %" GST_PTR_FORMAT, ret, query); @@ -1501,7 +1569,7 @@ gst_base_transform_query (GstPad * pad, GstObject * parent, GstQuery * query) GstBaseTransformClass *bclass; gboolean ret = FALSE; - trans = GST_BASE_TRANSFORM (parent); + trans = GST_BASE_TRANSFORM_CAST (parent); bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); if (bclass->query) @@ -1517,7 +1585,7 @@ default_prepare_output_buffer (GstBaseTransform * trans, GstBuffer * inbuf, GstBuffer ** outbuf) { GstBaseTransformPrivate *priv; - GstFlowReturn ret = GST_FLOW_OK; + GstFlowReturn ret; GstBaseTransformClass *bclass; GstCaps *incaps, *outcaps; gsize insize, outsize; @@ -1528,7 +1596,7 @@ default_prepare_output_buffer (GstBaseTransform * trans, /* figure out how to allocate an output buffer */ if (priv->passthrough) { - /* passthrough, we will not modify the incomming buffer so we can just + /* passthrough, we will not modify the incoming buffer so we can just * reuse it */ GST_DEBUG_OBJECT (trans, "passthrough: reusing input buffer"); *outbuf = inbuf; @@ -1545,6 +1613,9 @@ default_prepare_output_buffer (GstBaseTransform * trans, } GST_DEBUG_OBJECT (trans, "using pool alloc"); ret = gst_buffer_pool_acquire_buffer (priv->pool, outbuf, NULL); + if (ret != GST_FLOW_OK) + goto alloc_failed; + goto copy_meta; } @@ -1566,6 +1637,10 @@ default_prepare_output_buffer (GstBaseTransform * trans, incaps = gst_pad_get_current_caps (trans->sinkpad); outcaps = gst_pad_get_current_caps (trans->srcpad); + /* srcpad might be flushing already if we're being shut down */ + if (outcaps == NULL) + goto no_outcaps; + GST_DEBUG_OBJECT (trans, "getting output size for alloc"); /* copy transform, figure out the output size */ insize = gst_buffer_get_size (inbuf); @@ -1580,6 +1655,10 @@ default_prepare_output_buffer (GstBaseTransform * trans, GST_DEBUG_OBJECT (trans, "doing alloc of size %" G_GSIZE_FORMAT, outsize); *outbuf = gst_buffer_new_allocate (priv->allocator, outsize, &priv->params); + if (!*outbuf) { + ret = GST_FLOW_ERROR; + goto alloc_failed; + } copy_meta: /* copy the metadata */ @@ -1591,10 +1670,9 @@ copy_meta: } done: - return ret; + return GST_FLOW_OK; /* ERRORS */ - /* ERRORS */ activate_failed: { GST_ELEMENT_ERROR (trans, RESOURCE, SETTINGS, @@ -1606,6 +1684,17 @@ unknown_size: GST_ERROR_OBJECT (trans, "unknown output size"); return GST_FLOW_ERROR; } +alloc_failed: + { + GST_DEBUG_OBJECT (trans, "could not allocate buffer from pool"); + return ret; + } +no_outcaps: + { + GST_DEBUG_OBJECT (trans, "no output caps, source pad has been deactivated"); + gst_caps_unref (incaps); + return GST_FLOW_FLUSHING; + } } typedef struct @@ -1622,16 +1711,11 @@ foreach_metadata (GstBuffer * inbuf, GstMeta ** meta, gpointer user_data) GstBaseTransformClass *klass; const GstMetaInfo *info = (*meta)->info; GstBuffer *outbuf = data->outbuf; - gboolean do_copy; + gboolean do_copy = FALSE; klass = GST_BASE_TRANSFORM_GET_CLASS (trans); - if (GST_META_FLAG_IS_SET (*meta, GST_META_FLAG_POOLED)) { - /* never call the transform_meta with pool private metadata */ - GST_DEBUG_OBJECT (trans, "not copying pooled metadata %s", - g_type_name (info->api)); - do_copy = FALSE; - } else if (gst_meta_api_type_has_tag (info->api, GST_META_TAG_MEMORY)) { + if (gst_meta_api_type_has_tag (info->api, _gst_meta_tag_memory)) { /* never call the transform_meta with memory specific metadata */ GST_DEBUG_OBJECT (trans, "not copying memory specific metadata %s", g_type_name (info->api)); @@ -1640,20 +1724,21 @@ foreach_metadata (GstBuffer * inbuf, GstMeta ** meta, gpointer user_data) do_copy = klass->transform_meta (trans, outbuf, *meta, inbuf); GST_DEBUG_OBJECT (trans, "transformed metadata %s: copy: %d", g_type_name (info->api), do_copy); - } else { - do_copy = FALSE; - GST_DEBUG_OBJECT (trans, "not copying metadata %s", - g_type_name (info->api)); } /* we only copy metadata when the subclass implemented a transform_meta - * function and when it returns TRUE */ + * function and when it returns %TRUE */ if (do_copy) { GstMetaTransformCopy copy_data = { FALSE, 0, -1 }; - GST_DEBUG_OBJECT (trans, "copy metadata %s", g_type_name (info->api)); /* simply copy then */ - info->transform_func (outbuf, *meta, inbuf, - _gst_meta_transform_copy, ©_data); + if (info->transform_func) { + GST_DEBUG_OBJECT (trans, "copy metadata %s", g_type_name (info->api)); + info->transform_func (outbuf, *meta, inbuf, + _gst_meta_transform_copy, ©_data); + } else { + GST_DEBUG_OBJECT (trans, "couldn't copy metadata %s", + g_type_name (info->api)); + } } return TRUE; } @@ -1697,7 +1782,7 @@ not_writable: } } -/* Given @caps calcultate the size of one unit. +/* Given @caps calculate the size of one unit. * * For video caps, this is the size of one frame (and thus one buffer). * For audio caps, this is the size of one sample. @@ -1708,7 +1793,7 @@ not_writable: * We have two cache locations to store the size, one for the source caps * and one for the sink caps. * - * this function returns FALSE if no size could be calculated. + * this function returns %FALSE if no size could be calculated. */ static gboolean gst_base_transform_get_unit_size (GstBaseTransform * trans, GstCaps * caps, @@ -1735,7 +1820,7 @@ gst_base_transform_get_unit_size (GstBaseTransform * trans, GstCaps * caps, bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); res = bclass->get_unit_size (trans, caps, size); GST_DEBUG_OBJECT (trans, - "caps %" GST_PTR_FORMAT ") has unit size %" G_GSIZE_FORMAT ", res %s", + "caps %" GST_PTR_FORMAT " has unit size %" G_GSIZE_FORMAT ", res %s", caps, *size, res ? "TRUE" : "FALSE"); if (res) { @@ -1765,7 +1850,7 @@ gst_base_transform_sink_event (GstPad * pad, GstObject * parent, GstBaseTransformClass *bclass; gboolean ret = TRUE; - trans = GST_BASE_TRANSFORM (parent); + trans = GST_BASE_TRANSFORM_CAST (parent); bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); if (bclass->sink_event) @@ -1811,6 +1896,8 @@ gst_base_transform_sink_eventfunc (GstBaseTransform * trans, GstEvent * event) /* clear any pending reconfigure flag */ gst_pad_check_reconfigure (trans->srcpad); ret = gst_base_transform_setcaps (trans, trans->sinkpad, caps); + if (!ret) + gst_pad_mark_reconfigure (trans->srcpad); forward = FALSE; break; @@ -1844,7 +1931,7 @@ gst_base_transform_src_event (GstPad * pad, GstObject * parent, GstBaseTransformClass *bclass; gboolean ret = TRUE; - trans = GST_BASE_TRANSFORM (parent); + trans = GST_BASE_TRANSFORM_CAST (parent); bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); if (bclass->src_event) @@ -1886,55 +1973,30 @@ 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 */ +/* Takes the input buffer */ static GstFlowReturn -gst_base_transform_handle_buffer (GstBaseTransform * trans, GstBuffer * inbuf, - GstBuffer ** outbuf) +default_submit_input_buffer (GstBaseTransform * trans, gboolean is_discont, + GstBuffer * inbuf) { - GstBaseTransformClass *bclass; + GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); GstBaseTransformPrivate *priv = trans->priv; GstFlowReturn ret = GST_FLOW_OK; - gboolean want_in_place; GstClockTime running_time; GstClockTime timestamp; - gboolean reconfigure; - - bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - reconfigure = gst_pad_check_reconfigure (trans->srcpad); - - if (G_UNLIKELY (reconfigure)) { - GstCaps *incaps; - - GST_DEBUG_OBJECT (trans, "we had a pending reconfigure"); - - incaps = gst_pad_get_current_caps (trans->sinkpad); - if (incaps == NULL) - goto no_reconfigure; - /* if we need to reconfigure we pretend new caps arrived. This - * will reconfigure the transform with the new output format. */ - if (!gst_base_transform_setcaps (trans, trans->sinkpad, incaps)) { - gst_caps_unref (incaps); - goto not_negotiated; - } - gst_caps_unref (incaps); - } + if (G_UNLIKELY (!gst_base_transform_reconfigure (trans))) + goto not_negotiated; -no_reconfigure: if (GST_BUFFER_OFFSET_IS_VALID (inbuf)) GST_DEBUG_OBJECT (trans, - "handling buffer %p of size %" G_GSIZE_FORMAT " and offset %" - G_GUINT64_FORMAT, inbuf, gst_buffer_get_size (inbuf), - GST_BUFFER_OFFSET (inbuf)); + "handling buffer %p of size %" G_GSIZE_FORMAT ", PTS %" GST_TIME_FORMAT + " and offset %" G_GUINT64_FORMAT, inbuf, gst_buffer_get_size (inbuf), + GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)), GST_BUFFER_OFFSET (inbuf)); else GST_DEBUG_OBJECT (trans, - "handling buffer %p of size %" G_GSIZE_FORMAT " and offset NONE", inbuf, - gst_buffer_get_size (inbuf)); + "handling buffer %p of size %" G_GSIZE_FORMAT ", PTS %" GST_TIME_FORMAT + " and offset NONE", inbuf, gst_buffer_get_size (inbuf), + GST_TIME_ARGS (GST_BUFFER_PTS (inbuf))); /* Don't allow buffer handling before negotiation, except in passthrough mode * or if the class doesn't implement a set_caps function (in which case it doesn't @@ -1943,12 +2005,6 @@ no_reconfigure: if (!priv->negotiated && !priv->passthrough && (bclass->set_caps != NULL)) goto not_negotiated; - /* Set discont flag so we can mark the outgoing buffer */ - if (GST_BUFFER_IS_DISCONT (inbuf)) { - GST_DEBUG_OBJECT (trans, "got DISCONT buffer %p", inbuf); - priv->discont = TRUE; - } - /* can only do QoS if the segment is in TIME */ if (trans->segment.format != GST_FORMAT_TIME) goto no_qos; @@ -2002,11 +2058,49 @@ no_reconfigure: /* mark discont for next buffer */ priv->discont = TRUE; + ret = GST_BASE_TRANSFORM_FLOW_DROPPED; goto skip; } } no_qos: + /* Stash input buffer where the default generate_output + * function can find it */ + if (trans->queued_buf) + gst_buffer_unref (trans->queued_buf); + trans->queued_buf = inbuf; + return ret; +skip: + gst_buffer_unref (inbuf); + return ret; + +not_negotiated: + { + gst_buffer_unref (inbuf); + if (GST_PAD_IS_FLUSHING (trans->srcpad)) + return GST_FLOW_FLUSHING; + return GST_FLOW_NOT_NEGOTIATED; + } +} + +static GstFlowReturn +default_generate_output (GstBaseTransform * trans, GstBuffer ** outbuf) +{ + GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); + GstBaseTransformPrivate *priv = trans->priv; + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *inbuf; + gboolean want_in_place; + + /* Retrieve stashed input buffer, if the default submit_input_buffer + * was run. Takes ownership back from there */ + inbuf = trans->queued_buf; + trans->queued_buf = NULL; + + /* This default processing method needs one input buffer to feed to + * the transform functions, we can't do anything without it */ + if (inbuf == NULL) + return GST_FLOW_OK; /* first try to allocate an output buffer based on the currently negotiated * format. outbuf will contain a buffer suitable for doing the configured @@ -2050,7 +2144,6 @@ no_qos: } } -skip: /* only unref input buffer if we allocated a new outbuf buffer. If we reused * the input buffer, no refcount is changed to keep the input buffer writable * when needed. */ @@ -2060,14 +2153,6 @@ skip: return ret; /* ERRORS */ -not_negotiated: - { - gst_buffer_unref (inbuf); - *outbuf = NULL; - GST_ELEMENT_ERROR (trans, STREAM, FORMAT, - ("not negotiated"), ("not negotiated")); - return GST_FLOW_NOT_NEGOTIATED; - } no_prepare: { gst_buffer_unref (inbuf); @@ -2092,23 +2177,65 @@ static GstFlowReturn gst_base_transform_getrange (GstPad * pad, GstObject * parent, guint64 offset, guint length, GstBuffer ** buffer) { - GstBaseTransform *trans; - GstBaseTransformClass *klass; + GstBaseTransformClass *klass = GST_BASE_TRANSFORM_GET_CLASS (parent); + GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (parent); + GstBaseTransformPrivate *priv = trans->priv; GstFlowReturn ret; GstBuffer *inbuf = NULL; + GstBuffer *outbuf = NULL; - trans = GST_BASE_TRANSFORM (parent); + /* Try and generate a buffer, if the sub-class wants more data, + * pull some and repeat until a buffer (or error) is produced */ + do { + ret = klass->generate_output (trans, &outbuf); - ret = gst_pad_pull_range (trans->sinkpad, offset, length, &inbuf); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto pull_error; + /* Consume the DROPPED return value and go get more data */ + if (ret == GST_BASE_TRANSFORM_FLOW_DROPPED) + ret = GST_FLOW_OK; - klass = GST_BASE_TRANSFORM_GET_CLASS (trans); - if (klass->before_transform) - klass->before_transform (trans, inbuf); + if (ret != GST_FLOW_OK || outbuf != NULL) + break; + + /* No buffer generated, try and pull data */ + ret = gst_pad_pull_range (trans->sinkpad, offset, length, &inbuf); + if (G_UNLIKELY (ret != GST_FLOW_OK)) + goto pull_error; + + if (klass->before_transform) + klass->before_transform (trans, inbuf); + + /* Set discont flag so we can mark the next outgoing buffer */ + if (GST_BUFFER_IS_DISCONT (inbuf)) { + GST_DEBUG_OBJECT (trans, "got DISCONT buffer %p", inbuf); + priv->discont = TRUE; + } - ret = gst_base_transform_handle_buffer (trans, inbuf, buffer); + /* FIXME: Input offsets and lengths need to be translated, as per + * the FIXME above. For now, just advance somewhat */ + offset += gst_buffer_get_size (inbuf); + ret = klass->submit_input_buffer (trans, priv->discont, inbuf); + if (ret != GST_FLOW_OK) { + if (ret == GST_BASE_TRANSFORM_FLOW_DROPPED) + ret = GST_FLOW_OK; + goto done; + } + } while (ret == GST_FLOW_OK && outbuf == NULL); + + *buffer = outbuf; + if (outbuf) { + /* apply DISCONT flag if the buffer is not yet marked as such */ + if (priv->discont) { + GST_DEBUG_OBJECT (trans, "we have a pending DISCONT"); + if (!GST_BUFFER_IS_DISCONT (outbuf)) { + GST_DEBUG_OBJECT (trans, "marking DISCONT on output buffer"); + outbuf = gst_buffer_make_writable (outbuf); + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + } + priv->discont = FALSE; + } + priv->processed++; + } done: return ret; @@ -2121,20 +2248,21 @@ pull_error: } } +/* The flow of the chain function is the reverse of the + * getrange() function - we have data, feed it to the sub-class + * and then iterate, pushing buffers it generates until it either + * wants more data or returns an error */ static GstFlowReturn gst_base_transform_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) { - GstBaseTransform *trans; - GstBaseTransformClass *klass; - GstBaseTransformPrivate *priv; + GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (parent); + GstBaseTransformClass *klass = GST_BASE_TRANSFORM_GET_CLASS (trans); + GstBaseTransformPrivate *priv = trans->priv; GstFlowReturn ret; GstClockTime position = GST_CLOCK_TIME_NONE; GstClockTime timestamp, duration; GstBuffer *outbuf = NULL; - trans = GST_BASE_TRANSFORM (parent); - priv = trans->priv; - timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); @@ -2146,54 +2274,68 @@ gst_base_transform_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) position = timestamp; } - klass = GST_BASE_TRANSFORM_GET_CLASS (trans); if (klass->before_transform) klass->before_transform (trans, buffer); - /* protect transform method and concurrent buffer alloc */ - ret = gst_base_transform_handle_buffer (trans, buffer, &outbuf); - - /* 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. */ - if (outbuf != NULL) { - if (ret == GST_FLOW_OK) { - GstClockTime position_out = GST_CLOCK_TIME_NONE; - - /* Remember last stop position */ - if (position != GST_CLOCK_TIME_NONE && - trans->segment.format == GST_FORMAT_TIME) - trans->segment.position = position; - - if (GST_BUFFER_TIMESTAMP_IS_VALID (outbuf)) { - position_out = GST_BUFFER_TIMESTAMP (outbuf); - if (GST_BUFFER_DURATION_IS_VALID (outbuf)) - position_out += GST_BUFFER_DURATION (outbuf); - } else if (position != GST_CLOCK_TIME_NONE) { - position_out = position; - } - if (position_out != GST_CLOCK_TIME_NONE - && trans->segment.format == GST_FORMAT_TIME) - priv->position_out = position_out; - - /* apply DISCONT flag if the buffer is not yet marked as such */ - if (trans->priv->discont) { - GST_DEBUG_OBJECT (trans, "we have a pending DISCONT"); - if (!GST_BUFFER_IS_DISCONT (outbuf)) { - GST_DEBUG_OBJECT (trans, "marking DISCONT on output buffer"); - outbuf = gst_buffer_make_writable (outbuf); - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + /* Set discont flag so we can mark the outgoing buffer */ + if (GST_BUFFER_IS_DISCONT (buffer)) { + GST_DEBUG_OBJECT (trans, "got DISCONT buffer %p", buffer); + priv->discont = TRUE; + } + + /* Takes ownership of input buffer */ + ret = klass->submit_input_buffer (trans, priv->discont, buffer); + if (ret != GST_FLOW_OK) + goto done; + + do { + outbuf = NULL; + + ret = klass->generate_output (trans, &outbuf); + + /* 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. */ + if (outbuf != NULL) { + if (ret == GST_FLOW_OK) { + GstClockTime position_out = GST_CLOCK_TIME_NONE; + + /* Remember last stop position */ + if (position != GST_CLOCK_TIME_NONE && + trans->segment.format == GST_FORMAT_TIME) + trans->segment.position = position; + + if (GST_BUFFER_TIMESTAMP_IS_VALID (outbuf)) { + position_out = GST_BUFFER_TIMESTAMP (outbuf); + if (GST_BUFFER_DURATION_IS_VALID (outbuf)) + position_out += GST_BUFFER_DURATION (outbuf); + } else if (position != GST_CLOCK_TIME_NONE) { + position_out = position; } - priv->discont = FALSE; - } - priv->processed++; + if (position_out != GST_CLOCK_TIME_NONE + && trans->segment.format == GST_FORMAT_TIME) + priv->position_out = position_out; + + /* apply DISCONT flag if the buffer is not yet marked as such */ + if (trans->priv->discont) { + GST_DEBUG_OBJECT (trans, "we have a pending DISCONT"); + if (!GST_BUFFER_IS_DISCONT (outbuf)) { + GST_DEBUG_OBJECT (trans, "marking DISCONT on output buffer"); + outbuf = gst_buffer_make_writable (outbuf); + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + } + priv->discont = FALSE; + } + priv->processed++; - ret = gst_pad_push (trans->srcpad, outbuf); - } else { - GST_DEBUG_OBJECT (trans, "we got return %s", gst_flow_get_name (ret)); - gst_buffer_unref (outbuf); + ret = gst_pad_push (trans->srcpad, outbuf); + } else { + GST_DEBUG_OBJECT (trans, "we got return %s", gst_flow_get_name (ret)); + gst_buffer_unref (outbuf); + } } - } + } while (ret == GST_FLOW_OK && outbuf != NULL); +done: /* convert internal flow to OK and mark discont for the next buffer. */ if (ret == GST_BASE_TRANSFORM_FLOW_DROPPED) { GST_DEBUG_OBJECT (trans, "dropped a buffer, marking DISCONT"); @@ -2210,7 +2352,7 @@ gst_base_transform_set_property (GObject * object, guint prop_id, { GstBaseTransform *trans; - trans = GST_BASE_TRANSFORM (object); + trans = GST_BASE_TRANSFORM_CAST (object); switch (prop_id) { case PROP_QOS: @@ -2228,7 +2370,7 @@ gst_base_transform_get_property (GObject * object, guint prop_id, { GstBaseTransform *trans; - trans = GST_BASE_TRANSFORM (object); + trans = GST_BASE_TRANSFORM_CAST (object); switch (prop_id) { case PROP_QOS: @@ -2288,7 +2430,7 @@ gst_base_transform_activate (GstBaseTransform * trans, gboolean active) GST_PAD_STREAM_UNLOCK (trans->sinkpad); priv->have_same_caps = FALSE; - /* We can only reset the passthrough mode if the instance told us to + /* We can only reset the passthrough mode if the instance told us to handle it in configure_caps */ if (bclass->passthrough_on_same_caps) { gst_base_transform_set_passthrough (trans, FALSE); @@ -2296,6 +2438,9 @@ gst_base_transform_activate (GstBaseTransform * trans, gboolean active) gst_caps_replace (&priv->cache_caps1, NULL); gst_caps_replace (&priv->cache_caps2, NULL); + /* Make sure any left over buffer is freed */ + gst_buffer_replace (&trans->queued_buf, NULL); + if (priv->pad_mode != GST_PAD_MODE_NONE && bclass->stop) result &= bclass->stop (trans); @@ -2312,7 +2457,7 @@ gst_base_transform_sink_activate_mode (GstPad * pad, GstObject * parent, gboolean result = FALSE; GstBaseTransform *trans; - trans = GST_BASE_TRANSFORM (parent); + trans = GST_BASE_TRANSFORM_CAST (parent); switch (mode) { case GST_PAD_MODE_PUSH: @@ -2338,7 +2483,7 @@ gst_base_transform_src_activate_mode (GstPad * pad, GstObject * parent, gboolean result = FALSE; GstBaseTransform *trans; - trans = GST_BASE_TRANSFORM (parent); + trans = GST_BASE_TRANSFORM_CAST (parent); switch (mode) { case GST_PAD_MODE_PULL: @@ -2369,7 +2514,7 @@ gst_base_transform_src_activate_mode (GstPad * pad, GstObject * parent, * Set passthrough mode for this filter by default. This is mostly * useful for filters that do not care about negotiation. * - * Always TRUE for filters which don't implement either a transform + * Always %TRUE for filters which don't implement either a transform * or transform_ip method. * * MT safe. @@ -2385,7 +2530,7 @@ gst_base_transform_set_passthrough (GstBaseTransform * trans, bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); GST_OBJECT_LOCK (trans); - if (passthrough == FALSE) { + if (!passthrough) { if (bclass->transform_ip || bclass->transform) trans->priv->passthrough = FALSE; } else { @@ -2402,7 +2547,7 @@ gst_base_transform_set_passthrough (GstBaseTransform * trans, * * See if @trans is configured as a passthrough transform. * - * Returns: TRUE is the transform is configured in passthrough mode. + * Returns: %TRUE is the transform is configured in passthrough mode. * * MT safe. */ @@ -2428,10 +2573,9 @@ gst_base_transform_is_passthrough (GstBaseTransform * trans) * * Determines whether a non-writable buffer will be copied before passing * to the transform_ip function. - * - * Always TRUE if no transform function is implemented. - * Always FALSE if ONLY transform function is implemented. - * + * + * * Always %TRUE if no transform function is implemented. + * * Always %FALSE if ONLY transform function is implemented. * * MT safe. */ @@ -2467,7 +2611,7 @@ gst_base_transform_set_in_place (GstBaseTransform * trans, gboolean in_place) * * See if @trans is configured as a in_place transform. * - * Returns: TRUE is the transform is configured in in_place mode. + * Returns: %TRUE is the transform is configured in in_place mode. * * MT safe. */ @@ -2498,8 +2642,6 @@ gst_base_transform_is_in_place (GstBaseTransform * trans) * when needed. * * MT safe. - * - * Since: 0.10.5 */ void gst_base_transform_update_qos (GstBaseTransform * trans, @@ -2525,8 +2667,6 @@ gst_base_transform_update_qos (GstBaseTransform * trans, * Enable or disable QoS handling in the transform. * * MT safe. - * - * Since: 0.10.5 */ void gst_base_transform_set_qos_enabled (GstBaseTransform * trans, gboolean enabled) @@ -2546,11 +2686,9 @@ gst_base_transform_set_qos_enabled (GstBaseTransform * trans, gboolean enabled) * * Queries if the transform will handle QoS. * - * Returns: TRUE if QoS is enabled. + * Returns: %TRUE if QoS is enabled. * * MT safe. - * - * Since: 0.10.5 */ gboolean gst_base_transform_is_qos_enabled (GstBaseTransform * trans) @@ -2579,8 +2717,6 @@ gst_base_transform_is_qos_enabled (GstBaseTransform * trans) * unset the flag if the output is no neutral data. * * MT safe. - * - * Since: 0.10.16 */ void gst_base_transform_set_gap_aware (GstBaseTransform * trans, gboolean gap_aware) @@ -2594,6 +2730,37 @@ gst_base_transform_set_gap_aware (GstBaseTransform * trans, gboolean gap_aware) } /** + * gst_base_transform_set_prefer_passthrough: + * @trans: a #GstBaseTransform + * @prefer_passthrough: New state + * + * If @prefer_passthrough is %TRUE (the default), @trans will check and + * prefer passthrough caps from the list of caps returned by the + * transform_caps vmethod. + * + * If set to %FALSE, the element must order the caps returned from the + * transform_caps function in such a way that the preferred format is + * first in the list. This can be interesting for transforms that can do + * passthrough transforms but prefer to do something else, like a + * capsfilter. + * + * MT safe. + * + * Since: 1.0.1 + */ +void +gst_base_transform_set_prefer_passthrough (GstBaseTransform * trans, + gboolean prefer_passthrough) +{ + g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); + + GST_OBJECT_LOCK (trans); + trans->priv->prefer_passthrough = prefer_passthrough; + GST_DEBUG_OBJECT (trans, "prefer passthrough %d", prefer_passthrough); + GST_OBJECT_UNLOCK (trans); +} + +/** * gst_base_transform_reconfigure_sink: * @trans: a #GstBaseTransform * @@ -2619,8 +2786,6 @@ gst_base_transform_reconfigure_sink (GstBaseTransform * trans) * Instructs @trans to renegotiate a new downstream transform on the next * buffer. This function is typically called after properties on the transform * were set that influence the output format. - * - * Since: 0.10.21 */ void gst_base_transform_reconfigure_src (GstBaseTransform * trans) @@ -2629,3 +2794,80 @@ gst_base_transform_reconfigure_src (GstBaseTransform * trans) gst_pad_mark_reconfigure (trans->srcpad); } + +/** + * gst_base_transform_get_buffer_pool: + * @trans: a #GstBaseTransform + * + * Returns: (transfer full): the instance of the #GstBufferPool used + * by @trans; free it after use it + */ +GstBufferPool * +gst_base_transform_get_buffer_pool (GstBaseTransform * trans) +{ + g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), NULL); + + if (trans->priv->pool) + return gst_object_ref (trans->priv->pool); + + return NULL; +} + +/** + * gst_base_transform_get_allocator: + * @trans: a #GstBaseTransform + * @allocator: (out) (allow-none) (transfer full): the #GstAllocator + * used + * @params: (out) (allow-none) (transfer full): the + * #GstAllocationParams of @allocator + * + * Lets #GstBaseTransform sub-classes to know the memory @allocator + * used by the base class and its @params. + * + * Unref the @allocator after use it. + */ +void +gst_base_transform_get_allocator (GstBaseTransform * trans, + GstAllocator ** allocator, GstAllocationParams * params) +{ + g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); + + if (allocator) + *allocator = trans->priv->allocator ? + gst_object_ref (trans->priv->allocator) : NULL; + + if (params) + *params = trans->priv->params; +} + +/** + * gst_base_transform_update_src_caps: + * @trans: a #GstBaseTransform + * @updated_caps: An updated version of the srcpad caps to be pushed + * downstream + * + * Updates the srcpad caps and send the caps downstream. This function + * can be used by subclasses when they have already negotiated their caps + * but found a change in them (or computed new information). This way, + * they can notify downstream about that change without losing any + * buffer. + * + * Returns: %TRUE if the caps could be send downstream %FALSE otherwise + * + * Since: 1.6 + */ +gboolean +gst_base_transform_update_src_caps (GstBaseTransform * trans, + GstCaps * updated_caps) +{ + g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE); + + if (gst_pad_push_event (GST_BASE_TRANSFORM_SRC_PAD (trans), + gst_event_new_caps (updated_caps))) { + gst_pad_mark_reconfigure (trans->srcpad); + + return TRUE; + } + + return FALSE; +}