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