plugins: uddate gst_type_mark_as_plugin_api() calls
[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  * Copyright (C) 2007-2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 /**
23  * SECTION:element-audioresample
24  * @title: audioresample
25  *
26  * audioresample resamples raw audio buffers to different sample rates using
27  * a configurable windowing function to enhance quality.
28  *
29  * By default, the resampler uses a reduced sinc table, with cubic interpolation filling in
30  * the gaps. This ensures that the table does not become too big. However, the interpolation
31  * increases the CPU usage considerably. As an alternative, a full sinc table can be used.
32  * Doing so can drastically reduce CPU usage (4x faster with 44.1 -> 48 kHz conversions for
33  * example), at the cost of increased memory consumption, plus the sinc table takes longer
34  * to initialize when the element is created. A third mode exists, which uses the full table
35  * unless said table would become too large, in which case the interpolated one is used instead.
36  *
37  * ## Example launch line
38  * |[
39  * gst-launch-1.0 -v uridecodebin uri=file:///path/to/audio.ogg ! audioconvert ! audioresample ! audio/x-raw, rate=8000 ! autoaudiosink
40  * ]|
41  *  Decode an audio file and downsample it to 8Khz and play sound.
42  * To create the Ogg/Vorbis file refer to the documentation of vorbisenc.
43  * This assumes there is an audio sink that will accept/handle 8kHz audio.
44  *
45  */
46
47 /* TODO:
48  *  - Enable SSE/ARM optimizations and select at runtime
49  */
50
51 #ifdef HAVE_CONFIG_H
52 #include "config.h"
53 #endif
54
55 #include <string.h>
56 #include <math.h>
57
58 #include "gstaudioresample.h"
59 #include <gst/gstutils.h>
60 #include <gst/audio/audio.h>
61 #include <gst/base/gstbasetransform.h>
62
63 GST_DEBUG_CATEGORY (audio_resample_debug);
64 #define GST_CAT_DEFAULT audio_resample_debug
65
66 #undef USE_SPEEX
67
68 #define DEFAULT_QUALITY GST_AUDIO_RESAMPLER_QUALITY_DEFAULT
69 #define DEFAULT_RESAMPLE_METHOD GST_AUDIO_RESAMPLER_METHOD_KAISER
70 #define DEFAULT_SINC_FILTER_MODE GST_AUDIO_RESAMPLER_FILTER_MODE_AUTO
71 #define DEFAULT_SINC_FILTER_AUTO_THRESHOLD (1*1048576)
72 #define DEFAULT_SINC_FILTER_INTERPOLATION GST_AUDIO_RESAMPLER_FILTER_INTERPOLATION_CUBIC
73
74 enum
75 {
76   PROP_0,
77   PROP_QUALITY,
78   PROP_RESAMPLE_METHOD,
79   PROP_SINC_FILTER_MODE,
80   PROP_SINC_FILTER_AUTO_THRESHOLD,
81   PROP_SINC_FILTER_INTERPOLATION
82 };
83
84 #define SUPPORTED_CAPS \
85   GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL) \
86   ", layout = (string) { interleaved, non-interleaved }"
87
88 static GstStaticPadTemplate gst_audio_resample_sink_template =
89 GST_STATIC_PAD_TEMPLATE ("sink",
90     GST_PAD_SINK,
91     GST_PAD_ALWAYS,
92     GST_STATIC_CAPS (SUPPORTED_CAPS));
93
94 static GstStaticPadTemplate gst_audio_resample_src_template =
95 GST_STATIC_PAD_TEMPLATE ("src",
96     GST_PAD_SRC,
97     GST_PAD_ALWAYS,
98     GST_STATIC_CAPS (SUPPORTED_CAPS));
99
100 static void gst_audio_resample_set_property (GObject * object,
101     guint prop_id, const GValue * value, GParamSpec * pspec);
102 static void gst_audio_resample_get_property (GObject * object,
103     guint prop_id, GValue * value, GParamSpec * pspec);
104
105 /* vmethods */
106 static gboolean gst_audio_resample_get_unit_size (GstBaseTransform * base,
107     GstCaps * caps, gsize * size);
108 static GstCaps *gst_audio_resample_transform_caps (GstBaseTransform * base,
109     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
110 static GstCaps *gst_audio_resample_fixate_caps (GstBaseTransform * base,
111     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
112 static gboolean gst_audio_resample_transform_size (GstBaseTransform * trans,
113     GstPadDirection direction, GstCaps * incaps, gsize insize,
114     GstCaps * outcaps, gsize * outsize);
115 static gboolean gst_audio_resample_set_caps (GstBaseTransform * base,
116     GstCaps * incaps, GstCaps * outcaps);
117 static GstFlowReturn gst_audio_resample_transform (GstBaseTransform * base,
118     GstBuffer * inbuf, GstBuffer * outbuf);
119 static gboolean gst_audio_resample_transform_meta (GstBaseTransform * trans,
120     GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf);
121 static GstFlowReturn gst_audio_resample_submit_input_buffer (GstBaseTransform *
122     base, gboolean is_discont, GstBuffer * input);
123 static gboolean gst_audio_resample_sink_event (GstBaseTransform * base,
124     GstEvent * event);
125 static gboolean gst_audio_resample_start (GstBaseTransform * base);
126 static gboolean gst_audio_resample_stop (GstBaseTransform * base);
127 static gboolean gst_audio_resample_query (GstPad * pad, GstObject * parent,
128     GstQuery * query);
129
130 static void gst_audio_resample_push_drain (GstAudioResample * resample,
131     guint history_len);
132
133 #define gst_audio_resample_parent_class parent_class
134 G_DEFINE_TYPE (GstAudioResample, gst_audio_resample, GST_TYPE_BASE_TRANSFORM);
135
136 static void
137 gst_audio_resample_class_init (GstAudioResampleClass * klass)
138 {
139   GObjectClass *gobject_class = (GObjectClass *) klass;
140   GstElementClass *gstelement_class = (GstElementClass *) klass;
141
142   gobject_class->set_property = gst_audio_resample_set_property;
143   gobject_class->get_property = gst_audio_resample_get_property;
144
145   g_object_class_install_property (gobject_class, PROP_QUALITY,
146       g_param_spec_int ("quality", "Quality", "Resample quality with 0 being "
147           "the lowest and 10 being the best",
148           GST_AUDIO_RESAMPLER_QUALITY_MIN, GST_AUDIO_RESAMPLER_QUALITY_MAX,
149           DEFAULT_QUALITY,
150           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
151
152   g_object_class_install_property (gobject_class, PROP_RESAMPLE_METHOD,
153       g_param_spec_enum ("resample-method", "Resample method to use",
154           "What resample method to use",
155           GST_TYPE_AUDIO_RESAMPLER_METHOD,
156           DEFAULT_RESAMPLE_METHOD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
157   g_object_class_install_property (gobject_class, PROP_SINC_FILTER_MODE,
158       g_param_spec_enum ("sinc-filter-mode", "Sinc filter table mode",
159           "What sinc filter table mode to use",
160           GST_TYPE_AUDIO_RESAMPLER_FILTER_MODE,
161           DEFAULT_SINC_FILTER_MODE,
162           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
163
164   g_object_class_install_property (gobject_class,
165       PROP_SINC_FILTER_AUTO_THRESHOLD,
166       g_param_spec_uint ("sinc-filter-auto-threshold",
167           "Sinc filter auto mode threshold",
168           "Memory usage threshold to use if sinc filter mode is AUTO, given in bytes",
169           0, G_MAXUINT, DEFAULT_SINC_FILTER_AUTO_THRESHOLD,
170           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
171   g_object_class_install_property (gobject_class,
172       PROP_SINC_FILTER_INTERPOLATION,
173       g_param_spec_enum ("sinc-filter-interpolation",
174           "Sinc filter interpolation",
175           "How to interpolate the sinc filter table",
176           GST_TYPE_AUDIO_RESAMPLER_FILTER_INTERPOLATION,
177           DEFAULT_SINC_FILTER_INTERPOLATION,
178           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
179
180   gst_element_class_add_static_pad_template (gstelement_class,
181       &gst_audio_resample_src_template);
182   gst_element_class_add_static_pad_template (gstelement_class,
183       &gst_audio_resample_sink_template);
184
185   gst_element_class_set_static_metadata (gstelement_class, "Audio resampler",
186       "Filter/Converter/Audio", "Resamples audio",
187       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
188
189   GST_BASE_TRANSFORM_CLASS (klass)->start =
190       GST_DEBUG_FUNCPTR (gst_audio_resample_start);
191   GST_BASE_TRANSFORM_CLASS (klass)->stop =
192       GST_DEBUG_FUNCPTR (gst_audio_resample_stop);
193   GST_BASE_TRANSFORM_CLASS (klass)->transform_size =
194       GST_DEBUG_FUNCPTR (gst_audio_resample_transform_size);
195   GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size =
196       GST_DEBUG_FUNCPTR (gst_audio_resample_get_unit_size);
197   GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
198       GST_DEBUG_FUNCPTR (gst_audio_resample_transform_caps);
199   GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps =
200       GST_DEBUG_FUNCPTR (gst_audio_resample_fixate_caps);
201   GST_BASE_TRANSFORM_CLASS (klass)->set_caps =
202       GST_DEBUG_FUNCPTR (gst_audio_resample_set_caps);
203   GST_BASE_TRANSFORM_CLASS (klass)->transform =
204       GST_DEBUG_FUNCPTR (gst_audio_resample_transform);
205   GST_BASE_TRANSFORM_CLASS (klass)->sink_event =
206       GST_DEBUG_FUNCPTR (gst_audio_resample_sink_event);
207   GST_BASE_TRANSFORM_CLASS (klass)->transform_meta =
208       GST_DEBUG_FUNCPTR (gst_audio_resample_transform_meta);
209   GST_BASE_TRANSFORM_CLASS (klass)->submit_input_buffer =
210       GST_DEBUG_FUNCPTR (gst_audio_resample_submit_input_buffer);
211
212   GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE;
213
214   gst_type_mark_as_plugin_api (GST_TYPE_AUDIO_RESAMPLER_METHOD, 0);
215   gst_type_mark_as_plugin_api (GST_TYPE_AUDIO_RESAMPLER_FILTER_INTERPOLATION,
216       0);
217   gst_type_mark_as_plugin_api (GST_TYPE_AUDIO_RESAMPLER_FILTER_MODE, 0);
218 }
219
220 static void
221 gst_audio_resample_init (GstAudioResample * resample)
222 {
223   GstBaseTransform *trans = GST_BASE_TRANSFORM (resample);
224
225   resample->method = DEFAULT_RESAMPLE_METHOD;
226   resample->quality = DEFAULT_QUALITY;
227   resample->sinc_filter_mode = DEFAULT_SINC_FILTER_MODE;
228   resample->sinc_filter_auto_threshold = DEFAULT_SINC_FILTER_AUTO_THRESHOLD;
229   resample->sinc_filter_interpolation = DEFAULT_SINC_FILTER_INTERPOLATION;
230
231   gst_base_transform_set_gap_aware (trans, TRUE);
232   gst_pad_set_query_function (trans->srcpad, gst_audio_resample_query);
233 }
234
235 /* vmethods */
236 static gboolean
237 gst_audio_resample_start (GstBaseTransform * base)
238 {
239   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
240
241   resample->need_discont = TRUE;
242
243   resample->num_gap_samples = 0;
244   resample->num_nongap_samples = 0;
245   resample->t0 = GST_CLOCK_TIME_NONE;
246   resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
247   resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
248   resample->samples_in = 0;
249   resample->samples_out = 0;
250
251   return TRUE;
252 }
253
254 static gboolean
255 gst_audio_resample_stop (GstBaseTransform * base)
256 {
257   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
258
259   if (resample->converter) {
260     gst_audio_converter_free (resample->converter);
261     resample->converter = NULL;
262   }
263   return TRUE;
264 }
265
266 static gboolean
267 gst_audio_resample_get_unit_size (GstBaseTransform * base, GstCaps * caps,
268     gsize * size)
269 {
270   GstAudioInfo info;
271
272   if (!gst_audio_info_from_caps (&info, caps))
273     goto invalid_caps;
274
275   *size = GST_AUDIO_INFO_BPF (&info);
276
277   return TRUE;
278
279   /* ERRORS */
280 invalid_caps:
281   {
282     GST_ERROR_OBJECT (base, "invalid caps");
283     return FALSE;
284   }
285 }
286
287 static GstCaps *
288 gst_audio_resample_transform_caps (GstBaseTransform * base,
289     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
290 {
291   const GValue *val;
292   GstStructure *s;
293   GstCaps *res;
294   gint i, n;
295
296   /* transform single caps into input_caps + input_caps with the rate
297    * field set to our supported range. This ensures that upstream knows
298    * about downstream's preferred rate(s) and can negotiate accordingly. */
299   res = gst_caps_new_empty ();
300   n = gst_caps_get_size (caps);
301   for (i = 0; i < n; i++) {
302     s = gst_caps_get_structure (caps, i);
303
304     /* If this is already expressed by the existing caps
305      * skip this structure */
306     if (i > 0 && gst_caps_is_subset_structure (res, s))
307       continue;
308
309     /* first, however, check if the caps contain a range for the rate field, in
310      * which case that side isn't going to care much about the exact sample rate
311      * chosen and we should just assume things will get fixated to something sane
312      * and we may just as well offer our full range instead of the range in the
313      * caps. If the rate is not an int range value, it's likely to express a
314      * real preference or limitation and we should maintain that structure as
315      * preference by putting it first into the transformed caps, and only add
316      * our full rate range as second option  */
317     s = gst_structure_copy (s);
318     val = gst_structure_get_value (s, "rate");
319     if (val == NULL || GST_VALUE_HOLDS_INT_RANGE (val)) {
320       /* overwrite existing range, or add field if it doesn't exist yet */
321       gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
322     } else {
323       /* append caps with full range to existing caps with non-range rate field */
324       gst_caps_append_structure (res, gst_structure_copy (s));
325       gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
326     }
327     gst_caps_append_structure (res, s);
328   }
329
330   if (filter) {
331     GstCaps *intersection;
332
333     intersection =
334         gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST);
335     gst_caps_unref (res);
336     res = intersection;
337   }
338
339   return res;
340 }
341
342 /* Fixate rate to the allowed rate that has the smallest difference */
343 static GstCaps *
344 gst_audio_resample_fixate_caps (GstBaseTransform * base,
345     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
346 {
347   GstStructure *s;
348   gint rate;
349
350   s = gst_caps_get_structure (caps, 0);
351   if (G_UNLIKELY (!gst_structure_get_int (s, "rate", &rate)))
352     return othercaps;
353
354   othercaps = gst_caps_truncate (othercaps);
355   othercaps = gst_caps_make_writable (othercaps);
356   s = gst_caps_get_structure (othercaps, 0);
357   gst_structure_fixate_field_nearest_int (s, "rate", rate);
358
359   return othercaps;
360 }
361
362 static GstStructure *
363 make_options (GstAudioResample * resample, GstAudioInfo * in,
364     GstAudioInfo * out)
365 {
366   GstStructure *options;
367
368   options = gst_structure_new_empty ("resampler-options");
369   if (in != NULL && out != NULL)
370     gst_audio_resampler_options_set_quality (resample->method,
371         resample->quality, in->rate, out->rate, options);
372
373   gst_structure_set (options,
374       GST_AUDIO_CONVERTER_OPT_RESAMPLER_METHOD, GST_TYPE_AUDIO_RESAMPLER_METHOD,
375       resample->method,
376       GST_AUDIO_RESAMPLER_OPT_FILTER_MODE, GST_TYPE_AUDIO_RESAMPLER_FILTER_MODE,
377       resample->sinc_filter_mode, GST_AUDIO_RESAMPLER_OPT_FILTER_MODE_THRESHOLD,
378       G_TYPE_UINT, resample->sinc_filter_auto_threshold,
379       GST_AUDIO_RESAMPLER_OPT_FILTER_INTERPOLATION,
380       GST_TYPE_AUDIO_RESAMPLER_FILTER_INTERPOLATION,
381       resample->sinc_filter_interpolation, NULL);
382
383   return options;
384 }
385
386 static gboolean
387 gst_audio_resample_update_state (GstAudioResample * resample, GstAudioInfo * in,
388     GstAudioInfo * out)
389 {
390   gboolean updated_latency = FALSE;
391   gsize old_latency = -1;
392   GstStructure *options;
393
394   if (resample->converter == NULL && in == NULL && out == NULL)
395     return TRUE;
396
397   options = make_options (resample, in, out);
398
399   if (resample->converter)
400     old_latency = gst_audio_converter_get_max_latency (resample->converter);
401
402   /* if channels and layout changed, destroy existing resampler */
403   if (in != NULL && (in->finfo != resample->in.finfo ||
404           in->channels != resample->in.channels ||
405           in->layout != resample->in.layout) && resample->converter) {
406     gst_audio_converter_free (resample->converter);
407     resample->converter = NULL;
408   }
409   if (resample->converter == NULL) {
410     resample->converter =
411         gst_audio_converter_new (GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE, in,
412         out, options);
413     if (resample->converter == NULL)
414       goto resampler_failed;
415   } else if (in && out) {
416     gboolean ret;
417
418     ret =
419         gst_audio_converter_update_config (resample->converter, in->rate,
420         out->rate, options);
421     if (!ret)
422       goto update_failed;
423   } else {
424     gst_structure_free (options);
425   }
426   if (old_latency != -1)
427     updated_latency =
428         old_latency !=
429         gst_audio_converter_get_max_latency (resample->converter);
430
431   if (updated_latency)
432     gst_element_post_message (GST_ELEMENT (resample),
433         gst_message_new_latency (GST_OBJECT (resample)));
434
435   return TRUE;
436
437   /* ERRORS */
438 resampler_failed:
439   {
440     GST_ERROR_OBJECT (resample, "failed to create resampler");
441     return FALSE;
442   }
443 update_failed:
444   {
445     GST_ERROR_OBJECT (resample, "failed to update resampler");
446     return FALSE;
447   }
448 }
449
450 static void
451 gst_audio_resample_reset_state (GstAudioResample * resample)
452 {
453   if (resample->converter)
454     gst_audio_converter_reset (resample->converter);
455 }
456
457 static gboolean
458 gst_audio_resample_transform_size (GstBaseTransform * base,
459     GstPadDirection direction, GstCaps * caps, gsize size, GstCaps * othercaps,
460     gsize * othersize)
461 {
462   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
463   gboolean ret = TRUE;
464   gint bpf;
465
466   GST_LOG_OBJECT (base, "asked to transform size %" G_GSIZE_FORMAT
467       " in direction %s", size, direction == GST_PAD_SINK ? "SINK" : "SRC");
468
469   /* Number of samples in either buffer is size / (width*channels) ->
470    * calculate the factor */
471   bpf = GST_AUDIO_INFO_BPF (&resample->in);
472
473   /* Convert source buffer size to samples */
474   size /= bpf;
475
476   if (direction == GST_PAD_SINK) {
477     /* asked to convert size of an incoming buffer */
478     *othersize = gst_audio_converter_get_out_frames (resample->converter, size);
479     *othersize *= bpf;
480   } else {
481     /* asked to convert size of an outgoing buffer */
482     *othersize = gst_audio_converter_get_in_frames (resample->converter, size);
483     *othersize *= bpf;
484   }
485
486   GST_LOG_OBJECT (base,
487       "transformed size %" G_GSIZE_FORMAT " to %" G_GSIZE_FORMAT,
488       size * bpf, *othersize);
489
490   return ret;
491 }
492
493 static gboolean
494 gst_audio_resample_set_caps (GstBaseTransform * base, GstCaps * incaps,
495     GstCaps * outcaps)
496 {
497   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
498   GstAudioInfo in, out;
499
500   GST_LOG ("incaps %" GST_PTR_FORMAT ", outcaps %"
501       GST_PTR_FORMAT, incaps, outcaps);
502
503   if (!gst_audio_info_from_caps (&in, incaps))
504     goto invalid_incaps;
505   if (!gst_audio_info_from_caps (&out, outcaps))
506     goto invalid_outcaps;
507
508   /* Reset timestamp tracking and drain the resampler if the audio format is
509    * changing. Especially when changing the sample rate our timestamp tracking
510    * will be completely off, but even otherwise we would usually lose the last
511    * few samples if we don't drain here */
512   if (!gst_audio_info_is_equal (&in, &resample->in) ||
513       !gst_audio_info_is_equal (&out, &resample->out)) {
514     if (resample->converter) {
515       gsize latency = gst_audio_converter_get_max_latency (resample->converter);
516       gst_audio_resample_push_drain (resample, latency);
517     }
518     gst_audio_resample_reset_state (resample);
519     resample->num_gap_samples = 0;
520     resample->num_nongap_samples = 0;
521     resample->t0 = GST_CLOCK_TIME_NONE;
522     resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
523     resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
524     resample->samples_in = 0;
525     resample->samples_out = 0;
526     resample->need_discont = TRUE;
527   }
528
529   gst_audio_resample_update_state (resample, &in, &out);
530
531   resample->in = in;
532   resample->out = out;
533
534   return TRUE;
535
536   /* ERROR */
537 invalid_incaps:
538   {
539     GST_ERROR_OBJECT (base, "invalid incaps");
540     return FALSE;
541   }
542 invalid_outcaps:
543   {
544     GST_ERROR_OBJECT (base, "invalid outcaps");
545     return FALSE;
546   }
547 }
548
549 /* Push history_len zeros into the filter, but discard the output. */
550 static void
551 gst_audio_resample_dump_drain (GstAudioResample * resample, guint history_len)
552 {
553   gsize out_len, outsize;
554   gpointer out[1];
555
556   out_len =
557       gst_audio_converter_get_out_frames (resample->converter, history_len);
558   if (out_len == 0)
559     return;
560
561   outsize = out_len * resample->out.bpf;
562
563   out[0] = g_malloc (outsize);
564   gst_audio_converter_samples (resample->converter, 0, NULL, history_len,
565       out, out_len);
566   g_free (out[0]);
567 }
568
569 static void
570 gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len)
571 {
572   GstBuffer *outbuf;
573   GstFlowReturn res;
574   gint outsize;
575   gsize out_len;
576   GstAudioBuffer abuf;
577
578   g_assert (resample->converter != NULL);
579
580   /* Don't drain samples if we were reset. */
581   if (!GST_CLOCK_TIME_IS_VALID (resample->t0))
582     return;
583
584   out_len =
585       gst_audio_converter_get_out_frames (resample->converter, history_len);
586   if (out_len == 0)
587     return;
588
589   outsize = out_len * resample->in.bpf;
590   outbuf = gst_buffer_new_and_alloc (outsize);
591
592   if (GST_AUDIO_INFO_LAYOUT (&resample->out) ==
593       GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
594     gst_buffer_add_audio_meta (outbuf, &resample->out, out_len, NULL);
595   }
596
597   gst_audio_buffer_map (&abuf, &resample->out, outbuf, GST_MAP_WRITE);
598   gst_audio_converter_samples (resample->converter, 0, NULL, history_len,
599       abuf.planes, out_len);
600   gst_audio_buffer_unmap (&abuf);
601
602   /* time */
603   if (GST_CLOCK_TIME_IS_VALID (resample->t0)) {
604     GST_BUFFER_TIMESTAMP (outbuf) = resample->t0 +
605         gst_util_uint64_scale_int_round (resample->samples_out, GST_SECOND,
606         resample->out.rate);
607     GST_BUFFER_DURATION (outbuf) = resample->t0 +
608         gst_util_uint64_scale_int_round (resample->samples_out + out_len,
609         GST_SECOND, resample->out.rate) - GST_BUFFER_TIMESTAMP (outbuf);
610   } else {
611     GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
612     GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
613   }
614   /* offset */
615   if (resample->out_offset0 != GST_BUFFER_OFFSET_NONE) {
616     GST_BUFFER_OFFSET (outbuf) = resample->out_offset0 + resample->samples_out;
617     GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_len;
618   } else {
619     GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE;
620     GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;
621   }
622   /* move along */
623   resample->samples_out += out_len;
624   resample->samples_in += history_len;
625
626   GST_LOG_OBJECT (resample,
627       "Pushing drain buffer of %u bytes with timestamp %" GST_TIME_FORMAT
628       " duration %" GST_TIME_FORMAT " offset %" G_GUINT64_FORMAT " offset_end %"
629       G_GUINT64_FORMAT, outsize,
630       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
631       GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
632       GST_BUFFER_OFFSET_END (outbuf));
633
634   res = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (resample), outbuf);
635
636   if (G_UNLIKELY (res != GST_FLOW_OK))
637     GST_WARNING_OBJECT (resample, "Failed to push drain: %s",
638         gst_flow_get_name (res));
639
640   return;
641 }
642
643 static gboolean
644 gst_audio_resample_sink_event (GstBaseTransform * base, GstEvent * event)
645 {
646   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
647
648   switch (GST_EVENT_TYPE (event)) {
649     case GST_EVENT_FLUSH_STOP:
650       gst_audio_resample_reset_state (resample);
651       resample->num_gap_samples = 0;
652       resample->num_nongap_samples = 0;
653       resample->t0 = GST_CLOCK_TIME_NONE;
654       resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
655       resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
656       resample->samples_in = 0;
657       resample->samples_out = 0;
658       resample->need_discont = TRUE;
659       break;
660     case GST_EVENT_STREAM_START:
661     case GST_EVENT_SEGMENT:
662     case GST_EVENT_EOS:
663       if (resample->converter) {
664         gsize latency =
665             gst_audio_converter_get_max_latency (resample->converter);
666         gst_audio_resample_push_drain (resample, latency);
667       }
668       gst_audio_resample_reset_state (resample);
669       resample->num_gap_samples = 0;
670       resample->num_nongap_samples = 0;
671       resample->t0 = GST_CLOCK_TIME_NONE;
672       resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
673       resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
674       resample->samples_in = 0;
675       resample->samples_out = 0;
676       resample->need_discont = TRUE;
677       break;
678     default:
679       break;
680   }
681
682   return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (base, event);
683 }
684
685 static gboolean
686 gst_audio_resample_check_discont (GstAudioResample * resample, GstBuffer * buf)
687 {
688   guint64 offset;
689   guint64 delta;
690
691   /* is the incoming buffer a discontinuity? */
692   if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (buf)))
693     return TRUE;
694
695   /* no valid timestamps or offsets to compare --> no discontinuity */
696   if (G_UNLIKELY (!(GST_BUFFER_TIMESTAMP_IS_VALID (buf) &&
697               GST_CLOCK_TIME_IS_VALID (resample->t0))))
698     return FALSE;
699
700   /* convert the inbound timestamp to an offset. */
701   offset =
702       gst_util_uint64_scale_int_round (GST_BUFFER_TIMESTAMP (buf) -
703       resample->t0, resample->in.rate, GST_SECOND);
704
705   /* many elements generate imperfect streams due to rounding errors, so we
706    * permit a small error (up to one sample) without triggering a filter
707    * flush/restart (if triggered incorrectly, this will be audible) */
708   /* allow even up to more samples, since sink is not so strict anyway,
709    * so give that one a chance to handle this as configured */
710   delta = ABS ((gint64) (offset - resample->samples_in));
711   if (delta <= (resample->in.rate >> 5))
712     return FALSE;
713
714   GST_WARNING_OBJECT (resample,
715       "encountered timestamp discontinuity of %" G_GUINT64_FORMAT " samples = %"
716       GST_TIME_FORMAT, delta,
717       GST_TIME_ARGS (gst_util_uint64_scale_int_round (delta, GST_SECOND,
718               resample->in.rate)));
719   return TRUE;
720 }
721
722 static GstFlowReturn
723 gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
724     GstBuffer * outbuf)
725 {
726   GstAudioBuffer srcabuf, dstabuf;
727   gsize outsize;
728   gsize in_len;
729   gsize out_len;
730   guint filt_len =
731       gst_audio_converter_get_max_latency (resample->converter) * 2;
732   gboolean inbuf_writable;
733
734   inbuf_writable = gst_buffer_is_writable (inbuf)
735       && gst_buffer_n_memory (inbuf) == 1
736       && gst_memory_is_writable (gst_buffer_peek_memory (inbuf, 0));
737
738   gst_audio_buffer_map (&srcabuf, &resample->in, inbuf,
739       inbuf_writable ? GST_MAP_READWRITE : GST_MAP_READ);
740
741   in_len = srcabuf.n_samples;
742   out_len = gst_audio_converter_get_out_frames (resample->converter, in_len);
743
744   /* ensure that the output buffer is not bigger than what we need */
745   gst_buffer_set_size (outbuf, out_len * resample->in.bpf);
746
747   if (GST_AUDIO_INFO_LAYOUT (&resample->out) ==
748       GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
749     gst_buffer_add_audio_meta (outbuf, &resample->out, out_len, NULL);
750   }
751
752   gst_audio_buffer_map (&dstabuf, &resample->out, outbuf, GST_MAP_WRITE);
753
754   if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) {
755     resample->num_nongap_samples = 0;
756     if (resample->num_gap_samples < filt_len) {
757       guint zeros_to_push;
758       if (in_len >= filt_len - resample->num_gap_samples)
759         zeros_to_push = filt_len - resample->num_gap_samples;
760       else
761         zeros_to_push = in_len;
762
763       gst_audio_resample_push_drain (resample, zeros_to_push);
764       in_len -= zeros_to_push;
765       resample->num_gap_samples += zeros_to_push;
766     }
767
768     {
769       guint num, den;
770       gint i;
771
772       num = resample->in.rate;
773       den = resample->out.rate;
774
775       if (resample->samples_in + in_len >= filt_len / 2)
776         out_len =
777             gst_util_uint64_scale_int_ceil (resample->samples_in + in_len -
778             filt_len / 2, den, num) - resample->samples_out;
779       else
780         out_len = 0;
781
782       for (i = 0; i < dstabuf.n_planes; i++)
783         memset (dstabuf.planes[i], 0, GST_AUDIO_BUFFER_PLANE_SIZE (&dstabuf));
784
785       GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
786       resample->num_gap_samples += in_len;
787     }
788   } else {                      /* not a gap */
789     if (resample->num_gap_samples > filt_len) {
790       /* push in enough zeros to restore the filter to the right offset */
791       guint num;
792
793       num = resample->in.rate;
794
795       gst_audio_resample_dump_drain (resample,
796           (resample->num_gap_samples - filt_len) % num);
797     }
798     resample->num_gap_samples = 0;
799     if (resample->num_nongap_samples < filt_len) {
800       resample->num_nongap_samples += in_len;
801       if (resample->num_nongap_samples > filt_len)
802         resample->num_nongap_samples = filt_len;
803     }
804     {
805       /* process */
806       GstAudioConverterFlags flags;
807
808       flags = 0;
809       if (inbuf_writable)
810         flags |= GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE;
811
812       gst_audio_converter_samples (resample->converter, flags, srcabuf.planes,
813           in_len, dstabuf.planes, out_len);
814     }
815   }
816
817   /* time */
818   if (GST_CLOCK_TIME_IS_VALID (resample->t0)) {
819     GST_BUFFER_TIMESTAMP (outbuf) = resample->t0 +
820         gst_util_uint64_scale_int_round (resample->samples_out, GST_SECOND,
821         resample->out.rate);
822     GST_BUFFER_DURATION (outbuf) = resample->t0 +
823         gst_util_uint64_scale_int_round (resample->samples_out + out_len,
824         GST_SECOND, resample->out.rate) - GST_BUFFER_TIMESTAMP (outbuf);
825   } else {
826     GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
827     GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
828   }
829   /* offset */
830   if (resample->out_offset0 != GST_BUFFER_OFFSET_NONE) {
831     GST_BUFFER_OFFSET (outbuf) = resample->out_offset0 + resample->samples_out;
832     GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_len;
833   } else {
834     GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE;
835     GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;
836   }
837   /* move along */
838   resample->samples_out += out_len;
839   resample->samples_in += in_len;
840
841   gst_audio_buffer_unmap (&srcabuf);
842   gst_audio_buffer_unmap (&dstabuf);
843
844   outsize = out_len * resample->in.bpf;
845
846   GST_LOG_OBJECT (resample,
847       "Converted to buffer of %" G_GSIZE_FORMAT
848       " samples (%" G_GSIZE_FORMAT " bytes) with timestamp %" GST_TIME_FORMAT
849       ", duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
850       ", offset_end %" G_GUINT64_FORMAT, out_len, outsize,
851       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
852       GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)),
853       GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf));
854
855   if (outsize == 0)
856     return GST_BASE_TRANSFORM_FLOW_DROPPED;
857   else
858     return GST_FLOW_OK;
859 }
860
861 static GstFlowReturn
862 gst_audio_resample_transform (GstBaseTransform * base, GstBuffer * inbuf,
863     GstBuffer * outbuf)
864 {
865   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
866   GstFlowReturn ret;
867
868   GST_LOG_OBJECT (resample, "transforming buffer of %" G_GSIZE_FORMAT " bytes,"
869       " ts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %"
870       G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
871       gst_buffer_get_size (inbuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)),
872       GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)),
873       GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf));
874
875   /* check for timestamp discontinuities;  flush/reset if needed, and set
876    * flag to resync timestamp and offset counters and send event
877    * downstream */
878   if (G_UNLIKELY (gst_audio_resample_check_discont (resample, inbuf))) {
879     if (resample->converter) {
880       gsize latency = gst_audio_converter_get_max_latency (resample->converter);
881       gst_audio_resample_push_drain (resample, latency);
882     }
883
884     gst_audio_resample_reset_state (resample);
885     resample->need_discont = TRUE;
886   }
887
888   /* handle discontinuity */
889   if (G_UNLIKELY (resample->need_discont)) {
890     resample->num_gap_samples = 0;
891     resample->num_nongap_samples = 0;
892     /* reset */
893     resample->samples_in = 0;
894     resample->samples_out = 0;
895     GST_DEBUG_OBJECT (resample, "found discontinuity; resyncing");
896     /* resync the timestamp and offset counters if possible */
897     if (GST_BUFFER_TIMESTAMP_IS_VALID (inbuf)) {
898       resample->t0 = GST_BUFFER_TIMESTAMP (inbuf);
899     } else {
900       GST_DEBUG_OBJECT (resample, "... but new timestamp is invalid");
901       resample->t0 = GST_CLOCK_TIME_NONE;
902     }
903     if (GST_BUFFER_OFFSET_IS_VALID (inbuf)) {
904       resample->in_offset0 = GST_BUFFER_OFFSET (inbuf);
905       resample->out_offset0 =
906           gst_util_uint64_scale_int_round (resample->in_offset0,
907           resample->out.rate, resample->in.rate);
908     } else {
909       GST_DEBUG_OBJECT (resample, "... but new offset is invalid");
910       resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
911       resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
912     }
913     /* set DISCONT flag on output buffer */
914     GST_DEBUG_OBJECT (resample, "marking this buffer with the DISCONT flag");
915     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
916     resample->need_discont = FALSE;
917   }
918
919   ret = gst_audio_resample_process (resample, inbuf, outbuf);
920   if (G_UNLIKELY (ret != GST_FLOW_OK))
921     return ret;
922
923   GST_DEBUG_OBJECT (resample, "input = samples [%" G_GUINT64_FORMAT ", %"
924       G_GUINT64_FORMAT ") = [%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT
925       ") ns;  output = samples [%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT
926       ") = [%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ") ns",
927       GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf),
928       GST_BUFFER_TIMESTAMP (inbuf), GST_BUFFER_TIMESTAMP (inbuf) +
929       GST_BUFFER_DURATION (inbuf), GST_BUFFER_OFFSET (outbuf),
930       GST_BUFFER_OFFSET_END (outbuf), GST_BUFFER_TIMESTAMP (outbuf),
931       GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf));
932
933   return GST_FLOW_OK;
934 }
935
936 static gboolean
937 gst_audio_resample_transform_meta (GstBaseTransform * trans, GstBuffer * outbuf,
938     GstMeta * meta, GstBuffer * inbuf)
939 {
940   const GstMetaInfo *info = meta->info;
941   const gchar *const *tags;
942
943   tags = gst_meta_api_type_get_tags (info->api);
944
945   if (!tags || (g_strv_length ((gchar **) tags) == 1
946           && gst_meta_api_type_has_tag (info->api,
947               g_quark_from_string (GST_META_TAG_AUDIO_STR))))
948     return TRUE;
949
950   return FALSE;
951 }
952
953 static GstFlowReturn
954 gst_audio_resample_submit_input_buffer (GstBaseTransform * base,
955     gboolean is_discont, GstBuffer * input)
956 {
957   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
958
959   if (base->segment.format == GST_FORMAT_TIME) {
960     input =
961         gst_audio_buffer_clip (input, &base->segment, resample->in.rate,
962         resample->in.bpf);
963
964     if (!input)
965       return GST_FLOW_OK;
966   }
967
968   return GST_BASE_TRANSFORM_CLASS (parent_class)->submit_input_buffer (base,
969       is_discont, input);
970 }
971
972 static gboolean
973 gst_audio_resample_query (GstPad * pad, GstObject * parent, GstQuery * query)
974 {
975   GstAudioResample *resample = GST_AUDIO_RESAMPLE (parent);
976   GstBaseTransform *trans;
977   gboolean res = TRUE;
978
979   trans = GST_BASE_TRANSFORM (resample);
980
981   switch (GST_QUERY_TYPE (query)) {
982     case GST_QUERY_LATENCY:
983     {
984       GstClockTime min, max;
985       gboolean live;
986       guint64 latency;
987       gint rate = resample->in.rate;
988       gint resampler_latency;
989
990       if (resample->converter)
991         resampler_latency =
992             gst_audio_converter_get_max_latency (resample->converter);
993       else
994         resampler_latency = 0;
995
996       if (gst_base_transform_is_passthrough (trans))
997         resampler_latency = 0;
998
999       if ((res =
1000               gst_pad_peer_query (GST_BASE_TRANSFORM_SINK_PAD (trans),
1001                   query))) {
1002         gst_query_parse_latency (query, &live, &min, &max);
1003
1004         GST_DEBUG_OBJECT (resample, "Peer latency: min %"
1005             GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1006             GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1007
1008         /* add our own latency */
1009         if (rate != 0 && resampler_latency != 0)
1010           latency = gst_util_uint64_scale_round (resampler_latency,
1011               GST_SECOND, rate);
1012         else
1013           latency = 0;
1014
1015         GST_DEBUG_OBJECT (resample, "Our latency: %" GST_TIME_FORMAT,
1016             GST_TIME_ARGS (latency));
1017
1018         min += latency;
1019         if (GST_CLOCK_TIME_IS_VALID (max))
1020           max += latency;
1021
1022         GST_DEBUG_OBJECT (resample, "Calculated total latency : min %"
1023             GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1024             GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1025
1026         gst_query_set_latency (query, live, min, max);
1027       }
1028       break;
1029     }
1030     default:
1031       res = gst_pad_query_default (pad, parent, query);
1032       break;
1033   }
1034   return res;
1035 }
1036
1037 static void
1038 gst_audio_resample_set_property (GObject * object, guint prop_id,
1039     const GValue * value, GParamSpec * pspec)
1040 {
1041   GstAudioResample *resample;
1042
1043   resample = GST_AUDIO_RESAMPLE (object);
1044
1045   switch (prop_id) {
1046     case PROP_QUALITY:
1047       /* FIXME locking! */
1048       resample->quality = g_value_get_int (value);
1049       GST_DEBUG_OBJECT (resample, "new quality %d", resample->quality);
1050       gst_audio_resample_update_state (resample, NULL, NULL);
1051       break;
1052     case PROP_RESAMPLE_METHOD:
1053       resample->method = g_value_get_enum (value);
1054       gst_audio_resample_update_state (resample, NULL, NULL);
1055       break;
1056     case PROP_SINC_FILTER_MODE:
1057       /* FIXME locking! */
1058       resample->sinc_filter_mode = g_value_get_enum (value);
1059       gst_audio_resample_update_state (resample, NULL, NULL);
1060       break;
1061     case PROP_SINC_FILTER_AUTO_THRESHOLD:
1062       /* FIXME locking! */
1063       resample->sinc_filter_auto_threshold = g_value_get_uint (value);
1064       gst_audio_resample_update_state (resample, NULL, NULL);
1065       break;
1066     case PROP_SINC_FILTER_INTERPOLATION:
1067       /* FIXME locking! */
1068       resample->sinc_filter_interpolation = g_value_get_enum (value);
1069       gst_audio_resample_update_state (resample, NULL, NULL);
1070       break;
1071     default:
1072       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1073       break;
1074   }
1075 }
1076
1077 static void
1078 gst_audio_resample_get_property (GObject * object, guint prop_id,
1079     GValue * value, GParamSpec * pspec)
1080 {
1081   GstAudioResample *resample;
1082
1083   resample = GST_AUDIO_RESAMPLE (object);
1084
1085   switch (prop_id) {
1086     case PROP_QUALITY:
1087       g_value_set_int (value, resample->quality);
1088       break;
1089     case PROP_RESAMPLE_METHOD:
1090       g_value_set_enum (value, resample->method);
1091       break;
1092     case PROP_SINC_FILTER_MODE:
1093       g_value_set_enum (value, resample->sinc_filter_mode);
1094       break;
1095     case PROP_SINC_FILTER_AUTO_THRESHOLD:
1096       g_value_set_uint (value, resample->sinc_filter_auto_threshold);
1097       break;
1098     case PROP_SINC_FILTER_INTERPOLATION:
1099       g_value_set_enum (value, resample->sinc_filter_interpolation);
1100       break;
1101     default:
1102       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1103       break;
1104   }
1105 }
1106
1107 static gboolean
1108 plugin_init (GstPlugin * plugin)
1109 {
1110   GST_DEBUG_CATEGORY_INIT (audio_resample_debug, "audioresample", 0,
1111       "audio resampling element");
1112
1113   if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY,
1114           GST_TYPE_AUDIO_RESAMPLE)) {
1115     return FALSE;
1116   }
1117
1118   return TRUE;
1119 }
1120
1121 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1122     GST_VERSION_MINOR,
1123     audioresample,
1124     "Resamples audio", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
1125     GST_PACKAGE_ORIGIN);