diff options
author | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2009-01-23 12:46:28 +0100 |
---|---|---|
committer | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2009-01-23 12:47:19 +0100 |
commit | e4e3b44e048ddc1d7499c6108175a5f89c6273d9 (patch) | |
tree | f88b685f1b6baf849649494ec557d2ef0ef13a88 /gst/legacyresample/gstlegacyresample.c | |
parent | 6fec8619b597f5cc9c58d268ddd9f64ea0a94277 (diff) | |
download | gst-plugins-bad-e4e3b44e048ddc1d7499c6108175a5f89c6273d9.tar.gz gst-plugins-bad-e4e3b44e048ddc1d7499c6108175a5f89c6273d9.tar.bz2 gst-plugins-bad-e4e3b44e048ddc1d7499c6108175a5f89c6273d9.zip |
Rename audioresample files and types to legacyresample
Finish the move/rename of audioresample to legacyresample
to prevent any confusion.
Diffstat (limited to 'gst/legacyresample/gstlegacyresample.c')
-rw-r--r-- | gst/legacyresample/gstlegacyresample.c | 863 |
1 files changed, 863 insertions, 0 deletions
diff --git a/gst/legacyresample/gstlegacyresample.c b/gst/legacyresample/gstlegacyresample.c new file mode 100644 index 00000000..908b6ad9 --- /dev/null +++ b/gst/legacyresample/gstlegacyresample.c @@ -0,0 +1,863 @@ +/* GStreamer + * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) 2003,2004 David A. Schleef <ds@schleef.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * 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. + */ +/* Element-Checklist-Version: 5 */ + +/** + * SECTION:element-legacyresample + * + * legacyresample resamples raw audio buffers to different sample rates using + * a configurable windowing function to enhance quality. + * + * <refsect2> + * <title>Example launch line</title> + * |[ + * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! legacyresample ! audio/x-raw-int, rate=8000 ! alsasink + * ]| Decode an Ogg/Vorbis downsample to 8Khz and play sound through alsa. + * To create the Ogg/Vorbis file refer to the documentation of vorbisenc. + * </refsect2> + * + * Last reviewed on 2006-03-02 (0.10.4) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <math.h> + +/*#define DEBUG_ENABLED */ +#include "gstlegacyresample.h" +#include <gst/audio/audio.h> +#include <gst/base/gstbasetransform.h> + +GST_DEBUG_CATEGORY_STATIC (legacyresample_debug); +#define GST_CAT_DEFAULT legacyresample_debug + +/* elementfactory information */ +static const GstElementDetails gst_legacyresample_details = +GST_ELEMENT_DETAILS ("Audio scaler", + "Filter/Converter/Audio", + "Resample audio", + "David Schleef <ds@schleef.org>"); + +#define DEFAULT_FILTERLEN 16 + +enum +{ + PROP_0, + PROP_FILTERLEN +}; + +#define SUPPORTED_CAPS \ +GST_STATIC_CAPS ( \ + "audio/x-raw-int, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, MAX ], " \ + "endianness = (int) BYTE_ORDER, " \ + "width = (int) 16, " \ + "depth = (int) 16, " \ + "signed = (boolean) true;" \ + "audio/x-raw-int, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, MAX ], " \ + "endianness = (int) BYTE_ORDER, " \ + "width = (int) 32, " \ + "depth = (int) 32, " \ + "signed = (boolean) true;" \ + "audio/x-raw-float, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, MAX ], " \ + "endianness = (int) BYTE_ORDER, " \ + "width = (int) 32; " \ + "audio/x-raw-float, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, MAX ], " \ + "endianness = (int) BYTE_ORDER, " \ + "width = (int) 64" \ +) + +static GstStaticPadTemplate gst_legacyresample_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS); + +static GstStaticPadTemplate gst_legacyresample_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS); + +static void gst_legacyresample_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_legacyresample_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +/* vmethods */ +static gboolean legacyresample_get_unit_size (GstBaseTransform * base, + GstCaps * caps, guint * size); +static GstCaps *legacyresample_transform_caps (GstBaseTransform * base, + GstPadDirection direction, GstCaps * caps); +static void legacyresample_fixate_caps (GstBaseTransform * base, + GstPadDirection direction, GstCaps * caps, GstCaps * othercaps); +static gboolean legacyresample_transform_size (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * incaps, guint insize, + GstCaps * outcaps, guint * outsize); +static gboolean legacyresample_set_caps (GstBaseTransform * base, + GstCaps * incaps, GstCaps * outcaps); +static GstFlowReturn legacyresample_pushthrough (GstLegacyresample * + legacyresample); +static GstFlowReturn legacyresample_transform (GstBaseTransform * base, + GstBuffer * inbuf, GstBuffer * outbuf); +static gboolean legacyresample_event (GstBaseTransform * base, + GstEvent * event); +static gboolean legacyresample_start (GstBaseTransform * base); +static gboolean legacyresample_stop (GstBaseTransform * base); + +static gboolean legacyresample_query (GstPad * pad, GstQuery * query); +static const GstQueryType *legacyresample_query_type (GstPad * pad); + +#define DEBUG_INIT(bla) \ + GST_DEBUG_CATEGORY_INIT (legacyresample_debug, "legacyresample", 0, "audio resampling element"); + +GST_BOILERPLATE_FULL (GstLegacyresample, gst_legacyresample, GstBaseTransform, + GST_TYPE_BASE_TRANSFORM, DEBUG_INIT); + +static void +gst_legacyresample_base_init (gpointer g_class) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_legacyresample_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_legacyresample_sink_template)); + + gst_element_class_set_details (gstelement_class, &gst_legacyresample_details); +} + +static void +gst_legacyresample_class_init (GstLegacyresampleClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + gobject_class->set_property = gst_legacyresample_set_property; + gobject_class->get_property = gst_legacyresample_get_property; + + g_object_class_install_property (gobject_class, PROP_FILTERLEN, + g_param_spec_int ("filter-length", "filter length", + "Length of the resample filter", 0, G_MAXINT, DEFAULT_FILTERLEN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + GST_BASE_TRANSFORM_CLASS (klass)->start = + GST_DEBUG_FUNCPTR (legacyresample_start); + GST_BASE_TRANSFORM_CLASS (klass)->stop = + GST_DEBUG_FUNCPTR (legacyresample_stop); + GST_BASE_TRANSFORM_CLASS (klass)->transform_size = + GST_DEBUG_FUNCPTR (legacyresample_transform_size); + GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size = + GST_DEBUG_FUNCPTR (legacyresample_get_unit_size); + GST_BASE_TRANSFORM_CLASS (klass)->transform_caps = + GST_DEBUG_FUNCPTR (legacyresample_transform_caps); + GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps = + GST_DEBUG_FUNCPTR (legacyresample_fixate_caps); + GST_BASE_TRANSFORM_CLASS (klass)->set_caps = + GST_DEBUG_FUNCPTR (legacyresample_set_caps); + GST_BASE_TRANSFORM_CLASS (klass)->transform = + GST_DEBUG_FUNCPTR (legacyresample_transform); + GST_BASE_TRANSFORM_CLASS (klass)->event = + GST_DEBUG_FUNCPTR (legacyresample_event); + + GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE; +} + +static void +gst_legacyresample_init (GstLegacyresample * legacyresample, + GstLegacyresampleClass * klass) +{ + GstBaseTransform *trans; + + trans = GST_BASE_TRANSFORM (legacyresample); + + /* buffer alloc passthrough is too impossible. FIXME, it + * is trivial in the passthrough case. */ + gst_pad_set_bufferalloc_function (trans->sinkpad, NULL); + + legacyresample->filter_length = DEFAULT_FILTERLEN; + + legacyresample->need_discont = FALSE; + + gst_pad_set_query_function (trans->srcpad, legacyresample_query); + gst_pad_set_query_type_function (trans->srcpad, legacyresample_query_type); +} + +/* vmethods */ +static gboolean +legacyresample_start (GstBaseTransform * base) +{ + GstLegacyresample *legacyresample = GST_LEGACYRESAMPLE (base); + + legacyresample->resample = resample_new (); + legacyresample->ts_offset = -1; + legacyresample->offset = -1; + legacyresample->next_ts = -1; + + resample_set_filter_length (legacyresample->resample, + legacyresample->filter_length); + + return TRUE; +} + +static gboolean +legacyresample_stop (GstBaseTransform * base) +{ + GstLegacyresample *legacyresample = GST_LEGACYRESAMPLE (base); + + if (legacyresample->resample) { + resample_free (legacyresample->resample); + legacyresample->resample = NULL; + } + + gst_caps_replace (&legacyresample->sinkcaps, NULL); + gst_caps_replace (&legacyresample->srccaps, NULL); + + return TRUE; +} + +static gboolean +legacyresample_get_unit_size (GstBaseTransform * base, GstCaps * caps, + guint * size) +{ + gint width, channels; + GstStructure *structure; + gboolean ret; + + g_assert (size); + + /* this works for both float and int */ + structure = gst_caps_get_structure (caps, 0); + ret = gst_structure_get_int (structure, "width", &width); + ret &= gst_structure_get_int (structure, "channels", &channels); + g_return_val_if_fail (ret, FALSE); + + *size = width * channels / 8; + + return TRUE; +} + +static GstCaps * +legacyresample_transform_caps (GstBaseTransform * base, + GstPadDirection direction, GstCaps * caps) +{ + GstCaps *res; + GstStructure *structure; + + /* transform caps gives one single caps so we can just replace + * the rate property with our range. */ + res = gst_caps_copy (caps); + structure = gst_caps_get_structure (res, 0); + gst_structure_set (structure, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL); + + return res; +} + +/* Fixate rate to the allowed rate that has the smallest difference */ +static void +legacyresample_fixate_caps (GstBaseTransform * base, + GstPadDirection direction, GstCaps * caps, GstCaps * othercaps) +{ + GstStructure *s; + gint rate; + + s = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_int (s, "rate", &rate)) + return; + + s = gst_caps_get_structure (othercaps, 0); + gst_structure_fixate_field_nearest_int (s, "rate", rate); +} + +static gboolean +resample_set_state_from_caps (ResampleState * state, GstCaps * incaps, + GstCaps * outcaps, gint * channels, gint * inrate, gint * outrate) +{ + GstStructure *structure; + gboolean ret; + gint myinrate, myoutrate; + int mychannels; + gint width, depth; + ResampleFormat format; + + GST_DEBUG ("incaps %" GST_PTR_FORMAT ", outcaps %" + GST_PTR_FORMAT, incaps, outcaps); + + structure = gst_caps_get_structure (incaps, 0); + + /* get width */ + ret = gst_structure_get_int (structure, "width", &width); + if (!ret) + goto no_width; + + /* figure out the format */ + if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float")) { + if (width == 32) + format = RESAMPLE_FORMAT_F32; + else if (width == 64) + format = RESAMPLE_FORMAT_F64; + else + goto wrong_depth; + } else { + /* for int, depth and width must be the same */ + ret = gst_structure_get_int (structure, "depth", &depth); + if (!ret || width != depth) + goto not_equal; + + if (width == 16) + format = RESAMPLE_FORMAT_S16; + else if (width == 32) + format = RESAMPLE_FORMAT_S32; + else + goto wrong_depth; + } + ret = gst_structure_get_int (structure, "rate", &myinrate); + ret &= gst_structure_get_int (structure, "channels", &mychannels); + if (!ret) + goto no_in_rate_channels; + + structure = gst_caps_get_structure (outcaps, 0); + ret = gst_structure_get_int (structure, "rate", &myoutrate); + if (!ret) + goto no_out_rate; + + if (channels) + *channels = mychannels; + if (inrate) + *inrate = myinrate; + if (outrate) + *outrate = myoutrate; + + resample_set_format (state, format); + resample_set_n_channels (state, mychannels); + resample_set_input_rate (state, myinrate); + resample_set_output_rate (state, myoutrate); + + return TRUE; + + /* ERRORS */ +no_width: + { + GST_DEBUG ("failed to get width from caps"); + return FALSE; + } +not_equal: + { + GST_DEBUG ("width %d and depth %d must be the same", width, depth); + return FALSE; + } +wrong_depth: + { + GST_DEBUG ("unknown depth %d found", depth); + return FALSE; + } +no_in_rate_channels: + { + GST_DEBUG ("could not get input rate and channels"); + return FALSE; + } +no_out_rate: + { + GST_DEBUG ("could not get output rate"); + return FALSE; + } +} + +static gboolean +legacyresample_transform_size (GstBaseTransform * base, + GstPadDirection direction, GstCaps * caps, guint size, GstCaps * othercaps, + guint * othersize) +{ + GstLegacyresample *legacyresample = GST_LEGACYRESAMPLE (base); + ResampleState *state; + GstCaps *srccaps, *sinkcaps; + gboolean use_internal = FALSE; /* whether we use the internal state */ + gboolean ret = TRUE; + + GST_LOG_OBJECT (base, "asked to transform size %d in direction %s", + size, direction == GST_PAD_SINK ? "SINK" : "SRC"); + if (direction == GST_PAD_SINK) { + sinkcaps = caps; + srccaps = othercaps; + } else { + sinkcaps = othercaps; + srccaps = caps; + } + + /* if the caps are the ones that _set_caps got called with; we can use + * our own state; otherwise we'll have to create a state */ + if (gst_caps_is_equal (sinkcaps, legacyresample->sinkcaps) && + gst_caps_is_equal (srccaps, legacyresample->srccaps)) { + use_internal = TRUE; + state = legacyresample->resample; + } else { + GST_DEBUG_OBJECT (legacyresample, + "caps are not the set caps, creating state"); + state = resample_new (); + resample_set_filter_length (state, legacyresample->filter_length); + resample_set_state_from_caps (state, sinkcaps, srccaps, NULL, NULL, NULL); + } + + if (direction == GST_PAD_SINK) { + /* asked to convert size of an incoming buffer */ + *othersize = resample_get_output_size_for_input (state, size); + } else { + /* asked to convert size of an outgoing buffer */ + *othersize = resample_get_input_size_for_output (state, size); + } + g_assert (*othersize % state->sample_size == 0); + + /* we make room for one extra sample, given that the resampling filter + * can output an extra one for non-integral i_rate/o_rate */ + GST_LOG_OBJECT (base, "transformed size %d to %d", size, *othersize); + + if (!use_internal) { + resample_free (state); + } + + return ret; +} + +static gboolean +legacyresample_set_caps (GstBaseTransform * base, GstCaps * incaps, + GstCaps * outcaps) +{ + gboolean ret; + gint inrate, outrate; + int channels; + GstLegacyresample *legacyresample = GST_LEGACYRESAMPLE (base); + + GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %" + GST_PTR_FORMAT, incaps, outcaps); + + ret = resample_set_state_from_caps (legacyresample->resample, incaps, outcaps, + &channels, &inrate, &outrate); + + g_return_val_if_fail (ret, FALSE); + + legacyresample->channels = channels; + GST_DEBUG_OBJECT (legacyresample, "set channels to %d", channels); + legacyresample->i_rate = inrate; + GST_DEBUG_OBJECT (legacyresample, "set i_rate to %d", inrate); + legacyresample->o_rate = outrate; + GST_DEBUG_OBJECT (legacyresample, "set o_rate to %d", outrate); + + /* save caps so we can short-circuit in the size_transform if the caps + * are the same */ + gst_caps_replace (&legacyresample->sinkcaps, incaps); + gst_caps_replace (&legacyresample->srccaps, outcaps); + + return TRUE; +} + +static gboolean +legacyresample_event (GstBaseTransform * base, GstEvent * event) +{ + GstLegacyresample *legacyresample; + + legacyresample = GST_LEGACYRESAMPLE (base); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + break; + case GST_EVENT_FLUSH_STOP: + if (legacyresample->resample) + resample_input_flush (legacyresample->resample); + legacyresample->ts_offset = -1; + legacyresample->next_ts = -1; + legacyresample->offset = -1; + break; + case GST_EVENT_NEWSEGMENT: + resample_input_pushthrough (legacyresample->resample); + legacyresample_pushthrough (legacyresample); + legacyresample->ts_offset = -1; + legacyresample->next_ts = -1; + legacyresample->offset = -1; + break; + case GST_EVENT_EOS: + resample_input_eos (legacyresample->resample); + legacyresample_pushthrough (legacyresample); + break; + default: + break; + } + return parent_class->event (base, event); +} + +static GstFlowReturn +legacyresample_do_output (GstLegacyresample * legacyresample, + GstBuffer * outbuf) +{ + int outsize; + int outsamples; + ResampleState *r; + + r = legacyresample->resample; + + outsize = resample_get_output_size (r); + GST_LOG_OBJECT (legacyresample, "legacyresample can give me %d bytes", + outsize); + + /* protect against mem corruption */ + if (outsize > GST_BUFFER_SIZE (outbuf)) { + GST_WARNING_OBJECT (legacyresample, + "overriding legacyresample's outsize %d with outbuffer's size %d", + outsize, GST_BUFFER_SIZE (outbuf)); + outsize = GST_BUFFER_SIZE (outbuf); + } + /* catch possibly wrong size differences */ + if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) { + GST_WARNING_OBJECT (legacyresample, + "legacyresample's outsize %d too far from outbuffer's size %d", + outsize, GST_BUFFER_SIZE (outbuf)); + } + + outsize = resample_get_output_data (r, GST_BUFFER_DATA (outbuf), outsize); + outsamples = outsize / r->sample_size; + GST_LOG_OBJECT (legacyresample, "resample gave me %d bytes or %d samples", + outsize, outsamples); + + GST_BUFFER_OFFSET (outbuf) = legacyresample->offset; + GST_BUFFER_TIMESTAMP (outbuf) = legacyresample->next_ts; + + if (legacyresample->ts_offset != -1) { + legacyresample->offset += outsamples; + legacyresample->ts_offset += outsamples; + legacyresample->next_ts = + gst_util_uint64_scale_int (legacyresample->ts_offset, GST_SECOND, + legacyresample->o_rate); + GST_BUFFER_OFFSET_END (outbuf) = legacyresample->offset; + + /* we calculate DURATION as the difference between "next" timestamp + * and current timestamp so we ensure a contiguous stream, instead of + * having rounding errors. */ + GST_BUFFER_DURATION (outbuf) = legacyresample->next_ts - + GST_BUFFER_TIMESTAMP (outbuf); + } else { + /* no valid offset know, we can still sortof calculate the duration though */ + GST_BUFFER_DURATION (outbuf) = + gst_util_uint64_scale_int (outsamples, GST_SECOND, + legacyresample->o_rate); + } + + /* check for possible mem corruption */ + if (outsize > GST_BUFFER_SIZE (outbuf)) { + /* this is an error that when it happens, would need fixing in the + * resample library; we told it we wanted only GST_BUFFER_SIZE (outbuf), + * and it gave us more ! */ + GST_WARNING_OBJECT (legacyresample, + "legacyresample, you memory corrupting bastard. " + "you gave me outsize %d while my buffer was size %d", + outsize, GST_BUFFER_SIZE (outbuf)); + return GST_FLOW_ERROR; + } + /* catch possibly wrong size differences */ + if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) { + GST_WARNING_OBJECT (legacyresample, + "legacyresample's written outsize %d too far from outbuffer's size %d", + outsize, GST_BUFFER_SIZE (outbuf)); + } + GST_BUFFER_SIZE (outbuf) = outsize; + + if (G_UNLIKELY (legacyresample->need_discont)) { + GST_DEBUG_OBJECT (legacyresample, + "marking this buffer with the DISCONT flag"); + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + legacyresample->need_discont = FALSE; + } + + GST_LOG_OBJECT (legacyresample, "transformed to buffer of %d bytes, ts %" + GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %" + G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT, + outsize, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), + GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf)); + + + return GST_FLOW_OK; +} + +static gboolean +legacyresample_check_discont (GstLegacyresample * legacyresample, + GstClockTime timestamp) +{ + if (timestamp != GST_CLOCK_TIME_NONE && + legacyresample->prev_ts != GST_CLOCK_TIME_NONE && + legacyresample->prev_duration != GST_CLOCK_TIME_NONE && + timestamp != legacyresample->prev_ts + legacyresample->prev_duration) { + /* Potentially a discontinuous buffer. However, it turns out that many + * elements generate imperfect streams due to rounding errors, so we permit + * a small error (up to one sample) without triggering a filter + * flush/restart (if triggered incorrectly, this will be audible) */ + GstClockTimeDiff diff = timestamp - + (legacyresample->prev_ts + legacyresample->prev_duration); + + if (ABS (diff) > GST_SECOND / legacyresample->i_rate) { + GST_WARNING_OBJECT (legacyresample, + "encountered timestamp discontinuity of %" G_GINT64_FORMAT, diff); + return TRUE; + } + } + + return FALSE; +} + +static GstFlowReturn +legacyresample_transform (GstBaseTransform * base, GstBuffer * inbuf, + GstBuffer * outbuf) +{ + GstLegacyresample *legacyresample; + ResampleState *r; + guchar *data, *datacopy; + gulong size; + GstClockTime timestamp; + + legacyresample = GST_LEGACYRESAMPLE (base); + r = legacyresample->resample; + + data = GST_BUFFER_DATA (inbuf); + size = GST_BUFFER_SIZE (inbuf); + timestamp = GST_BUFFER_TIMESTAMP (inbuf); + + GST_LOG_OBJECT (legacyresample, "transforming buffer of %ld bytes, ts %" + GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %" + G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT, + size, GST_TIME_ARGS (timestamp), + GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)), + GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf)); + + /* check for timestamp discontinuities and flush/reset if needed */ + if (G_UNLIKELY (legacyresample_check_discont (legacyresample, timestamp))) { + /* Flush internal samples */ + legacyresample_pushthrough (legacyresample); + /* Inform downstream element about discontinuity */ + legacyresample->need_discont = TRUE; + /* We want to recalculate the offset */ + legacyresample->ts_offset = -1; + } + + if (legacyresample->ts_offset == -1) { + /* if we don't know the initial offset yet, calculate it based on the + * input timestamp. */ + if (GST_CLOCK_TIME_IS_VALID (timestamp)) { + GstClockTime stime; + + /* offset used to calculate the timestamps. We use the sample offset for + * this to make it more accurate. We want the first buffer to have the + * same timestamp as the incoming timestamp. */ + legacyresample->next_ts = timestamp; + legacyresample->ts_offset = + gst_util_uint64_scale_int (timestamp, r->o_rate, GST_SECOND); + /* offset used to set as the buffer offset, this offset is always + * relative to the stream time, note that timestamp is not... */ + stime = (timestamp - base->segment.start) + base->segment.time; + legacyresample->offset = + gst_util_uint64_scale_int (stime, r->o_rate, GST_SECOND); + } + } + legacyresample->prev_ts = timestamp; + legacyresample->prev_duration = GST_BUFFER_DURATION (inbuf); + + /* need to memdup, resample takes ownership. */ + datacopy = g_memdup (data, size); + resample_add_input_data (r, datacopy, size, g_free, datacopy); + + return legacyresample_do_output (legacyresample, outbuf); +} + +/* push remaining data in the buffers out */ +static GstFlowReturn +legacyresample_pushthrough (GstLegacyresample * legacyresample) +{ + int outsize; + ResampleState *r; + GstBuffer *outbuf; + GstFlowReturn res = GST_FLOW_OK; + GstBaseTransform *trans; + + r = legacyresample->resample; + + outsize = resample_get_output_size (r); + if (outsize == 0) { + GST_DEBUG_OBJECT (legacyresample, "no internal buffers needing flush"); + goto done; + } + + trans = GST_BASE_TRANSFORM (legacyresample); + + res = gst_pad_alloc_buffer (trans->srcpad, GST_BUFFER_OFFSET_NONE, outsize, + GST_PAD_CAPS (trans->srcpad), &outbuf); + if (G_UNLIKELY (res != GST_FLOW_OK)) { + GST_WARNING_OBJECT (legacyresample, "failed allocating buffer of %d bytes", + outsize); + goto done; + } + + res = legacyresample_do_output (legacyresample, outbuf); + if (G_UNLIKELY (res != GST_FLOW_OK)) + goto done; + + res = gst_pad_push (trans->srcpad, outbuf); + +done: + return res; +} + +static gboolean +legacyresample_query (GstPad * pad, GstQuery * query) +{ + GstLegacyresample *legacyresample = + GST_LEGACYRESAMPLE (gst_pad_get_parent (pad)); + GstBaseTransform *trans = GST_BASE_TRANSFORM (legacyresample); + gboolean res = TRUE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_LATENCY: + { + GstClockTime min, max; + gboolean live; + guint64 latency; + GstPad *peer; + gint rate = legacyresample->i_rate; + gint resampler_latency = legacyresample->filter_length / 2; + + if (gst_base_transform_is_passthrough (trans)) + resampler_latency = 0; + + if ((peer = gst_pad_get_peer (trans->sinkpad))) { + if ((res = gst_pad_query (peer, query))) { + gst_query_parse_latency (query, &live, &min, &max); + + GST_DEBUG ("Peer latency: min %" + GST_TIME_FORMAT " max %" GST_TIME_FORMAT, + GST_TIME_ARGS (min), GST_TIME_ARGS (max)); + + /* add our own latency */ + if (rate != 0 && resampler_latency != 0) + latency = + gst_util_uint64_scale (resampler_latency, GST_SECOND, rate); + else + latency = 0; + + GST_DEBUG ("Our latency: %" GST_TIME_FORMAT, GST_TIME_ARGS (latency)); + + min += latency; + if (max != GST_CLOCK_TIME_NONE) + max += latency; + + GST_DEBUG ("Calculated total latency : min %" + GST_TIME_FORMAT " max %" GST_TIME_FORMAT, + GST_TIME_ARGS (min), GST_TIME_ARGS (max)); + + gst_query_set_latency (query, live, min, max); + } + gst_object_unref (peer); + } + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } + gst_object_unref (legacyresample); + return res; +} + +static const GstQueryType * +legacyresample_query_type (GstPad * pad) +{ + static const GstQueryType types[] = { + GST_QUERY_LATENCY, + 0 + }; + + return types; +} + +static void +gst_legacyresample_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstLegacyresample *legacyresample; + + legacyresample = GST_LEGACYRESAMPLE (object); + + switch (prop_id) { + case PROP_FILTERLEN: + legacyresample->filter_length = g_value_get_int (value); + GST_DEBUG_OBJECT (GST_ELEMENT (legacyresample), "new filter length %d", + legacyresample->filter_length); + if (legacyresample->resample) { + resample_set_filter_length (legacyresample->resample, + legacyresample->filter_length); + gst_element_post_message (GST_ELEMENT (legacyresample), + gst_message_new_latency (GST_OBJECT (legacyresample))); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_legacyresample_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstLegacyresample *legacyresample; + + legacyresample = GST_LEGACYRESAMPLE (object); + + switch (prop_id) { + case PROP_FILTERLEN: + g_value_set_int (value, legacyresample->filter_length); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +plugin_init (GstPlugin * plugin) +{ + resample_init (); + + if (!gst_element_register (plugin, "legacyresample", GST_RANK_MARGINAL, + GST_TYPE_LEGACYRESAMPLE)) { + return FALSE; + } + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "legacyresample", + "Resamples audio", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN); |