diff options
Diffstat (limited to 'gst/speexresample/gstspeexresample.c')
-rw-r--r-- | gst/speexresample/gstspeexresample.c | 733 |
1 files changed, 733 insertions, 0 deletions
diff --git a/gst/speexresample/gstspeexresample.c b/gst/speexresample/gstspeexresample.c new file mode 100644 index 00000000..307b8180 --- /dev/null +++ b/gst/speexresample/gstspeexresample.c @@ -0,0 +1,733 @@ +/* GStreamer + * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) 2003,2004 David A. Schleef <ds@schleef.org> + * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.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. + */ + +/** + * SECTION:element-speexresample + * + * <refsect2> + * speexresample resamples raw audio buffers to different sample rates using + * a configurable windowing function to enhance quality. + * <title>Example launch line</title> + * <para> + * <programlisting> + * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! speexresample ! audio/x-raw-int, rate=8000 ! alsasink + * </programlisting> + * Decode an Ogg/Vorbis downsample to 8Khz and play sound through alsa. + * To create the Ogg/Vorbis file refer to the documentation of vorbisenc. + * </para> + * </refsect2> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <math.h> + +#include "gstspeexresample.h" +#include <gst/audio/audio.h> +#include <gst/base/gstbasetransform.h> + +GST_DEBUG_CATEGORY (speex_resample_debug); +#define GST_CAT_DEFAULT speex_resample_debug + +enum +{ + PROP_0, + PROP_QUALITY +}; + +#define SUPPORTED_CAPS \ +GST_STATIC_CAPS ( \ + "audio/x-raw-float, " \ + "rate = (int) [ 1, MAX ], " \ + "channels = (int) [ 1, MAX ], " \ + "endianness = (int) BYTE_ORDER, " \ + "width = (int) 32; " \ + "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" \ +) + +static GstStaticPadTemplate gst_speex_resample_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS); + +static GstStaticPadTemplate gst_speex_resample_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS); + +static void gst_speex_resample_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_speex_resample_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +/* vmethods */ +static gboolean gst_speex_resample_get_unit_size (GstBaseTransform * base, + GstCaps * caps, guint * size); +static GstCaps *gst_speex_resample_transform_caps (GstBaseTransform * base, + GstPadDirection direction, GstCaps * caps); +static gboolean gst_speex_resample_transform_size (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * incaps, guint insize, + GstCaps * outcaps, guint * outsize); +static gboolean gst_speex_resample_set_caps (GstBaseTransform * base, + GstCaps * incaps, GstCaps * outcaps); +static GstFlowReturn gst_speex_resample_transform (GstBaseTransform * base, + GstBuffer * inbuf, GstBuffer * outbuf); +static gboolean gst_speex_resample_event (GstBaseTransform * base, + GstEvent * event); +static gboolean gst_speex_resample_start (GstBaseTransform * base); +static gboolean gst_speex_resample_stop (GstBaseTransform * base); + +#define DEBUG_INIT(bla) \ + GST_DEBUG_CATEGORY_INIT (speex_resample_debug, "speex_resample", 0, "audio resampling element"); + +GST_BOILERPLATE_FULL (GstSpeexResample, gst_speex_resample, GstBaseTransform, + GST_TYPE_BASE_TRANSFORM, DEBUG_INIT); + +static void +gst_speex_resample_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_speex_resample_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_speex_resample_sink_template)); + + gst_element_class_set_details_simple (gstelement_class, "Audio resampler", + "Filter/Converter/Audio", "Resamples audio", + "Sebastian Dröge <slomo@circular-chaos.org>"); +} + +static void +gst_speex_resample_class_init (GstSpeexResampleClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->set_property = gst_speex_resample_set_property; + gobject_class->get_property = gst_speex_resample_get_property; + + g_object_class_install_property (gobject_class, PROP_QUALITY, + g_param_spec_int ("quality", "Quality", "Resample quality with 0 being " + "the lowest and 10 being the best", + SPEEX_RESAMPLER_QUALITY_MIN, SPEEX_RESAMPLER_QUALITY_MAX, + SPEEX_RESAMPLER_QUALITY_DEFAULT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + GST_BASE_TRANSFORM_CLASS (klass)->start = + GST_DEBUG_FUNCPTR (gst_speex_resample_start); + GST_BASE_TRANSFORM_CLASS (klass)->stop = + GST_DEBUG_FUNCPTR (gst_speex_resample_stop); + GST_BASE_TRANSFORM_CLASS (klass)->transform_size = + GST_DEBUG_FUNCPTR (gst_speex_resample_transform_size); + GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size = + GST_DEBUG_FUNCPTR (gst_speex_resample_get_unit_size); + GST_BASE_TRANSFORM_CLASS (klass)->transform_caps = + GST_DEBUG_FUNCPTR (gst_speex_resample_transform_caps); + GST_BASE_TRANSFORM_CLASS (klass)->set_caps = + GST_DEBUG_FUNCPTR (gst_speex_resample_set_caps); + GST_BASE_TRANSFORM_CLASS (klass)->transform = + GST_DEBUG_FUNCPTR (gst_speex_resample_transform); + GST_BASE_TRANSFORM_CLASS (klass)->event = + GST_DEBUG_FUNCPTR (gst_speex_resample_event); + + GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE; +} + +static void +gst_speex_resample_init (GstSpeexResample * resample, + GstSpeexResampleClass * klass) +{ + resample->quality = SPEEX_RESAMPLER_QUALITY_DEFAULT; + + resample->need_discont = FALSE; +} + +/* vmethods */ +static gboolean +gst_speex_resample_start (GstBaseTransform * base) +{ + GstSpeexResample *resample = GST_SPEEX_RESAMPLE (base); + + resample->ts_offset = -1; + resample->offset = -1; + resample->next_ts = -1; + + return TRUE; +} + +static gboolean +gst_speex_resample_stop (GstBaseTransform * base) +{ + GstSpeexResample *resample = GST_SPEEX_RESAMPLE (base); + + if (resample->state) { + resample_resampler_destroy (resample->state); + resample->state = NULL; + } + + gst_caps_replace (&resample->sinkcaps, NULL); + gst_caps_replace (&resample->srccaps, NULL); + + return TRUE; +} + +static gboolean +gst_speex_resample_get_unit_size (GstBaseTransform * base, GstCaps * caps, + guint * size) +{ + gint width, channels; + GstStructure *structure; + gboolean ret; + + g_return_val_if_fail (size != NULL, FALSE); + + /* 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 * +gst_speex_resample_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; +} + +static SpeexResamplerState * +gst_speex_resample_init_state (guint channels, guint inrate, guint outrate, + guint quality, gboolean fp) +{ + SpeexResamplerState *ret = NULL; + gint err = RESAMPLER_ERR_SUCCESS; + + if (fp) + ret = + resample_float_resampler_init (channels, inrate, outrate, quality, + &err); + else + ret = + resample_int_resampler_init (channels, inrate, outrate, quality, &err); + + if (err != RESAMPLER_ERR_SUCCESS) { + GST_ERROR ("Failed to create resampler state: %s", + resample_resampler_strerror (err)); + return NULL; + } + + return ret; +} + +static gboolean +gst_speex_resample_update_state (GstSpeexResample * resample, gint channels, + gint inrate, gint outrate, gint quality, gboolean fp) +{ + gboolean ret = TRUE; + + if (resample->state == NULL) { + ret = TRUE; + } else if (resample->channels != channels || fp != resample->fp) { + resample_resampler_destroy (resample->state); + resample->state = + gst_speex_resample_init_state (channels, inrate, outrate, quality, fp); + + ret = (resample->state != NULL); + } else if (resample->inrate != inrate || resample->outrate != outrate) { + gint err = RESAMPLER_ERR_SUCCESS; + + if (fp) + err = + resample_float_resampler_set_rate (resample->state, inrate, outrate); + else + err = resample_int_resampler_set_rate (resample->state, inrate, outrate); + + if (err != RESAMPLER_ERR_SUCCESS) + GST_ERROR ("Failed to update rate: %s", + resample_resampler_strerror (err)); + + ret = (err == RESAMPLER_ERR_SUCCESS); + } else if (quality != resample->quality) { + gint err = RESAMPLER_ERR_SUCCESS; + + if (fp) + err = resample_float_resampler_set_quality (resample->state, quality); + else + err = resample_int_resampler_set_quality (resample->state, quality); + + if (err != RESAMPLER_ERR_SUCCESS) + GST_ERROR ("Failed to update quality: %s", + resample_resampler_strerror (err)); + + ret = (err == RESAMPLER_ERR_SUCCESS); + } + + resample->channels = channels; + resample->fp = fp; + resample->quality = quality; + resample->inrate = inrate; + resample->outrate = outrate; + + return ret; +} + +static void +gst_speex_resample_reset_state (GstSpeexResample * resample) +{ + if (resample->state && resample->fp) + resample_float_resampler_reset_mem (resample->state); + else if (resample->state && !resample->fp) + resample_int_resampler_reset_mem (resample->state); +} + +static gboolean +gst_speex_resample_parse_caps (GstCaps * incaps, + GstCaps * outcaps, gint * channels, gint * inrate, gint * outrate, + gboolean * fp) +{ + GstStructure *structure; + gboolean ret; + gint myinrate, myoutrate, mychannels; + gboolean myfp; + + GST_DEBUG ("incaps %" GST_PTR_FORMAT ", outcaps %" + GST_PTR_FORMAT, incaps, outcaps); + + structure = gst_caps_get_structure (incaps, 0); + + if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float")) + myfp = TRUE; + else + myfp = FALSE; + + 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; + + if (fp) + *fp = myfp; + + return TRUE; + + /* ERRORS */ +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 +gst_speex_resample_transform_size (GstBaseTransform * base, + GstPadDirection direction, GstCaps * caps, guint size, GstCaps * othercaps, + guint * othersize) +{ + GstSpeexResample *resample = GST_SPEEX_RESAMPLE (base); + SpeexResamplerState *state; + GstCaps *srccaps, *sinkcaps; + gboolean use_internal = FALSE; /* whether we use the internal state */ + gboolean ret = TRUE; + guint32 ratio_den, ratio_num; + gboolean fp; + + GST_LOG ("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 (resample->state && gst_caps_is_equal (sinkcaps, resample->sinkcaps) && + gst_caps_is_equal (srccaps, resample->srccaps)) { + use_internal = TRUE; + state = resample->state; + fp = resample->fp; + } else { + gint inrate, outrate, channels; + + GST_DEBUG ("Can't use internal state, creating state"); + + ret = + gst_speex_resample_parse_caps (caps, othercaps, &channels, &inrate, + &outrate, &fp); + + if (!ret) { + GST_ERROR ("Wrong caps"); + return FALSE; + } + + state = gst_speex_resample_init_state (channels, inrate, outrate, 0, TRUE); + if (!state) + return FALSE; + } + + if (resample->fp || use_internal) + resample_float_resampler_get_ratio (state, &ratio_num, &ratio_den); + else + resample_int_resampler_get_ratio (state, &ratio_num, &ratio_den); + + if (direction == GST_PAD_SINK) { + gint fac = (fp) ? 4 : 2; + + /* asked to convert size of an incoming buffer */ + size /= fac; + *othersize = (size * ratio_den + (ratio_num >> 1)) / ratio_num; + *othersize *= fac; + } else { + gint fac = (fp) ? 4 : 2; + + /* asked to convert size of an outgoing buffer */ + size /= fac; + *othersize = (size * ratio_num + (ratio_den >> 1)) / ratio_den; + *othersize *= fac; + } + + GST_LOG ("transformed size %d to %d", size, *othersize); + + if (!use_internal) + resample_resampler_destroy (state); + + return ret; +} + +static gboolean +gst_speex_resample_set_caps (GstBaseTransform * base, GstCaps * incaps, + GstCaps * outcaps) +{ + gboolean ret; + gint inrate = 0, outrate = 0, channels = 0; + gboolean fp = FALSE; + GstSpeexResample *resample = GST_SPEEX_RESAMPLE (base); + + GST_LOG ("incaps %" GST_PTR_FORMAT ", outcaps %" + GST_PTR_FORMAT, incaps, outcaps); + + ret = gst_speex_resample_parse_caps (incaps, outcaps, + &channels, &inrate, &outrate, &fp); + + g_return_val_if_fail (ret, FALSE); + + ret = + gst_speex_resample_update_state (resample, channels, inrate, outrate, + resample->quality, fp); + + g_return_val_if_fail (ret, FALSE); + + /* save caps so we can short-circuit in the size_transform if the caps + * are the same */ + gst_caps_replace (&resample->sinkcaps, incaps); + gst_caps_replace (&resample->srccaps, outcaps); + + return TRUE; +} + +static gboolean +gst_speex_resample_event (GstBaseTransform * base, GstEvent * event) +{ + GstSpeexResample *resample = GST_SPEEX_RESAMPLE (base); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + break; + case GST_EVENT_FLUSH_STOP: + case GST_EVENT_NEWSEGMENT: + gst_speex_resample_reset_state (resample); + resample->ts_offset = -1; + resample->next_ts = -1; + resample->offset = -1; + break; + case GST_EVENT_EOS: + gst_speex_resample_reset_state (resample); + break; + default: + break; + } + parent_class->event (base, event); + + return TRUE; +} + +static gboolean +gst_speex_resample_check_discont (GstSpeexResample * resample, + GstClockTime timestamp) +{ + if (timestamp != GST_CLOCK_TIME_NONE && + resample->prev_ts != GST_CLOCK_TIME_NONE && + resample->prev_duration != GST_CLOCK_TIME_NONE && + timestamp != resample->prev_ts + resample->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 - + (resample->prev_ts + resample->prev_duration); + + if (ABS (diff) > GST_SECOND / resample->inrate) { + GST_WARNING ("encountered timestamp discontinuity of %" G_GINT64_FORMAT, + diff); + return TRUE; + } + } + + return FALSE; +} + +static GstFlowReturn +gst_speex_resample_process (GstSpeexResample * resample, GstBuffer * inbuf, + GstBuffer * outbuf) +{ + guint32 in_len, in_processed; + guint32 out_len, out_processed; + gint err = RESAMPLER_ERR_SUCCESS; + + in_len = GST_BUFFER_SIZE (inbuf) / resample->channels; + out_len = GST_BUFFER_SIZE (outbuf) / resample->channels; + + if (resample->fp) { + in_len /= 4; + out_len /= 4; + } else { + in_len /= 2; + out_len /= 2; + } + + in_processed = in_len; + out_processed = out_len; + + if (resample->fp) + err = resample_float_resampler_process_interleaved_float (resample->state, + (const gfloat *) GST_BUFFER_DATA (inbuf), &in_processed, + (gfloat *) GST_BUFFER_DATA (outbuf), &out_processed); + else + err = resample_int_resampler_process_interleaved_int (resample->state, + (const gint16 *) GST_BUFFER_DATA (inbuf), &in_processed, + (gint16 *) GST_BUFFER_DATA (outbuf), &out_processed); + + if (in_len != in_processed) + GST_WARNING ("Converted %d of %d input samples", in_processed, in_len); + + if (out_len != out_processed) + GST_WARNING ("Converted to %d instead of %d output samples", out_processed, + out_len); + + if (err != RESAMPLER_ERR_SUCCESS) { + GST_ERROR ("Failed to convert data: %s", resample_resampler_strerror (err)); + return GST_FLOW_ERROR; + } else { + return GST_FLOW_OK; + } +} + +static GstFlowReturn +gst_speex_resample_transform (GstBaseTransform * base, GstBuffer * inbuf, + GstBuffer * outbuf) +{ + GstSpeexResample *resample = GST_SPEEX_RESAMPLE (base); + guint8 *data; + gulong size; + GstClockTime timestamp; + gint outsamples; + + if (resample->state == NULL) + if (!(resample->state = gst_speex_resample_init_state (resample->channels, + resample->inrate, resample->outrate, resample->quality, + resample->fp))) + return GST_FLOW_ERROR; + + data = GST_BUFFER_DATA (inbuf); + size = GST_BUFFER_SIZE (inbuf); + timestamp = GST_BUFFER_TIMESTAMP (inbuf); + + GST_LOG ("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 (gst_speex_resample_check_discont (resample, timestamp) + || GST_BUFFER_IS_DISCONT (inbuf))) { + /* Flush internal samples */ + gst_speex_resample_reset_state (resample); + /* Inform downstream element about discontinuity */ + resample->need_discont = TRUE; + /* We want to recalculate the offset */ + resample->ts_offset = -1; + } + + outsamples = GST_BUFFER_SIZE (outbuf) / resample->channels; + outsamples /= (resample->fp) ? 4 : 2; + + if (resample->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. */ + resample->next_ts = timestamp; + resample->ts_offset = + gst_util_uint64_scale_int (timestamp, resample->outrate, 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; + resample->offset = + gst_util_uint64_scale_int (stime, resample->outrate, GST_SECOND); + } + } + resample->prev_ts = timestamp; + resample->prev_duration = GST_BUFFER_DURATION (inbuf); + + GST_BUFFER_OFFSET (outbuf) = resample->offset; + GST_BUFFER_TIMESTAMP (outbuf) = resample->next_ts; + + if (resample->ts_offset != -1) { + resample->offset += outsamples; + resample->ts_offset += outsamples; + resample->next_ts = + gst_util_uint64_scale_int (resample->ts_offset, GST_SECOND, + resample->outrate); + GST_BUFFER_OFFSET_END (outbuf) = resample->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) = resample->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, resample->outrate); + } + + if (G_UNLIKELY (resample->need_discont)) { + GST_DEBUG ("marking this buffer with the DISCONT flag"); + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + resample->need_discont = FALSE; + } + + return gst_speex_resample_process (resample, inbuf, outbuf); +} + +static void +gst_speex_resample_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstSpeexResample *resample; + + resample = GST_SPEEX_RESAMPLE (object); + + switch (prop_id) { + case PROP_QUALITY: + resample->quality = g_value_get_int (value); + GST_DEBUG ("new quality %d", resample->quality); + + gst_speex_resample_update_state (resample, resample->channels, + resample->inrate, resample->outrate, resample->quality, resample->fp); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_speex_resample_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstSpeexResample *resample; + + resample = GST_SPEEX_RESAMPLE (object); + + switch (prop_id) { + case PROP_QUALITY: + g_value_set_int (value, resample->quality); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "speexresample", GST_RANK_NONE, + GST_TYPE_SPEEX_RESAMPLE)) { + return FALSE; + } + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "speexresample", + "Resamples audio", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN); |