Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / gst / audioconvert / gstaudioconvert.c
1 /* GStreamer
2  * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
3  * Copyright (C) 2005 Thomas Vander Stichele <thomas at apestaart dot org>
4  * Copyright (C) 2011 Wim Taymans <wim.taymans at gmail dot com>
5  *
6  * gstaudioconvert.c: Convert audio to different audio formats automatically
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /**
25  * SECTION:element-audioconvert
26  *
27  * Audioconvert converts raw audio buffers between various possible formats.
28  * It supports integer to float conversion, width/depth conversion,
29  * signedness and endianness conversion and channel transformations.
30  *
31  * <refsect2>
32  * <title>Example launch line</title>
33  * |[
34  * gst-launch -v -m audiotestsrc ! audioconvert ! audio/x-raw,format=S8,channels=2 ! level ! fakesink silent=TRUE
35  * ]| This pipeline converts audio to 8-bit.  The level element shows that
36  * the output levels still match the one for a sine wave.
37  * |[
38  * gst-launch -v -m audiotestsrc ! audioconvert ! vorbisenc ! fakesink silent=TRUE
39  * ]| The vorbis encoder takes float audio data instead of the integer data
40  * generated by audiotestsrc.
41  * </refsect2>
42  *
43  * Last reviewed on 2006-03-02 (0.10.4)
44  */
45
46 /*
47  * design decisions:
48  * - audioconvert converts buffers in a set of supported caps. If it supports
49  *   a caps, it supports conversion from these caps to any other caps it
50  *   supports. (example: if it does A=>B and A=>C, it also does B=>C)
51  * - audioconvert does not save state between buffers. Every incoming buffer is
52  *   converted and the converted buffer is pushed out.
53  * conclusion:
54  * audioconvert is not supposed to be a one-element-does-anything solution for
55  * audio conversions.
56  */
57
58 #ifdef HAVE_CONFIG_H
59 #include "config.h"
60 #endif
61
62 #include <string.h>
63
64 #include "gstaudioconvert.h"
65 #include "gstchannelmix.h"
66 #include "gstaudioquantize.h"
67 #include "plugin.h"
68
69 GST_DEBUG_CATEGORY (audio_convert_debug);
70 GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
71
72 /*** DEFINITIONS **************************************************************/
73
74 /* type functions */
75 static void gst_audio_convert_dispose (GObject * obj);
76
77 /* gstreamer functions */
78 static gboolean gst_audio_convert_get_unit_size (GstBaseTransform * base,
79     GstCaps * caps, gsize * size);
80 static GstCaps *gst_audio_convert_transform_caps (GstBaseTransform * base,
81     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
82 static void gst_audio_convert_fixate_caps (GstBaseTransform * base,
83     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
84 static gboolean gst_audio_convert_set_caps (GstBaseTransform * base,
85     GstCaps * incaps, GstCaps * outcaps);
86 static GstFlowReturn gst_audio_convert_transform (GstBaseTransform * base,
87     GstBuffer * inbuf, GstBuffer * outbuf);
88 static GstFlowReturn gst_audio_convert_transform_ip (GstBaseTransform * base,
89     GstBuffer * buf);
90 static void gst_audio_convert_set_property (GObject * object, guint prop_id,
91     const GValue * value, GParamSpec * pspec);
92 static void gst_audio_convert_get_property (GObject * object, guint prop_id,
93     GValue * value, GParamSpec * pspec);
94
95 /* AudioConvert signals and args */
96 enum
97 {
98   /* FILL ME */
99   LAST_SIGNAL
100 };
101
102 enum
103 {
104   ARG_0,
105   ARG_DITHERING,
106   ARG_NOISE_SHAPING,
107 };
108
109 #define DEBUG_INIT \
110   GST_DEBUG_CATEGORY_INIT (audio_convert_debug, "audioconvert", 0, "audio conversion element"); \
111   GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
112 #define gst_audio_convert_parent_class parent_class
113 G_DEFINE_TYPE_WITH_CODE (GstAudioConvert, gst_audio_convert,
114     GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
115
116 /*** GSTREAMER PROTOTYPES *****************************************************/
117
118 #define STATIC_CAPS \
119 GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL))
120
121 static GstStaticPadTemplate gst_audio_convert_src_template =
122 GST_STATIC_PAD_TEMPLATE ("src",
123     GST_PAD_SRC,
124     GST_PAD_ALWAYS,
125     STATIC_CAPS);
126
127 static GstStaticPadTemplate gst_audio_convert_sink_template =
128 GST_STATIC_PAD_TEMPLATE ("sink",
129     GST_PAD_SINK,
130     GST_PAD_ALWAYS,
131     STATIC_CAPS);
132
133 #define GST_TYPE_AUDIO_CONVERT_DITHERING (gst_audio_convert_dithering_get_type ())
134 static GType
135 gst_audio_convert_dithering_get_type (void)
136 {
137   static GType gtype = 0;
138
139   if (gtype == 0) {
140     static const GEnumValue values[] = {
141       {DITHER_NONE, "No dithering",
142           "none"},
143       {DITHER_RPDF, "Rectangular dithering", "rpdf"},
144       {DITHER_TPDF, "Triangular dithering (default)", "tpdf"},
145       {DITHER_TPDF_HF, "High frequency triangular dithering", "tpdf-hf"},
146       {0, NULL, NULL}
147     };
148
149     gtype = g_enum_register_static ("GstAudioConvertDithering", values);
150   }
151   return gtype;
152 }
153
154 #define GST_TYPE_AUDIO_CONVERT_NOISE_SHAPING (gst_audio_convert_ns_get_type ())
155 static GType
156 gst_audio_convert_ns_get_type (void)
157 {
158   static GType gtype = 0;
159
160   if (gtype == 0) {
161     static const GEnumValue values[] = {
162       {NOISE_SHAPING_NONE, "No noise shaping (default)",
163           "none"},
164       {NOISE_SHAPING_ERROR_FEEDBACK, "Error feedback", "error-feedback"},
165       {NOISE_SHAPING_SIMPLE, "Simple 2-pole noise shaping", "simple"},
166       {NOISE_SHAPING_MEDIUM, "Medium 5-pole noise shaping", "medium"},
167       {NOISE_SHAPING_HIGH, "High 8-pole noise shaping", "high"},
168       {0, NULL, NULL}
169     };
170
171     gtype = g_enum_register_static ("GstAudioConvertNoiseShaping", values);
172   }
173   return gtype;
174 }
175
176
177 /*** TYPE FUNCTIONS ***********************************************************/
178 static void
179 gst_audio_convert_class_init (GstAudioConvertClass * klass)
180 {
181   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
182   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
183   GstBaseTransformClass *basetransform_class = GST_BASE_TRANSFORM_CLASS (klass);
184
185   gobject_class->dispose = gst_audio_convert_dispose;
186   gobject_class->set_property = gst_audio_convert_set_property;
187   gobject_class->get_property = gst_audio_convert_get_property;
188
189   g_object_class_install_property (gobject_class, ARG_DITHERING,
190       g_param_spec_enum ("dithering", "Dithering",
191           "Selects between different dithering methods.",
192           GST_TYPE_AUDIO_CONVERT_DITHERING, DITHER_TPDF,
193           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
194
195   g_object_class_install_property (gobject_class, ARG_NOISE_SHAPING,
196       g_param_spec_enum ("noise-shaping", "Noise shaping",
197           "Selects between different noise shaping methods.",
198           GST_TYPE_AUDIO_CONVERT_NOISE_SHAPING, NOISE_SHAPING_NONE,
199           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
200
201   gst_element_class_add_pad_template (element_class,
202       gst_static_pad_template_get (&gst_audio_convert_src_template));
203   gst_element_class_add_pad_template (element_class,
204       gst_static_pad_template_get (&gst_audio_convert_sink_template));
205   gst_element_class_set_details_simple (element_class,
206       "Audio converter", "Filter/Converter/Audio",
207       "Convert audio to different formats", "Benjamin Otte <otte@gnome.org>");
208
209   basetransform_class->get_unit_size =
210       GST_DEBUG_FUNCPTR (gst_audio_convert_get_unit_size);
211   basetransform_class->transform_caps =
212       GST_DEBUG_FUNCPTR (gst_audio_convert_transform_caps);
213   basetransform_class->fixate_caps =
214       GST_DEBUG_FUNCPTR (gst_audio_convert_fixate_caps);
215   basetransform_class->set_caps =
216       GST_DEBUG_FUNCPTR (gst_audio_convert_set_caps);
217   basetransform_class->transform_ip =
218       GST_DEBUG_FUNCPTR (gst_audio_convert_transform_ip);
219   basetransform_class->transform =
220       GST_DEBUG_FUNCPTR (gst_audio_convert_transform);
221
222   basetransform_class->passthrough_on_same_caps = TRUE;
223 }
224
225 static void
226 gst_audio_convert_init (GstAudioConvert * this)
227 {
228   this->dither = DITHER_TPDF;
229   this->ns = NOISE_SHAPING_NONE;
230   memset (&this->ctx, 0, sizeof (AudioConvertCtx));
231
232   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (this), TRUE);
233 }
234
235 static void
236 gst_audio_convert_dispose (GObject * obj)
237 {
238   GstAudioConvert *this = GST_AUDIO_CONVERT (obj);
239
240   audio_convert_clean_context (&this->ctx);
241
242   G_OBJECT_CLASS (parent_class)->dispose (obj);
243 }
244
245 /*** GSTREAMER FUNCTIONS ******************************************************/
246
247 /* BaseTransform vmethods */
248 static gboolean
249 gst_audio_convert_get_unit_size (GstBaseTransform * base, GstCaps * caps,
250     gsize * size)
251 {
252   GstAudioInfo info;
253
254   g_assert (size);
255
256   if (!gst_audio_info_from_caps (&info, caps))
257     goto parse_error;
258
259   *size = info.bpf;
260   GST_INFO_OBJECT (base, "unit_size = %" G_GSIZE_FORMAT, *size);
261
262   return TRUE;
263
264 parse_error:
265   {
266     GST_INFO_OBJECT (base, "failed to parse caps to get unit_size");
267     return FALSE;
268   }
269 }
270
271 /* copies the given caps */
272 static GstCaps *
273 gst_audio_convert_caps_remove_format_info (GstCaps * caps)
274 {
275   GstStructure *st;
276   gint i, n;
277   GstCaps *res;
278
279   res = gst_caps_new_empty ();
280
281   n = gst_caps_get_size (caps);
282   for (i = 0; i < n; i++) {
283     st = gst_caps_get_structure (caps, i);
284
285     /* If this is already expressed by the existing caps
286      * skip this structure */
287     if (i > 0 && gst_caps_is_subset_structure (res, st))
288       continue;
289
290     st = gst_structure_copy (st);
291     gst_structure_remove_fields (st, "format", "channel-positions", NULL);
292
293     gst_caps_append_structure (res, st);
294   }
295
296   return res;
297 }
298
299 /* The caps can be transformed into any other caps with format info removed.
300  * However, we should prefer passthrough, so if passthrough is possible,
301  * put it first in the list. */
302 static GstCaps *
303 gst_audio_convert_transform_caps (GstBaseTransform * btrans,
304     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
305 {
306   GstCaps *tmp, *tmp2;
307   GstCaps *result;
308
309   result = gst_caps_copy (caps);
310
311   /* Get all possible caps that we can transform to */
312   tmp = gst_audio_convert_caps_remove_format_info (caps);
313
314   if (filter) {
315     tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
316     gst_caps_unref (tmp);
317     tmp = tmp2;
318   }
319
320   result = tmp;
321
322   GST_DEBUG_OBJECT (btrans, "transformed %" GST_PTR_FORMAT " into %"
323       GST_PTR_FORMAT, caps, result);
324
325   return result;
326 }
327
328 static const GstAudioChannelPosition default_positions[8][8] = {
329   /* 1 channel */
330   {
331         GST_AUDIO_CHANNEL_POSITION_FRONT_MONO,
332       },
333   /* 2 channels */
334   {
335         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
336         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
337       },
338   /* 3 channels (2.1) */
339   {
340         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
341         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
342         GST_AUDIO_CHANNEL_POSITION_LFE, /* or FRONT_CENTER for 3.0? */
343       },
344   /* 4 channels (4.0 or 3.1?) */
345   {
346         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
347         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
348         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
349         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
350       },
351   /* 5 channels */
352   {
353         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
354         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
355         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
356         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
357         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
358       },
359   /* 6 channels */
360   {
361         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
362         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
363         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
364         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
365         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
366         GST_AUDIO_CHANNEL_POSITION_LFE,
367       },
368   /* 7 channels */
369   {
370         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
371         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
372         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
373         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
374         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
375         GST_AUDIO_CHANNEL_POSITION_LFE,
376         GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
377       },
378   /* 8 channels */
379   {
380         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
381         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
382         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
383         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
384         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
385         GST_AUDIO_CHANNEL_POSITION_LFE,
386         GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
387         GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
388       }
389 };
390
391 static const GValue *
392 find_suitable_channel_layout (const GValue * val, guint chans)
393 {
394   /* if output layout is fixed already and looks sane, we're done */
395   if (GST_VALUE_HOLDS_ARRAY (val) && gst_value_array_get_size (val) == chans)
396     return val;
397
398   /* if it's a list, go through it recursively and return the first
399    * sane-enough looking value we find */
400   if (GST_VALUE_HOLDS_LIST (val)) {
401     gint i;
402
403     for (i = 0; i < gst_value_list_get_size (val); ++i) {
404       const GValue *v, *ret;
405
406       v = gst_value_list_get_value (val, i);
407       if ((ret = find_suitable_channel_layout (v, chans)))
408         return ret;
409     }
410   }
411
412   return NULL;
413 }
414
415 static void
416 gst_audio_convert_fixate_channels (GstBaseTransform * base, GstStructure * ins,
417     GstStructure * outs)
418 {
419   const GValue *in_layout, *out_layout;
420   gint in_chans, out_chans;
421
422   if (!gst_structure_get_int (ins, "channels", &in_chans))
423     return;                     /* this shouldn't really happen, should it? */
424
425   if (!gst_structure_has_field (outs, "channels")) {
426     /* we could try to get the implied number of channels from the layout,
427      * but that seems overdoing it for a somewhat exotic corner case */
428     gst_structure_remove_field (outs, "channel-positions");
429     return;
430   }
431
432   /* ok, let's fixate the channels if they are not fixated yet */
433   gst_structure_fixate_field_nearest_int (outs, "channels", in_chans);
434
435   if (!gst_structure_get_int (outs, "channels", &out_chans)) {
436     /* shouldn't really happen ... */
437     gst_structure_remove_field (outs, "channel-positions");
438     return;
439   }
440
441   /* check if the output has a channel layout (or a list of layouts) */
442   out_layout = gst_structure_get_value (outs, "channel-positions");
443
444   /* get the channel layout of the input if any */
445   in_layout = gst_structure_get_value (ins, "channel-positions");
446
447   if (out_layout == NULL) {
448     if (out_chans <= 2 && (in_chans != out_chans || in_layout == NULL))
449       return;                   /* nothing to do, default layout will be assumed */
450     GST_WARNING_OBJECT (base, "downstream caps contain no channel layout");
451   }
452
453   if (in_chans == out_chans && in_layout != NULL) {
454     GValue res = { 0, };
455
456     /* same number of channels and no output layout: just use input layout */
457     if (out_layout == NULL) {
458       gst_structure_set_value (outs, "channel-positions", in_layout);
459       return;
460     }
461
462     /* if output layout is fixed already and looks sane, we're done */
463     if (GST_VALUE_HOLDS_ARRAY (out_layout) &&
464         gst_value_array_get_size (out_layout) == out_chans) {
465       return;
466     }
467
468     /* if the output layout is not fixed, check if the output layout contains
469      * the input layout */
470     if (gst_value_intersect (&res, in_layout, out_layout)) {
471       gst_structure_set_value (outs, "channel-positions", in_layout);
472       g_value_unset (&res);
473       return;
474     }
475
476     /* output layout is not fixed and does not contain the input layout, so
477      * just pick the first layout in the list (it should be a list ...) */
478     if ((out_layout = find_suitable_channel_layout (out_layout, out_chans))) {
479       gst_structure_set_value (outs, "channel-positions", out_layout);
480       return;
481     }
482
483     /* ... else fall back to default layout (NB: out_layout is NULL here) */
484     GST_WARNING_OBJECT (base, "unexpected output channel layout");
485   }
486
487   /* number of input channels != number of output channels:
488    * if this value contains a list of channel layouts (or even worse: a list
489    * with another list), just pick the first value and repeat until we find a
490    * channel position array or something else that's not a list; we assume
491    * the input if half-way sane and don't try to fall back on other list items
492    * if the first one is something unexpected or non-channel-pos-array-y */
493   if (out_layout != NULL && GST_VALUE_HOLDS_LIST (out_layout))
494     out_layout = find_suitable_channel_layout (out_layout, out_chans);
495
496   if (out_layout != NULL) {
497     if (GST_VALUE_HOLDS_ARRAY (out_layout) &&
498         gst_value_array_get_size (out_layout) == out_chans) {
499       /* looks sane enough, let's use it */
500       gst_structure_set_value (outs, "channel-positions", out_layout);
501       return;
502     }
503
504     /* what now?! Just ignore what we're given and use default positions */
505     GST_WARNING_OBJECT (base, "invalid or unexpected channel-positions");
506   }
507
508   /* missing or invalid output layout and we can't use the input layout for
509    * one reason or another, so just pick a default layout (we could be smarter
510    * and try to add/remove channels from the input layout, or pick a default
511    * layout based on LFE-presence in input layout, but let's save that for
512    * another day) */
513   if (out_chans > 0 && out_chans <= G_N_ELEMENTS (default_positions[0])) {
514     GST_DEBUG_OBJECT (base, "using default channel layout as fallback");
515     gst_audio_set_channel_positions (outs, default_positions[out_chans - 1]);
516   }
517 }
518
519 /* try to keep as many of the structure members the same by fixating the
520  * possible ranges; this way we convert the least amount of things as possible
521  */
522 static void
523 gst_audio_convert_fixate_caps (GstBaseTransform * base,
524     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
525 {
526   GstStructure *ins, *outs;
527   gint rate;
528   const gchar *fmt;
529
530   g_return_if_fail (gst_caps_is_fixed (caps));
531
532   GST_DEBUG_OBJECT (base, "trying to fixate othercaps %" GST_PTR_FORMAT
533       " based on caps %" GST_PTR_FORMAT, othercaps, caps);
534
535   ins = gst_caps_get_structure (caps, 0);
536   outs = gst_caps_get_structure (othercaps, 0);
537
538   gst_audio_convert_fixate_channels (base, ins, outs);
539
540   if ((fmt = gst_structure_get_string (ins, "format"))) {
541     /* FIXME, find the best format */
542     gst_structure_fixate_field_string (outs, "format", fmt);
543   }
544
545   if (gst_structure_get_int (ins, "rate", &rate)) {
546     if (gst_structure_has_field (outs, "rate")) {
547       gst_structure_fixate_field_nearest_int (outs, "rate", rate);
548     }
549   }
550   GST_DEBUG_OBJECT (base, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
551 }
552
553 static gboolean
554 gst_audio_convert_set_caps (GstBaseTransform * base, GstCaps * incaps,
555     GstCaps * outcaps)
556 {
557   GstAudioConvert *this = GST_AUDIO_CONVERT (base);
558   GstAudioInfo in_info;
559   GstAudioInfo out_info;
560
561   GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %"
562       GST_PTR_FORMAT, incaps, outcaps);
563
564   if (!gst_audio_info_from_caps (&in_info, incaps))
565     goto invalid_in;
566   if (!gst_audio_info_from_caps (&out_info, outcaps))
567     goto invalid_out;
568
569   if (!audio_convert_prepare_context (&this->ctx, &in_info, &out_info,
570           this->dither, this->ns))
571     goto no_converter;
572
573   return TRUE;
574
575   /* ERRORS */
576 invalid_in:
577   {
578     GST_ERROR_OBJECT (base, "invalid input caps");
579     return FALSE;
580   }
581 invalid_out:
582   {
583     GST_ERROR_OBJECT (base, "invalid output caps");
584     return FALSE;
585   }
586 no_converter:
587   {
588     GST_ERROR_OBJECT (base, "could not find converter");
589     return FALSE;
590   }
591 }
592
593 static GstFlowReturn
594 gst_audio_convert_transform_ip (GstBaseTransform * base, GstBuffer * buf)
595 {
596   /* nothing to do here */
597   return GST_FLOW_OK;
598 }
599
600 static GstFlowReturn
601 gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
602     GstBuffer * outbuf)
603 {
604   GstFlowReturn ret;
605   GstAudioConvert *this = GST_AUDIO_CONVERT (base);
606   gsize srcsize, dstsize;
607   gint insize, outsize;
608   gint samples;
609   gpointer src, dst;
610
611   /* get amount of samples to convert. */
612   samples = gst_buffer_get_size (inbuf) / this->ctx.in.bpf;
613
614   /* get in/output sizes, to see if the buffers we got are of correct
615    * sizes */
616   if (!audio_convert_get_sizes (&this->ctx, samples, &insize, &outsize))
617     goto error;
618
619   if (insize == 0 || outsize == 0)
620     return GST_FLOW_OK;
621
622   /* get src and dst data */
623   src = gst_buffer_map (inbuf, &srcsize, NULL, GST_MAP_READ);
624   dst = gst_buffer_map (outbuf, &dstsize, NULL, GST_MAP_WRITE);
625
626   /* check in and outsize */
627   if (srcsize < insize)
628     goto wrong_size;
629   if (dstsize < outsize)
630     goto wrong_size;
631
632   /* and convert the samples */
633   if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) {
634     if (!audio_convert_convert (&this->ctx, src, dst,
635             samples, gst_buffer_is_writable (inbuf)))
636       goto convert_error;
637   } else {
638     /* Create silence buffer */
639     gst_audio_format_fill_silence (this->ctx.out.finfo, dst, outsize);
640   }
641   ret = GST_FLOW_OK;
642
643 done:
644   gst_buffer_unmap (outbuf, dst, outsize);
645   gst_buffer_unmap (inbuf, src, srcsize);
646
647   return ret;
648
649   /* ERRORS */
650 error:
651   {
652     GST_ELEMENT_ERROR (this, STREAM, FORMAT,
653         (NULL), ("cannot get input/output sizes for %d samples", samples));
654     return GST_FLOW_ERROR;
655   }
656 wrong_size:
657   {
658     GST_ELEMENT_ERROR (this, STREAM, FORMAT,
659         (NULL),
660         ("input/output buffers are of wrong size in: %" G_GSIZE_FORMAT " < %d"
661             " or out: %" G_GSIZE_FORMAT " < %d",
662             srcsize, insize, dstsize, outsize));
663     ret = GST_FLOW_ERROR;
664     goto done;
665   }
666 convert_error:
667   {
668     GST_ELEMENT_ERROR (this, STREAM, FORMAT,
669         (NULL), ("error while converting"));
670     ret = GST_FLOW_ERROR;
671     goto done;
672   }
673 }
674
675 static void
676 gst_audio_convert_set_property (GObject * object, guint prop_id,
677     const GValue * value, GParamSpec * pspec)
678 {
679   GstAudioConvert *this = GST_AUDIO_CONVERT (object);
680
681   switch (prop_id) {
682     case ARG_DITHERING:
683       this->dither = g_value_get_enum (value);
684       break;
685     case ARG_NOISE_SHAPING:
686       this->ns = g_value_get_enum (value);
687       break;
688     default:
689       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
690       break;
691   }
692 }
693
694 static void
695 gst_audio_convert_get_property (GObject * object, guint prop_id,
696     GValue * value, GParamSpec * pspec)
697 {
698   GstAudioConvert *this = GST_AUDIO_CONVERT (object);
699
700   switch (prop_id) {
701     case ARG_DITHERING:
702       g_value_set_enum (value, this->dither);
703       break;
704     case ARG_NOISE_SHAPING:
705       g_value_set_enum (value, this->ns);
706       break;
707     default:
708       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
709       break;
710   }
711 }