From: Jan Schmidt Date: Fri, 9 Sep 2005 17:42:20 +0000 (+0000) Subject: check/elements/identity.c: Make the error a little clearer when the test fails becaus... X-Git-Tag: RELEASE-0_9_3~177 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6ed5bee3b03f8c661cdf8d865bcc206b4fab469a;p=platform%2Fupstream%2Fgstreamer.git check/elements/identity.c: Make the error a little clearer when the test fails because identity made a copy of the bu... Original commit message from CVS: * check/elements/identity.c: (GST_START_TEST): Make the error a little clearer when the test fails because identity made a copy of the buffer. * docs/gst/gstreamer-sections.txt: New symbols in gstbasetransform.h * gst/base/gstbasetransform.c: (gst_base_transform_class_init), (gst_base_transform_init), (gst_base_transform_transform_size), (gst_base_transform_configure_caps), (gst_base_transform_setcaps), (gst_base_transform_default_prepare_buf), (gst_base_transform_get_unit_size), (gst_base_transform_buffer_alloc), (gst_base_transform_handle_buffer), (gst_base_transform_chain), (gst_base_transform_change_state), (gst_base_transform_set_passthrough), (gst_base_transform_set_in_place), (gst_base_transform_is_in_place): * gst/base/gstbasetransform.h: Change BaseTransform to separate in_place operate from same_caps output. in_place implies that the element can perform the transform on incoming buffers in-place, even if the caps on the output are different. Sub-class elements can now implement special buffer allocation methods for outgoing buffers if they wish to. Big documentation addition. * gst/elements/gstcapsfilter.c: (gst_capsfilter_transform_ip): * gst/elements/gstelements.c: Changes for basetransform modifications. * gst/elements/Makefile.am: * gst/elements/gstfdsrc.c: (gst_fdsrc_init), (gst_fdsrc_create): Compile fix. Extra debug output. --- diff --git a/ChangeLog b/ChangeLog index 468937b..772b029 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,35 @@ +2005-09-09 Jan Schmidt + * check/elements/identity.c: (GST_START_TEST): + Make the error a little clearer when the test fails because + identity made a copy of the buffer. + * docs/gst/gstreamer-sections.txt: + New symbols in gstbasetransform.h + * gst/base/gstbasetransform.c: (gst_base_transform_class_init), + (gst_base_transform_init), (gst_base_transform_transform_size), + (gst_base_transform_configure_caps), (gst_base_transform_setcaps), + (gst_base_transform_default_prepare_buf), + (gst_base_transform_get_unit_size), + (gst_base_transform_buffer_alloc), + (gst_base_transform_handle_buffer), (gst_base_transform_chain), + (gst_base_transform_change_state), + (gst_base_transform_set_passthrough), + (gst_base_transform_set_in_place), + (gst_base_transform_is_in_place): + * gst/base/gstbasetransform.h: + Change BaseTransform to separate in_place operate from same_caps + output. in_place implies that the element can perform the transform + on incoming buffers in-place, even if the caps on the output are + different. + Sub-class elements can now implement special buffer allocation + methods for outgoing buffers if they wish to. + Big documentation addition. + * gst/elements/gstcapsfilter.c: (gst_capsfilter_transform_ip): + * gst/elements/gstelements.c: + Changes for basetransform modifications. + * gst/elements/Makefile.am: + * gst/elements/gstfdsrc.c: (gst_fdsrc_init), (gst_fdsrc_create): + Compile fix. Extra debug output. + 2005-09-09 Thomas Vander Stichele * check/gst/gstpad.c: (GST_START_TEST), (name_is_valid), diff --git a/check/elements/identity.c b/check/elements/identity.c index eb5f41e..c5ce61a 100644 --- a/check/elements/identity.c +++ b/check/elements/identity.c @@ -101,7 +101,9 @@ GST_START_TEST (test_one_buffer) memcpy (GST_BUFFER_DATA (buffer), "data", 4); /* pushing gives away my reference ... */ gst_pad_push (mysrcpad, buffer); - /* ... but it ends up being collected on the global buffer list */ + /* ... but it should end up being collected on the global buffer list */ + fail_unless (g_list_length (buffers) == 1); + fail_unless ((GstBuffer *) (g_list_first (buffers)->data) == buffer); ASSERT_BUFFER_REFCOUNT (buffer, "buffer", 1); /* cleanup */ diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index 385e5ec..43b5485 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -2296,6 +2296,8 @@ GstBaseTransformClass gst_base_transform_is_passthrough gst_base_transform_set_passthrough +gst_base_transform_is_in_place +gst_base_transform_set_in_place GST_BASE_TRANSFORM_SINK_NAME GST_BASE_TRANSFORM_SRC_NAME diff --git a/gst/base/gstbasetransform.c b/gst/base/gstbasetransform.c index dc3d574..2651d9d 100644 --- a/gst/base/gstbasetransform.c +++ b/gst/base/gstbasetransform.c @@ -28,28 +28,174 @@ * * This base class is for filter elements that process data. * + * It provides for: * * one sinkpad and one srcpad * - * possible formats on sink and source pad implemented + * 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 + * Handles state changes + * Does flushing + * Push mode * - * pull mode if transform can operate on arbitrary data + * 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 + * 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. + * + * + * + * + * + * Retrictions on sub-class behaviour + * + * Sub-classes which override the prepare_output_buffer function need to call + * the parent implementation if they allocate a new buffer, which will copy + * buffer flags onto the new buffer. + * + * + * */ -#include - #ifdef HAVE_CONFIG_H # include "config.h" #endif +#include +#include + #include "../gst-i18n-lib.h" #include "gstbasetransform.h" #include @@ -75,7 +221,9 @@ static GstElementClass *parent_class = NULL; static void gst_base_transform_base_init (gpointer g_class); static void gst_base_transform_class_init (GstBaseTransformClass * klass); static void gst_base_transform_init (GstBaseTransform * trans, - gpointer g_class); + GstBaseTransformClass * klass); +static GstFlowReturn gst_base_transform_default_prepare_buf (GstBaseTransform * + trans, GstBuffer * input, gint size, GstCaps * caps, GstBuffer ** buf); GType gst_base_transform_get_type (void) @@ -161,17 +309,22 @@ gst_base_transform_class_init (GstBaseTransformClass * klass) gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_base_transform_change_state); + + klass->prepare_output_buffer = + GST_DEBUG_FUNCPTR (gst_base_transform_default_prepare_buf); + klass->passthrough_on_same_caps = FALSE; } static void -gst_base_transform_init (GstBaseTransform * trans, gpointer g_class) +gst_base_transform_init (GstBaseTransform * trans, + GstBaseTransformClass * bclass) { GstPadTemplate *pad_template; GST_DEBUG ("gst_base_transform_init"); pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink"); + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink"); g_return_if_fail (pad_template != NULL); trans->sinkpad = gst_pad_new_from_template (pad_template, "sink"); gst_pad_set_getcaps_function (trans->sinkpad, @@ -189,7 +342,7 @@ gst_base_transform_init (GstBaseTransform * trans, gpointer g_class) gst_element_add_pad (GST_ELEMENT (trans), trans->sinkpad); pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src"); + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src"); g_return_if_fail (pad_template != NULL); trans->srcpad = gst_pad_new_from_template (pad_template, "src"); gst_pad_set_getcaps_function (trans->srcpad, @@ -202,11 +355,20 @@ gst_base_transform_init (GstBaseTransform * trans, gpointer g_class) GST_DEBUG_FUNCPTR (gst_base_transform_src_activate_pull)); gst_element_add_pad (GST_ELEMENT (trans), trans->srcpad); - trans->passthrough = FALSE; trans->delay_configure = FALSE; trans->pending_configure = FALSE; trans->cache_caps1 = NULL; trans->cache_caps2 = NULL; + + trans->passthrough = FALSE; + if (bclass->transform == NULL) { + /* If no transform function, always_in_place is TRUE */ + GST_DEBUG_OBJECT (trans, "setting in_place TRUE"); + trans->always_in_place = TRUE; + + if (bclass->transform_ip == NULL) + trans->passthrough = TRUE; + } } static GstCaps * @@ -283,7 +445,10 @@ gst_base_transform_transform_size (GstBaseTransform * trans, GST_DEBUG_OBJECT (trans, "input size %d, input unit size %d", size, inunitsize); g_return_val_if_fail (inunitsize != 0, FALSE); - g_return_val_if_fail (size % inunitsize == 0, FALSE); + if (size % inunitsize != 0) { + g_warning ("Size %u is not a multiple of unit size %u", size, inunitsize); + return FALSE; + } units = size / inunitsize; g_return_val_if_fail (gst_base_transform_get_unit_size (trans, othercaps, @@ -363,8 +528,20 @@ gst_base_transform_configure_caps (GstBaseTransform * trans, GstCaps * in, gst_caps_replace (&trans->cache_caps1, NULL); gst_caps_replace (&trans->cache_caps2, NULL); + /* 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 */ + gst_base_transform_set_in_place (trans, + klass->transform_ip && trans->have_same_caps); + + /* Set the passthrough if the class wants passthrough_on_same_caps + * and we have the same caps on each pad */ + if (klass->passthrough_on_same_caps) + gst_base_transform_set_passthrough (trans, trans->have_same_caps); + /* now configure the element with the caps */ if (klass->set_caps) { + GST_DEBUG_OBJECT (trans, "Calling set_caps method to setup caps"); ret = klass->set_caps (trans, in, out); } @@ -402,6 +579,19 @@ gst_base_transform_setcaps (GstPad * pad, GstCaps * caps) othercaps = gst_base_transform_transform_caps (trans, GST_PAD_DIRECTION (pad), caps); + /* The caps we can actually output is the intersection of the transformed + * caps with the pad template for the pad */ + if (othercaps) { + GstCaps *intersect; + const GstCaps *templ_caps; + + templ_caps = gst_pad_get_pad_template_caps (otherpad); + intersect = gst_caps_intersect (othercaps, templ_caps); + + gst_caps_unref (othercaps); + othercaps = intersect; + } + /* check if transform is empty */ if (!othercaps || gst_caps_is_empty (othercaps)) goto no_transform; @@ -512,8 +702,8 @@ gst_base_transform_setcaps (GstPad * pad, GstCaps * caps) GST_DEBUG_OBJECT (trans, "got final caps %" GST_PTR_FORMAT, othercaps); - trans->in_place = gst_caps_is_equal (caps, othercaps); - GST_DEBUG_OBJECT (trans, "in_place: %d", trans->in_place); + trans->have_same_caps = gst_caps_is_equal (caps, othercaps); + GST_DEBUG_OBJECT (trans, "have_same_caps: %d", trans->have_same_caps); /* see if we have to configure the element now */ if (!trans->delay_configure) { @@ -590,10 +780,61 @@ failed_configure: { GST_DEBUG_OBJECT (trans, "FAILED to configure caps %" GST_PTR_FORMAT " to accept %" GST_PTR_FORMAT, otherpad, othercaps); + ret = FALSE; goto done; } } +/* Allocate a buffer using gst_pad_alloc_buffer */ +static GstFlowReturn +gst_base_transform_default_prepare_buf (GstBaseTransform * trans, + GstBuffer * input, gint size, GstCaps * caps, GstBuffer ** buf) +{ + GstBaseTransformClass *bclass; + GstFlowReturn ret = GST_FLOW_OK; + gboolean copy_inbuf = FALSE; + + bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); + + /* See if we want to prepare the buffer for in place output */ + if (GST_BUFFER_SIZE (input) == size && bclass->transform_ip) { + if (gst_buffer_is_writable (input) && (*buf == NULL)) { + /* Input buffer is already writable, just ref and return it */ + *buf = input; + gst_buffer_ref (input); + gst_caps_replace (&GST_BUFFER_CAPS (*buf), caps); + + return GST_FLOW_OK; + } else { + /* Make a writable buffer below and copy the data */ + copy_inbuf = TRUE; + } + } + + if (*buf == NULL) { + /* Sub-class didn't already implement a buffer for us. Make one */ + ret = gst_pad_alloc_buffer (trans->srcpad, GST_BUFFER_OFFSET (input), + size, caps, buf); + if (ret != GST_FLOW_OK || *buf == NULL) + return ret; + } + + /* If the output buffer metadata is modifiable, copy timestamps and + * buffer flags */ + if (*buf != input && GST_MINI_OBJECT_REFCOUNT_VALUE (*buf) == 1) { + + gst_buffer_stamp (*buf, input); + GST_BUFFER_FLAGS (*buf) |= GST_BUFFER_FLAGS (input) & + (GST_BUFFER_FLAG_PREROLL | GST_BUFFER_FLAG_IN_CAPS | + GST_BUFFER_FLAG_DELTA_UNIT); + } + + if (copy_inbuf && gst_buffer_is_writable (*buf)) + memcpy (GST_BUFFER_DATA (*buf), GST_BUFFER_DATA (input), size); + + return ret; +} + static gboolean gst_base_transform_get_unit_size (GstBaseTransform * trans, GstCaps * caps, guint * size) @@ -628,6 +869,8 @@ gst_base_transform_get_unit_size (GstBaseTransform * trans, GstCaps * caps, trans->cache_caps2_size = *size; } } + } else { + GST_DEBUG ("Sub-class does not implement get_unit_size"); } return res; } @@ -657,11 +900,13 @@ gst_base_transform_buffer_alloc (GstPad * pad, guint64 offset, guint size, GST_DEBUG_OBJECT (trans, "... and offset NONE"); else GST_DEBUG_OBJECT (trans, "... and offset %" G_GUINT64_FORMAT, offset); - /* before any buffers are pushed, in_place is TRUE; allocating can trigger + + /* before any buffers are pushed, have_same_caps is TRUE; allocating can trigger * a renegotiation and change that to FALSE */ - if (trans->in_place) { + if (trans->have_same_caps) { /* request a buffer with the same caps */ GST_DEBUG_OBJECT (trans, "requesting buffer with same caps, size %d", size); + res = gst_pad_alloc_buffer (trans->srcpad, offset, size, caps, buf); } else { /* if we are configured, request a buffer with the src caps */ @@ -681,8 +926,8 @@ gst_base_transform_buffer_alloc (GstPad * pad, guint64 offset, guint size, gst_caps_unref (srccaps); } - if (res == GST_FLOW_OK && !trans->in_place) { - /* note that we might have been in place before, but calling the + 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 @@ -813,94 +1058,101 @@ gst_base_transform_handle_buffer (GstBaseTransform * trans, GstBuffer * inbuf, GstBaseTransformClass *bclass; GstFlowReturn ret = GST_FLOW_OK; guint out_size; + gboolean want_in_place; bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - GST_LOG_OBJECT (trans, "handling buffer %p of size %d ...", inbuf, - GST_BUFFER_SIZE (inbuf)); if (GST_BUFFER_OFFSET_IS_VALID (inbuf)) - GST_LOG_OBJECT (trans, "... and offset %" G_GUINT64_FORMAT, + GST_LOG_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, "... and offset NONE"); + GST_LOG_OBJECT (trans, "handling buffer %p of size %d and offset NONE", + inbuf, GST_BUFFER_SIZE (inbuf)); if (!trans->negotiated && !trans->passthrough) goto not_negotiated; - if (trans->in_place) { - /* passthrough elements or when the buffer is writable - * can be performed with the _ip method */ - gboolean may_do_in_place = gst_buffer_is_writable (inbuf) || - trans->passthrough; - - /* check if we can and may do inplace */ - if (bclass->transform_ip && may_do_in_place) { - /* in place transform and subclass supports method */ - GST_LOG_OBJECT (trans, "doing inplace transform"); - gst_buffer_ref (inbuf); + 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; - } else { - GST_LOG_OBJECT (trans, "doing fake inplace transform"); - /* in place transform and subclass does not support method or - * buffer is not writable. */ - if (bclass->transform) { - /* make a copy of the buffer. We really need a copy since the - * element might not be able to really do inplace. */ - *outbuf = inbuf; - inbuf = gst_buffer_copy (inbuf); - - ret = bclass->transform (trans, inbuf, *outbuf); - } else { - ret = GST_FLOW_NOT_SUPPORTED; - } - } + + *outbuf = inbuf; + + return ret; + } + + want_in_place = (bclass->transform_ip != NULL) && trans->always_in_place; + + 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"); + + /* 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; + ret = bclass->prepare_output_buffer (trans, inbuf, + GST_BUFFER_SIZE (inbuf), GST_PAD_CAPS (trans->srcpad), outbuf); + trans->delay_configure = FALSE; + 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 inplace, figure out the output size */ - 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; + + /* not transforming inplace, figure out the output size */ + if (trans->always_in_place) { + out_size = GST_BUFFER_SIZE (inbuf); + } 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; + } } /* 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; - /* no in place transform, get buffer, this might renegotiate. */ - ret = gst_pad_alloc_buffer (trans->srcpad, - GST_BUFFER_OFFSET (inbuf), out_size, + ret = bclass->prepare_output_buffer (trans, inbuf, out_size, GST_PAD_CAPS (trans->srcpad), outbuf); - trans->delay_configure = FALSE; if (ret != GST_FLOW_OK) goto no_buffer; - gst_buffer_stamp (*outbuf, inbuf); - if (bclass->transform) ret = bclass->transform (trans, inbuf, *outbuf); else ret = GST_FLOW_NOT_SUPPORTED; + } - /* if we got renegotiated we can configure now */ - if (trans->pending_configure) { - gboolean success; + /* 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)); + success = + gst_base_transform_configure_caps (trans, + GST_PAD_CAPS (trans->sinkpad), GST_PAD_CAPS (trans->srcpad)); - trans->pending_configure = FALSE; + trans->pending_configure = FALSE; - if (!success) - goto configure_failed; - } + if (!success) + goto configure_failed; } gst_buffer_unref (inbuf); @@ -964,14 +1216,15 @@ gst_base_transform_chain (GstPad * pad, GstBuffer * buffer) { GstBaseTransform *trans; GstFlowReturn ret; - GstBuffer *outbuf; + GstBuffer *outbuf = NULL; trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); ret = gst_base_transform_handle_buffer (trans, buffer, &outbuf); if (ret == GST_FLOW_OK) { ret = gst_pad_push (trans->srcpad, outbuf); - } + } else if (outbuf != NULL) + gst_buffer_unref (outbuf); gst_object_unref (trans); @@ -1066,11 +1319,12 @@ gst_base_transform_change_state (GstElement * element, case GST_STATE_CHANGE_READY_TO_PAUSED: GST_LOCK (trans); if (GST_PAD_CAPS (trans->sinkpad) && GST_PAD_CAPS (trans->srcpad)) - trans->in_place = gst_caps_is_equal (GST_PAD_CAPS (trans->sinkpad), + trans->have_same_caps = + gst_caps_is_equal (GST_PAD_CAPS (trans->sinkpad), GST_PAD_CAPS (trans->srcpad)) || trans->passthrough; else - trans->in_place = trans->passthrough; - GST_DEBUG_OBJECT (trans, "in_place %d", trans->in_place); + trans->have_same_caps = trans->passthrough; + GST_DEBUG_OBJECT (trans, "have_same_caps %d", trans->have_same_caps); gst_caps_replace (&trans->cache_caps1, NULL); gst_caps_replace (&trans->cache_caps2, NULL); trans->negotiated = FALSE; @@ -1106,7 +1360,10 @@ gst_base_transform_change_state (GstElement * element, * @passthrough: boolean indicating passthrough mode. * * Set passthrough mode for this filter by default. This is mostly - * usefull for filters that do not care about negotiation. + * useful for filters that do not care about negotiation. + * + * Always TRUE for filters which don't implement either a transform + * or transform_ip method. * * MT safe. */ @@ -1114,12 +1371,21 @@ void gst_base_transform_set_passthrough (GstBaseTransform * trans, gboolean passthrough) { + GstBaseTransformClass *bclass; + g_return_if_fail (trans != NULL); - GST_DEBUG_OBJECT (trans, "setting passthrough %d", passthrough); + bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); GST_LOCK (trans); - trans->passthrough = passthrough; + if (passthrough == FALSE) { + if (bclass->transform_ip || bclass->transform) + trans->passthrough = FALSE; + } else { + trans->passthrough = TRUE; + } + + GST_DEBUG_OBJECT (trans, "set passthrough %d", trans->passthrough); GST_UNLOCK (trans); } @@ -1146,3 +1412,68 @@ gst_base_transform_is_passthrough (GstBaseTransform * trans) return result; } + +/** + * gst_base_transform_set_in_place: + * @trans: the #GstBaseTransform to modify + * @in_place: Boolean value indicating that we would like to operate + * on in_place buffers. + * + * 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_ip function is implemented. + * + * + * MT safe. + */ +void +gst_base_transform_set_in_place (GstBaseTransform * trans, gboolean in_place) +{ + GstBaseTransformClass *bclass; + + g_return_if_fail (trans != NULL); + + bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); + + GST_LOCK (trans); + + if (in_place) { + if (bclass->transform_ip) { + GST_DEBUG_OBJECT (trans, "setting in_place TRUE"); + trans->always_in_place = TRUE; + } + } else { + if (bclass->transform) { + GST_DEBUG_OBJECT (trans, "setting in_place FALSE"); + trans->always_in_place = FALSE; + } + } + + GST_UNLOCK (trans); +} + +/** + * gst_base_transform_is_in_place: + * @trans: the #GstBaseTransform to query + * + * See if @trans is configured as a in_place transform. + * + * Returns: TRUE is the transform is configured in in_place mode. + * + * MT safe. + */ +gboolean +gst_base_transform_is_in_place (GstBaseTransform * trans) +{ + gboolean result; + + g_return_val_if_fail (trans != NULL, FALSE); + + GST_LOCK (trans); + result = trans->always_in_place; + GST_UNLOCK (trans); + + return result; +} diff --git a/gst/base/gstbasetransform.h b/gst/base/gstbasetransform.h index db3c16c..ae8bba8 100644 --- a/gst/base/gstbasetransform.h +++ b/gst/base/gstbasetransform.h @@ -47,9 +47,12 @@ struct _GstBaseTransform { GstPad *sinkpad; GstPad *srcpad; + /* Set by sub-class */ gboolean passthrough; + gboolean always_in_place; - gboolean in_place; + /* Set if caps on each pad are equal */ + gboolean have_same_caps; GstCaps *cache_caps1; guint cache_caps1_size; @@ -121,7 +124,7 @@ struct _GstBaseTransformClass { gboolean (*event) (GstBaseTransform *trans, GstEvent *event); /* transform one incoming buffer to one outgoing buffer. - * Always needs to be implemented. + * Always needs to be implemented unless always operating in-place. * transform function is allowed to change size/timestamp/duration of * the outgoing buffer. */ GstFlowReturn (*transform) (GstBaseTransform *trans, GstBuffer *inbuf, @@ -130,13 +133,29 @@ struct _GstBaseTransformClass { /* transform a buffer inplace */ GstFlowReturn (*transform_ip) (GstBaseTransform *trans, GstBuffer *buf); + /* FIXME: When adjusting the padding, more these to nicer places in the class */ + /* Set by child classes to automatically do passthrough mode */ + gboolean passthrough_on_same_caps; + + /* Subclasses can override this to do their own allocation of output buffers. + * Elements that only do analysis can return a subbuffer or even just + * increment the reference to the input buffer (if in passthrough mode) + */ + GstFlowReturn (*prepare_output_buffer) (GstBaseTransform * trans, + GstBuffer *input, gint size, GstCaps *caps, GstBuffer **buf); + /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; + gpointer _gst_reserved[GST_PADDING - 2]; }; -void gst_base_transform_set_passthrough (GstBaseTransform *trans, gboolean passthrough); +void gst_base_transform_set_passthrough (GstBaseTransform *trans, + gboolean passthrough); gboolean gst_base_transform_is_passthrough (GstBaseTransform *trans); +void gst_base_transform_set_in_place (GstBaseTransform *trans, + gboolean in_place); +gboolean gst_base_transform_is_in_place (GstBaseTransform *trans); + GType gst_base_transform_get_type (void); G_END_DECLS diff --git a/gst/elements/Makefile.am b/gst/elements/Makefile.am index 1c7d194..04ece91 100644 --- a/gst/elements/Makefile.am +++ b/gst/elements/Makefile.am @@ -12,6 +12,7 @@ libgstelements_la_SOURCES = \ gstcapsfilter.c \ gstfakesrc.c \ gstfakesink.c \ + gstfdsrc.c \ gstfilesink.c \ gstfilesrc.c \ gstidentity.c \ @@ -29,6 +30,7 @@ noinst_HEADERS = \ gstbufferstore.h \ gstfakesink.h \ gstfakesrc.h \ + gstfdsrc.h \ gstfilesink.h \ gstfilesrc.h \ gstidentity.h \ diff --git a/gst/elements/gstcapsfilter.c b/gst/elements/gstcapsfilter.c index 3799dda..b113007 100644 --- a/gst/elements/gstcapsfilter.c +++ b/gst/elements/gstcapsfilter.c @@ -216,5 +216,15 @@ gst_capsfilter_transform_caps (GstBaseTransform * base, static GstFlowReturn gst_capsfilter_transform_ip (GstBaseTransform * base, GstBuffer * buf) { + /* Ensure that outgoing buffers have caps if we can, so that pipelines + * like: + * gst-launch filesrc location=rawsamples.raw ! + * audio/x-raw-int,width=16,depth=16,rate=48000,channels=2, + * endianness=4321,signed='(boolean)'true ! alsasink + * will work. + */ + if (GST_BUFFER_CAPS (buf) == NULL) { + } + return GST_FLOW_OK; } diff --git a/gst/elements/gstelements.c b/gst/elements/gstelements.c index 9851688..9583403 100644 --- a/gst/elements/gstelements.c +++ b/gst/elements/gstelements.c @@ -29,6 +29,7 @@ #include "gstfakesink.h" #include "gstfakesrc.h" +#include "gstfdsrc.h" #include "gstfilesink.h" #include "gstfilesrc.h" #include "gstidentity.h" @@ -50,6 +51,7 @@ static struct _elements_entry _elements[] = { {"capsfilter", GST_RANK_NONE, gst_capsfilter_get_type}, {"fakesrc", GST_RANK_NONE, gst_fake_src_get_type}, {"fakesink", GST_RANK_NONE, gst_fake_sink_get_type}, + {"fdsrc", GST_RANK_NONE, gst_fdsrc_get_type}, {"filesrc", GST_RANK_NONE, gst_file_src_get_type}, {"identity", GST_RANK_NONE, gst_identity_get_type}, {"filesink", GST_RANK_NONE, gst_file_sink_get_type}, diff --git a/gst/elements/gstfdsrc.c b/gst/elements/gstfdsrc.c index 938ae85..98c0cd8 100644 --- a/gst/elements/gstfdsrc.c +++ b/gst/elements/gstfdsrc.c @@ -136,7 +136,7 @@ gst_fdsrc_class_init (GstFdSrcClass * klass) } static void -gst_fdsrc_init (GstFdSrc * fdsrc) +gst_fdsrc_init (GstFdSrc * fdsrc, GstFdSrcClass * klass) { // TODO set live only if it's actually a live source gst_base_src_set_live (GST_BASE_SRC (fdsrc), TRUE); @@ -260,9 +260,11 @@ gst_fdsrc_create (GstPushSrc * psrc, GstBuffer ** outbuf) if (retval == -1) { GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("select on file descriptor: %s.", g_strerror (errno))); + GST_DEBUG_OBJECT (psrc, "Error during select"); return GST_FLOW_ERROR; } else if (retval == 0) { g_signal_emit (G_OBJECT (src), gst_fdsrc_signals[SIGNAL_TIMEOUT], 0); + GST_DEBUG_OBJECT (psrc, "Timeout in select"); return GST_FLOW_ERROR; } #endif @@ -277,14 +279,18 @@ gst_fdsrc_create (GstPushSrc * psrc, GstBuffer ** outbuf) GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE; src->curoffset += readbytes; + GST_DEBUG_OBJECT (psrc, "Read buffer of size %u.", readbytes); + /* we're done, return the buffer */ *outbuf = buf; return GST_FLOW_OK; } else if (readbytes == 0) { + GST_DEBUG_OBJECT (psrc, "Read 0 bytes. EOS."); return GST_FLOW_ERROR; } else { GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("read on file descriptor: %s.", g_strerror (errno))); + GST_DEBUG_OBJECT (psrc, "Error reading from fd"); return GST_FLOW_ERROR; } } diff --git a/libs/gst/base/gstbasetransform.c b/libs/gst/base/gstbasetransform.c index dc3d574..2651d9d 100644 --- a/libs/gst/base/gstbasetransform.c +++ b/libs/gst/base/gstbasetransform.c @@ -28,28 +28,174 @@ * * This base class is for filter elements that process data. * + * It provides for: * * one sinkpad and one srcpad * - * possible formats on sink and source pad implemented + * 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 + * Handles state changes + * Does flushing + * Push mode * - * pull mode if transform can operate on arbitrary data + * 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 + * 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. + * + * + * + * + * + * Retrictions on sub-class behaviour + * + * Sub-classes which override the prepare_output_buffer function need to call + * the parent implementation if they allocate a new buffer, which will copy + * buffer flags onto the new buffer. + * + * + * */ -#include - #ifdef HAVE_CONFIG_H # include "config.h" #endif +#include +#include + #include "../gst-i18n-lib.h" #include "gstbasetransform.h" #include @@ -75,7 +221,9 @@ static GstElementClass *parent_class = NULL; static void gst_base_transform_base_init (gpointer g_class); static void gst_base_transform_class_init (GstBaseTransformClass * klass); static void gst_base_transform_init (GstBaseTransform * trans, - gpointer g_class); + GstBaseTransformClass * klass); +static GstFlowReturn gst_base_transform_default_prepare_buf (GstBaseTransform * + trans, GstBuffer * input, gint size, GstCaps * caps, GstBuffer ** buf); GType gst_base_transform_get_type (void) @@ -161,17 +309,22 @@ gst_base_transform_class_init (GstBaseTransformClass * klass) gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_base_transform_change_state); + + klass->prepare_output_buffer = + GST_DEBUG_FUNCPTR (gst_base_transform_default_prepare_buf); + klass->passthrough_on_same_caps = FALSE; } static void -gst_base_transform_init (GstBaseTransform * trans, gpointer g_class) +gst_base_transform_init (GstBaseTransform * trans, + GstBaseTransformClass * bclass) { GstPadTemplate *pad_template; GST_DEBUG ("gst_base_transform_init"); pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink"); + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink"); g_return_if_fail (pad_template != NULL); trans->sinkpad = gst_pad_new_from_template (pad_template, "sink"); gst_pad_set_getcaps_function (trans->sinkpad, @@ -189,7 +342,7 @@ gst_base_transform_init (GstBaseTransform * trans, gpointer g_class) gst_element_add_pad (GST_ELEMENT (trans), trans->sinkpad); pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src"); + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src"); g_return_if_fail (pad_template != NULL); trans->srcpad = gst_pad_new_from_template (pad_template, "src"); gst_pad_set_getcaps_function (trans->srcpad, @@ -202,11 +355,20 @@ gst_base_transform_init (GstBaseTransform * trans, gpointer g_class) GST_DEBUG_FUNCPTR (gst_base_transform_src_activate_pull)); gst_element_add_pad (GST_ELEMENT (trans), trans->srcpad); - trans->passthrough = FALSE; trans->delay_configure = FALSE; trans->pending_configure = FALSE; trans->cache_caps1 = NULL; trans->cache_caps2 = NULL; + + trans->passthrough = FALSE; + if (bclass->transform == NULL) { + /* If no transform function, always_in_place is TRUE */ + GST_DEBUG_OBJECT (trans, "setting in_place TRUE"); + trans->always_in_place = TRUE; + + if (bclass->transform_ip == NULL) + trans->passthrough = TRUE; + } } static GstCaps * @@ -283,7 +445,10 @@ gst_base_transform_transform_size (GstBaseTransform * trans, GST_DEBUG_OBJECT (trans, "input size %d, input unit size %d", size, inunitsize); g_return_val_if_fail (inunitsize != 0, FALSE); - g_return_val_if_fail (size % inunitsize == 0, FALSE); + if (size % inunitsize != 0) { + g_warning ("Size %u is not a multiple of unit size %u", size, inunitsize); + return FALSE; + } units = size / inunitsize; g_return_val_if_fail (gst_base_transform_get_unit_size (trans, othercaps, @@ -363,8 +528,20 @@ gst_base_transform_configure_caps (GstBaseTransform * trans, GstCaps * in, gst_caps_replace (&trans->cache_caps1, NULL); gst_caps_replace (&trans->cache_caps2, NULL); + /* 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 */ + gst_base_transform_set_in_place (trans, + klass->transform_ip && trans->have_same_caps); + + /* Set the passthrough if the class wants passthrough_on_same_caps + * and we have the same caps on each pad */ + if (klass->passthrough_on_same_caps) + gst_base_transform_set_passthrough (trans, trans->have_same_caps); + /* now configure the element with the caps */ if (klass->set_caps) { + GST_DEBUG_OBJECT (trans, "Calling set_caps method to setup caps"); ret = klass->set_caps (trans, in, out); } @@ -402,6 +579,19 @@ gst_base_transform_setcaps (GstPad * pad, GstCaps * caps) othercaps = gst_base_transform_transform_caps (trans, GST_PAD_DIRECTION (pad), caps); + /* The caps we can actually output is the intersection of the transformed + * caps with the pad template for the pad */ + if (othercaps) { + GstCaps *intersect; + const GstCaps *templ_caps; + + templ_caps = gst_pad_get_pad_template_caps (otherpad); + intersect = gst_caps_intersect (othercaps, templ_caps); + + gst_caps_unref (othercaps); + othercaps = intersect; + } + /* check if transform is empty */ if (!othercaps || gst_caps_is_empty (othercaps)) goto no_transform; @@ -512,8 +702,8 @@ gst_base_transform_setcaps (GstPad * pad, GstCaps * caps) GST_DEBUG_OBJECT (trans, "got final caps %" GST_PTR_FORMAT, othercaps); - trans->in_place = gst_caps_is_equal (caps, othercaps); - GST_DEBUG_OBJECT (trans, "in_place: %d", trans->in_place); + trans->have_same_caps = gst_caps_is_equal (caps, othercaps); + GST_DEBUG_OBJECT (trans, "have_same_caps: %d", trans->have_same_caps); /* see if we have to configure the element now */ if (!trans->delay_configure) { @@ -590,10 +780,61 @@ failed_configure: { GST_DEBUG_OBJECT (trans, "FAILED to configure caps %" GST_PTR_FORMAT " to accept %" GST_PTR_FORMAT, otherpad, othercaps); + ret = FALSE; goto done; } } +/* Allocate a buffer using gst_pad_alloc_buffer */ +static GstFlowReturn +gst_base_transform_default_prepare_buf (GstBaseTransform * trans, + GstBuffer * input, gint size, GstCaps * caps, GstBuffer ** buf) +{ + GstBaseTransformClass *bclass; + GstFlowReturn ret = GST_FLOW_OK; + gboolean copy_inbuf = FALSE; + + bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); + + /* See if we want to prepare the buffer for in place output */ + if (GST_BUFFER_SIZE (input) == size && bclass->transform_ip) { + if (gst_buffer_is_writable (input) && (*buf == NULL)) { + /* Input buffer is already writable, just ref and return it */ + *buf = input; + gst_buffer_ref (input); + gst_caps_replace (&GST_BUFFER_CAPS (*buf), caps); + + return GST_FLOW_OK; + } else { + /* Make a writable buffer below and copy the data */ + copy_inbuf = TRUE; + } + } + + if (*buf == NULL) { + /* Sub-class didn't already implement a buffer for us. Make one */ + ret = gst_pad_alloc_buffer (trans->srcpad, GST_BUFFER_OFFSET (input), + size, caps, buf); + if (ret != GST_FLOW_OK || *buf == NULL) + return ret; + } + + /* If the output buffer metadata is modifiable, copy timestamps and + * buffer flags */ + if (*buf != input && GST_MINI_OBJECT_REFCOUNT_VALUE (*buf) == 1) { + + gst_buffer_stamp (*buf, input); + GST_BUFFER_FLAGS (*buf) |= GST_BUFFER_FLAGS (input) & + (GST_BUFFER_FLAG_PREROLL | GST_BUFFER_FLAG_IN_CAPS | + GST_BUFFER_FLAG_DELTA_UNIT); + } + + if (copy_inbuf && gst_buffer_is_writable (*buf)) + memcpy (GST_BUFFER_DATA (*buf), GST_BUFFER_DATA (input), size); + + return ret; +} + static gboolean gst_base_transform_get_unit_size (GstBaseTransform * trans, GstCaps * caps, guint * size) @@ -628,6 +869,8 @@ gst_base_transform_get_unit_size (GstBaseTransform * trans, GstCaps * caps, trans->cache_caps2_size = *size; } } + } else { + GST_DEBUG ("Sub-class does not implement get_unit_size"); } return res; } @@ -657,11 +900,13 @@ gst_base_transform_buffer_alloc (GstPad * pad, guint64 offset, guint size, GST_DEBUG_OBJECT (trans, "... and offset NONE"); else GST_DEBUG_OBJECT (trans, "... and offset %" G_GUINT64_FORMAT, offset); - /* before any buffers are pushed, in_place is TRUE; allocating can trigger + + /* before any buffers are pushed, have_same_caps is TRUE; allocating can trigger * a renegotiation and change that to FALSE */ - if (trans->in_place) { + if (trans->have_same_caps) { /* request a buffer with the same caps */ GST_DEBUG_OBJECT (trans, "requesting buffer with same caps, size %d", size); + res = gst_pad_alloc_buffer (trans->srcpad, offset, size, caps, buf); } else { /* if we are configured, request a buffer with the src caps */ @@ -681,8 +926,8 @@ gst_base_transform_buffer_alloc (GstPad * pad, guint64 offset, guint size, gst_caps_unref (srccaps); } - if (res == GST_FLOW_OK && !trans->in_place) { - /* note that we might have been in place before, but calling the + 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 @@ -813,94 +1058,101 @@ gst_base_transform_handle_buffer (GstBaseTransform * trans, GstBuffer * inbuf, GstBaseTransformClass *bclass; GstFlowReturn ret = GST_FLOW_OK; guint out_size; + gboolean want_in_place; bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - GST_LOG_OBJECT (trans, "handling buffer %p of size %d ...", inbuf, - GST_BUFFER_SIZE (inbuf)); if (GST_BUFFER_OFFSET_IS_VALID (inbuf)) - GST_LOG_OBJECT (trans, "... and offset %" G_GUINT64_FORMAT, + GST_LOG_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, "... and offset NONE"); + GST_LOG_OBJECT (trans, "handling buffer %p of size %d and offset NONE", + inbuf, GST_BUFFER_SIZE (inbuf)); if (!trans->negotiated && !trans->passthrough) goto not_negotiated; - if (trans->in_place) { - /* passthrough elements or when the buffer is writable - * can be performed with the _ip method */ - gboolean may_do_in_place = gst_buffer_is_writable (inbuf) || - trans->passthrough; - - /* check if we can and may do inplace */ - if (bclass->transform_ip && may_do_in_place) { - /* in place transform and subclass supports method */ - GST_LOG_OBJECT (trans, "doing inplace transform"); - gst_buffer_ref (inbuf); + 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; - } else { - GST_LOG_OBJECT (trans, "doing fake inplace transform"); - /* in place transform and subclass does not support method or - * buffer is not writable. */ - if (bclass->transform) { - /* make a copy of the buffer. We really need a copy since the - * element might not be able to really do inplace. */ - *outbuf = inbuf; - inbuf = gst_buffer_copy (inbuf); - - ret = bclass->transform (trans, inbuf, *outbuf); - } else { - ret = GST_FLOW_NOT_SUPPORTED; - } - } + + *outbuf = inbuf; + + return ret; + } + + want_in_place = (bclass->transform_ip != NULL) && trans->always_in_place; + + 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"); + + /* 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; + ret = bclass->prepare_output_buffer (trans, inbuf, + GST_BUFFER_SIZE (inbuf), GST_PAD_CAPS (trans->srcpad), outbuf); + trans->delay_configure = FALSE; + 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 inplace, figure out the output size */ - 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; + + /* not transforming inplace, figure out the output size */ + if (trans->always_in_place) { + out_size = GST_BUFFER_SIZE (inbuf); + } 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; + } } /* 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; - /* no in place transform, get buffer, this might renegotiate. */ - ret = gst_pad_alloc_buffer (trans->srcpad, - GST_BUFFER_OFFSET (inbuf), out_size, + ret = bclass->prepare_output_buffer (trans, inbuf, out_size, GST_PAD_CAPS (trans->srcpad), outbuf); - trans->delay_configure = FALSE; if (ret != GST_FLOW_OK) goto no_buffer; - gst_buffer_stamp (*outbuf, inbuf); - if (bclass->transform) ret = bclass->transform (trans, inbuf, *outbuf); else ret = GST_FLOW_NOT_SUPPORTED; + } - /* if we got renegotiated we can configure now */ - if (trans->pending_configure) { - gboolean success; + /* 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)); + success = + gst_base_transform_configure_caps (trans, + GST_PAD_CAPS (trans->sinkpad), GST_PAD_CAPS (trans->srcpad)); - trans->pending_configure = FALSE; + trans->pending_configure = FALSE; - if (!success) - goto configure_failed; - } + if (!success) + goto configure_failed; } gst_buffer_unref (inbuf); @@ -964,14 +1216,15 @@ gst_base_transform_chain (GstPad * pad, GstBuffer * buffer) { GstBaseTransform *trans; GstFlowReturn ret; - GstBuffer *outbuf; + GstBuffer *outbuf = NULL; trans = GST_BASE_TRANSFORM (gst_pad_get_parent (pad)); ret = gst_base_transform_handle_buffer (trans, buffer, &outbuf); if (ret == GST_FLOW_OK) { ret = gst_pad_push (trans->srcpad, outbuf); - } + } else if (outbuf != NULL) + gst_buffer_unref (outbuf); gst_object_unref (trans); @@ -1066,11 +1319,12 @@ gst_base_transform_change_state (GstElement * element, case GST_STATE_CHANGE_READY_TO_PAUSED: GST_LOCK (trans); if (GST_PAD_CAPS (trans->sinkpad) && GST_PAD_CAPS (trans->srcpad)) - trans->in_place = gst_caps_is_equal (GST_PAD_CAPS (trans->sinkpad), + trans->have_same_caps = + gst_caps_is_equal (GST_PAD_CAPS (trans->sinkpad), GST_PAD_CAPS (trans->srcpad)) || trans->passthrough; else - trans->in_place = trans->passthrough; - GST_DEBUG_OBJECT (trans, "in_place %d", trans->in_place); + trans->have_same_caps = trans->passthrough; + GST_DEBUG_OBJECT (trans, "have_same_caps %d", trans->have_same_caps); gst_caps_replace (&trans->cache_caps1, NULL); gst_caps_replace (&trans->cache_caps2, NULL); trans->negotiated = FALSE; @@ -1106,7 +1360,10 @@ gst_base_transform_change_state (GstElement * element, * @passthrough: boolean indicating passthrough mode. * * Set passthrough mode for this filter by default. This is mostly - * usefull for filters that do not care about negotiation. + * useful for filters that do not care about negotiation. + * + * Always TRUE for filters which don't implement either a transform + * or transform_ip method. * * MT safe. */ @@ -1114,12 +1371,21 @@ void gst_base_transform_set_passthrough (GstBaseTransform * trans, gboolean passthrough) { + GstBaseTransformClass *bclass; + g_return_if_fail (trans != NULL); - GST_DEBUG_OBJECT (trans, "setting passthrough %d", passthrough); + bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); GST_LOCK (trans); - trans->passthrough = passthrough; + if (passthrough == FALSE) { + if (bclass->transform_ip || bclass->transform) + trans->passthrough = FALSE; + } else { + trans->passthrough = TRUE; + } + + GST_DEBUG_OBJECT (trans, "set passthrough %d", trans->passthrough); GST_UNLOCK (trans); } @@ -1146,3 +1412,68 @@ gst_base_transform_is_passthrough (GstBaseTransform * trans) return result; } + +/** + * gst_base_transform_set_in_place: + * @trans: the #GstBaseTransform to modify + * @in_place: Boolean value indicating that we would like to operate + * on in_place buffers. + * + * 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_ip function is implemented. + * + * + * MT safe. + */ +void +gst_base_transform_set_in_place (GstBaseTransform * trans, gboolean in_place) +{ + GstBaseTransformClass *bclass; + + g_return_if_fail (trans != NULL); + + bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); + + GST_LOCK (trans); + + if (in_place) { + if (bclass->transform_ip) { + GST_DEBUG_OBJECT (trans, "setting in_place TRUE"); + trans->always_in_place = TRUE; + } + } else { + if (bclass->transform) { + GST_DEBUG_OBJECT (trans, "setting in_place FALSE"); + trans->always_in_place = FALSE; + } + } + + GST_UNLOCK (trans); +} + +/** + * gst_base_transform_is_in_place: + * @trans: the #GstBaseTransform to query + * + * See if @trans is configured as a in_place transform. + * + * Returns: TRUE is the transform is configured in in_place mode. + * + * MT safe. + */ +gboolean +gst_base_transform_is_in_place (GstBaseTransform * trans) +{ + gboolean result; + + g_return_val_if_fail (trans != NULL, FALSE); + + GST_LOCK (trans); + result = trans->always_in_place; + GST_UNLOCK (trans); + + return result; +} diff --git a/libs/gst/base/gstbasetransform.h b/libs/gst/base/gstbasetransform.h index db3c16c..ae8bba8 100644 --- a/libs/gst/base/gstbasetransform.h +++ b/libs/gst/base/gstbasetransform.h @@ -47,9 +47,12 @@ struct _GstBaseTransform { GstPad *sinkpad; GstPad *srcpad; + /* Set by sub-class */ gboolean passthrough; + gboolean always_in_place; - gboolean in_place; + /* Set if caps on each pad are equal */ + gboolean have_same_caps; GstCaps *cache_caps1; guint cache_caps1_size; @@ -121,7 +124,7 @@ struct _GstBaseTransformClass { gboolean (*event) (GstBaseTransform *trans, GstEvent *event); /* transform one incoming buffer to one outgoing buffer. - * Always needs to be implemented. + * Always needs to be implemented unless always operating in-place. * transform function is allowed to change size/timestamp/duration of * the outgoing buffer. */ GstFlowReturn (*transform) (GstBaseTransform *trans, GstBuffer *inbuf, @@ -130,13 +133,29 @@ struct _GstBaseTransformClass { /* transform a buffer inplace */ GstFlowReturn (*transform_ip) (GstBaseTransform *trans, GstBuffer *buf); + /* FIXME: When adjusting the padding, more these to nicer places in the class */ + /* Set by child classes to automatically do passthrough mode */ + gboolean passthrough_on_same_caps; + + /* Subclasses can override this to do their own allocation of output buffers. + * Elements that only do analysis can return a subbuffer or even just + * increment the reference to the input buffer (if in passthrough mode) + */ + GstFlowReturn (*prepare_output_buffer) (GstBaseTransform * trans, + GstBuffer *input, gint size, GstCaps *caps, GstBuffer **buf); + /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; + gpointer _gst_reserved[GST_PADDING - 2]; }; -void gst_base_transform_set_passthrough (GstBaseTransform *trans, gboolean passthrough); +void gst_base_transform_set_passthrough (GstBaseTransform *trans, + gboolean passthrough); gboolean gst_base_transform_is_passthrough (GstBaseTransform *trans); +void gst_base_transform_set_in_place (GstBaseTransform *trans, + gboolean in_place); +gboolean gst_base_transform_is_in_place (GstBaseTransform *trans); + GType gst_base_transform_get_type (void); G_END_DECLS diff --git a/plugins/elements/Makefile.am b/plugins/elements/Makefile.am index 1c7d194..04ece91 100644 --- a/plugins/elements/Makefile.am +++ b/plugins/elements/Makefile.am @@ -12,6 +12,7 @@ libgstelements_la_SOURCES = \ gstcapsfilter.c \ gstfakesrc.c \ gstfakesink.c \ + gstfdsrc.c \ gstfilesink.c \ gstfilesrc.c \ gstidentity.c \ @@ -29,6 +30,7 @@ noinst_HEADERS = \ gstbufferstore.h \ gstfakesink.h \ gstfakesrc.h \ + gstfdsrc.h \ gstfilesink.h \ gstfilesrc.h \ gstidentity.h \ diff --git a/plugins/elements/gstcapsfilter.c b/plugins/elements/gstcapsfilter.c index 3799dda..b113007 100644 --- a/plugins/elements/gstcapsfilter.c +++ b/plugins/elements/gstcapsfilter.c @@ -216,5 +216,15 @@ gst_capsfilter_transform_caps (GstBaseTransform * base, static GstFlowReturn gst_capsfilter_transform_ip (GstBaseTransform * base, GstBuffer * buf) { + /* Ensure that outgoing buffers have caps if we can, so that pipelines + * like: + * gst-launch filesrc location=rawsamples.raw ! + * audio/x-raw-int,width=16,depth=16,rate=48000,channels=2, + * endianness=4321,signed='(boolean)'true ! alsasink + * will work. + */ + if (GST_BUFFER_CAPS (buf) == NULL) { + } + return GST_FLOW_OK; } diff --git a/plugins/elements/gstelements.c b/plugins/elements/gstelements.c index 9851688..9583403 100644 --- a/plugins/elements/gstelements.c +++ b/plugins/elements/gstelements.c @@ -29,6 +29,7 @@ #include "gstfakesink.h" #include "gstfakesrc.h" +#include "gstfdsrc.h" #include "gstfilesink.h" #include "gstfilesrc.h" #include "gstidentity.h" @@ -50,6 +51,7 @@ static struct _elements_entry _elements[] = { {"capsfilter", GST_RANK_NONE, gst_capsfilter_get_type}, {"fakesrc", GST_RANK_NONE, gst_fake_src_get_type}, {"fakesink", GST_RANK_NONE, gst_fake_sink_get_type}, + {"fdsrc", GST_RANK_NONE, gst_fdsrc_get_type}, {"filesrc", GST_RANK_NONE, gst_file_src_get_type}, {"identity", GST_RANK_NONE, gst_identity_get_type}, {"filesink", GST_RANK_NONE, gst_file_sink_get_type}, diff --git a/plugins/elements/gstfdsrc.c b/plugins/elements/gstfdsrc.c index 938ae85..98c0cd8 100644 --- a/plugins/elements/gstfdsrc.c +++ b/plugins/elements/gstfdsrc.c @@ -136,7 +136,7 @@ gst_fdsrc_class_init (GstFdSrcClass * klass) } static void -gst_fdsrc_init (GstFdSrc * fdsrc) +gst_fdsrc_init (GstFdSrc * fdsrc, GstFdSrcClass * klass) { // TODO set live only if it's actually a live source gst_base_src_set_live (GST_BASE_SRC (fdsrc), TRUE); @@ -260,9 +260,11 @@ gst_fdsrc_create (GstPushSrc * psrc, GstBuffer ** outbuf) if (retval == -1) { GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("select on file descriptor: %s.", g_strerror (errno))); + GST_DEBUG_OBJECT (psrc, "Error during select"); return GST_FLOW_ERROR; } else if (retval == 0) { g_signal_emit (G_OBJECT (src), gst_fdsrc_signals[SIGNAL_TIMEOUT], 0); + GST_DEBUG_OBJECT (psrc, "Timeout in select"); return GST_FLOW_ERROR; } #endif @@ -277,14 +279,18 @@ gst_fdsrc_create (GstPushSrc * psrc, GstBuffer ** outbuf) GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE; src->curoffset += readbytes; + GST_DEBUG_OBJECT (psrc, "Read buffer of size %u.", readbytes); + /* we're done, return the buffer */ *outbuf = buf; return GST_FLOW_OK; } else if (readbytes == 0) { + GST_DEBUG_OBJECT (psrc, "Read 0 bytes. EOS."); return GST_FLOW_ERROR; } else { GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("read on file descriptor: %s.", g_strerror (errno))); + GST_DEBUG_OBJECT (psrc, "Error reading from fd"); return GST_FLOW_ERROR; } } diff --git a/tests/check/elements/identity.c b/tests/check/elements/identity.c index eb5f41e..c5ce61a 100644 --- a/tests/check/elements/identity.c +++ b/tests/check/elements/identity.c @@ -101,7 +101,9 @@ GST_START_TEST (test_one_buffer) memcpy (GST_BUFFER_DATA (buffer), "data", 4); /* pushing gives away my reference ... */ gst_pad_push (mysrcpad, buffer); - /* ... but it ends up being collected on the global buffer list */ + /* ... but it should end up being collected on the global buffer list */ + fail_unless (g_list_length (buffers) == 1); + fail_unless ((GstBuffer *) (g_list_first (buffers)->data) == buffer); ASSERT_BUFFER_REFCOUNT (buffer, "buffer", 1); /* cleanup */