Fix for formats so they give better output on failure conditions. (#6141)
[asterisk/asterisk.git] / formats / format_pcm_alaw.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, alaw PCM file format.
22  * \arg File name extensions: alaw, al
23  * \ingroup formats
24  */
25  
26 #include <unistd.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <stdlib.h>
30 #include <sys/time.h>
31 #include <sys/times.h>
32 #include <sys/types.h>
33 #include <stdio.h>
34 #include <errno.h>
35 #include <string.h>
36
37 #include "asterisk.h"
38
39 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40
41 #include "asterisk/lock.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/file.h"
44 #include "asterisk/logger.h"
45 #include "asterisk/sched.h"
46 #include "asterisk/module.h"
47 #include "asterisk/endian.h"
48 #include "asterisk/alaw.h"
49
50 #define BUF_SIZE 160            /* 160 samples */
51
52 /* #define REALTIME_WRITE */
53
54 struct ast_filestream {
55         void *reserved[AST_RESERVED_POINTERS];
56         /* Believe it or not, we must decode/recode to account for the
57            weird MS format */
58         /* This is what a filestream means to us */
59         FILE *f; /* Descriptor */
60         struct ast_frame fr;                            /* Frame information */
61         char waste[AST_FRIENDLY_OFFSET];        /* Buffer for sending frames, etc */
62         char empty;                                                     /* Empty character */
63         unsigned char buf[BUF_SIZE];                            /* Output Buffer */
64 #ifdef REALTIME_WRITE
65         unsigned long start_time;
66 #endif
67 };
68
69
70 AST_MUTEX_DEFINE_STATIC(pcm_lock);
71 static int glistcnt = 0;
72
73 static char *name = "alaw";
74 static char *desc = "Raw aLaw 8khz PCM Audio support";
75 static char *exts = "alaw|al";
76
77 static char alaw_silence[BUF_SIZE];
78
79
80 #if 0
81 /* Returns time in msec since system boot. */
82 static unsigned long get_time(void)
83 {
84         struct tms buf;
85         clock_t cur;
86
87         cur = times( &buf );
88         if( cur < 0 )
89         {
90                 ast_log( LOG_WARNING, "Cannot get current time\n" );
91                 return 0;
92         }
93         return cur * 1000 / sysconf( _SC_CLK_TCK );
94 }
95 #endif
96
97 static struct ast_filestream *pcm_open(FILE *f)
98 {
99         /* We don't have any header to read or anything really, but
100            if we did, it would go here.  We also might want to check
101            and be sure it's a valid file.  */
102         struct ast_filestream *tmp;
103         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
104                 memset(tmp, 0, sizeof(struct ast_filestream));
105                 if (ast_mutex_lock(&pcm_lock)) {
106                         ast_log(LOG_WARNING, "Unable to lock pcm list\n");
107                         free(tmp);
108                         return NULL;
109                 }
110                 tmp->f = f;
111                 tmp->fr.data = tmp->buf;
112                 tmp->fr.frametype = AST_FRAME_VOICE;
113                 tmp->fr.subclass = AST_FORMAT_ALAW;
114                 /* datalen will vary for each frame */
115                 tmp->fr.src = name;
116                 tmp->fr.mallocd = 0;
117 #ifdef REALTIME_WRITE
118                 tmp->start_time = get_time();
119 #endif
120                 glistcnt++;
121                 ast_mutex_unlock(&pcm_lock);
122                 ast_update_use_count();
123         }
124         return tmp;
125 }
126
127 static struct ast_filestream *pcm_rewrite(FILE *f, const char *comment)
128 {
129         /* We don't have any header to read or anything really, but
130            if we did, it would go here.  We also might want to check
131            and be sure it's a valid file.  */
132         struct ast_filestream *tmp;
133         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
134                 memset(tmp, 0, sizeof(struct ast_filestream));
135                 if (ast_mutex_lock(&pcm_lock)) {
136                         ast_log(LOG_WARNING, "Unable to lock pcm list\n");
137                         free(tmp);
138                         return NULL;
139                 }
140                 tmp->f = f;
141 #ifdef REALTIME_WRITE
142                 tmp->start_time = get_time();
143 #endif
144                 glistcnt++;
145                 ast_mutex_unlock(&pcm_lock);
146                 ast_update_use_count();
147         } else
148                 ast_log(LOG_WARNING, "Out of memory\n");
149         return tmp;
150 }
151
152 static void pcm_close(struct ast_filestream *s)
153 {
154         if (ast_mutex_lock(&pcm_lock)) {
155                 ast_log(LOG_WARNING, "Unable to lock pcm list\n");
156                 return;
157         }
158         glistcnt--;
159         ast_mutex_unlock(&pcm_lock);
160         ast_update_use_count();
161         fclose(s->f);
162         free(s);
163         s = NULL;
164 }
165
166 static struct ast_frame *pcm_read(struct ast_filestream *s, int *whennext)
167 {
168         int res;
169         /* Send a frame from the file to the appropriate channel */
170
171         s->fr.frametype = AST_FRAME_VOICE;
172         s->fr.subclass = AST_FORMAT_ALAW;
173         s->fr.offset = AST_FRIENDLY_OFFSET;
174         s->fr.mallocd = 0;
175         s->fr.data = s->buf;
176         if ((res = fread(s->buf, 1, BUF_SIZE, s->f)) < 1) {
177                 if (res)
178                         ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
179                 return NULL;
180         }
181         s->fr.samples = res;
182         s->fr.datalen = res;
183         *whennext = s->fr.samples;
184         return &s->fr;
185 }
186
187 static int pcm_write(struct ast_filestream *fs, struct ast_frame *f)
188 {
189         int res;
190 #ifdef REALTIME_WRITE
191         unsigned long cur_time;
192         unsigned long fpos;
193         struct stat stat_buf;
194 #endif
195
196         if (f->frametype != AST_FRAME_VOICE) {
197                 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
198                 return -1;
199         }
200         if (f->subclass != AST_FORMAT_ALAW) {
201                 ast_log(LOG_WARNING, "Asked to write non-alaw frame (%d)!\n", f->subclass);
202                 return -1;
203         }
204
205 #ifdef REALTIME_WRITE
206         cur_time = get_time();
207         fpos = ( cur_time - fs->start_time ) * 8;       /* 8 bytes per msec */
208         /* Check if we have written to this position yet. If we have, then increment pos by one frame
209         *  for some degree of protection against receiving packets in the same clock tick.
210         */
211         
212         fstat(fileno(fs->f), &stat_buf );
213         if (stat_buf.st_size > fpos ) {
214                 fpos += f->datalen;     /* Incrementing with the size of this current frame */
215         }
216
217         if (stat_buf.st_size < fpos) {
218                 /* fill the gap with 0x55 rather than 0. */
219                 char buf[ 512 ];
220                 unsigned long cur, to_write;
221
222                 cur = stat_buf.st_size;
223                 if (fseek(fs->f, cur, SEEK_SET) < 0) {
224                         ast_log( LOG_WARNING, "Cannot seek in file: %s\n", strerror(errno) );
225                         return -1;
226                 }
227                 memset(buf, 0x55, 512);
228                 while (cur < fpos) {
229                         to_write = fpos - cur;
230                         if (to_write > 512) {
231                                 to_write = 512;
232                         }
233                         fwrite(buf, 1, to_write, fs->f);
234                         cur += to_write;
235                 }
236         }
237
238
239         if (fseek(s->f, fpos, SEEK_SET) < 0) {
240                 ast_log( LOG_WARNING, "Cannot seek in file: %s\n", strerror(errno) );
241                 return -1;
242         }
243 #endif  /* REALTIME_WRITE */
244         
245         if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) {
246                         ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
247                         return -1;
248         }
249         return 0;
250 }
251
252 static int pcm_seek(struct ast_filestream *fs, long sample_offset, int whence)
253 {
254         long cur, max, offset = 0;
255         int ret = -1; /* assume error */
256
257         cur = ftell(fs->f);
258         fseek(fs->f, 0, SEEK_END);
259         max = ftell(fs->f);
260
261         switch (whence) {
262         case SEEK_SET:
263                 offset = sample_offset;
264                 break;
265         case SEEK_END:
266                 offset = max - sample_offset;
267                 break;
268         case SEEK_CUR:
269         case SEEK_FORCECUR:
270                 offset = cur + sample_offset;
271                 break;
272         default:
273                 ast_log(LOG_WARNING, "invalid whence %d, assuming SEEK_SET\n", whence);
274                 offset = sample_offset;
275         }
276
277         if (offset < 0) {
278                 offset = 0;
279                 ast_log(LOG_WARNING, "negative offset %ld, resetting to 0\n", offset);
280         }
281         if (whence == SEEK_FORCECUR && offset > max) {
282                 size_t left = offset - max;
283
284                 while (left) {
285                         size_t written = fwrite(alaw_silence, sizeof(alaw_silence[0]),
286                                      (left > BUF_SIZE) ? BUF_SIZE : left, fs->f);
287                         if (written == -1)
288                                 break; /* error */
289                         left -= written * sizeof(alaw_silence[0]);
290                 }
291                 ret = 0; /* success */
292         } else {
293                 if (offset > max) {
294                         ast_log(LOG_WARNING, "offset too large %ld, truncating to %ld\n", offset, max);
295                         offset = max;
296                 }
297                 ret = fseek(fs->f, offset, SEEK_SET);
298         }
299         return ret;
300 }
301
302 static int pcm_trunc(struct ast_filestream *fs)
303 {
304         return ftruncate(fileno(fs->f), ftell(fs->f));
305 }
306
307 static long pcm_tell(struct ast_filestream *fs)
308 {
309         off_t offset;
310         offset = ftell(fs->f);
311         return offset;
312 }
313
314
315 static char *pcm_getcomment(struct ast_filestream *s)
316 {
317         return NULL;
318 }
319
320 int load_module()
321 {
322         int index;
323
324         for (index = 0; index < (sizeof(alaw_silence) / sizeof(alaw_silence[0])); index++)
325                 alaw_silence[index] = AST_LIN2A(0);
326
327         return ast_format_register(name, exts, AST_FORMAT_ALAW,
328                                    pcm_open,
329                                    pcm_rewrite,
330                                    pcm_write,
331                                    pcm_seek,
332                                    pcm_trunc,
333                                    pcm_tell,
334                                    pcm_read,
335                                    pcm_close,
336                                    pcm_getcomment);
337 }
338
339 int unload_module()
340 {
341         return ast_format_unregister(name);
342 }       
343
344 int usecount()
345 {
346         return glistcnt;
347 }
348
349 char *description()
350 {
351         return desc;
352 }
353
354
355 char *key()
356 {
357         return ASTERISK_GPL_KEY;
358 }