Updates for two-arg init from GST_BOILERPLATE_FULL.
[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_STATIC (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 enum
52 {
53   ARG_0,
54   ARG_FILTERLEN
55 };
56
57 #define SUPPORTED_CAPS \
58 GST_STATIC_CAPS ( \
59     "audio/x-raw-int, " \
60       "rate = (int) [ 1, MAX ], " \
61       "channels = (int) [ 1, MAX ], " \
62       "endianness = (int) BYTE_ORDER, " \
63       "width = (int) 16, " \
64       "depth = (int) 16, " \
65       "signed = (boolean) true " \
66 )
67
68 #if 0
69   /* disabled because it segfaults */
70 "audio/x-raw-float, "
71     "rate = (int) [ 1, MAX ], "
72     "channels = (int) [ 1, MAX ], "
73     "endianness = (int) BYTE_ORDER, " "width = (int) 32")
74 #endif
75      static GstStaticPadTemplate gst_audioresample_sink_template =
76          GST_STATIC_PAD_TEMPLATE ("sink",
77          GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS);
78
79      static GstStaticPadTemplate gst_audioresample_src_template =
80          GST_STATIC_PAD_TEMPLATE ("src",
81          GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS);
82
83      static void gst_audioresample_dispose (GObject * object);
84
85      static void gst_audioresample_set_property (GObject * object,
86          guint prop_id, const GValue * value, GParamSpec * pspec);
87      static void gst_audioresample_get_property (GObject * object,
88          guint prop_id, GValue * value, GParamSpec * pspec);
89
90 /* vmethods */
91      gboolean audioresample_get_unit_size (GstBaseTransform * base,
92          GstCaps * caps, guint * size);
93      GstCaps *audioresample_transform_caps (GstBaseTransform * base,
94          GstPadDirection direction, GstCaps * caps);
95      gboolean audioresample_transform_size (GstBaseTransform * trans,
96          GstPadDirection direction, GstCaps * incaps, guint insize,
97          GstCaps * outcaps, guint * outsize);
98      gboolean audioresample_set_caps (GstBaseTransform * base, GstCaps * incaps,
99          GstCaps * outcaps);
100      static GstFlowReturn audioresample_transform (GstBaseTransform * base,
101          GstBuffer * inbuf, GstBuffer * outbuf);
102
103 /*static guint gst_audioresample_signals[LAST_SIGNAL] = { 0 }; */
104
105 #define DEBUG_INIT(bla) \
106   GST_DEBUG_CATEGORY_INIT (audioresample_debug, "audioresample", 0, "audio resampling element");
107
108 GST_BOILERPLATE_FULL (GstAudioresample, gst_audioresample, GstBaseTransform,
109     GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
110
111      static void gst_audioresample_base_init (gpointer g_class)
112      {
113        GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
114
115        gst_element_class_add_pad_template (gstelement_class,
116            gst_static_pad_template_get (&gst_audioresample_src_template));
117        gst_element_class_add_pad_template (gstelement_class,
118            gst_static_pad_template_get (&gst_audioresample_sink_template));
119
120        gst_element_class_set_details (gstelement_class,
121            &gst_audioresample_details);
122      }
123
124 static void gst_audioresample_class_init (GstAudioresampleClass * klass)
125 {
126   GObjectClass *gobject_class;
127
128   gobject_class = (GObjectClass *) klass;
129
130   gobject_class->set_property = gst_audioresample_set_property;
131   gobject_class->get_property = gst_audioresample_get_property;
132   gobject_class->dispose = gst_audioresample_dispose;
133
134   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FILTERLEN,
135       g_param_spec_int ("filter_length", "filter_length", "filter_length",
136           0, G_MAXINT, 16, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
137
138   GST_BASE_TRANSFORM_CLASS (klass)->transform_size =
139       GST_DEBUG_FUNCPTR (audioresample_transform_size);
140   GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size =
141       GST_DEBUG_FUNCPTR (audioresample_get_unit_size);
142   GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
143       GST_DEBUG_FUNCPTR (audioresample_transform_caps);
144   GST_BASE_TRANSFORM_CLASS (klass)->set_caps =
145       GST_DEBUG_FUNCPTR (audioresample_set_caps);
146   GST_BASE_TRANSFORM_CLASS (klass)->transform =
147       GST_DEBUG_FUNCPTR (audioresample_transform);
148 }
149
150 static void gst_audioresample_init (GstAudioresample * audioresample,
151     GstAudioresampleClass * klass)
152 {
153   ResampleState *r;
154
155   r = resample_new ();
156   audioresample->resample = r;
157
158   resample_set_filter_length (r, 64);
159   resample_set_format (r, RESAMPLE_FORMAT_S16);
160 }
161
162 static void gst_audioresample_dispose (GObject * object)
163 {
164   GstAudioresample *audioresample = GST_AUDIORESAMPLE (object);
165
166   if (audioresample->resample) {
167     resample_free (audioresample->resample);
168     audioresample->resample = NULL;
169   }
170
171   G_OBJECT_CLASS (parent_class)->dispose (object);
172 }
173
174 /* vmethods */
175 gboolean
176     audioresample_get_unit_size (GstBaseTransform * base, GstCaps * caps,
177     guint * size) {
178   gint width, channels;
179   GstStructure *structure;
180   gboolean ret;
181
182   g_return_val_if_fail (size, FALSE);
183
184   /* this works for both float and int */
185   structure = gst_caps_get_structure (caps, 0);
186   ret = gst_structure_get_int (structure, "width", &width);
187   ret &= gst_structure_get_int (structure, "channels", &channels);
188   g_return_val_if_fail (ret, FALSE);
189
190   *size = width * channels / 8;
191
192   return TRUE;
193 }
194
195 GstCaps *audioresample_transform_caps (GstBaseTransform * base,
196     GstPadDirection direction, GstCaps * caps)
197 {
198   GstCaps *temp, *res;
199   const GstCaps *templcaps;
200   GstStructure *structure;
201
202   temp = gst_caps_copy (caps);
203   structure = gst_caps_get_structure (temp, 0);
204   gst_structure_remove_field (structure, "rate");
205   templcaps = gst_pad_get_pad_template_caps (base->srcpad);
206   res = gst_caps_intersect (templcaps, temp);
207   gst_caps_unref (temp);
208
209   return res;
210 }
211
212 static gboolean
213     resample_set_state_from_caps (ResampleState * state, GstCaps * incaps,
214     GstCaps * outcaps, gint * channels, gint * inrate, gint * outrate)
215 {
216   GstStructure *structure;
217   gboolean ret;
218   gint myinrate, myoutrate;
219   int mychannels;
220
221   GST_DEBUG ("incaps %" GST_PTR_FORMAT ", outcaps %"
222       GST_PTR_FORMAT, incaps, outcaps);
223
224   structure = gst_caps_get_structure (incaps, 0);
225
226   /* FIXME: once it does float, set the correct format */
227 #if 0
228   if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float")) {
229     r->format = GST_RESAMPLE_FLOAT;
230   } else {
231     r->format = GST_RESAMPLE_S16;
232   }
233 #endif
234
235   ret = gst_structure_get_int (structure, "rate", &myinrate);
236   ret &= gst_structure_get_int (structure, "channels", &mychannels);
237   g_return_val_if_fail (ret, FALSE);
238
239   structure = gst_caps_get_structure (outcaps, 0);
240   ret = gst_structure_get_int (structure, "rate", &myoutrate);
241   g_return_val_if_fail (ret, FALSE);
242
243   if (channels)
244     *channels = mychannels;
245   if (inrate)
246     *inrate = myinrate;
247   if (outrate)
248     *outrate = myoutrate;
249
250   resample_set_n_channels (state, mychannels);
251   resample_set_input_rate (state, myinrate);
252   resample_set_output_rate (state, myoutrate);
253
254   return TRUE;
255 }
256
257 gboolean
258     audioresample_transform_size (GstBaseTransform * base,
259     GstPadDirection direction, GstCaps * caps, guint size, GstCaps * othercaps,
260     guint * othersize) {
261   GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
262   ResampleState *state;
263   GstCaps *srccaps, *sinkcaps;
264   gboolean use_internal = FALSE;        /* whether we use the internal state */
265   gboolean ret = TRUE;
266
267   GST_DEBUG_OBJECT (base, "asked to transform size %d in direction %s",
268       size, direction == GST_PAD_SINK ? "SINK" : "SRC");
269   if (direction == GST_PAD_SINK) {
270     sinkcaps = caps;
271     srccaps = othercaps;
272   } else {
273     sinkcaps = othercaps;
274     srccaps = caps;
275   }
276
277   /* if the caps are the ones that _set_caps got called with; we can use
278    * our own state; otherwise we'll have to create a state */
279   if (gst_caps_is_equal (sinkcaps, audioresample->sinkcaps) &&
280       gst_caps_is_equal (srccaps, audioresample->srccaps)) {
281     use_internal = TRUE;
282     state = audioresample->resample;
283   } else {
284     GST_DEBUG_OBJECT (audioresample,
285         "caps are not the set caps, creating state");
286     state = resample_new ();
287     resample_set_state_from_caps (state, sinkcaps, srccaps, NULL, NULL, NULL);
288   }
289
290   if (direction == GST_PAD_SINK) {
291     /* asked to convert size of an incoming buffer */
292     *othersize = resample_get_output_size_for_input (state, size);
293   } else {
294     /* take a best guess, this is called cheating */
295     *othersize = floor (size * state->i_rate / state->o_rate);
296     *othersize -= *othersize % state->sample_size;
297   }
298   *othersize += state->sample_size;
299
300   g_assert (*othersize % state->sample_size == 0);
301
302   /* we make room for one extra sample, given that the resampling filter
303    * can output an extra one for non-integral i_rate/o_rate */
304   GST_DEBUG_OBJECT (base, "transformed size %d to %d", size, *othersize);
305
306   if (!use_internal) {
307     resample_free (state);
308   }
309
310   return ret;
311 }
312
313 gboolean
314     audioresample_set_caps (GstBaseTransform * base, GstCaps * incaps,
315     GstCaps * outcaps) {
316   gboolean ret;
317   gint inrate, outrate;
318   int channels;
319   GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
320
321   GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %"
322       GST_PTR_FORMAT, incaps, outcaps);
323
324   ret = resample_set_state_from_caps (audioresample->resample, incaps, outcaps,
325       &channels, &inrate, &outrate);
326
327   g_return_val_if_fail (ret, FALSE);
328
329   audioresample->channels = channels;
330   GST_DEBUG_OBJECT (audioresample, "set channels to %d", channels);
331   audioresample->i_rate = inrate;
332   GST_DEBUG_OBJECT (audioresample, "set i_rate to %d", inrate);
333   audioresample->o_rate = outrate;
334   GST_DEBUG_OBJECT (audioresample, "set o_rate to %d", outrate);
335
336   /* save caps so we can short-circuit in the size_transform if the caps
337    * are the same */
338   /* FIXME: clean them up in state change ? */
339   gst_caps_ref (incaps);
340   gst_caps_replace (&audioresample->sinkcaps, incaps);
341   gst_caps_ref (outcaps);
342   gst_caps_replace (&audioresample->srccaps, outcaps);
343
344   return TRUE;
345 }
346
347 static GstFlowReturn
348     audioresample_transform (GstBaseTransform * base, GstBuffer * inbuf,
349     GstBuffer * outbuf)
350 {
351   /* FIXME: this-> */
352   GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
353   ResampleState *r;
354   guchar *data;
355   gulong size;
356   int outsize;
357   int outsamples;
358
359   /* FIXME: move to _inplace */
360 #if 0
361   if (audioresample->passthru) {
362     gst_pad_push (audioresample->srcpad, GST_DATA (buf));
363     return;
364   }
365 #endif
366
367   r = audioresample->resample;
368
369   data = GST_BUFFER_DATA (inbuf);
370   size = GST_BUFFER_SIZE (inbuf);
371
372   GST_DEBUG_OBJECT (audioresample, "got buffer of %ld bytes", size);
373
374   resample_add_input_data (r, data, size, NULL, NULL);
375
376   outsize = resample_get_output_size (r);
377   GST_DEBUG_OBJECT (audioresample, "audioresample can give me %d bytes",
378       outsize);
379
380   /* protect against mem corruption */
381   if (outsize > GST_BUFFER_SIZE (outbuf)) {
382     GST_WARNING_OBJECT (audioresample,
383         "overriding audioresample's outsize %d with outbuffer's size %d",
384         outsize, GST_BUFFER_SIZE (outbuf));
385     outsize = GST_BUFFER_SIZE (outbuf);
386   }
387   /* catch possibly wrong size differences */
388   if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) {
389     GST_WARNING_OBJECT (audioresample,
390         "audioresample's outsize %d too far from outbuffer's size %d",
391         outsize, GST_BUFFER_SIZE (outbuf));
392   }
393
394   outsize = resample_get_output_data (r, GST_BUFFER_DATA (outbuf), outsize);
395   outsamples = outsize / r->sample_size;
396   GST_LOG_OBJECT (audioresample, "resample gave me %d bytes or %d samples",
397       outsize, outsamples);
398
399   GST_BUFFER_OFFSET (outbuf) = audioresample->offset;
400   GST_BUFFER_TIMESTAMP (outbuf) = base->segment_start +
401       audioresample->offset * GST_SECOND / audioresample->o_rate;
402
403   audioresample->offset += outsamples;
404   GST_BUFFER_OFFSET_END (outbuf) = audioresample->offset;
405
406   /* we calculate DURATION as the difference between "next" timestamp
407    * and current timestamp so we ensure a contiguous stream, instead of
408    * having rounding errors. */
409   GST_BUFFER_DURATION (outbuf) = base->segment_start +
410       audioresample->offset * GST_SECOND / audioresample->o_rate -
411       GST_BUFFER_TIMESTAMP (outbuf);
412
413   /* check for possible mem corruption */
414   if (outsize > GST_BUFFER_SIZE (outbuf)) {
415     /* this is an error that when it happens, would need fixing in the
416      * resample library; we told
417      * it we wanted only GST_BUFFER_SIZE (outbuf), and it gave us more ! */
418     GST_WARNING_OBJECT (audioresample,
419         "audioresample, you memory corrupting bastard. "
420         "you gave me outsize %d while my buffer was size %d",
421         outsize, GST_BUFFER_SIZE (outbuf));
422     return GST_FLOW_ERROR;
423   }
424   /* catch possibly wrong size differences */
425   if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) {
426     GST_WARNING_OBJECT (audioresample,
427         "audioresample's written outsize %d too far from outbuffer's size %d",
428         outsize, GST_BUFFER_SIZE (outbuf));
429   }
430
431   return GST_FLOW_OK;
432 }
433
434 static void
435     gst_audioresample_set_property (GObject * object, guint prop_id,
436     const GValue * value, GParamSpec * pspec)
437 {
438   GstAudioresample *audioresample;
439
440     g_return_if_fail (GST_IS_AUDIORESAMPLE (object));
441     audioresample = GST_AUDIORESAMPLE (object);
442
443   switch (prop_id) {
444     case ARG_FILTERLEN:
445       audioresample->filter_length = g_value_get_int (value);
446       GST_DEBUG_OBJECT (GST_ELEMENT (audioresample), "new filter length %d",
447           audioresample->filter_length);
448       resample_set_filter_length (audioresample->resample,
449           audioresample->filter_length);
450       break;
451       default:G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
452       break;
453   }
454 }
455
456 static void
457     gst_audioresample_get_property (GObject * object, guint prop_id,
458     GValue * value, GParamSpec * pspec)
459 {
460   GstAudioresample *audioresample;
461
462   g_return_if_fail (GST_IS_AUDIORESAMPLE (object));
463   audioresample = GST_AUDIORESAMPLE (object);
464
465   switch (prop_id) {
466     case ARG_FILTERLEN:
467       g_value_set_int (value, audioresample->filter_length);
468       break;
469     default:
470       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
471       break;
472   }
473 }
474
475
476 static gboolean plugin_init (GstPlugin * plugin)
477 {
478   resample_init ();
479
480   if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY,
481           GST_TYPE_AUDIORESAMPLE)) {
482     return FALSE;
483   }
484
485   return TRUE;
486 }
487
488 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
489     GST_VERSION_MINOR,
490     "audioresample",
491     "Resamples audio", plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN);