2 * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) 2003,2004 David A. Schleef <ds@schleef.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 /* Element-Checklist-Version: 5 */
29 /*#define DEBUG_ENABLED */
30 #include "gstaudioresample.h"
31 #include <gst/audio/audio.h>
32 #include <gst/base/gstbasetransform.h>
34 GST_DEBUG_CATEGORY (audioresample_debug);
35 #define GST_CAT_DEFAULT audioresample_debug
37 /* elementfactory information */
38 static GstElementDetails gst_audioresample_details =
39 GST_ELEMENT_DETAILS ("Audio scaler",
40 "Filter/Converter/Audio",
42 "David Schleef <ds@schleef.org>");
44 /* GstAudioresample signals and args */
51 #define DEFAULT_FILTERLEN 16
59 #define SUPPORTED_CAPS \
62 "rate = (int) [ 1, MAX ], " \
63 "channels = (int) [ 1, MAX ], " \
64 "endianness = (int) BYTE_ORDER, " \
65 "width = (int) 16, " \
66 "depth = (int) 16, " \
67 "signed = (boolean) true " \
71 /* disabled because it segfaults */
73 "rate = (int) [ 1, MAX ], "
74 "channels = (int) [ 1, MAX ], "
75 "endianness = (int) BYTE_ORDER, " "width = (int) 32")
77 static GstStaticPadTemplate gst_audioresample_sink_template =
78 GST_STATIC_PAD_TEMPLATE ("sink",
79 GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS);
81 static GstStaticPadTemplate gst_audioresample_src_template =
82 GST_STATIC_PAD_TEMPLATE ("src",
83 GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS);
85 static void gst_audioresample_dispose (GObject * object);
87 static void gst_audioresample_set_property (GObject * object,
88 guint prop_id, const GValue * value, GParamSpec * pspec);
89 static void gst_audioresample_get_property (GObject * object,
90 guint prop_id, GValue * value, GParamSpec * pspec);
93 gboolean audioresample_get_unit_size (GstBaseTransform * base,
94 GstCaps * caps, guint * size);
95 GstCaps *audioresample_transform_caps (GstBaseTransform * base,
96 GstPadDirection direction, GstCaps * caps);
97 gboolean audioresample_transform_size (GstBaseTransform * trans,
98 GstPadDirection direction, GstCaps * incaps, guint insize,
99 GstCaps * outcaps, guint * outsize);
100 gboolean audioresample_set_caps (GstBaseTransform * base, GstCaps * incaps,
102 static GstFlowReturn audioresample_pushthrough (GstAudioresample *
104 static GstFlowReturn audioresample_transform (GstBaseTransform * base,
105 GstBuffer * inbuf, GstBuffer * outbuf);
106 static gboolean audioresample_event (GstBaseTransform * base,
109 /*static guint gst_audioresample_signals[LAST_SIGNAL] = { 0 }; */
111 #define DEBUG_INIT(bla) \
112 GST_DEBUG_CATEGORY_INIT (audioresample_debug, "audioresample", 0, "audio resampling element");
114 GST_BOILERPLATE_FULL (GstAudioresample, gst_audioresample, GstBaseTransform,
115 GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
117 static void gst_audioresample_base_init (gpointer g_class)
119 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
121 gst_element_class_add_pad_template (gstelement_class,
122 gst_static_pad_template_get (&gst_audioresample_src_template));
123 gst_element_class_add_pad_template (gstelement_class,
124 gst_static_pad_template_get (&gst_audioresample_sink_template));
126 gst_element_class_set_details (gstelement_class,
127 &gst_audioresample_details);
130 static void gst_audioresample_class_init (GstAudioresampleClass * klass)
132 GObjectClass *gobject_class;
134 gobject_class = (GObjectClass *) klass;
136 gobject_class->set_property = gst_audioresample_set_property;
137 gobject_class->get_property = gst_audioresample_get_property;
138 gobject_class->dispose = gst_audioresample_dispose;
140 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FILTERLEN,
141 g_param_spec_int ("filter_length", "filter_length", "filter_length",
142 0, G_MAXINT, DEFAULT_FILTERLEN,
143 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
145 GST_BASE_TRANSFORM_CLASS (klass)->transform_size =
146 GST_DEBUG_FUNCPTR (audioresample_transform_size);
147 GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size =
148 GST_DEBUG_FUNCPTR (audioresample_get_unit_size);
149 GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
150 GST_DEBUG_FUNCPTR (audioresample_transform_caps);
151 GST_BASE_TRANSFORM_CLASS (klass)->set_caps =
152 GST_DEBUG_FUNCPTR (audioresample_set_caps);
153 GST_BASE_TRANSFORM_CLASS (klass)->transform =
154 GST_DEBUG_FUNCPTR (audioresample_transform);
155 GST_BASE_TRANSFORM_CLASS (klass)->event =
156 GST_DEBUG_FUNCPTR (audioresample_event);
158 GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE;
162 gst_audioresample_init (GstAudioresample * audioresample,
163 GstAudioresampleClass * klass)
166 GstBaseTransform *trans;
168 trans = GST_BASE_TRANSFORM (audioresample);
170 /* buffer alloc passthrough is too impossible. FIXME, it
171 * is trivial in the passtrough case. */
172 gst_pad_set_bufferalloc_function (trans->sinkpad, NULL);
175 audioresample->resample = r;
176 audioresample->ts_offset = -1;
177 audioresample->offset = -1;
178 audioresample->next_ts = -1;
180 resample_set_filter_length (r, DEFAULT_FILTERLEN);
181 resample_set_format (r, RESAMPLE_FORMAT_S16);
184 static void gst_audioresample_dispose (GObject * object)
186 GstAudioresample *audioresample = GST_AUDIORESAMPLE (object);
188 if (audioresample->resample) {
189 resample_free (audioresample->resample);
190 audioresample->resample = NULL;
193 G_OBJECT_CLASS (parent_class)->dispose (object);
198 audioresample_get_unit_size (GstBaseTransform * base, GstCaps * caps,
200 gint width, channels;
201 GstStructure *structure;
204 g_return_val_if_fail (size, FALSE);
206 /* this works for both float and int */
207 structure = gst_caps_get_structure (caps, 0);
208 ret = gst_structure_get_int (structure, "width", &width);
209 ret &= gst_structure_get_int (structure, "channels", &channels);
210 g_return_val_if_fail (ret, FALSE);
212 *size = width * channels / 8;
217 GstCaps *audioresample_transform_caps (GstBaseTransform * base,
218 GstPadDirection direction, GstCaps * caps)
221 GstStructure *structure;
223 /* transform caps gives one single caps so we can just replace
224 * the rate property with our range. */
225 res = gst_caps_copy (caps);
226 structure = gst_caps_get_structure (res, 0);
227 gst_structure_set (structure, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
233 resample_set_state_from_caps (ResampleState * state, GstCaps * incaps,
234 GstCaps * outcaps, gint * channels, gint * inrate, gint * outrate)
236 GstStructure *structure;
238 gint myinrate, myoutrate;
241 GST_DEBUG ("incaps %" GST_PTR_FORMAT ", outcaps %"
242 GST_PTR_FORMAT, incaps, outcaps);
244 structure = gst_caps_get_structure (incaps, 0);
246 /* FIXME: once it does float, set the correct format */
248 if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float")) {
249 r->format = GST_RESAMPLE_FLOAT;
251 r->format = GST_RESAMPLE_S16;
255 ret = gst_structure_get_int (structure, "rate", &myinrate);
256 ret &= gst_structure_get_int (structure, "channels", &mychannels);
257 g_return_val_if_fail (ret, FALSE);
259 structure = gst_caps_get_structure (outcaps, 0);
260 ret = gst_structure_get_int (structure, "rate", &myoutrate);
261 g_return_val_if_fail (ret, FALSE);
264 *channels = mychannels;
268 *outrate = myoutrate;
270 resample_set_n_channels (state, mychannels);
271 resample_set_input_rate (state, myinrate);
272 resample_set_output_rate (state, myoutrate);
278 audioresample_transform_size (GstBaseTransform * base,
279 GstPadDirection direction, GstCaps * caps, guint size, GstCaps * othercaps,
281 GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
282 ResampleState *state;
283 GstCaps *srccaps, *sinkcaps;
284 gboolean use_internal = FALSE; /* whether we use the internal state */
287 GST_DEBUG_OBJECT (base, "asked to transform size %d in direction %s",
288 size, direction == GST_PAD_SINK ? "SINK" : "SRC");
289 if (direction == GST_PAD_SINK) {
293 sinkcaps = othercaps;
297 /* if the caps are the ones that _set_caps got called with; we can use
298 * our own state; otherwise we'll have to create a state */
299 if (gst_caps_is_equal (sinkcaps, audioresample->sinkcaps) &&
300 gst_caps_is_equal (srccaps, audioresample->srccaps)) {
302 state = audioresample->resample;
304 GST_DEBUG_OBJECT (audioresample,
305 "caps are not the set caps, creating state");
306 state = resample_new ();
307 resample_set_filter_length (state, audioresample->filter_length);
308 resample_set_state_from_caps (state, sinkcaps, srccaps, NULL, NULL, NULL);
311 if (direction == GST_PAD_SINK) {
312 /* asked to convert size of an incoming buffer */
313 *othersize = resample_get_output_size_for_input (state, size);
315 /* asked to convert size of an outgoing buffer */
316 *othersize = resample_get_input_size_for_output (state, size);
318 g_assert (*othersize % state->sample_size == 0);
320 /* we make room for one extra sample, given that the resampling filter
321 * can output an extra one for non-integral i_rate/o_rate */
322 GST_DEBUG_OBJECT (base, "transformed size %d to %d", size, *othersize);
325 resample_free (state);
332 audioresample_set_caps (GstBaseTransform * base, GstCaps * incaps,
335 gint inrate, outrate;
337 GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
339 GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %"
340 GST_PTR_FORMAT, incaps, outcaps);
342 ret = resample_set_state_from_caps (audioresample->resample, incaps, outcaps,
343 &channels, &inrate, &outrate);
345 g_return_val_if_fail (ret, FALSE);
347 audioresample->channels = channels;
348 GST_DEBUG_OBJECT (audioresample, "set channels to %d", channels);
349 audioresample->i_rate = inrate;
350 GST_DEBUG_OBJECT (audioresample, "set i_rate to %d", inrate);
351 audioresample->o_rate = outrate;
352 GST_DEBUG_OBJECT (audioresample, "set o_rate to %d", outrate);
354 /* save caps so we can short-circuit in the size_transform if the caps
356 /* FIXME: clean them up in state change ? */
357 gst_caps_ref (incaps);
358 gst_caps_replace (&audioresample->sinkcaps, incaps);
359 gst_caps_ref (outcaps);
360 gst_caps_replace (&audioresample->srccaps, outcaps);
365 static gboolean audioresample_event (GstBaseTransform * base, GstEvent * event)
367 GstAudioresample *audioresample;
369 audioresample = GST_AUDIORESAMPLE (base);
371 switch (GST_EVENT_TYPE (event)) {
372 case GST_EVENT_FLUSH_START:
374 case GST_EVENT_FLUSH_STOP:
375 resample_input_flush (audioresample->resample);
376 audioresample->ts_offset = -1;
377 audioresample->next_ts = -1;
378 audioresample->offset = -1;
380 case GST_EVENT_NEWSEGMENT:
381 resample_input_pushthrough (audioresample->resample);
382 audioresample_pushthrough (audioresample);
383 audioresample->ts_offset = -1;
384 audioresample->next_ts = -1;
385 audioresample->offset = -1;
388 resample_input_eos (audioresample->resample);
389 audioresample_pushthrough (audioresample);
394 parent_class->event (base, event);
400 audioresample_do_output (GstAudioresample * audioresample,
407 r = audioresample->resample;
409 outsize = resample_get_output_size (r);
410 GST_DEBUG_OBJECT (audioresample, "audioresample can give me %d bytes",
413 /* protect against mem corruption */
414 if (outsize > GST_BUFFER_SIZE (outbuf)) {
415 GST_WARNING_OBJECT (audioresample,
416 "overriding audioresample's outsize %d with outbuffer's size %d",
417 outsize, GST_BUFFER_SIZE (outbuf));
418 outsize = GST_BUFFER_SIZE (outbuf);
420 /* catch possibly wrong size differences */
421 if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) {
422 GST_WARNING_OBJECT (audioresample,
423 "audioresample's outsize %d too far from outbuffer's size %d",
424 outsize, GST_BUFFER_SIZE (outbuf));
427 outsize = resample_get_output_data (r, GST_BUFFER_DATA (outbuf), outsize);
428 outsamples = outsize / r->sample_size;
429 GST_LOG_OBJECT (audioresample, "resample gave me %d bytes or %d samples",
430 outsize, outsamples);
432 GST_BUFFER_OFFSET (outbuf) = audioresample->offset;
433 GST_BUFFER_TIMESTAMP (outbuf) = audioresample->next_ts;
435 if (audioresample->ts_offset != -1) {
436 audioresample->offset += outsamples;
437 audioresample->ts_offset += outsamples;
438 audioresample->next_ts =
439 gst_util_uint64_scale_int (audioresample->ts_offset, GST_SECOND,
440 audioresample->o_rate);
441 GST_BUFFER_OFFSET_END (outbuf) = audioresample->offset;
443 /* we calculate DURATION as the difference between "next" timestamp
444 * and current timestamp so we ensure a contiguous stream, instead of
445 * having rounding errors. */
446 GST_BUFFER_DURATION (outbuf) = audioresample->next_ts -
447 GST_BUFFER_TIMESTAMP (outbuf);
449 /* no valid offset know, we can still sortof calculate the duration though */
450 GST_BUFFER_DURATION (outbuf) =
451 gst_util_uint64_scale_int (outsamples, GST_SECOND,
452 audioresample->o_rate);
455 /* check for possible mem corruption */
456 if (outsize > GST_BUFFER_SIZE (outbuf)) {
457 /* this is an error that when it happens, would need fixing in the
458 * resample library; we told
459 * it we wanted only GST_BUFFER_SIZE (outbuf), and it gave us more ! */
460 GST_WARNING_OBJECT (audioresample,
461 "audioresample, you memory corrupting bastard. "
462 "you gave me outsize %d while my buffer was size %d",
463 outsize, GST_BUFFER_SIZE (outbuf));
464 return GST_FLOW_ERROR;
466 /* catch possibly wrong size differences */
467 if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) {
468 GST_WARNING_OBJECT (audioresample,
469 "audioresample's written outsize %d too far from outbuffer's size %d",
470 outsize, GST_BUFFER_SIZE (outbuf));
472 GST_BUFFER_SIZE (outbuf) = outsize;
478 audioresample_transform (GstBaseTransform * base, GstBuffer * inbuf,
481 GstAudioresample *audioresample;
485 GstClockTime timestamp;
487 audioresample = GST_AUDIORESAMPLE (base);
488 r = audioresample->resample;
490 data = GST_BUFFER_DATA (inbuf);
491 size = GST_BUFFER_SIZE (inbuf);
492 timestamp = GST_BUFFER_TIMESTAMP (inbuf);
494 GST_DEBUG_OBJECT (audioresample, "got buffer of %ld bytes", size);
496 if (audioresample->ts_offset == -1) {
497 /* if we don't know the initial offset yet, calculate it based on the
498 * input timestamp. */
499 if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
502 /* offset used to calculate the timestamps. We use the sample offset for this
503 * to make it more accurate. We want the first buffer to have the same timestamp
504 * as the incomming timestamp. */
505 audioresample->next_ts = timestamp;
506 audioresample->ts_offset =
507 gst_util_uint64_scale_int (timestamp, r->o_rate, GST_SECOND);
508 /* offset used to set as the buffer offset, this offset is always relative
509 * to the stream time, note that timestamp is not... */
510 stime = (timestamp - base->segment.start) + base->segment.time;
511 audioresample->offset =
512 gst_util_uint64_scale_int (stime, r->o_rate, GST_SECOND);
516 /* need to memdup, resample takes ownership. */
517 resample_add_input_data (r, g_memdup (data, size), size, NULL, NULL);
519 return audioresample_do_output (audioresample, outbuf);
522 /* push remaining data in the buffers out */
524 audioresample_pushthrough (GstAudioresample * audioresample)
529 GstFlowReturn res = GST_FLOW_OK;
530 GstBaseTransform *trans;
532 r = audioresample->resample;
534 outsize = resample_get_output_size (r);
538 outbuf = gst_buffer_new_and_alloc (outsize);
540 res = audioresample_do_output (audioresample, outbuf);
541 if (res != GST_FLOW_OK)
544 trans = GST_BASE_TRANSFORM (audioresample);
546 res = gst_pad_push (trans->srcpad, outbuf);
554 gst_audioresample_set_property (GObject * object, guint prop_id,
555 const GValue * value, GParamSpec * pspec)
557 GstAudioresample *audioresample;
559 g_return_if_fail (GST_IS_AUDIORESAMPLE (object));
560 audioresample = GST_AUDIORESAMPLE (object);
564 audioresample->filter_length = g_value_get_int (value);
565 GST_DEBUG_OBJECT (GST_ELEMENT (audioresample), "new filter length %d",
566 audioresample->filter_length);
567 resample_set_filter_length (audioresample->resample,
568 audioresample->filter_length);
570 default:G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
576 gst_audioresample_get_property (GObject * object, guint prop_id,
577 GValue * value, GParamSpec * pspec)
579 GstAudioresample *audioresample;
581 g_return_if_fail (GST_IS_AUDIORESAMPLE (object));
582 audioresample = GST_AUDIORESAMPLE (object);
586 g_value_set_int (value, audioresample->filter_length);
589 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
595 static gboolean plugin_init (GstPlugin * plugin)
599 if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY,
600 GST_TYPE_AUDIORESAMPLE)) {
607 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
610 "Resamples audio", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,