Remove audioresample files.
[platform/upstream/gstreamer.git] / gst / audioresample / gstaudioresample.c
1 /* GStreamer
2  * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2003,2004 David A. Schleef <ds@schleef.org>
4  *
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.
9  *
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.
14  *
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.
19  */
20 /* Element-Checklist-Version: 5 */
21
22 /**
23  * SECTION:element-audioresample
24  *
25  * Audioresample resamples raw audio buffers to different sample rates using
26  * a configurable windowing function to enhance quality.
27  *
28  * <refsect2>
29  * <title>Example launch line</title>
30  * |[
31  * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! audio/x-raw-int, rate=8000 ! alsasink
32  * ]| Decode an Ogg/Vorbis downsample to 8Khz and play sound through alsa. 
33  * To create the Ogg/Vorbis file refer to the documentation of vorbisenc.
34  * </refsect2>
35  *
36  * Last reviewed on 2006-03-02 (0.10.4)
37  */
38
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42
43 #include <string.h>
44 #include <math.h>
45
46 /*#define DEBUG_ENABLED */
47 #include "gstaudioresample.h"
48 #include <gst/audio/audio.h>
49 #include <gst/base/gstbasetransform.h>
50
51 GST_DEBUG_CATEGORY_STATIC (audioresample_debug);
52 #define GST_CAT_DEFAULT audioresample_debug
53
54 /* elementfactory information */
55 static const GstElementDetails gst_audioresample_details =
56 GST_ELEMENT_DETAILS ("Audio scaler",
57     "Filter/Converter/Audio",
58     "Resample audio",
59     "David Schleef <ds@schleef.org>");
60
61 #define DEFAULT_FILTERLEN       16
62
63 enum
64 {
65   PROP_0,
66   PROP_FILTERLEN
67 };
68
69 #define SUPPORTED_CAPS \
70 GST_STATIC_CAPS ( \
71     "audio/x-raw-int, " \
72       "rate = (int) [ 1, MAX ], " \
73       "channels = (int) [ 1, MAX ], " \
74       "endianness = (int) BYTE_ORDER, " \
75       "width = (int) 16, " \
76       "depth = (int) 16, " \
77       "signed = (boolean) true;" \
78     "audio/x-raw-int, " \
79       "rate = (int) [ 1, MAX ], " \
80       "channels = (int) [ 1, MAX ], " \
81       "endianness = (int) BYTE_ORDER, " \
82       "width = (int) 32, " \
83       "depth = (int) 32, " \
84       "signed = (boolean) true;" \
85     "audio/x-raw-float, " \
86       "rate = (int) [ 1, MAX ], "       \
87       "channels = (int) [ 1, MAX ], " \
88       "endianness = (int) BYTE_ORDER, " \
89       "width = (int) 32; " \
90     "audio/x-raw-float, " \
91       "rate = (int) [ 1, MAX ], "       \
92       "channels = (int) [ 1, MAX ], " \
93       "endianness = (int) BYTE_ORDER, " \
94       "width = (int) 64" \
95 )
96
97 static GstStaticPadTemplate gst_audioresample_sink_template =
98 GST_STATIC_PAD_TEMPLATE ("sink",
99     GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS);
100
101 static GstStaticPadTemplate gst_audioresample_src_template =
102 GST_STATIC_PAD_TEMPLATE ("src",
103     GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS);
104
105 static void gst_audioresample_set_property (GObject * object,
106     guint prop_id, const GValue * value, GParamSpec * pspec);
107 static void gst_audioresample_get_property (GObject * object,
108     guint prop_id, GValue * value, GParamSpec * pspec);
109
110 /* vmethods */
111 static gboolean audioresample_get_unit_size (GstBaseTransform * base,
112     GstCaps * caps, guint * size);
113 static GstCaps *audioresample_transform_caps (GstBaseTransform * base,
114     GstPadDirection direction, GstCaps * caps);
115 static gboolean audioresample_transform_size (GstBaseTransform * trans,
116     GstPadDirection direction, GstCaps * incaps, guint insize,
117     GstCaps * outcaps, guint * outsize);
118 static gboolean audioresample_set_caps (GstBaseTransform * base,
119     GstCaps * incaps, GstCaps * outcaps);
120 static GstFlowReturn audioresample_pushthrough (GstAudioresample *
121     audioresample);
122 static GstFlowReturn audioresample_transform (GstBaseTransform * base,
123     GstBuffer * inbuf, GstBuffer * outbuf);
124 static gboolean audioresample_event (GstBaseTransform * base, GstEvent * event);
125 static gboolean audioresample_start (GstBaseTransform * base);
126 static gboolean audioresample_stop (GstBaseTransform * base);
127
128 static gboolean audioresample_query (GstPad * pad, GstQuery * query);
129 static const GstQueryType *audioresample_query_type (GstPad * pad);
130
131 #define DEBUG_INIT(bla) \
132   GST_DEBUG_CATEGORY_INIT (audioresample_debug, "audioresample", 0, "audio resampling element");
133
134 GST_BOILERPLATE_FULL (GstAudioresample, gst_audioresample, GstBaseTransform,
135     GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
136
137 static void
138 gst_audioresample_base_init (gpointer g_class)
139 {
140   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
141
142   gst_element_class_add_pad_template (gstelement_class,
143       gst_static_pad_template_get (&gst_audioresample_src_template));
144   gst_element_class_add_pad_template (gstelement_class,
145       gst_static_pad_template_get (&gst_audioresample_sink_template));
146
147   gst_element_class_set_details (gstelement_class, &gst_audioresample_details);
148 }
149
150 static void
151 gst_audioresample_class_init (GstAudioresampleClass * klass)
152 {
153   GObjectClass *gobject_class;
154
155   gobject_class = (GObjectClass *) klass;
156
157   gobject_class->set_property = gst_audioresample_set_property;
158   gobject_class->get_property = gst_audioresample_get_property;
159
160   g_object_class_install_property (gobject_class, PROP_FILTERLEN,
161       g_param_spec_int ("filter-length", "filter length",
162           "Length of the resample filter", 0, G_MAXINT, DEFAULT_FILTERLEN,
163           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
164
165   GST_BASE_TRANSFORM_CLASS (klass)->start =
166       GST_DEBUG_FUNCPTR (audioresample_start);
167   GST_BASE_TRANSFORM_CLASS (klass)->stop =
168       GST_DEBUG_FUNCPTR (audioresample_stop);
169   GST_BASE_TRANSFORM_CLASS (klass)->transform_size =
170       GST_DEBUG_FUNCPTR (audioresample_transform_size);
171   GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size =
172       GST_DEBUG_FUNCPTR (audioresample_get_unit_size);
173   GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
174       GST_DEBUG_FUNCPTR (audioresample_transform_caps);
175   GST_BASE_TRANSFORM_CLASS (klass)->set_caps =
176       GST_DEBUG_FUNCPTR (audioresample_set_caps);
177   GST_BASE_TRANSFORM_CLASS (klass)->transform =
178       GST_DEBUG_FUNCPTR (audioresample_transform);
179   GST_BASE_TRANSFORM_CLASS (klass)->event =
180       GST_DEBUG_FUNCPTR (audioresample_event);
181
182   GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE;
183 }
184
185 static void
186 gst_audioresample_init (GstAudioresample * audioresample,
187     GstAudioresampleClass * klass)
188 {
189   GstBaseTransform *trans;
190
191   trans = GST_BASE_TRANSFORM (audioresample);
192
193   /* buffer alloc passthrough is too impossible. FIXME, it
194    * is trivial in the passthrough case. */
195   gst_pad_set_bufferalloc_function (trans->sinkpad, NULL);
196
197   audioresample->filter_length = DEFAULT_FILTERLEN;
198
199   audioresample->need_discont = FALSE;
200
201   gst_pad_set_query_function (trans->srcpad, audioresample_query);
202   gst_pad_set_query_type_function (trans->srcpad, audioresample_query_type);
203 }
204
205 /* vmethods */
206 static gboolean
207 audioresample_start (GstBaseTransform * base)
208 {
209   GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
210
211   audioresample->resample = resample_new ();
212   audioresample->ts_offset = -1;
213   audioresample->offset = -1;
214   audioresample->next_ts = -1;
215
216   resample_set_filter_length (audioresample->resample,
217       audioresample->filter_length);
218
219   return TRUE;
220 }
221
222 static gboolean
223 audioresample_stop (GstBaseTransform * base)
224 {
225   GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
226
227   if (audioresample->resample) {
228     resample_free (audioresample->resample);
229     audioresample->resample = NULL;
230   }
231
232   gst_caps_replace (&audioresample->sinkcaps, NULL);
233   gst_caps_replace (&audioresample->srccaps, NULL);
234
235   return TRUE;
236 }
237
238 static gboolean
239 audioresample_get_unit_size (GstBaseTransform * base, GstCaps * caps,
240     guint * size)
241 {
242   gint width, channels;
243   GstStructure *structure;
244   gboolean ret;
245
246   g_assert (size);
247
248   /* this works for both float and int */
249   structure = gst_caps_get_structure (caps, 0);
250   ret = gst_structure_get_int (structure, "width", &width);
251   ret &= gst_structure_get_int (structure, "channels", &channels);
252   g_return_val_if_fail (ret, FALSE);
253
254   *size = width * channels / 8;
255
256   return TRUE;
257 }
258
259 static GstCaps *
260 audioresample_transform_caps (GstBaseTransform * base,
261     GstPadDirection direction, GstCaps * caps)
262 {
263   GstCaps *res;
264   GstStructure *structure;
265
266   /* transform caps gives one single caps so we can just replace
267    * the rate property with our range. */
268   res = gst_caps_copy (caps);
269   structure = gst_caps_get_structure (res, 0);
270   gst_structure_set (structure, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
271
272   return res;
273 }
274
275 static gboolean
276 resample_set_state_from_caps (ResampleState * state, GstCaps * incaps,
277     GstCaps * outcaps, gint * channels, gint * inrate, gint * outrate)
278 {
279   GstStructure *structure;
280   gboolean ret;
281   gint myinrate, myoutrate;
282   int mychannels;
283   gint width, depth;
284   ResampleFormat format;
285
286   GST_DEBUG ("incaps %" GST_PTR_FORMAT ", outcaps %"
287       GST_PTR_FORMAT, incaps, outcaps);
288
289   structure = gst_caps_get_structure (incaps, 0);
290
291   /* get width */
292   ret = gst_structure_get_int (structure, "width", &width);
293   if (!ret)
294     goto no_width;
295
296   /* figure out the format */
297   if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float")) {
298     if (width == 32)
299       format = RESAMPLE_FORMAT_F32;
300     else if (width == 64)
301       format = RESAMPLE_FORMAT_F64;
302     else
303       goto wrong_depth;
304   } else {
305     /* for int, depth and width must be the same */
306     ret = gst_structure_get_int (structure, "depth", &depth);
307     if (!ret || width != depth)
308       goto not_equal;
309
310     if (width == 16)
311       format = RESAMPLE_FORMAT_S16;
312     else if (width == 32)
313       format = RESAMPLE_FORMAT_S32;
314     else
315       goto wrong_depth;
316   }
317   ret = gst_structure_get_int (structure, "rate", &myinrate);
318   ret &= gst_structure_get_int (structure, "channels", &mychannels);
319   if (!ret)
320     goto no_in_rate_channels;
321
322   structure = gst_caps_get_structure (outcaps, 0);
323   ret = gst_structure_get_int (structure, "rate", &myoutrate);
324   if (!ret)
325     goto no_out_rate;
326
327   if (channels)
328     *channels = mychannels;
329   if (inrate)
330     *inrate = myinrate;
331   if (outrate)
332     *outrate = myoutrate;
333
334   resample_set_format (state, format);
335   resample_set_n_channels (state, mychannels);
336   resample_set_input_rate (state, myinrate);
337   resample_set_output_rate (state, myoutrate);
338
339   return TRUE;
340
341   /* ERRORS */
342 no_width:
343   {
344     GST_DEBUG ("failed to get width from caps");
345     return FALSE;
346   }
347 not_equal:
348   {
349     GST_DEBUG ("width %d and depth %d must be the same", width, depth);
350     return FALSE;
351   }
352 wrong_depth:
353   {
354     GST_DEBUG ("unknown depth %d found", depth);
355     return FALSE;
356   }
357 no_in_rate_channels:
358   {
359     GST_DEBUG ("could not get input rate and channels");
360     return FALSE;
361   }
362 no_out_rate:
363   {
364     GST_DEBUG ("could not get output rate");
365     return FALSE;
366   }
367 }
368
369 static gboolean
370 audioresample_transform_size (GstBaseTransform * base,
371     GstPadDirection direction, GstCaps * caps, guint size, GstCaps * othercaps,
372     guint * othersize)
373 {
374   GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
375   ResampleState *state;
376   GstCaps *srccaps, *sinkcaps;
377   gboolean use_internal = FALSE;        /* whether we use the internal state */
378   gboolean ret = TRUE;
379
380   GST_LOG_OBJECT (base, "asked to transform size %d in direction %s",
381       size, direction == GST_PAD_SINK ? "SINK" : "SRC");
382   if (direction == GST_PAD_SINK) {
383     sinkcaps = caps;
384     srccaps = othercaps;
385   } else {
386     sinkcaps = othercaps;
387     srccaps = caps;
388   }
389
390   /* if the caps are the ones that _set_caps got called with; we can use
391    * our own state; otherwise we'll have to create a state */
392   if (gst_caps_is_equal (sinkcaps, audioresample->sinkcaps) &&
393       gst_caps_is_equal (srccaps, audioresample->srccaps)) {
394     use_internal = TRUE;
395     state = audioresample->resample;
396   } else {
397     GST_DEBUG_OBJECT (audioresample,
398         "caps are not the set caps, creating state");
399     state = resample_new ();
400     resample_set_filter_length (state, audioresample->filter_length);
401     resample_set_state_from_caps (state, sinkcaps, srccaps, NULL, NULL, NULL);
402   }
403
404   if (direction == GST_PAD_SINK) {
405     /* asked to convert size of an incoming buffer */
406     *othersize = resample_get_output_size_for_input (state, size);
407   } else {
408     /* asked to convert size of an outgoing buffer */
409     *othersize = resample_get_input_size_for_output (state, size);
410   }
411   g_assert (*othersize % state->sample_size == 0);
412
413   /* we make room for one extra sample, given that the resampling filter
414    * can output an extra one for non-integral i_rate/o_rate */
415   GST_LOG_OBJECT (base, "transformed size %d to %d", size, *othersize);
416
417   if (!use_internal) {
418     resample_free (state);
419   }
420
421   return ret;
422 }
423
424 static gboolean
425 audioresample_set_caps (GstBaseTransform * base, GstCaps * incaps,
426     GstCaps * outcaps)
427 {
428   gboolean ret;
429   gint inrate, outrate;
430   int channels;
431   GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
432
433   GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %"
434       GST_PTR_FORMAT, incaps, outcaps);
435
436   ret = resample_set_state_from_caps (audioresample->resample, incaps, outcaps,
437       &channels, &inrate, &outrate);
438
439   g_return_val_if_fail (ret, FALSE);
440
441   audioresample->channels = channels;
442   GST_DEBUG_OBJECT (audioresample, "set channels to %d", channels);
443   audioresample->i_rate = inrate;
444   GST_DEBUG_OBJECT (audioresample, "set i_rate to %d", inrate);
445   audioresample->o_rate = outrate;
446   GST_DEBUG_OBJECT (audioresample, "set o_rate to %d", outrate);
447
448   /* save caps so we can short-circuit in the size_transform if the caps
449    * are the same */
450   gst_caps_replace (&audioresample->sinkcaps, incaps);
451   gst_caps_replace (&audioresample->srccaps, outcaps);
452
453   return TRUE;
454 }
455
456 static gboolean
457 audioresample_event (GstBaseTransform * base, GstEvent * event)
458 {
459   GstAudioresample *audioresample;
460
461   audioresample = GST_AUDIORESAMPLE (base);
462
463   switch (GST_EVENT_TYPE (event)) {
464     case GST_EVENT_FLUSH_START:
465       break;
466     case GST_EVENT_FLUSH_STOP:
467       resample_input_flush (audioresample->resample);
468       audioresample->ts_offset = -1;
469       audioresample->next_ts = -1;
470       audioresample->offset = -1;
471       break;
472     case GST_EVENT_NEWSEGMENT:
473       resample_input_pushthrough (audioresample->resample);
474       audioresample_pushthrough (audioresample);
475       audioresample->ts_offset = -1;
476       audioresample->next_ts = -1;
477       audioresample->offset = -1;
478       break;
479     case GST_EVENT_EOS:
480       resample_input_eos (audioresample->resample);
481       audioresample_pushthrough (audioresample);
482       break;
483     default:
484       break;
485   }
486   parent_class->event (base, event);
487
488   return TRUE;
489 }
490
491 static GstFlowReturn
492 audioresample_do_output (GstAudioresample * audioresample, GstBuffer * outbuf)
493 {
494   int outsize;
495   int outsamples;
496   ResampleState *r;
497
498   r = audioresample->resample;
499
500   outsize = resample_get_output_size (r);
501   GST_LOG_OBJECT (audioresample, "audioresample can give me %d bytes", outsize);
502
503   /* protect against mem corruption */
504   if (outsize > GST_BUFFER_SIZE (outbuf)) {
505     GST_WARNING_OBJECT (audioresample,
506         "overriding audioresample's outsize %d with outbuffer's size %d",
507         outsize, GST_BUFFER_SIZE (outbuf));
508     outsize = GST_BUFFER_SIZE (outbuf);
509   }
510   /* catch possibly wrong size differences */
511   if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) {
512     GST_WARNING_OBJECT (audioresample,
513         "audioresample's outsize %d too far from outbuffer's size %d",
514         outsize, GST_BUFFER_SIZE (outbuf));
515   }
516
517   outsize = resample_get_output_data (r, GST_BUFFER_DATA (outbuf), outsize);
518   outsamples = outsize / r->sample_size;
519   GST_LOG_OBJECT (audioresample, "resample gave me %d bytes or %d samples",
520       outsize, outsamples);
521
522   GST_BUFFER_OFFSET (outbuf) = audioresample->offset;
523   GST_BUFFER_TIMESTAMP (outbuf) = audioresample->next_ts;
524
525   if (audioresample->ts_offset != -1) {
526     audioresample->offset += outsamples;
527     audioresample->ts_offset += outsamples;
528     audioresample->next_ts =
529         gst_util_uint64_scale_int (audioresample->ts_offset, GST_SECOND,
530         audioresample->o_rate);
531     GST_BUFFER_OFFSET_END (outbuf) = audioresample->offset;
532
533     /* we calculate DURATION as the difference between "next" timestamp
534      * and current timestamp so we ensure a contiguous stream, instead of
535      * having rounding errors. */
536     GST_BUFFER_DURATION (outbuf) = audioresample->next_ts -
537         GST_BUFFER_TIMESTAMP (outbuf);
538   } else {
539     /* no valid offset know, we can still sortof calculate the duration though */
540     GST_BUFFER_DURATION (outbuf) =
541         gst_util_uint64_scale_int (outsamples, GST_SECOND,
542         audioresample->o_rate);
543   }
544
545   /* check for possible mem corruption */
546   if (outsize > GST_BUFFER_SIZE (outbuf)) {
547     /* this is an error that when it happens, would need fixing in the
548      * resample library; we told it we wanted only GST_BUFFER_SIZE (outbuf),
549      * and it gave us more ! */
550     GST_WARNING_OBJECT (audioresample,
551         "audioresample, you memory corrupting bastard. "
552         "you gave me outsize %d while my buffer was size %d",
553         outsize, GST_BUFFER_SIZE (outbuf));
554     return GST_FLOW_ERROR;
555   }
556   /* catch possibly wrong size differences */
557   if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) {
558     GST_WARNING_OBJECT (audioresample,
559         "audioresample's written outsize %d too far from outbuffer's size %d",
560         outsize, GST_BUFFER_SIZE (outbuf));
561   }
562   GST_BUFFER_SIZE (outbuf) = outsize;
563
564   if (G_UNLIKELY (audioresample->need_discont)) {
565     GST_DEBUG_OBJECT (audioresample,
566         "marking this buffer with the DISCONT flag");
567     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
568     audioresample->need_discont = FALSE;
569   }
570
571   GST_LOG_OBJECT (audioresample, "transformed to buffer of %d bytes, ts %"
572       GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %"
573       G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
574       outsize, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
575       GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)),
576       GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf));
577
578
579   return GST_FLOW_OK;
580 }
581
582 static gboolean
583 audioresample_check_discont (GstAudioresample * audioresample,
584     GstClockTime timestamp)
585 {
586   if (timestamp != GST_CLOCK_TIME_NONE &&
587       audioresample->prev_ts != GST_CLOCK_TIME_NONE &&
588       audioresample->prev_duration != GST_CLOCK_TIME_NONE &&
589       timestamp != audioresample->prev_ts + audioresample->prev_duration) {
590     /* Potentially a discontinuous buffer. However, it turns out that many
591      * elements generate imperfect streams due to rounding errors, so we permit
592      * a small error (up to one sample) without triggering a filter 
593      * flush/restart (if triggered incorrectly, this will be audible) */
594     GstClockTimeDiff diff = timestamp -
595         (audioresample->prev_ts + audioresample->prev_duration);
596
597     if (ABS (diff) > GST_SECOND / audioresample->i_rate) {
598       GST_WARNING_OBJECT (audioresample,
599           "encountered timestamp discontinuity of %" G_GINT64_FORMAT, diff);
600       return TRUE;
601     }
602   }
603
604   return FALSE;
605 }
606
607 static GstFlowReturn
608 audioresample_transform (GstBaseTransform * base, GstBuffer * inbuf,
609     GstBuffer * outbuf)
610 {
611   GstAudioresample *audioresample;
612   ResampleState *r;
613   guchar *data, *datacopy;
614   gulong size;
615   GstClockTime timestamp;
616
617   audioresample = GST_AUDIORESAMPLE (base);
618   r = audioresample->resample;
619
620   data = GST_BUFFER_DATA (inbuf);
621   size = GST_BUFFER_SIZE (inbuf);
622   timestamp = GST_BUFFER_TIMESTAMP (inbuf);
623
624   GST_LOG_OBJECT (audioresample, "transforming buffer of %ld bytes, ts %"
625       GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %"
626       G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
627       size, GST_TIME_ARGS (timestamp),
628       GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)),
629       GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf));
630
631   /* check for timestamp discontinuities and flush/reset if needed */
632   if (G_UNLIKELY (audioresample_check_discont (audioresample, timestamp))) {
633     /* Flush internal samples */
634     audioresample_pushthrough (audioresample);
635     /* Inform downstream element about discontinuity */
636     audioresample->need_discont = TRUE;
637     /* We want to recalculate the offset */
638     audioresample->ts_offset = -1;
639   }
640
641   if (audioresample->ts_offset == -1) {
642     /* if we don't know the initial offset yet, calculate it based on the 
643      * input timestamp. */
644     if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
645       GstClockTime stime;
646
647       /* offset used to calculate the timestamps. We use the sample offset for
648        * this to make it more accurate. We want the first buffer to have the
649        * same timestamp as the incoming timestamp. */
650       audioresample->next_ts = timestamp;
651       audioresample->ts_offset =
652           gst_util_uint64_scale_int (timestamp, r->o_rate, GST_SECOND);
653       /* offset used to set as the buffer offset, this offset is always
654        * relative to the stream time, note that timestamp is not... */
655       stime = (timestamp - base->segment.start) + base->segment.time;
656       audioresample->offset =
657           gst_util_uint64_scale_int (stime, r->o_rate, GST_SECOND);
658     }
659   }
660   audioresample->prev_ts = timestamp;
661   audioresample->prev_duration = GST_BUFFER_DURATION (inbuf);
662
663   /* need to memdup, resample takes ownership. */
664   datacopy = g_memdup (data, size);
665   resample_add_input_data (r, datacopy, size, g_free, datacopy);
666
667   return audioresample_do_output (audioresample, outbuf);
668 }
669
670 /* push remaining data in the buffers out */
671 static GstFlowReturn
672 audioresample_pushthrough (GstAudioresample * audioresample)
673 {
674   int outsize;
675   ResampleState *r;
676   GstBuffer *outbuf;
677   GstFlowReturn res = GST_FLOW_OK;
678   GstBaseTransform *trans;
679
680   r = audioresample->resample;
681
682   outsize = resample_get_output_size (r);
683   if (outsize == 0) {
684     GST_DEBUG_OBJECT (audioresample, "no internal buffers needing flush");
685     goto done;
686   }
687
688   trans = GST_BASE_TRANSFORM (audioresample);
689
690   res = gst_pad_alloc_buffer (trans->srcpad, GST_BUFFER_OFFSET_NONE, outsize,
691       GST_PAD_CAPS (trans->srcpad), &outbuf);
692   if (G_UNLIKELY (res != GST_FLOW_OK)) {
693     GST_WARNING_OBJECT (audioresample, "failed allocating buffer of %d bytes",
694         outsize);
695     goto done;
696   }
697
698   res = audioresample_do_output (audioresample, outbuf);
699   if (G_UNLIKELY (res != GST_FLOW_OK))
700     goto done;
701
702   res = gst_pad_push (trans->srcpad, outbuf);
703
704 done:
705   return res;
706 }
707
708 static gboolean
709 audioresample_query (GstPad * pad, GstQuery * query)
710 {
711   GstAudioresample *audioresample =
712       GST_AUDIORESAMPLE (gst_pad_get_parent (pad));
713   GstBaseTransform *trans = GST_BASE_TRANSFORM (audioresample);
714   gboolean res = TRUE;
715
716   switch (GST_QUERY_TYPE (query)) {
717     case GST_QUERY_LATENCY:
718     {
719       GstClockTime min, max;
720       gboolean live;
721       guint64 latency;
722       GstPad *peer;
723       gint rate = audioresample->i_rate;
724       gint resampler_latency = audioresample->filter_length / 2;
725
726       if (gst_base_transform_is_passthrough (trans))
727         resampler_latency = 0;
728
729       if ((peer = gst_pad_get_peer (trans->sinkpad))) {
730         if ((res = gst_pad_query (peer, query))) {
731           gst_query_parse_latency (query, &live, &min, &max);
732
733           GST_DEBUG ("Peer latency: min %"
734               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
735               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
736
737           /* add our own latency */
738           if (rate != 0 && resampler_latency != 0)
739             latency =
740                 gst_util_uint64_scale (resampler_latency, GST_SECOND, rate);
741           else
742             latency = 0;
743
744           GST_DEBUG ("Our latency: %" GST_TIME_FORMAT, GST_TIME_ARGS (latency));
745
746           min += latency;
747           if (max != GST_CLOCK_TIME_NONE)
748             max += latency;
749
750           GST_DEBUG ("Calculated total latency : min %"
751               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
752               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
753
754           gst_query_set_latency (query, live, min, max);
755         }
756         gst_object_unref (peer);
757       }
758       break;
759     }
760     default:
761       res = gst_pad_query_default (pad, query);
762       break;
763   }
764   gst_object_unref (audioresample);
765   return res;
766 }
767
768 static const GstQueryType *
769 audioresample_query_type (GstPad * pad)
770 {
771   static const GstQueryType types[] = {
772     GST_QUERY_LATENCY,
773     0
774   };
775
776   return types;
777 }
778
779 static void
780 gst_audioresample_set_property (GObject * object, guint prop_id,
781     const GValue * value, GParamSpec * pspec)
782 {
783   GstAudioresample *audioresample;
784
785   audioresample = GST_AUDIORESAMPLE (object);
786
787   switch (prop_id) {
788     case PROP_FILTERLEN:
789       audioresample->filter_length = g_value_get_int (value);
790       GST_DEBUG_OBJECT (GST_ELEMENT (audioresample), "new filter length %d",
791           audioresample->filter_length);
792       if (audioresample->resample) {
793         resample_set_filter_length (audioresample->resample,
794             audioresample->filter_length);
795         gst_element_post_message (GST_ELEMENT (audioresample),
796             gst_message_new_latency (GST_OBJECT (audioresample)));
797       }
798       break;
799     default:
800       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
801       break;
802   }
803 }
804
805 static void
806 gst_audioresample_get_property (GObject * object, guint prop_id,
807     GValue * value, GParamSpec * pspec)
808 {
809   GstAudioresample *audioresample;
810
811   audioresample = GST_AUDIORESAMPLE (object);
812
813   switch (prop_id) {
814     case PROP_FILTERLEN:
815       g_value_set_int (value, audioresample->filter_length);
816       break;
817     default:
818       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
819       break;
820   }
821 }
822
823
824 static gboolean
825 plugin_init (GstPlugin * plugin)
826 {
827   resample_init ();
828
829   if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY,
830           GST_TYPE_AUDIORESAMPLE)) {
831     return FALSE;
832   }
833
834   return TRUE;
835 }
836
837 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
838     GST_VERSION_MINOR,
839     "audioresample",
840     "Resamples audio", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
841     GST_PACKAGE_ORIGIN);