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