expand tabs
[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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <string.h>
27 #include <math.h>
28
29 /*#define DEBUG_ENABLED */
30 #include "gstaudioresample.h"
31 #include <gst/audio/audio.h>
32 #include <gst/base/gstbasetransform.h>
33
34 GST_DEBUG_CATEGORY (audioresample_debug);
35 #define GST_CAT_DEFAULT audioresample_debug
36
37 /* elementfactory information */
38 static GstElementDetails gst_audioresample_details =
39 GST_ELEMENT_DETAILS ("Audio scaler",
40     "Filter/Converter/Audio",
41     "Resample audio",
42     "David Schleef <ds@schleef.org>");
43
44 /* GstAudioresample signals and args */
45 enum
46 {
47   /* FILL ME */
48   LAST_SIGNAL
49 };
50
51 #define DEFAULT_FILTERLEN       16
52
53 enum
54 {
55   ARG_0,
56   ARG_FILTERLEN
57 };
58
59 #define SUPPORTED_CAPS \
60 GST_STATIC_CAPS ( \
61     "audio/x-raw-int, " \
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 " \
68 )
69
70 #if 0
71   /* disabled because it segfaults */
72 "audio/x-raw-float, "
73     "rate = (int) [ 1, MAX ], "
74     "channels = (int) [ 1, MAX ], "
75     "endianness = (int) BYTE_ORDER, " "width = (int) 32")
76 #endif
77      static GstStaticPadTemplate gst_audioresample_sink_template =
78          GST_STATIC_PAD_TEMPLATE ("sink",
79          GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS);
80
81      static GstStaticPadTemplate gst_audioresample_src_template =
82          GST_STATIC_PAD_TEMPLATE ("src",
83          GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS);
84
85      static void gst_audioresample_dispose (GObject * object);
86
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);
91
92 /* vmethods */
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,
101          GstCaps * outcaps);
102      static GstFlowReturn audioresample_pushthrough (GstAudioresample *
103          audioresample);
104      static GstFlowReturn audioresample_transform (GstBaseTransform * base,
105          GstBuffer * inbuf, GstBuffer * outbuf);
106      static gboolean audioresample_event (GstBaseTransform * base,
107          GstEvent * event);
108
109 /*static guint gst_audioresample_signals[LAST_SIGNAL] = { 0 }; */
110
111 #define DEBUG_INIT(bla) \
112   GST_DEBUG_CATEGORY_INIT (audioresample_debug, "audioresample", 0, "audio resampling element");
113
114 GST_BOILERPLATE_FULL (GstAudioresample, gst_audioresample, GstBaseTransform,
115     GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
116
117      static void gst_audioresample_base_init (gpointer g_class)
118      {
119        GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
120
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));
125
126        gst_element_class_set_details (gstelement_class,
127            &gst_audioresample_details);
128      }
129
130 static void gst_audioresample_class_init (GstAudioresampleClass * klass)
131 {
132   GObjectClass *gobject_class;
133
134   gobject_class = (GObjectClass *) klass;
135
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;
139
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));
144
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);
157
158   GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE;
159 }
160
161 static void
162     gst_audioresample_init (GstAudioresample * audioresample,
163     GstAudioresampleClass * klass)
164 {
165   ResampleState *r;
166   GstBaseTransform *trans;
167
168   trans = GST_BASE_TRANSFORM (audioresample);
169
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);
173
174   r = resample_new ();
175   audioresample->resample = r;
176   audioresample->ts_offset = -1;
177   audioresample->offset = -1;
178   audioresample->next_ts = -1;
179
180   resample_set_filter_length (r, DEFAULT_FILTERLEN);
181   resample_set_format (r, RESAMPLE_FORMAT_S16);
182 }
183
184 static void gst_audioresample_dispose (GObject * object)
185 {
186   GstAudioresample *audioresample = GST_AUDIORESAMPLE (object);
187
188   if (audioresample->resample) {
189     resample_free (audioresample->resample);
190     audioresample->resample = NULL;
191   }
192
193   G_OBJECT_CLASS (parent_class)->dispose (object);
194 }
195
196 /* vmethods */
197 gboolean
198     audioresample_get_unit_size (GstBaseTransform * base, GstCaps * caps,
199     guint * size) {
200   gint width, channels;
201   GstStructure *structure;
202   gboolean ret;
203
204   g_return_val_if_fail (size, FALSE);
205
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);
211
212   *size = width * channels / 8;
213
214   return TRUE;
215 }
216
217 GstCaps *audioresample_transform_caps (GstBaseTransform * base,
218     GstPadDirection direction, GstCaps * caps)
219 {
220   GstCaps *res;
221   GstStructure *structure;
222
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);
228
229   return res;
230 }
231
232 static gboolean
233     resample_set_state_from_caps (ResampleState * state, GstCaps * incaps,
234     GstCaps * outcaps, gint * channels, gint * inrate, gint * outrate)
235 {
236   GstStructure *structure;
237   gboolean ret;
238   gint myinrate, myoutrate;
239   int mychannels;
240
241   GST_DEBUG ("incaps %" GST_PTR_FORMAT ", outcaps %"
242       GST_PTR_FORMAT, incaps, outcaps);
243
244   structure = gst_caps_get_structure (incaps, 0);
245
246   /* FIXME: once it does float, set the correct format */
247 #if 0
248   if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float")) {
249     r->format = GST_RESAMPLE_FLOAT;
250   } else {
251     r->format = GST_RESAMPLE_S16;
252   }
253 #endif
254
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);
258
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);
262
263   if (channels)
264     *channels = mychannels;
265   if (inrate)
266     *inrate = myinrate;
267   if (outrate)
268     *outrate = myoutrate;
269
270   resample_set_n_channels (state, mychannels);
271   resample_set_input_rate (state, myinrate);
272   resample_set_output_rate (state, myoutrate);
273
274   return TRUE;
275 }
276
277 gboolean
278     audioresample_transform_size (GstBaseTransform * base,
279     GstPadDirection direction, GstCaps * caps, guint size, GstCaps * othercaps,
280     guint * othersize) {
281   GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
282   ResampleState *state;
283   GstCaps *srccaps, *sinkcaps;
284   gboolean use_internal = FALSE;        /* whether we use the internal state */
285   gboolean ret = TRUE;
286
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) {
290     sinkcaps = caps;
291     srccaps = othercaps;
292   } else {
293     sinkcaps = othercaps;
294     srccaps = caps;
295   }
296
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)) {
301     use_internal = TRUE;
302     state = audioresample->resample;
303   } else {
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);
309   }
310
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);
314   } else {
315     /* asked to convert size of an outgoing buffer */
316     *othersize = resample_get_input_size_for_output (state, size);
317   }
318   g_assert (*othersize % state->sample_size == 0);
319
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);
323
324   if (!use_internal) {
325     resample_free (state);
326   }
327
328   return ret;
329 }
330
331 gboolean
332     audioresample_set_caps (GstBaseTransform * base, GstCaps * incaps,
333     GstCaps * outcaps) {
334   gboolean ret;
335   gint inrate, outrate;
336   int channels;
337   GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
338
339   GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %"
340       GST_PTR_FORMAT, incaps, outcaps);
341
342   ret = resample_set_state_from_caps (audioresample->resample, incaps, outcaps,
343       &channels, &inrate, &outrate);
344
345   g_return_val_if_fail (ret, FALSE);
346
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);
353
354   /* save caps so we can short-circuit in the size_transform if the caps
355    * are the same */
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);
361
362   return TRUE;
363 }
364
365 static gboolean audioresample_event (GstBaseTransform * base, GstEvent * event)
366 {
367   GstAudioresample *audioresample;
368
369   audioresample = GST_AUDIORESAMPLE (base);
370
371   switch (GST_EVENT_TYPE (event)) {
372     case GST_EVENT_FLUSH_START:
373       break;
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;
379       break;
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;
386       break;
387     case GST_EVENT_EOS:
388       resample_input_eos (audioresample->resample);
389       audioresample_pushthrough (audioresample);
390       break;
391     default:
392       break;
393   }
394   parent_class->event (base, event);
395
396   return TRUE;
397 }
398
399 static GstFlowReturn
400     audioresample_do_output (GstAudioresample * audioresample,
401     GstBuffer * outbuf)
402 {
403   int outsize;
404   int outsamples;
405   ResampleState *r;
406
407   r = audioresample->resample;
408
409   outsize = resample_get_output_size (r);
410   GST_DEBUG_OBJECT (audioresample, "audioresample can give me %d bytes",
411       outsize);
412
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);
419   }
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));
425   }
426
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);
431
432   GST_BUFFER_OFFSET (outbuf) = audioresample->offset;
433   GST_BUFFER_TIMESTAMP (outbuf) = audioresample->next_ts;
434
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;
442
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);
448   } else {
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);
453   }
454
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;
465   }
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));
471   }
472   GST_BUFFER_SIZE (outbuf) = outsize;
473
474   return GST_FLOW_OK;
475 }
476
477 static GstFlowReturn
478     audioresample_transform (GstBaseTransform * base, GstBuffer * inbuf,
479     GstBuffer * outbuf)
480 {
481   GstAudioresample *audioresample;
482   ResampleState *r;
483   guchar *data;
484   gulong size;
485   GstClockTime timestamp;
486
487   audioresample = GST_AUDIORESAMPLE (base);
488   r = audioresample->resample;
489
490   data = GST_BUFFER_DATA (inbuf);
491   size = GST_BUFFER_SIZE (inbuf);
492   timestamp = GST_BUFFER_TIMESTAMP (inbuf);
493
494   GST_DEBUG_OBJECT (audioresample, "got buffer of %ld bytes", size);
495
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)) {
500       GstClockTime stime;
501
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);
513     }
514   }
515
516   /* need to memdup, resample takes ownership. */
517   resample_add_input_data (r, g_memdup (data, size), size, NULL, NULL);
518
519   return audioresample_do_output (audioresample, outbuf);
520 }
521
522 /* push remaining data in the buffers out */
523 static GstFlowReturn
524     audioresample_pushthrough (GstAudioresample * audioresample)
525 {
526   int outsize;
527   ResampleState *r;
528   GstBuffer *outbuf;
529   GstFlowReturn res = GST_FLOW_OK;
530   GstBaseTransform *trans;
531
532   r = audioresample->resample;
533
534   outsize = resample_get_output_size (r);
535   if (outsize == 0)
536     goto done;
537
538   outbuf = gst_buffer_new_and_alloc (outsize);
539
540   res = audioresample_do_output (audioresample, outbuf);
541   if (res != GST_FLOW_OK)
542     goto done;
543
544   trans = GST_BASE_TRANSFORM (audioresample);
545
546   res = gst_pad_push (trans->srcpad, outbuf);
547
548 done:
549   return res;
550 }
551
552
553 static void
554     gst_audioresample_set_property (GObject * object, guint prop_id,
555     const GValue * value, GParamSpec * pspec)
556 {
557   GstAudioresample *audioresample;
558
559     g_return_if_fail (GST_IS_AUDIORESAMPLE (object));
560     audioresample = GST_AUDIORESAMPLE (object);
561
562   switch (prop_id) {
563     case ARG_FILTERLEN:
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);
569       break;
570       default:G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
571       break;
572   }
573 }
574
575 static void
576     gst_audioresample_get_property (GObject * object, guint prop_id,
577     GValue * value, GParamSpec * pspec)
578 {
579   GstAudioresample *audioresample;
580
581   g_return_if_fail (GST_IS_AUDIORESAMPLE (object));
582   audioresample = GST_AUDIORESAMPLE (object);
583
584   switch (prop_id) {
585     case ARG_FILTERLEN:
586       g_value_set_int (value, audioresample->filter_length);
587       break;
588     default:
589       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
590       break;
591   }
592 }
593
594
595 static gboolean plugin_init (GstPlugin * plugin)
596 {
597   resample_init ();
598
599   if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY,
600           GST_TYPE_AUDIORESAMPLE)) {
601     return FALSE;
602   }
603
604   return TRUE;
605 }
606
607 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
608     GST_VERSION_MINOR,
609     "audioresample",
610     "Resamples audio", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
611     GST_PACKAGE_ORIGIN);