audioconvert: Port to the new multichannel caps
[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   guint64 channel_mask;
279
280   res = gst_caps_new_empty ();
281
282   n = gst_caps_get_size (caps);
283   for (i = 0; i < n; i++) {
284     st = gst_caps_get_structure (caps, i);
285
286     /* If this is already expressed by the existing caps
287      * skip this structure */
288     if (i > 0 && gst_caps_is_subset_structure (res, st))
289       continue;
290
291     st = gst_structure_copy (st);
292     gst_structure_remove_field (st, "format");
293
294     /* Only remove the channels and channel-mask for non-NONE layouts */
295     if (gst_structure_get (st, "channel-mask", GST_TYPE_BITMASK, &channel_mask,
296             NULL)) {
297       if (channel_mask != 0)
298         gst_structure_remove_fields (st, "channel-mask", "channels", NULL);
299     } else {
300       gst_structure_remove_fields (st, "channel-mask", "channels", NULL);
301     }
302
303     gst_caps_append_structure (res, st);
304   }
305
306   return res;
307 }
308
309 /* The caps can be transformed into any other caps with format info removed.
310  * However, we should prefer passthrough, so if passthrough is possible,
311  * put it first in the list. */
312 static GstCaps *
313 gst_audio_convert_transform_caps (GstBaseTransform * btrans,
314     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
315 {
316   GstCaps *tmp, *tmp2;
317   GstCaps *result;
318
319   /* Get all possible caps that we can transform to */
320   tmp = gst_audio_convert_caps_remove_format_info (caps);
321
322   if (filter) {
323     tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
324     gst_caps_unref (tmp);
325     tmp = tmp2;
326   }
327
328   result = tmp;
329
330   GST_DEBUG_OBJECT (btrans, "transformed %" GST_PTR_FORMAT " into %"
331       GST_PTR_FORMAT, caps, result);
332
333   return result;
334 }
335
336 static const GstAudioChannelPosition default_positions[8][8] = {
337   /* 1 channel */
338   {
339         GST_AUDIO_CHANNEL_POSITION_MONO,
340       },
341   /* 2 channels */
342   {
343         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
344         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
345       },
346   /* 3 channels (2.1) */
347   {
348         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
349         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
350         GST_AUDIO_CHANNEL_POSITION_LFE1,
351       },
352   /* 4 channels (4.0) */
353   {
354         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
355         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
356         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
357         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
358       },
359   /* 5 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       },
367   /* 6 channels (5.1) */
368   {
369         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
370         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
371         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
372         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
373         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
374         GST_AUDIO_CHANNEL_POSITION_LFE1,
375       },
376   /* 7 channels (6.1) */
377   {
378         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
379         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
380         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
381         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
382         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
383         GST_AUDIO_CHANNEL_POSITION_LFE1,
384         GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
385       },
386   /* 8 channels (7.1) */
387   {
388         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
389         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
390         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
391         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
392         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
393         GST_AUDIO_CHANNEL_POSITION_LFE1,
394         GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
395         GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
396       }
397 };
398
399 static gint
400 n_bits_set (guint64 x)
401 {
402   gint i;
403   gint c = 0;
404   guint64 y = 1;
405
406   for (i = 0; i < 64; i++) {
407     if (x & y)
408       c++;
409     y <<= 1;
410   }
411
412   return c;
413 }
414
415 static guint64
416 find_suitable_mask (guint64 mask, gint n_chans)
417 {
418   guint64 intersection;
419   gint i;
420
421   i = 0;
422
423   g_assert (n_bits_set (mask) >= n_chans);
424
425   intersection = mask;
426   do {
427     intersection = intersection & ((~G_GUINT64_CONSTANT (0)) >> i);
428     i++;
429   } while (n_bits_set (intersection) > n_chans && i < 64);
430
431   if (i < 64)
432     return intersection;
433   return 0;
434 }
435
436 static void
437 gst_audio_convert_fixate_channels (GstBaseTransform * base, GstStructure * ins,
438     GstStructure * outs)
439 {
440   gint in_chans, out_chans;
441   guint64 in_mask = 0, out_mask = 0;
442   gboolean has_in_mask = FALSE, has_out_mask = FALSE;
443
444   if (!gst_structure_get_int (ins, "channels", &in_chans))
445     return;                     /* this shouldn't really happen, should it? */
446
447   if (!gst_structure_has_field (outs, "channels")) {
448     /* we could try to get the implied number of channels from the layout,
449      * but that seems overdoing it for a somewhat exotic corner case */
450     gst_structure_remove_field (outs, "channel-mask");
451     return;
452   }
453
454   /* ok, let's fixate the channels if they are not fixated yet */
455   gst_structure_fixate_field_nearest_int (outs, "channels", in_chans);
456
457   if (!gst_structure_get_int (outs, "channels", &out_chans)) {
458     /* shouldn't really happen ... */
459     gst_structure_remove_field (outs, "channel-mask");
460     return;
461   }
462
463   /* get the channel layout of the output if any */
464   has_out_mask = gst_structure_has_field (outs, "channel-mask");
465   if (has_out_mask) {
466     gst_structure_get (outs, "channel-mask", GST_TYPE_BITMASK, &out_mask, NULL);
467   } else {
468     /* channels == 1 => MONO */
469     if (out_chans == 2) {
470       out_mask =
471           GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT |
472           GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
473       has_out_mask = TRUE;
474     }
475   }
476
477   /* get the channel layout of the input if any */
478   has_in_mask = gst_structure_has_field (ins, "channel-mask");
479   if (has_in_mask) {
480     gst_structure_get (ins, "channel-mask", GST_TYPE_BITMASK, &in_mask, NULL);
481   } else {
482     /* channels == 1 => MONO */
483     if (in_chans == 2) {
484       in_mask =
485           GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT |
486           GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
487       has_in_mask = TRUE;
488     } else if (in_chans > 2)
489       g_warning ("%s: Upstream caps contain no channel mask",
490           GST_ELEMENT_NAME (base));
491   }
492
493   if (!has_out_mask && out_chans == 1 && (in_chans != out_chans
494           || !has_in_mask))
495     return;                     /* nothing to do, default layout will be assumed */
496
497   if (in_chans == out_chans && (has_in_mask || in_chans == 1)) {
498     /* same number of channels and no output layout: just use input layout */
499     if (!has_out_mask) {
500       /* in_chans == 1 handled above already */
501       gst_structure_set (outs, "channel-mask", GST_TYPE_BITMASK, in_mask, NULL);
502       return;
503     }
504
505     /* If both masks are the same we're done, this includes the NONE layout case */
506     if (in_mask == out_mask)
507       return;
508
509     /* if output layout is fixed already and looks sane, we're done */
510     if (n_bits_set (out_mask) == out_chans)
511       return;
512
513     if (n_bits_set (out_mask) < in_chans) {
514       /* Not much we can do here, this shouldn't just happen */
515       g_warning ("%s: Invalid downstream channel-mask with too few bits set",
516           GST_ELEMENT_NAME (base));
517     } else {
518       guint64 intersection;
519
520       /* if the output layout is not fixed, check if the output layout contains
521        * the input layout */
522       intersection = in_mask & out_mask;
523       if (n_bits_set (intersection) >= in_chans) {
524         gst_structure_set (outs, "channel-mask", GST_TYPE_BITMASK, in_mask,
525             NULL);
526         return;
527       }
528
529       /* output layout is not fixed and does not contain the input layout, so
530        * just pick the first possibility */
531       intersection = find_suitable_mask (out_mask, out_chans);
532       if (intersection) {
533         gst_structure_set (outs, "channel-mask", GST_TYPE_BITMASK, intersection,
534             NULL);
535         return;
536       }
537     }
538
539     /* ... else fall back to default layout (NB: out_layout is NULL here) */
540     GST_WARNING_OBJECT (base, "unexpected output channel layout");
541   } else {
542     guint64 intersection;
543
544     /* number of input channels != number of output channels:
545      * if this value contains a list of channel layouts (or even worse: a list
546      * with another list), just pick the first value and repeat until we find a
547      * channel position array or something else that's not a list; we assume
548      * the input if half-way sane and don't try to fall back on other list items
549      * if the first one is something unexpected or non-channel-pos-array-y */
550     if (n_bits_set (out_mask) >= out_chans) {
551       intersection = find_suitable_mask (out_mask, out_chans);
552       gst_structure_set (outs, "channel-mask", GST_TYPE_BITMASK, intersection,
553           NULL);
554       return;
555     }
556
557     /* what now?! Just ignore what we're given and use default positions */
558     GST_WARNING_OBJECT (base, "invalid or unexpected channel-positions");
559   }
560
561   /* missing or invalid output layout and we can't use the input layout for
562    * one reason or another, so just pick a default layout (we could be smarter
563    * and try to add/remove channels from the input layout, or pick a default
564    * layout based on LFE-presence in input layout, but let's save that for
565    * another day) */
566   if (out_chans > 0 && out_chans <= G_N_ELEMENTS (default_positions[0])) {
567     gint i;
568
569     GST_DEBUG_OBJECT (base, "using default channel layout as fallback");
570
571     out_mask = 0;
572     for (i = 0; i < out_chans; i++)
573       out_mask |= default_positions[out_chans - 1][i];
574
575     gst_structure_set (outs, "channel-mask", GST_TYPE_BITMASK, out_mask, NULL);
576   } else {
577     GST_ERROR_OBJECT (base, "Have no default layout for %d channels",
578         out_chans);
579   }
580 }
581
582 /* try to keep as many of the structure members the same by fixating the
583  * possible ranges; this way we convert the least amount of things as possible
584  */
585 static void
586 gst_audio_convert_fixate_caps (GstBaseTransform * base,
587     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
588 {
589   GstStructure *ins, *outs;
590   gint rate;
591   const gchar *fmt;
592
593   g_return_if_fail (gst_caps_is_fixed (caps));
594
595   GST_DEBUG_OBJECT (base, "trying to fixate othercaps %" GST_PTR_FORMAT
596       " based on caps %" GST_PTR_FORMAT, othercaps, caps);
597
598   ins = gst_caps_get_structure (caps, 0);
599   outs = gst_caps_get_structure (othercaps, 0);
600
601   gst_audio_convert_fixate_channels (base, ins, outs);
602
603   if ((fmt = gst_structure_get_string (ins, "format"))) {
604     /* FIXME, find the best format */
605     gst_structure_fixate_field_string (outs, "format", fmt);
606   }
607
608   if (gst_structure_get_int (ins, "rate", &rate)) {
609     if (gst_structure_has_field (outs, "rate")) {
610       gst_structure_fixate_field_nearest_int (outs, "rate", rate);
611     }
612   }
613
614   gst_caps_truncate (othercaps);
615   GST_DEBUG_OBJECT (base, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
616 }
617
618 static gboolean
619 gst_audio_convert_set_caps (GstBaseTransform * base, GstCaps * incaps,
620     GstCaps * outcaps)
621 {
622   GstAudioConvert *this = GST_AUDIO_CONVERT (base);
623   GstAudioInfo in_info;
624   GstAudioInfo out_info;
625
626   GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %"
627       GST_PTR_FORMAT, incaps, outcaps);
628
629   if (!gst_audio_info_from_caps (&in_info, incaps))
630     goto invalid_in;
631   if (!gst_audio_info_from_caps (&out_info, outcaps))
632     goto invalid_out;
633
634   if (!audio_convert_prepare_context (&this->ctx, &in_info, &out_info,
635           this->dither, this->ns))
636     goto no_converter;
637
638   return TRUE;
639
640   /* ERRORS */
641 invalid_in:
642   {
643     GST_ERROR_OBJECT (base, "invalid input caps");
644     return FALSE;
645   }
646 invalid_out:
647   {
648     GST_ERROR_OBJECT (base, "invalid output caps");
649     return FALSE;
650   }
651 no_converter:
652   {
653     GST_ERROR_OBJECT (base, "could not find converter");
654     return FALSE;
655   }
656 }
657
658 static GstFlowReturn
659 gst_audio_convert_transform_ip (GstBaseTransform * base, GstBuffer * buf)
660 {
661   /* nothing to do here */
662   return GST_FLOW_OK;
663 }
664
665 static GstFlowReturn
666 gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
667     GstBuffer * outbuf)
668 {
669   GstFlowReturn ret;
670   GstAudioConvert *this = GST_AUDIO_CONVERT (base);
671   gsize srcsize, dstsize;
672   gint insize, outsize;
673   gint samples;
674   gpointer src, dst;
675
676   /* get amount of samples to convert. */
677   samples = gst_buffer_get_size (inbuf) / this->ctx.in.bpf;
678
679   /* get in/output sizes, to see if the buffers we got are of correct
680    * sizes */
681   if (!audio_convert_get_sizes (&this->ctx, samples, &insize, &outsize))
682     goto error;
683
684   if (insize == 0 || outsize == 0)
685     return GST_FLOW_OK;
686
687   /* get src and dst data */
688   src = gst_buffer_map (inbuf, &srcsize, NULL, GST_MAP_READ);
689   dst = gst_buffer_map (outbuf, &dstsize, NULL, GST_MAP_WRITE);
690
691   /* check in and outsize */
692   if (srcsize < insize)
693     goto wrong_size;
694   if (dstsize < outsize)
695     goto wrong_size;
696
697   /* and convert the samples */
698   if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) {
699     if (!audio_convert_convert (&this->ctx, src, dst,
700             samples, gst_buffer_is_writable (inbuf)))
701       goto convert_error;
702   } else {
703     /* Create silence buffer */
704     gst_audio_format_fill_silence (this->ctx.out.finfo, dst, outsize);
705   }
706   ret = GST_FLOW_OK;
707
708 done:
709   gst_buffer_unmap (outbuf, dst, outsize);
710   gst_buffer_unmap (inbuf, src, srcsize);
711
712   return ret;
713
714   /* ERRORS */
715 error:
716   {
717     GST_ELEMENT_ERROR (this, STREAM, FORMAT,
718         (NULL), ("cannot get input/output sizes for %d samples", samples));
719     return GST_FLOW_ERROR;
720   }
721 wrong_size:
722   {
723     GST_ELEMENT_ERROR (this, STREAM, FORMAT,
724         (NULL),
725         ("input/output buffers are of wrong size in: %" G_GSIZE_FORMAT " < %d"
726             " or out: %" G_GSIZE_FORMAT " < %d",
727             srcsize, insize, dstsize, outsize));
728     ret = GST_FLOW_ERROR;
729     goto done;
730   }
731 convert_error:
732   {
733     GST_ELEMENT_ERROR (this, STREAM, FORMAT,
734         (NULL), ("error while converting"));
735     ret = GST_FLOW_ERROR;
736     goto done;
737   }
738 }
739
740 static void
741 gst_audio_convert_set_property (GObject * object, guint prop_id,
742     const GValue * value, GParamSpec * pspec)
743 {
744   GstAudioConvert *this = GST_AUDIO_CONVERT (object);
745
746   switch (prop_id) {
747     case ARG_DITHERING:
748       this->dither = g_value_get_enum (value);
749       break;
750     case ARG_NOISE_SHAPING:
751       this->ns = g_value_get_enum (value);
752       break;
753     default:
754       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
755       break;
756   }
757 }
758
759 static void
760 gst_audio_convert_get_property (GObject * object, guint prop_id,
761     GValue * value, GParamSpec * pspec)
762 {
763   GstAudioConvert *this = GST_AUDIO_CONVERT (object);
764
765   switch (prop_id) {
766     case ARG_DITHERING:
767       g_value_set_enum (value, this->dither);
768       break;
769     case ARG_NOISE_SHAPING:
770       g_value_set_enum (value, this->ns);
771       break;
772     default:
773       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
774       break;
775   }
776 }