summaryrefslogtreecommitdiffstats
path: root/gst/speexresample/gstspeexresample.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/speexresample/gstspeexresample.c')
-rw-r--r--gst/speexresample/gstspeexresample.c733
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);