4ad1958fc8075a7b212b121903d6c130cd5b45d7
[asterisk/asterisk.git] / formats / format_au.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Work with Sun Microsystems AU format.
5  * 
6  * Copyright (C) 2005, Andriy Pylypenko
7  * Code based on format_wav.c by Mark Spencer
8  *
9  * This program is free software, distributed under the terms of
10  * the GNU General Public License
11  */
12  
13 #include <stdlib.h>
14 #include <sys/time.h>
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <errno.h>
18 #include <string.h>
19 #ifdef __linux__
20 #include <endian.h>
21 #else
22 #include <machine/endian.h>
23 #endif
24 #include "asterisk/lock.h"
25 #include "asterisk/channel.h"
26 #include "asterisk/file.h"
27 #include "asterisk/logger.h"
28 #include "asterisk/sched.h"
29 #include "asterisk/module.h"
30
31 #define BUF_SIZE                160
32
33 #define AU_HEADER_SIZE          24
34 #define AU_HEADER(var)          u_int32_t var[6]
35
36 #define AU_HDR_MAGIC_OFF        0
37 #define AU_HDR_HDR_SIZE_OFF     1
38 #define AU_HDR_DATA_SIZE_OFF    2
39 #define AU_HDR_ENCODING_OFF     3
40 #define AU_HDR_SAMPLE_RATE_OFF  4
41 #define AU_HDR_CHANNELS_OFF     5
42
43 #define AU_ENC_8BIT_ULAW        1
44
45 struct ast_filestream {
46         void *reserved[AST_RESERVED_POINTERS];
47         /* This is what a filestream means to us */
48         int fd;                                 /* Descriptor */
49         struct ast_channel *owner;
50         struct ast_frame fr;                    /* Frame information */
51         char waste[AST_FRIENDLY_OFFSET];        /* Buffer for sending frames, etc */
52         char empty;                             /* Empty character */
53         short buf[BUF_SIZE];
54 };
55
56
57 AST_MUTEX_DEFINE_STATIC(au_lock);
58 static int localusecnt = 0;
59
60 static char *name = "au";
61 static char *desc = "Sun Microsystems AU format (signed linear)";
62 static char *exts = "au";
63
64
65 #define AU_MAGIC 0x2e736e64
66 #if __BYTE_ORDER == __BIG_ENDIAN
67 #define htoll(b) (b)
68 #define htols(b) (b)
69 #define ltohl(b) (b)
70 #define ltohs(b) (b)
71 #else
72 #if __BYTE_ORDER == __LITTLE_ENDIAN
73 #define htoll(b)  \
74           (((((b)      ) & 0xFF) << 24) | \
75                ((((b) >>  8) & 0xFF) << 16) | \
76                    ((((b) >> 16) & 0xFF) <<  8) | \
77                    ((((b) >> 24) & 0xFF)      ))
78 #define htols(b) \
79           (((((b)      ) & 0xFF) << 8) | \
80                    ((((b) >> 8) & 0xFF)      ))
81 #define ltohl(b) htoll(b)
82 #define ltohs(b) htols(b)
83 #else
84 #error "Endianess not defined"
85 #endif
86 #endif
87
88
89 static int check_header(int fd)
90 {
91         AU_HEADER(header);
92         u_int32_t magic;
93         u_int32_t hdr_size;
94         u_int32_t data_size;
95         u_int32_t encoding;
96         u_int32_t sample_rate;
97         u_int32_t channels;
98
99         if (read(fd, header, AU_HEADER_SIZE) != AU_HEADER_SIZE) {
100                 ast_log(LOG_WARNING, "Read failed (header)\n");
101                 return -1;
102         }
103         magic = ltohl(header[AU_HDR_MAGIC_OFF]);
104         if (magic != (u_int32_t) AU_MAGIC) {
105                 ast_log(LOG_WARNING, "Bad magic: 0x%x\n", magic);
106         }
107 /*      hdr_size = ltohl(header[AU_HDR_HDR_SIZE_OFF]);
108         if (hdr_size < AU_HEADER_SIZE)*/
109         hdr_size = AU_HEADER_SIZE;
110 /*      data_size = ltohl(header[AU_HDR_DATA_SIZE_OFF]); */
111         encoding = ltohl(header[AU_HDR_ENCODING_OFF]);
112         if (encoding != AU_ENC_8BIT_ULAW) {
113                 ast_log(LOG_WARNING, "Unexpected format: %d. Only 8bit ULAW allowed (%d)\n", encoding, AU_ENC_8BIT_ULAW);
114                 return -1;
115         }
116         sample_rate = ltohl(header[AU_HDR_SAMPLE_RATE_OFF]);
117         if (sample_rate != 8000) {
118                 ast_log(LOG_WARNING, "Sample rate can only be 8000 not %d\n", sample_rate);
119                 return -1;
120         }
121         channels = ltohl(header[AU_HDR_CHANNELS_OFF]);
122         if (channels != 1) {
123                 ast_log(LOG_WARNING, "Not in mono: channels=%d\n", channels);
124                 return -1;
125         }
126         /* Skip to data */
127         data_size = lseek(fd, 0, SEEK_END) - hdr_size;
128         if (lseek(fd, hdr_size, SEEK_SET) == -1 ) {
129                 ast_log(LOG_WARNING, "Failed to skip to data: %d\n", hdr_size);
130                 return -1;
131         }
132         return data_size;
133 }
134
135 static int update_header(int fd)
136 {
137         off_t cur, end;
138         u_int32_t datalen;
139         int bytes;
140
141         cur = lseek(fd, 0, SEEK_CUR);
142         end = lseek(fd, 0, SEEK_END);
143         /* data starts 24 bytes in */
144         bytes = end - AU_HEADER_SIZE;
145         datalen = htoll(bytes);
146
147         if (cur < 0) {
148                 ast_log(LOG_WARNING, "Unable to find our position\n");
149                 return -1;
150         }
151         if (lseek(fd, AU_HDR_DATA_SIZE_OFF * sizeof(u_int32_t), SEEK_SET) != (AU_HDR_DATA_SIZE_OFF * sizeof(u_int32_t))) {
152                 ast_log(LOG_WARNING, "Unable to set our position\n");
153                 return -1;
154         }
155         if (write(fd, &datalen, sizeof(datalen)) != sizeof(datalen)) {
156                 ast_log(LOG_WARNING, "Unable to set write file size\n");
157                 return -1;
158         }
159         if (lseek(fd, cur, SEEK_SET) != cur) {
160                 ast_log(LOG_WARNING, "Unable to return to position\n");
161                 return -1;
162         }
163         return 0;
164 }
165
166 static int write_header(int fd)
167 {
168         AU_HEADER(header);
169
170         header[AU_HDR_MAGIC_OFF] = htoll((u_int32_t) AU_MAGIC);
171         header[AU_HDR_HDR_SIZE_OFF] = htoll(AU_HEADER_SIZE);
172         header[AU_HDR_DATA_SIZE_OFF] = 0;
173         header[AU_HDR_ENCODING_OFF] = htoll(AU_ENC_8BIT_ULAW);
174         header[AU_HDR_SAMPLE_RATE_OFF] = htoll(8000);
175         header[AU_HDR_CHANNELS_OFF] = htoll(1);
176
177         /* Write an au header, ignoring sizes which will be filled in later */
178         lseek(fd, 0, SEEK_SET);
179         if (write(fd, header, AU_HEADER_SIZE) != AU_HEADER_SIZE) {
180                 ast_log(LOG_WARNING, "Unable to write header\n");
181                 return -1;
182         }
183         return 0;
184 }
185
186 static struct ast_filestream *au_open(int fd)
187 {
188         struct ast_filestream *tmp;
189
190         if (!(tmp = malloc(sizeof(struct ast_filestream)))) {
191                 ast_log(LOG_ERROR, "Out of memory\n");
192                 return NULL;
193         }
194
195         memset(tmp, 0, sizeof(struct ast_filestream));
196         if (check_header(fd) < 0) {
197                 free(tmp);
198                 return NULL;
199         }
200         if (ast_mutex_lock(&au_lock)) {
201                 ast_log(LOG_WARNING, "Unable to lock au count\n");
202                 free(tmp);
203                 return NULL;
204         }
205         tmp->fd = fd;
206         tmp->fr.data = tmp->buf;
207         tmp->fr.frametype = AST_FRAME_VOICE;
208         tmp->fr.subclass = AST_FORMAT_ULAW;
209         /* datalen will vary for each frame */
210         tmp->fr.src = name;
211         tmp->fr.mallocd = 0;
212         localusecnt++;
213         ast_mutex_unlock(&au_lock);
214         ast_update_use_count();
215         return tmp;
216 }
217
218 static struct ast_filestream *au_rewrite(int fd, const char *comment)
219 {
220         struct ast_filestream *tmp;
221
222         if ((tmp = malloc(sizeof(struct ast_filestream))) == NULL) {
223                 ast_log(LOG_ERROR, "Out of memory\n");
224                 return NULL;
225         }
226
227         memset(tmp, 0, sizeof(struct ast_filestream));
228         if (write_header(fd)) {
229                 free(tmp);
230                 return NULL;
231         }
232         if (ast_mutex_lock(&au_lock)) {
233                 ast_log(LOG_WARNING, "Unable to lock au count\n");
234                 free(tmp);
235                 return NULL;
236         }
237         tmp->fd = fd;
238         localusecnt++;
239         ast_mutex_unlock(&au_lock);
240         ast_update_use_count();
241         return tmp;
242 }
243
244 static void au_close(struct ast_filestream *s)
245 {
246         if (ast_mutex_lock(&au_lock)) {
247                 ast_log(LOG_WARNING, "Unable to lock au count\n");
248                 return;
249         }
250         localusecnt--;
251         ast_mutex_unlock(&au_lock);
252         ast_update_use_count();
253         close(s->fd);
254         free(s);
255 }
256
257 static struct ast_frame *au_read(struct ast_filestream *s, int *whennext)
258 {
259         int res;
260         int delay;
261         /* Send a frame from the file to the appropriate channel */
262
263         s->fr.frametype = AST_FRAME_VOICE;
264         s->fr.subclass = AST_FORMAT_ULAW;
265         s->fr.offset = AST_FRIENDLY_OFFSET;
266         s->fr.mallocd = 0;
267         s->fr.data = s->buf;
268         if ((res = read(s->fd, s->buf, BUF_SIZE)) < 1) {
269                 if (res)
270                         ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
271                 return NULL;
272         }
273         s->fr.samples = res;
274         s->fr.datalen = res;
275         delay = s->fr.samples;
276         *whennext = delay;
277         return &s->fr;
278 }
279
280 static int au_write(struct ast_filestream *fs, struct ast_frame *f)
281 {
282         int res;
283
284         if (f->frametype != AST_FRAME_VOICE) {
285                 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
286                 return -1;
287         }
288         if (f->subclass != AST_FORMAT_ULAW) {
289                 ast_log(LOG_WARNING, "Asked to write non-ulaw frame (%d)!\n", f->subclass);
290                 return -1;
291         }
292         if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
293                         ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
294                         return -1;
295         }
296         update_header(fs->fd);
297         return 0;
298 }
299
300 static int au_seek(struct ast_filestream *fs, long sample_offset, int whence)
301 {
302         off_t min, max, cur;
303         long offset = 0, samples;
304         
305         samples = sample_offset;
306         min = AU_HEADER_SIZE;
307         cur = lseek(fs->fd, 0, SEEK_CUR);
308         max = lseek(fs->fd, 0, SEEK_END);
309         if (whence == SEEK_SET)
310                 offset = samples + min;
311         else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
312                 offset = samples + cur;
313         else if (whence == SEEK_END)
314                 offset = max - samples;
315         if (whence != SEEK_FORCECUR) {
316                 offset = (offset > max) ? max : offset;
317         }
318         /* always protect the header space. */
319         offset = (offset < min) ? min : offset;
320         return lseek(fs->fd, offset, SEEK_SET);
321 }
322
323 static int au_trunc(struct ast_filestream *fs)
324 {
325         if(ftruncate(fs->fd, lseek(fs->fd, 0, SEEK_CUR)))
326                 return -1;
327         return update_header(fs->fd);
328 }
329
330 static long au_tell(struct ast_filestream *fs)
331 {
332         off_t offset;
333
334         offset = lseek(fs->fd, 0, SEEK_CUR);
335         return offset - AU_HEADER_SIZE;
336 }
337
338 static char *au_getcomment(struct ast_filestream *s)
339 {
340         return NULL;
341 }
342
343 int load_module()
344 {
345         return ast_format_register(name, exts, AST_FORMAT_ULAW,
346                                    au_open,
347                                    au_rewrite,
348                                    au_write,
349                                    au_seek,
350                                    au_trunc,
351                                    au_tell,
352                                    au_read,
353                                    au_close,
354                                    au_getcomment);
355 }
356
357 int unload_module()
358 {
359         return ast_format_unregister(name);
360 }
361
362 int usecount()
363 {
364         return localusecnt;
365 }
366
367 char *description()
368 {
369         return desc;
370 }
371
372 char *key()
373 {
374         return ASTERISK_GPL_KEY;
375 }