fix int and float audio caps in auparse, partially fixes bug #142812
[platform/upstream/gst-plugins-good.git] / gst / auparse / gstauparse.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 /* Element-Checklist-Version: 5 */
20
21 /* 2001/04/03 - Updated parseau to use caps nego
22  *              Zaheer Abbas Merali <zaheerabbas at merali dot org>
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "gstauparse.h"
32 #include <gst/audio/audio.h>
33
34 /* elementfactory information */
35 static GstElementDetails gst_auparse_details =
36 GST_ELEMENT_DETAILS (".au parser",
37     "Codec/Demuxer/Audio",
38     "Parse an .au file into raw audio",
39     "Erik Walthinsen <omega@cse.ogi.edu>");
40
41 static GstStaticPadTemplate gst_auparse_sink_template =
42 GST_STATIC_PAD_TEMPLATE ("sink",
43     GST_PAD_SINK,
44     GST_PAD_ALWAYS,
45     GST_STATIC_CAPS ("audio/x-au")
46     );
47
48 static GstStaticPadTemplate gst_auparse_src_template =
49     GST_STATIC_PAD_TEMPLATE ("src",
50     GST_PAD_SRC,
51     GST_PAD_SOMETIMES,          /* FIXME: spider */
52     GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
53         /* we don't use GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS
54            because of min buffer-frames which is 1, not 0 */
55         "audio/x-raw-float, "
56         "rate = (int) [ 1, MAX ], "
57         "channels = (int) [ 1, MAX ], "
58         "endianness = (int) { LITTLE_ENDIAN , BIG_ENDIAN }, "
59         "width = (int) { 32, 64 }, "
60         "buffer-frames = (int) [ 0, MAX]" "; "
61         "audio/x-alaw, "
62         "rate = (int) [ 8000, 192000 ], "
63         "channels = (int) [ 1, 2 ]" "; "
64         "audio/x-mulaw, "
65         "rate = (int) [ 8000, 192000 ], " "channels = (int) [ 1, 2 ]" "; "
66         /* Nothing to decode those ADPCM streams for now */
67         "audio/x-adpcm, " "layout = (string) { g721, g722, g723_3, g723_5 }")
68     );
69
70 /* AuParse signals and args */
71 enum
72 {
73   /* FILL ME */
74   LAST_SIGNAL
75 };
76
77 enum
78 {
79   ARG_0
80       /* FILL ME */
81 };
82
83 static void gst_auparse_base_init (gpointer g_class);
84 static void gst_auparse_class_init (GstAuParseClass * klass);
85 static void gst_auparse_init (GstAuParse * auparse);
86
87 static void gst_auparse_chain (GstPad * pad, GstData * _data);
88
89 static GstElementStateReturn gst_auparse_change_state (GstElement * element);
90
91 static GstElementClass *parent_class = NULL;
92
93 /*static guint gst_auparse_signals[LAST_SIGNAL] = { 0 }; */
94
95 GType
96 gst_auparse_get_type (void)
97 {
98   static GType auparse_type = 0;
99
100   if (!auparse_type) {
101     static const GTypeInfo auparse_info = {
102       sizeof (GstAuParseClass),
103       gst_auparse_base_init,
104       NULL,
105       (GClassInitFunc) gst_auparse_class_init,
106       NULL,
107       NULL,
108       sizeof (GstAuParse),
109       0,
110       (GInstanceInitFunc) gst_auparse_init,
111     };
112
113     auparse_type =
114         g_type_register_static (GST_TYPE_ELEMENT, "GstAuParse", &auparse_info,
115         0);
116   }
117   return auparse_type;
118 }
119
120 static void
121 gst_auparse_base_init (gpointer g_class)
122 {
123   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
124
125   gst_element_class_add_pad_template (element_class,
126       gst_static_pad_template_get (&gst_auparse_sink_template));
127   gst_element_class_add_pad_template (element_class,
128       gst_static_pad_template_get (&gst_auparse_src_template));
129   gst_element_class_set_details (element_class, &gst_auparse_details);
130
131 }
132
133 static void
134 gst_auparse_class_init (GstAuParseClass * klass)
135 {
136   GstElementClass *gstelement_class;
137
138   gstelement_class = (GstElementClass *) klass;
139
140   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
141
142   gstelement_class->change_state = gst_auparse_change_state;
143 }
144
145 static void
146 gst_auparse_init (GstAuParse * auparse)
147 {
148   auparse->sinkpad =
149       gst_pad_new_from_template (gst_static_pad_template_get
150       (&gst_auparse_sink_template), "sink");
151   gst_element_add_pad (GST_ELEMENT (auparse), auparse->sinkpad);
152   gst_pad_set_chain_function (auparse->sinkpad, gst_auparse_chain);
153
154   auparse->srcpad = NULL;
155 #if 0                           /* FIXME: spider */
156   gst_pad_new_from_template (gst_static_pad_template_get
157       (&gst_auparse_src_template), "src");
158   gst_element_add_pad (GST_ELEMENT (auparse), auparse->srcpad);
159   gst_pad_use_explicit_caps (auparse->srcpad);
160 #endif
161
162   auparse->offset = 0;
163   auparse->size = 0;
164   auparse->encoding = 0;
165   auparse->frequency = 0;
166   auparse->channels = 0;
167 }
168
169 static void
170 gst_auparse_chain (GstPad * pad, GstData * _data)
171 {
172   GstBuffer *buf = GST_BUFFER (_data);
173   GstAuParse *auparse;
174   gchar *data;
175   glong size;
176   GstCaps *tempcaps;
177   gint law = 0, depth = 0, ieee = 0;
178   gchar layout[7];
179
180   layout[0] = 0;
181
182   g_return_if_fail (pad != NULL);
183   g_return_if_fail (GST_IS_PAD (pad));
184   g_return_if_fail (buf != NULL);
185
186   auparse = GST_AUPARSE (gst_pad_get_parent (pad));
187
188   GST_DEBUG ("gst_auparse_chain: got buffer in '%s'",
189       gst_element_get_name (GST_ELEMENT (auparse)));
190
191   data = GST_BUFFER_DATA (buf);
192   size = GST_BUFFER_SIZE (buf);
193
194   /* if we haven't seen any data yet... */
195   if (auparse->size == 0) {
196     GstBuffer *newbuf;
197     guint32 *head = (guint32 *) data;
198
199     /* normal format is big endian (au is a Sparc format) */
200     if (GST_READ_UINT32_BE (head) == 0x2e736e64) {      /* ".snd" */
201       head++;
202       auparse->le = 0;
203       auparse->offset = GST_READ_UINT32_BE (head);
204       head++;
205       /* Do not trust size, could be set to -1 : unknown */
206       auparse->size = GST_READ_UINT32_BE (head);
207       head++;
208       auparse->encoding = GST_READ_UINT32_BE (head);
209       head++;
210       auparse->frequency = GST_READ_UINT32_BE (head);
211       head++;
212       auparse->channels = GST_READ_UINT32_BE (head);
213       head++;
214
215       /* and of course, someone had to invent a little endian
216        * version.  Used by DEC systems. */
217     } else if (GST_READ_UINT32_LE (head) == 0x0064732E) {       /* other source say it is "dns." */
218       head++;
219       auparse->le = 1;
220       auparse->offset = GST_READ_UINT32_LE (head);
221       head++;
222       /* Do not trust size, could be set to -1 : unknown */
223       auparse->size = GST_READ_UINT32_LE (head);
224       head++;
225       auparse->encoding = GST_READ_UINT32_LE (head);
226       head++;
227       auparse->frequency = GST_READ_UINT32_LE (head);
228       head++;
229       auparse->channels = GST_READ_UINT32_LE (head);
230       head++;
231
232     } else {
233       GST_ELEMENT_ERROR (auparse, STREAM, WRONG_TYPE, (NULL), (NULL));
234       gst_buffer_unref (buf);
235       return;
236     }
237
238     GST_DEBUG
239         ("offset %ld, size %ld, encoding %ld, frequency %ld, channels %ld",
240         auparse->offset, auparse->size, auparse->encoding, auparse->frequency,
241         auparse->channels);
242
243 /*
244 Docs :
245         http://www.opengroup.org/public/pubs/external/auformat.html
246         http://astronomy.swin.edu.au/~pbourke/dataformats/au/
247         Solaris headers : /usr/include/audio/au.h
248         libsndfile : src/au.c
249 Samples :
250         http://www.tsp.ece.mcgill.ca/MMSP/Documents/AudioFormats/AU/Samples.html
251 */
252
253     switch (auparse->encoding) {
254
255       case 1:                  /* 8-bit ISDN mu-law G.711 */
256         law = 1;
257         depth = 8;
258         break;
259       case 27:                 /* 8-bit ISDN  A-law G.711 */
260         law = 2;
261         depth = 8;
262         break;
263
264       case 2:                  /*  8-bit linear PCM */
265         depth = 8;
266         break;
267       case 3:                  /* 16-bit linear PCM */
268         depth = 16;
269         break;
270       case 4:                  /* 24-bit linear PCM */
271         depth = 24;
272         break;
273       case 5:                  /* 32-bit linear PCM */
274         depth = 32;
275         break;
276
277       case 6:                  /* 32-bit IEEE floating point */
278         ieee = 1;
279         depth = 32;
280         break;
281       case 7:                  /* 64-bit IEEE floating point */
282         ieee = 1;
283         depth = 64;
284         break;
285
286       case 23:                 /* 4-bit CCITT G.721   ADPCM 32kbps -> modplug/libsndfile (compressed 8-bit mu-law) */
287         strcpy (layout, "g721");
288         break;
289       case 24:                 /* 8-bit CCITT G.722   ADPCM        -> rtp */
290         strcpy (layout, "g722");
291         break;
292       case 25:                 /* 3-bit CCITT G.723.3 ADPCM 24kbps -> rtp/xine/modplug/libsndfile */
293         strcpy (layout, "g723_3");
294         break;
295       case 26:                 /* 5-bit CCITT G.723.5 ADPCM 40kbps -> rtp/xine/modplug/libsndfile */
296         strcpy (layout, "g723_5");
297         break;
298
299       case 8:                  /* Fragmented sample data */
300       case 9:                  /* AU_ENCODING_NESTED */
301
302       case 10:                 /* DSP program */
303       case 11:                 /* DSP  8-bit fixed point */
304       case 12:                 /* DSP 16-bit fixed point */
305       case 13:                 /* DSP 24-bit fixed point */
306       case 14:                 /* DSP 32-bit fixed point */
307
308       case 16:                 /* AU_ENCODING_DISPLAY : non-audio display data */
309       case 17:                 /* AU_ENCODING_MULAW_SQUELCH */
310
311       case 18:                 /* 16-bit linear with emphasis */
312       case 19:                 /* 16-bit linear compressed (NeXT) */
313       case 20:                 /* 16-bit linear with emphasis and compression */
314
315       case 21:                 /* Music kit DSP commands */
316       case 22:                 /* Music kit DSP commands samples */
317
318       default:
319         GST_ELEMENT_ERROR (auparse, STREAM, FORMAT, (NULL), (NULL));
320         gst_buffer_unref (buf);
321         return;
322     }
323
324     auparse->srcpad =
325         gst_pad_new_from_template (gst_static_pad_template_get
326         (&gst_auparse_src_template), "src");
327     gst_pad_use_explicit_caps (auparse->srcpad);
328
329     if (law) {
330       tempcaps =
331           gst_caps_new_simple ((law == 1) ? "audio/x-mulaw" : "audio/x-alaw",
332           "rate", G_TYPE_INT, auparse->frequency,
333           "channels", G_TYPE_INT, auparse->channels, NULL);
334     } else if (ieee) {
335       tempcaps = gst_caps_new_simple ("audio/x-raw-float",
336           "rate", G_TYPE_INT, auparse->frequency,
337           "channels", G_TYPE_INT, auparse->channels,
338           "endianness", G_TYPE_INT,
339           auparse->le ? G_LITTLE_ENDIAN : G_BIG_ENDIAN, "width", G_TYPE_INT,
340           depth, "buffer-frames", G_TYPE_INT, 0, NULL);
341     } else if (layout[0]) {
342       tempcaps = gst_caps_new_simple ("audio/x-adpcm",
343           "layout", G_TYPE_STRING, layout, NULL);
344     } else {
345       tempcaps = gst_caps_new_simple ("audio/x-raw-int",
346           "rate", G_TYPE_INT, auparse->frequency,
347           "channels", G_TYPE_INT, auparse->channels,
348           "endianness", G_TYPE_INT,
349           auparse->le ? G_LITTLE_ENDIAN : G_BIG_ENDIAN, "depth", G_TYPE_INT,
350           depth, "width", G_TYPE_INT, depth, "signed", G_TYPE_BOOLEAN, TRUE,
351           NULL);
352     }
353
354     if (!gst_pad_set_explicit_caps (auparse->srcpad, tempcaps)) {
355       GST_ELEMENT_ERROR (auparse, CORE, NEGOTIATION, (NULL), (NULL));
356       gst_buffer_unref (buf);
357       gst_object_unref (GST_OBJECT (auparse->srcpad));
358       auparse->srcpad = NULL;
359       return;
360     }
361
362     gst_element_add_pad (GST_ELEMENT (auparse), auparse->srcpad);
363
364     newbuf = gst_buffer_new ();
365     GST_BUFFER_DATA (newbuf) = (gpointer) malloc (size - (auparse->offset));
366     memcpy (GST_BUFFER_DATA (newbuf), data + (auparse->offset),
367         size - (auparse->offset));
368     GST_BUFFER_SIZE (newbuf) = size - (auparse->offset);
369
370     gst_buffer_unref (buf);
371
372     gst_pad_push (auparse->srcpad, GST_DATA (newbuf));
373     return;
374   }
375
376   gst_pad_push (auparse->srcpad, GST_DATA (buf));
377 }
378
379 static GstElementStateReturn
380 gst_auparse_change_state (GstElement * element)
381 {
382   GstAuParse *auparse = GST_AUPARSE (element);
383
384   switch (GST_STATE_TRANSITION (element)) {
385     case GST_STATE_PAUSED_TO_READY:
386       if (auparse->srcpad) {
387         gst_element_remove_pad (element, auparse->srcpad);
388         auparse->srcpad = NULL;
389       }
390       break;
391     default:
392       break;
393   }
394
395   if (parent_class->change_state)
396     return parent_class->change_state (element);
397
398   return GST_STATE_SUCCESS;
399 }
400
401 static gboolean
402 plugin_init (GstPlugin * plugin)
403 {
404   if (!gst_element_register (plugin, "auparse", GST_RANK_SECONDARY,
405           GST_TYPE_AUPARSE)) {
406     return FALSE;
407   }
408
409   return TRUE;
410 }
411
412 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
413     GST_VERSION_MINOR,
414     "auparse",
415     "parses au streams", plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN)