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