Update for alloc_buffer changes.
[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 enum
71 {
72   ARG_0
73       /* FILL ME */
74 };
75
76 static void gst_auparse_base_init (gpointer g_class);
77 static void gst_auparse_class_init (GstAuParseClass * klass);
78 static void gst_auparse_init (GstAuParse * auparse);
79 static void gst_auparse_dispose (GObject * object);
80
81 static GstFlowReturn gst_auparse_chain (GstPad * pad, GstBuffer * buf);
82
83 static GstStateChangeReturn gst_auparse_change_state (GstElement * element,
84     GstStateChange transition);
85
86 static GstElementClass *parent_class = NULL;
87
88
89 /*static guint gst_auparse_signals[LAST_SIGNAL] = { 0 }; */
90
91 GType
92 gst_auparse_get_type (void)
93 {
94   static GType auparse_type = 0;
95
96   if (!auparse_type) {
97     static const GTypeInfo auparse_info = {
98       sizeof (GstAuParseClass),
99       gst_auparse_base_init,
100       NULL,
101       (GClassInitFunc) gst_auparse_class_init,
102       NULL,
103       NULL,
104       sizeof (GstAuParse),
105       0,
106       (GInstanceInitFunc) gst_auparse_init,
107     };
108
109     auparse_type =
110         g_type_register_static (GST_TYPE_ELEMENT, "GstAuParse", &auparse_info,
111         0);
112   }
113   return auparse_type;
114 }
115
116 static void
117 gst_auparse_base_init (gpointer g_class)
118 {
119   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
120
121   gst_element_class_add_pad_template (element_class,
122       gst_static_pad_template_get (&gst_auparse_sink_template));
123   gst_element_class_add_pad_template (element_class,
124       gst_static_pad_template_get (&gst_auparse_src_template));
125   gst_element_class_set_details (element_class, &gst_auparse_details);
126
127 }
128
129 static void
130 gst_auparse_class_init (GstAuParseClass * klass)
131 {
132   GObjectClass *gobject_class;
133   GstElementClass *gstelement_class;
134
135   gobject_class = (GObjectClass *) klass;
136   gstelement_class = (GstElementClass *) klass;
137
138   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
139
140   gobject_class->dispose = gst_auparse_dispose;
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 = gst_pad_new_from_template (gst_static_pad_template_get
155       (&gst_auparse_src_template), "src");
156   gst_pad_use_fixed_caps (auparse->srcpad);
157   gst_element_add_pad (GST_ELEMENT (auparse), auparse->srcpad);
158
159   auparse->offset = 0;
160   auparse->buffer_offset = 0;
161   auparse->adapter = gst_adapter_new ();
162   auparse->size = 0;
163   auparse->encoding = 0;
164   auparse->frequency = 0;
165   auparse->channels = 0;
166 }
167
168 static void
169 gst_auparse_dispose (GObject * object)
170 {
171   GstAuParse *au = GST_AUPARSE (object);
172
173   if (au->adapter != NULL) {
174     gst_object_unref (au->adapter);
175     au->adapter = NULL;
176   }
177   G_OBJECT_CLASS (parent_class)->dispose (object);
178 }
179
180 static GstFlowReturn
181 gst_auparse_chain (GstPad * pad, GstBuffer * buf)
182 {
183   GstFlowReturn ret;
184   GstAuParse *auparse;
185   guchar *data;
186   glong size;
187   GstCaps *tempcaps;
188   gint law = 0, depth = 0, ieee = 0;
189   gchar layout[7];
190   GstBuffer *subbuf;
191   GstEvent *event;
192
193   layout[0] = 0;
194
195   auparse = GST_AUPARSE (gst_pad_get_parent (pad));
196
197   GST_DEBUG ("gst_auparse_chain: got buffer in '%s'",
198       gst_element_get_name (GST_ELEMENT (auparse)));
199
200   data = GST_BUFFER_DATA (buf);
201   size = GST_BUFFER_SIZE (buf);
202
203   /* if we haven't seen any data yet... */
204   if (auparse->size == 0) {
205     guint32 *head = (guint32 *) data;
206
207     /* normal format is big endian (au is a Sparc format) */
208     if (GST_READ_UINT32_BE (head) == 0x2e736e64) {      /* ".snd" */
209       head++;
210       auparse->le = 0;
211       auparse->offset = GST_READ_UINT32_BE (head);
212       head++;
213       /* Do not trust size, could be set to -1 : unknown */
214       auparse->size = GST_READ_UINT32_BE (head);
215       head++;
216       auparse->encoding = GST_READ_UINT32_BE (head);
217       head++;
218       auparse->frequency = GST_READ_UINT32_BE (head);
219       head++;
220       auparse->channels = GST_READ_UINT32_BE (head);
221       head++;
222
223       /* and of course, someone had to invent a little endian
224        * version.  Used by DEC systems. */
225     } else if (GST_READ_UINT32_LE (head) == 0x0064732E) {       /* other source say it is "dns." */
226       head++;
227       auparse->le = 1;
228       auparse->offset = GST_READ_UINT32_LE (head);
229       head++;
230       /* Do not trust size, could be set to -1 : unknown */
231       auparse->size = GST_READ_UINT32_LE (head);
232       head++;
233       auparse->encoding = GST_READ_UINT32_LE (head);
234       head++;
235       auparse->frequency = GST_READ_UINT32_LE (head);
236       head++;
237       auparse->channels = GST_READ_UINT32_LE (head);
238       head++;
239
240     } else {
241       GST_ELEMENT_ERROR (auparse, STREAM, WRONG_TYPE, (NULL), (NULL));
242       gst_buffer_unref (buf);
243       g_object_unref (auparse);
244       return GST_FLOW_ERROR;
245     }
246
247     GST_DEBUG
248         ("offset %ld, size %ld, encoding %ld, frequency %ld, channels %ld\n",
249         auparse->offset, auparse->size, auparse->encoding, auparse->frequency,
250         auparse->channels);
251
252 /*
253 Docs :
254         http://www.opengroup.org/public/pubs/external/auformat.html
255         http://astronomy.swin.edu.au/~pbourke/dataformats/au/
256         Solaris headers : /usr/include/audio/au.h
257         libsndfile : src/au.c
258 Samples :
259         http://www.tsp.ece.mcgill.ca/MMSP/Documents/AudioFormats/AU/Samples.html
260 */
261
262     switch (auparse->encoding) {
263
264       case 1:                  /* 8-bit ISDN mu-law G.711 */
265         law = 1;
266         depth = 8;
267         break;
268       case 27:                 /* 8-bit ISDN  A-law G.711 */
269         law = 2;
270         depth = 8;
271         break;
272
273       case 2:                  /*  8-bit linear PCM */
274         depth = 8;
275         break;
276       case 3:                  /* 16-bit linear PCM */
277         depth = 16;
278         break;
279       case 4:                  /* 24-bit linear PCM */
280         depth = 24;
281         break;
282       case 5:                  /* 32-bit linear PCM */
283         depth = 32;
284         break;
285
286       case 6:                  /* 32-bit IEEE floating point */
287         ieee = 1;
288         depth = 32;
289         break;
290       case 7:                  /* 64-bit IEEE floating point */
291         ieee = 1;
292         depth = 64;
293         break;
294
295       case 23:                 /* 4-bit CCITT G.721   ADPCM 32kbps -> modplug/libsndfile (compressed 8-bit mu-law) */
296         strcpy (layout, "g721");
297         break;
298       case 24:                 /* 8-bit CCITT G.722   ADPCM        -> rtp */
299         strcpy (layout, "g722");
300         break;
301       case 25:                 /* 3-bit CCITT G.723.3 ADPCM 24kbps -> rtp/xine/modplug/libsndfile */
302         strcpy (layout, "g723_3");
303         break;
304       case 26:                 /* 5-bit CCITT G.723.5 ADPCM 40kbps -> rtp/xine/modplug/libsndfile */
305         strcpy (layout, "g723_5");
306         break;
307
308       case 8:                  /* Fragmented sample data */
309       case 9:                  /* AU_ENCODING_NESTED */
310
311       case 10:                 /* DSP program */
312       case 11:                 /* DSP  8-bit fixed point */
313       case 12:                 /* DSP 16-bit fixed point */
314       case 13:                 /* DSP 24-bit fixed point */
315       case 14:                 /* DSP 32-bit fixed point */
316
317       case 16:                 /* AU_ENCODING_DISPLAY : non-audio display data */
318       case 17:                 /* AU_ENCODING_MULAW_SQUELCH */
319
320       case 18:                 /* 16-bit linear with emphasis */
321       case 19:                 /* 16-bit linear compressed (NeXT) */
322       case 20:                 /* 16-bit linear with emphasis and compression */
323
324       case 21:                 /* Music kit DSP commands */
325       case 22:                 /* Music kit DSP commands samples */
326
327       default:
328         GST_ELEMENT_ERROR (auparse, STREAM, FORMAT, (NULL), (NULL));
329         gst_buffer_unref (buf);
330         g_object_unref (auparse);
331         return GST_FLOW_ERROR;
332     }
333
334     if (law) {
335       tempcaps =
336           gst_caps_new_simple ((law == 1) ? "audio/x-mulaw" : "audio/x-alaw",
337           "rate", G_TYPE_INT, auparse->frequency,
338           "channels", G_TYPE_INT, auparse->channels, NULL);
339       auparse->sample_size = auparse->channels;
340     } else if (ieee) {
341       tempcaps = gst_caps_new_simple ("audio/x-raw-float",
342           "rate", G_TYPE_INT, auparse->frequency,
343           "channels", G_TYPE_INT, auparse->channels,
344           "endianness", G_TYPE_INT,
345           auparse->le ? G_LITTLE_ENDIAN : G_BIG_ENDIAN,
346           "width", G_TYPE_INT, depth, NULL);
347       auparse->sample_size = auparse->channels * depth / 8;
348     } else if (layout[0]) {
349       tempcaps = gst_caps_new_simple ("audio/x-adpcm",
350           "layout", G_TYPE_STRING, layout, NULL);
351       auparse->sample_size = 0;
352     } else {
353       tempcaps = gst_caps_new_simple ("audio/x-raw-int",
354           "rate", G_TYPE_INT, auparse->frequency,
355           "channels", G_TYPE_INT, auparse->channels,
356           "endianness", G_TYPE_INT,
357           auparse->le ? G_LITTLE_ENDIAN : G_BIG_ENDIAN, "depth", G_TYPE_INT,
358           depth, "width", G_TYPE_INT, depth, "signed", G_TYPE_BOOLEAN, TRUE,
359           NULL);
360       auparse->sample_size = auparse->channels * depth / 8;
361     }
362
363     gst_pad_set_active (auparse->srcpad, TRUE);
364     gst_pad_set_caps (auparse->srcpad, tempcaps);
365
366     event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_DEFAULT,
367         0, GST_CLOCK_TIME_NONE, 0);
368
369     gst_pad_push_event (auparse->srcpad, event);
370
371     subbuf = gst_buffer_create_sub (buf, auparse->offset,
372         size - auparse->offset);
373
374     gst_buffer_unref (buf);
375
376     gst_adapter_push (auparse->adapter, subbuf);
377   } else {
378     gst_adapter_push (auparse->adapter, buf);
379   }
380
381   if (auparse->sample_size) {
382     /* Ensure we push a buffer that's a multiple of the frame size downstream */
383     int avail = gst_adapter_available (auparse->adapter);
384
385     avail -= avail % auparse->sample_size;
386
387     if (avail > 0) {
388       const guint8 *data = gst_adapter_peek (auparse->adapter, avail);
389       GstBuffer *newbuf;
390
391       if ((ret =
392               gst_pad_alloc_buffer_and_set_caps (auparse->srcpad,
393                   auparse->buffer_offset, avail, GST_PAD_CAPS (auparse->srcpad),
394                   &newbuf)) == GST_FLOW_OK) {
395
396         memcpy (GST_BUFFER_DATA (newbuf), data, avail);
397         gst_adapter_flush (auparse->adapter, avail);
398
399         auparse->buffer_offset += avail;
400
401         ret = gst_pad_push (auparse->srcpad, newbuf);
402       }
403     } else
404       ret = GST_FLOW_OK;
405   } else {
406     /* It's something non-trivial (such as ADPCM), we don't understand it, so
407      * just push downstream and assume this will know what to do with it */
408     ret = gst_pad_push (auparse->srcpad, buf);
409   }
410
411   g_object_unref (auparse);
412
413   return ret;
414 }
415
416 static GstStateChangeReturn
417 gst_auparse_change_state (GstElement * element, GstStateChange transition)
418 {
419   GstAuParse *auparse = GST_AUPARSE (element);
420   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
421
422   if (parent_class->change_state)
423     ret = parent_class->change_state (element, transition);
424
425   switch (transition) {
426     case GST_STATE_CHANGE_READY_TO_NULL:
427       gst_adapter_clear (auparse->adapter);
428       auparse->buffer_offset = 0;
429       auparse->offset = 0;
430       auparse->size = 0;
431       auparse->encoding = 0;
432       auparse->frequency = 0;
433       auparse->channels = 0;
434     default:
435       break;
436   }
437
438   return ret;
439 }
440
441 static gboolean
442 plugin_init (GstPlugin * plugin)
443 {
444   if (!gst_element_register (plugin, "auparse", GST_RANK_SECONDARY,
445           GST_TYPE_AUPARSE)) {
446     return FALSE;
447   }
448
449   return TRUE;
450 }
451
452 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
453     GST_VERSION_MINOR,
454     "auparse",
455     "parses au streams", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
456     GST_PACKAGE_ORIGIN)