Add new object for VoicemailUserEntry
[asterisk/asterisk.git] / formats / format_pcm.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Flat, binary, ulaw PCM file format.
22  * \arg File name extension: alaw, al, alw, pcm, ulaw, ul, mu, ulw, g722, au
23  * 
24  * \ingroup formats
25  */
26
27 /*** MODULEINFO
28         <support_level>core</support_level>
29  ***/
30  
31 #include "asterisk.h"
32
33 #include "asterisk/mod_format.h"
34 #include "asterisk/module.h"
35 #include "asterisk/endian.h"
36 #include "asterisk/ulaw.h"
37 #include "asterisk/alaw.h"
38 #include "asterisk/format_cache.h"
39
40 #define BUF_SIZE 160            /* 160 bytes, and same number of samples */
41
42 static char ulaw_silence[BUF_SIZE];
43 static char alaw_silence[BUF_SIZE];
44
45 /* #define REALTIME_WRITE */    /* XXX does it work at all ? */
46
47 #ifdef REALTIME_WRITE
48 struct pcm_desc {
49         unsigned long start_time;
50 };
51
52 /* Returns time in msec since system boot. */
53 static unsigned long get_time(void)
54 {
55         struct tms buf;
56         clock_t cur;
57
58         cur = times( &buf );
59         if( cur < 0 ) {
60                 ast_log( LOG_WARNING, "Cannot get current time\n" );
61                 return 0;
62         }
63         return cur * 1000 / sysconf( _SC_CLK_TCK );
64 }
65
66 static int pcma_open(struct ast_filestream *s)
67 {
68         if (ast_format_cmp(s->fmt->format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL)
69                 pd->starttime = get_time();
70         return 0;
71 }
72
73 static int pcma_rewrite(struct ast_filestream *s, const char *comment)
74 {
75         return pcma_open(s);
76 }
77 #endif
78
79 static struct ast_frame *pcm_read(struct ast_filestream *s, int *whennext)
80 {
81         size_t res;
82
83         /* Send a frame from the file to the appropriate channel */
84         AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
85         if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) < 1) {
86                 if (res) {
87                         ast_log(LOG_WARNING, "Short read of %s data (expected %d bytes, read %zu): %s\n",
88                                         ast_format_get_name(s->fr.subclass.format), s->fr.datalen, res,
89                                         strerror(errno));
90                 }
91                 return NULL;
92         }
93         s->fr.datalen = res;
94         if (ast_format_cmp(s->fmt->format, ast_format_g722) == AST_FORMAT_CMP_EQUAL)
95                 *whennext = s->fr.samples = res * 2;
96         else
97                 *whennext = s->fr.samples = res;
98         return &s->fr;
99 }
100
101 static int pcm_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
102 {
103         off_t cur, max, offset = 0;
104         int ret = -1;   /* assume error */
105
106         if ((cur = ftello(fs->f)) < 0) {
107                 ast_log(AST_LOG_WARNING, "Unable to determine current position in pcm filestream %p: %s\n", fs, strerror(errno));
108                 return -1;
109         }
110
111         if (fseeko(fs->f, 0, SEEK_END) < 0) {
112                 ast_log(AST_LOG_WARNING, "Unable to seek to end of pcm filestream %p: %s\n", fs, strerror(errno));
113                 return -1;
114         }
115
116         if ((max = ftello(fs->f)) < 0) {
117                 ast_log(AST_LOG_WARNING, "Unable to determine max position in pcm filestream %p: %s\n", fs, strerror(errno));
118                 return -1;
119         }
120
121         switch (whence) {
122         case SEEK_SET:
123                 offset = sample_offset;
124                 break;
125         case SEEK_END:
126                 offset = max - sample_offset;
127                 break;
128         case SEEK_CUR:
129         case SEEK_FORCECUR:
130                 offset = cur + sample_offset;
131                 break;
132         default:
133                 ast_log(LOG_WARNING, "invalid whence %d, assuming SEEK_SET\n", whence);
134                 offset = sample_offset;
135         }
136         if (offset < 0) {
137                 ast_log(LOG_WARNING, "negative offset %ld, resetting to 0\n", (long) offset);
138                 offset = 0;
139         }
140         if (whence == SEEK_FORCECUR && offset > max) { /* extend the file */
141                 size_t left = offset - max;
142                 const char *src = (ast_format_cmp(fs->fmt->format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) ? alaw_silence : ulaw_silence;
143
144                 while (left) {
145                         size_t written = fwrite(src, 1, MIN(left, BUF_SIZE), fs->f);
146                         if (written < MIN(left, BUF_SIZE)) {
147                                 break;  /* error */
148                         }
149                         left -= written;
150                 }
151                 ret = 0; /* successful */
152         } else {
153                 if (offset > max) {
154                         ast_log(LOG_WARNING, "offset too large %ld, truncating to %ld\n", (long) offset, (long) max);
155                         offset = max;
156                 }
157                 ret = fseeko(fs->f, offset, SEEK_SET);
158         }
159         return ret;
160 }
161
162 static int pcm_trunc(struct ast_filestream *fs)
163 {
164         int cur, fd;
165
166         if ((fd = fileno(fs->f)) < 0) {
167                 ast_log(AST_LOG_WARNING, "Unable to determine file descriptor for pcm filestream %p: %s\n", fs, strerror(errno));
168                 return -1;
169         }
170         if ((cur = ftello(fs->f)) < 0) {
171                 ast_log(AST_LOG_WARNING, "Unable to determine current position in pcm filestream %p: %s\n", fs, strerror(errno));
172                 return -1;
173         }
174         /* Truncate file to current length */
175         return ftruncate(fd, cur);
176 }
177
178 static off_t pcm_tell(struct ast_filestream *fs)
179 {
180         return ftello(fs->f);
181 }
182
183 static int pcm_write(struct ast_filestream *fs, struct ast_frame *f)
184 {
185         int res;
186
187 #ifdef REALTIME_WRITE
188         if (ast_format_cmp(s->fmt->format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL) {
189                 struct pcm_desc *pd = (struct pcm_desc *)fs->_private;
190                 struct stat stat_buf;
191                 unsigned long cur_time = get_time();
192                 unsigned long fpos = ( cur_time - pd->start_time ) * 8; /* 8 bytes per msec */
193                 /* Check if we have written to this position yet. If we have, then increment pos by one frame
194                 *  for some degree of protection against receiving packets in the same clock tick.
195                 */
196                 
197                 fstat(fileno(fs->f), &stat_buf );
198                 if (stat_buf.st_size > fpos )
199                         fpos += f->datalen;     /* Incrementing with the size of this current frame */
200
201                 if (stat_buf.st_size < fpos) {
202                         /* fill the gap with 0x55 rather than 0. */
203                         char buf[1024];
204                         unsigned long cur, to_write;
205
206                         cur = stat_buf.st_size;
207                         if (fseek(fs->f, cur, SEEK_SET) < 0) {
208                                 ast_log( LOG_WARNING, "Cannot seek in file: %s\n", strerror(errno) );
209                                 return -1;
210                         }
211                         memset(buf, 0x55, 512);
212                         while (cur < fpos) {
213                                 to_write = fpos - cur;
214                                 if (to_write > sizeof(buf))
215                                         to_write = sizeof(buf);
216                                 if (fwrite(buf, 1, to_write, fs->f) != to_write) {
217                                         ast_log(LOG_ERROR, "Failed to write to file: %s\n", strerror(errno));
218                                         return -1;
219                                 }
220                                 cur += to_write;
221                         }
222                 }
223
224                 if (fseek(s->f, fpos, SEEK_SET) < 0) {
225                         ast_log( LOG_WARNING, "Cannot seek in file: %s\n", strerror(errno) );
226                         return -1;
227                 }
228         }
229 #endif  /* REALTIME_WRITE */
230         
231         if ((res = fwrite(f->data.ptr, 1, f->datalen, fs->f)) != f->datalen) {
232                 ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
233                 return -1;
234         }
235         return 0;
236 }
237
238 /* SUN .au support routines */
239
240 #define MIN_AU_HEADER_SIZE      24
241 #define AU_HEADER(var)          uint32_t var[6]
242
243 #define AU_HDR_MAGIC_OFF        0
244 #define AU_HDR_HDR_SIZE_OFF     1
245 #define AU_HDR_DATA_SIZE_OFF    2
246 #define AU_HDR_ENCODING_OFF     3
247 #define AU_HDR_SAMPLE_RATE_OFF  4
248 #define AU_HDR_CHANNELS_OFF     5
249
250 #define AU_ENC_8BIT_ULAW        1
251
252 #define AU_MAGIC 0x2e736e64
253 #if __BYTE_ORDER == __BIG_ENDIAN
254 #define htoll(b) (b)
255 #define htols(b) (b)
256 #define ltohl(b) (b)
257 #define ltohs(b) (b)
258 #else
259 #if __BYTE_ORDER == __LITTLE_ENDIAN
260 #define htoll(b)  \
261           (((((b)      ) & 0xFF) << 24) | \
262                ((((b) >>  8) & 0xFF) << 16) | \
263                    ((((b) >> 16) & 0xFF) <<  8) | \
264                    ((((b) >> 24) & 0xFF)      ))
265 #define htols(b) \
266           (((((b)      ) & 0xFF) << 8) | \
267                    ((((b) >> 8) & 0xFF)      ))
268 #define ltohl(b) htoll(b)
269 #define ltohs(b) htols(b)
270 #else
271 #error "Endianess not defined"
272 #endif
273 #endif
274
275 struct au_desc {
276         uint32_t hdr_size;
277 };
278
279 static int check_header(struct ast_filestream *fs)
280 {
281         AU_HEADER(header);
282         uint32_t magic;
283         uint32_t hdr_size;
284         uint32_t data_size;
285         uint32_t encoding;
286         uint32_t sample_rate;
287         uint32_t channels;
288
289         struct au_desc *desc = fs->_private;
290         FILE *f = fs->f;
291
292         if (fread(header, 1, MIN_AU_HEADER_SIZE, f) != MIN_AU_HEADER_SIZE) {
293                 ast_log(LOG_WARNING, "Read failed (header)\n");
294                 return -1;
295         }
296         magic = ltohl(header[AU_HDR_MAGIC_OFF]);
297         if (magic != (uint32_t) AU_MAGIC) {
298                 ast_log(LOG_WARNING, "Bad magic: 0x%x\n", magic);
299         }
300         hdr_size = ltohl(header[AU_HDR_HDR_SIZE_OFF]);
301         if (hdr_size < MIN_AU_HEADER_SIZE) {
302                 hdr_size = MIN_AU_HEADER_SIZE;
303         }
304 /*      data_size = ltohl(header[AU_HDR_DATA_SIZE_OFF]); */
305         encoding = ltohl(header[AU_HDR_ENCODING_OFF]);
306         if (encoding != AU_ENC_8BIT_ULAW) {
307                 ast_log(LOG_WARNING, "Unexpected format: %u. Only 8bit ULAW allowed (%d)\n", encoding, AU_ENC_8BIT_ULAW);
308                 return -1;
309         }
310         sample_rate = ltohl(header[AU_HDR_SAMPLE_RATE_OFF]);
311         if (sample_rate != DEFAULT_SAMPLE_RATE) {
312                 ast_log(LOG_WARNING, "Sample rate can only be 8000 not %u\n", sample_rate);
313                 return -1;
314         }
315         channels = ltohl(header[AU_HDR_CHANNELS_OFF]);
316         if (channels != 1) {
317                 ast_log(LOG_WARNING, "Not in mono: channels=%u\n", channels);
318                 return -1;
319         }
320         /* Skip to data */
321         fseek(f, 0, SEEK_END);
322         data_size = ftell(f) - hdr_size;
323         if (fseek(f, hdr_size, SEEK_SET) == -1 ) {
324                 ast_log(LOG_WARNING, "Failed to skip to data: %u\n", hdr_size);
325                 return -1;
326         }
327
328         /* We'll need this later */
329         desc->hdr_size = hdr_size;
330
331         return data_size;
332 }
333
334 static int update_header(struct ast_filestream *fs)
335 {
336         off_t cur, end;
337         uint32_t datalen;
338         int bytes;
339         struct au_desc *desc = fs->_private;
340         FILE *f = fs->f;
341
342         cur = ftell(f);
343         fseek(f, 0, SEEK_END);
344         end = ftell(f);
345         /* data starts 24 bytes in */
346         bytes = end - desc->hdr_size;
347         datalen = htoll(bytes);
348
349         if (cur < 0) {
350                 ast_log(LOG_WARNING, "Unable to find our position\n");
351                 return -1;
352         }
353         if (fseek(f, AU_HDR_DATA_SIZE_OFF * sizeof(uint32_t), SEEK_SET)) {
354                 ast_log(LOG_WARNING, "Unable to set our position\n");
355                 return -1;
356         }
357         if (fwrite(&datalen, 1, sizeof(datalen), f) != sizeof(datalen)) {
358                 ast_log(LOG_WARNING, "Unable to set write file size\n");
359                 return -1;
360         }
361         if (fseek(f, cur, SEEK_SET)) {
362                 ast_log(LOG_WARNING, "Unable to return to position\n");
363                 return -1;
364         }
365         return 0;
366 }
367
368 static int write_header(struct ast_filestream *fs)
369 {
370         struct au_desc *desc = fs->_private;
371         FILE *f = fs->f;
372
373         AU_HEADER(header);
374
375         header[AU_HDR_MAGIC_OFF] = htoll((uint32_t) AU_MAGIC);
376         header[AU_HDR_HDR_SIZE_OFF] = htoll(desc->hdr_size);
377         header[AU_HDR_DATA_SIZE_OFF] = 0;
378         header[AU_HDR_ENCODING_OFF] = htoll(AU_ENC_8BIT_ULAW);
379         header[AU_HDR_SAMPLE_RATE_OFF] = htoll(DEFAULT_SAMPLE_RATE);
380         header[AU_HDR_CHANNELS_OFF] = htoll(1);
381
382         /* Write an au header, ignoring sizes which will be filled in later */
383         fseek(f, 0, SEEK_SET);
384         if (fwrite(header, 1, MIN_AU_HEADER_SIZE, f) != MIN_AU_HEADER_SIZE) {
385                 ast_log(LOG_WARNING, "Unable to write header\n");
386                 return -1;
387         }
388         return 0;
389 }
390
391 static int au_open(struct ast_filestream *s)
392 {
393         if (check_header(s) < 0)
394                 return -1;
395         return 0;
396 }
397
398 static int au_rewrite(struct ast_filestream *s, const char *comment)
399 {
400         struct au_desc *desc = s->_private;
401
402         desc->hdr_size = MIN_AU_HEADER_SIZE;
403
404         if (write_header(s))
405                 return -1;
406         return 0;
407 }
408
409 /* XXX check this, probably incorrect */
410 static int au_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
411 {
412         off_t min, max, cur;
413         long offset = 0, bytes;
414         struct au_desc *desc = fs->_private;
415
416         min = desc->hdr_size;
417
418         if (ast_format_cmp(fs->fmt->format, ast_format_g722) == AST_FORMAT_CMP_EQUAL)
419                 bytes = sample_offset / 2;
420         else
421                 bytes = sample_offset;
422
423         if ((cur = ftello(fs->f)) < 0) {
424                 ast_log(AST_LOG_WARNING, "Unable to determine current position in au filestream %p: %s\n", fs, strerror(errno));
425                 return -1;
426         }
427
428         if (fseeko(fs->f, 0, SEEK_END) < 0) {
429                 ast_log(AST_LOG_WARNING, "Unable to seek to end of au filestream %p: %s\n", fs, strerror(errno));
430                 return -1;
431         }
432
433         if ((max = ftello(fs->f)) < 0) {
434                 ast_log(AST_LOG_WARNING, "Unable to determine max position in au filestream %p: %s\n", fs, strerror(errno));
435                 return -1;
436         }
437
438         if (whence == SEEK_SET)
439                 offset = bytes + min;
440         else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
441                 offset = bytes + cur;
442         else if (whence == SEEK_END)
443                 offset = max - bytes;
444
445         if (whence != SEEK_FORCECUR) {
446                 offset = (offset > max) ? max : offset;
447         }
448
449         /* always protect the header space. */
450         offset = (offset < min) ? min : offset;
451
452         return fseeko(fs->f, offset, SEEK_SET);
453 }
454
455 static int au_trunc(struct ast_filestream *fs)
456 {
457         int fd;
458         off_t cur;
459
460         if ((fd = fileno(fs->f)) < 0) {
461                 ast_log(AST_LOG_WARNING, "Unable to determine file descriptor for au filestream %p: %s\n", fs, strerror(errno));
462                 return -1;
463         }
464         if ((cur = ftello(fs->f)) < 0) {
465                 ast_log(AST_LOG_WARNING, "Unable to determine current position in au filestream %p: %s\n", fs, strerror(errno));
466                 return -1;
467         }
468         /* Truncate file to current length */
469         if (ftruncate(fd, cur)) {
470                 return -1;
471         }
472         return update_header(fs);
473 }
474
475 static off_t au_tell(struct ast_filestream *fs)
476 {
477         struct au_desc *desc = fs->_private;
478         off_t offset = ftello(fs->f);
479         return offset - desc->hdr_size;
480 }
481
482 static struct ast_format_def alaw_f = {
483         .name = "alaw",
484         .exts = "alaw|al|alw",
485         .write = pcm_write,
486         .seek = pcm_seek,
487         .trunc = pcm_trunc,
488         .tell = pcm_tell,
489         .read = pcm_read,
490         .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
491 #ifdef REALTIME_WRITE
492         .open = pcma_open,
493         .rewrite = pcma_rewrite,
494         .desc_size = sizeof(struct pcm_desc),
495 #endif
496 };
497
498 static struct ast_format_def pcm_f = {
499         .name = "pcm",
500         .exts = "pcm|ulaw|ul|mu|ulw",
501         .write = pcm_write,
502         .seek = pcm_seek,
503         .trunc = pcm_trunc,
504         .tell = pcm_tell,
505         .read = pcm_read,
506         .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
507 };
508
509 static struct ast_format_def g722_f = {
510         .name = "g722",
511         .exts = "g722",
512         .write = pcm_write,
513         .seek = pcm_seek,
514         .trunc = pcm_trunc,
515         .tell = pcm_tell,
516         .read = pcm_read,
517         .buf_size = (BUF_SIZE * 2) + AST_FRIENDLY_OFFSET,
518 };
519
520 static struct ast_format_def au_f = {
521         .name = "au",
522         .exts = "au",
523         .open = au_open,
524         .rewrite = au_rewrite,
525         .write = pcm_write,
526         .seek = au_seek,
527         .trunc = au_trunc,
528         .tell = au_tell,
529         .read = pcm_read,
530         .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,     /* this many shorts */
531         .desc_size = sizeof(struct au_desc),
532 };
533
534 static int unload_module(void)
535 {
536         return ast_format_def_unregister(pcm_f.name)
537                 || ast_format_def_unregister(alaw_f.name)
538                 || ast_format_def_unregister(au_f.name)
539                 || ast_format_def_unregister(g722_f.name);
540 }
541
542 static int load_module(void)
543 {
544         int i;
545
546         /* XXX better init ? */
547         for (i = 0; i < ARRAY_LEN(ulaw_silence); i++)
548                 ulaw_silence[i] = AST_LIN2MU(0);
549         for (i = 0; i < ARRAY_LEN(alaw_silence); i++)
550                 alaw_silence[i] = AST_LIN2A(0);
551
552         pcm_f.format = ast_format_ulaw;
553         alaw_f.format = ast_format_alaw;
554         au_f.format = ast_format_ulaw;
555         g722_f.format = ast_format_g722;
556         if ( ast_format_def_register(&pcm_f)
557                 || ast_format_def_register(&alaw_f)
558                 || ast_format_def_register(&au_f)
559                 || ast_format_def_register(&g722_f) ) {
560                 unload_module();
561                 return AST_MODULE_LOAD_DECLINE;
562         }
563         return AST_MODULE_LOAD_SUCCESS;
564 }
565
566 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Raw/Sun uLaw/ALaw 8KHz (PCM,PCMA,AU), G.722 16Khz",
567         .support_level = AST_MODULE_SUPPORT_CORE,
568         .load = load_module,
569         .unload = unload_module,
570         .load_pri = AST_MODPRI_APP_DEPEND
571 );