Version 0.3.0 from FTP
[asterisk/asterisk.git] / formats / format_g723.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Old-style G.723 frame/timestamp format.
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13  
14 #include <asterisk/lock.h>
15 #include <asterisk/channel.h>
16 #include <asterisk/file.h>
17 #include <asterisk/logger.h>
18 #include <asterisk/sched.h>
19 #include <asterisk/module.h>
20 #include <arpa/inet.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <pthread.h>
27 #include <sys/time.h>
28 #include "../channels/adtranvofr.h"
29
30
31 #define G723_MAX_SIZE 1024
32
33 struct ast_filestream {
34         /* First entry MUST be reserved for the channel type */
35         void *reserved[AST_RESERVED_POINTERS];
36         /* This is what a filestream means to us */
37         int fd; /* Descriptor */
38         u_int32_t lasttimeout;  /* Last amount of timeout */
39         struct ast_channel *owner;
40         struct ast_filestream *next;
41         struct ast_frame *fr;   /* Frame representation of buf */
42         struct timeval orig;    /* Original frame time */
43         char buf[G723_MAX_SIZE + AST_FRIENDLY_OFFSET];  /* Buffer for sending frames, etc */
44 };
45
46
47 static struct ast_filestream *glist = NULL;
48 static pthread_mutex_t g723_lock = AST_MUTEX_INITIALIZER;
49 static int glistcnt = 0;
50
51 static char *name = "g723sf";
52 static char *desc = "G.723.1 Simple Timestamp File Format";
53 static char *exts = "g723";
54
55 static struct ast_filestream *g723_open(int fd)
56 {
57         /* We don't have any header to read or anything really, but
58            if we did, it would go here.  We also might want to check
59            and be sure it's a valid file.  */
60         struct ast_filestream *tmp;
61         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
62                 if (ast_pthread_mutex_lock(&g723_lock)) {
63                         ast_log(LOG_WARNING, "Unable to lock g723 list\n");
64                         free(tmp);
65                         return NULL;
66                 }
67                 tmp->next = glist;
68                 glist = tmp;
69                 tmp->fd = fd;
70                 tmp->owner = NULL;
71                 tmp->fr = (struct ast_frame *)tmp->buf;
72                 tmp->fr->data = tmp->buf + sizeof(struct ast_frame);
73                 tmp->fr->frametype = AST_FRAME_VOICE;
74                 tmp->fr->subclass = AST_FORMAT_G723_1;
75                 /* datalen will vary for each frame */
76                 tmp->fr->src = name;
77                 tmp->fr->mallocd = 0;
78                 tmp->lasttimeout = -1;
79                 tmp->orig.tv_usec = 0;
80                 tmp->orig.tv_sec = 0;
81                 glistcnt++;
82                 ast_pthread_mutex_unlock(&g723_lock);
83                 ast_update_use_count();
84         }
85         return tmp;
86 }
87
88 static struct ast_filestream *g723_rewrite(int fd, char *comment)
89 {
90         /* We don't have any header to read or anything really, but
91            if we did, it would go here.  We also might want to check
92            and be sure it's a valid file.  */
93         struct ast_filestream *tmp;
94         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
95                 if (ast_pthread_mutex_lock(&g723_lock)) {
96                         ast_log(LOG_WARNING, "Unable to lock g723 list\n");
97                         free(tmp);
98                         return NULL;
99                 }
100                 tmp->next = glist;
101                 glist = tmp;
102                 tmp->fd = fd;
103                 tmp->owner = NULL;
104                 tmp->fr = NULL;
105                 tmp->lasttimeout = -1;
106                 tmp->orig.tv_usec = 0;
107                 tmp->orig.tv_sec = 0;
108                 glistcnt++;
109                 ast_pthread_mutex_unlock(&g723_lock);
110                 ast_update_use_count();
111         } else
112                 ast_log(LOG_WARNING, "Out of memory\n");
113         return tmp;
114 }
115
116 static struct ast_frame *g723_read(struct ast_filestream *s)
117 {
118         return NULL;
119 }
120
121 static void g723_close(struct ast_filestream *s)
122 {
123         struct ast_filestream *tmp, *tmpl = NULL;
124         if (ast_pthread_mutex_lock(&g723_lock)) {
125                 ast_log(LOG_WARNING, "Unable to lock g723 list\n");
126                 return;
127         }
128         tmp = glist;
129         while(tmp) {
130                 if (tmp == s) {
131                         if (tmpl)
132                                 tmpl->next = tmp->next;
133                         else
134                                 glist = tmp->next;
135                         break;
136                 }
137                 tmpl = tmp;
138                 tmp = tmp->next;
139         }
140         glistcnt--;
141         if (s->owner) {
142                 s->owner->stream = NULL;
143                 if (s->owner->streamid > -1)
144                         ast_sched_del(s->owner->sched, s->owner->streamid);
145                 s->owner->streamid = -1;
146         }
147         ast_pthread_mutex_unlock(&g723_lock);
148         ast_update_use_count();
149         if (!tmp) 
150                 ast_log(LOG_WARNING, "Freeing a filestream we don't seem to own\n");
151         close(s->fd);
152         free(s);
153         s = NULL;
154 }
155
156 static int ast_read_callback(void *data)
157 {
158         u_int16_t size;
159         u_int32_t delay = -1;
160         int looper = 1;
161         int retval = 0;
162         int res;
163         struct ast_filestream *s = data;
164         /* Send a frame from the file to the appropriate channel */
165         while(looper) {
166                 if (read(s->fd, &size, 2) != 2) {
167                         /* Out of data, or the file is no longer valid.  In any case
168                            go ahead and stop the stream */
169                         s->owner->streamid = -1;
170                         return 0;
171                 }
172                 /* Looks like we have a frame to read from here */
173                 size = ntohs(size);
174                 if (size > G723_MAX_SIZE - sizeof(struct ast_frame)) {
175                         ast_log(LOG_WARNING, "Size %d is invalid\n", size);
176                         /* The file is apparently no longer any good, as we
177                            shouldn't ever get frames even close to this 
178                            size.  */
179                         s->owner->streamid = -1;
180                         return 0;
181                 }
182                 /* Read the data into the buffer */
183                 s->fr->offset = AST_FRIENDLY_OFFSET;
184                 s->fr->datalen = size;
185                 s->fr->data = s->buf + sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET;
186                 if ((res = read(s->fd, s->fr->data , size)) != size) {
187                         ast_log(LOG_WARNING, "Short read (%d of %d bytes) (%s)!\n", res, size, strerror(errno));
188                         s->owner->streamid = -1;
189                         return 0;
190                 }
191                 /* Read the delay for the next packet, and schedule again if necessary */
192                 if (read(s->fd, &delay, 4) == 4) 
193                         delay = ntohl(delay);
194                 else
195                         delay = -1;
196 #if 0
197                 /* Average out frames <= 50 ms */
198                 if (delay < 50)
199                         s->fr->timelen = 30;
200                 else
201                         s->fr->timelen = delay;
202 #else
203                 s->fr->samples = 240;
204 #endif
205                 /* Unless there is no delay, we're going to exit out as soon as we
206                    have processed the current frame. */
207                 if (delay > VOFR_FUDGE) {
208                         looper = 0;
209                         /* If there is a delay, lets schedule the next event */
210                         if (delay != s->lasttimeout) {
211                                 /* We'll install the next timeout now. */
212                                 s->owner->streamid = ast_sched_add(s->owner->sched, 
213                                                                                                           delay - VOFR_FUDGE, 
214                                                                                                           ast_read_callback, s);
215                                 
216                                 s->lasttimeout = delay;
217                         } else
218                                 /* Just come back again at the same time */
219                                 retval = -1;
220                 }
221                 /* Lastly, process the frame */
222                 if (ast_write(s->owner, s->fr)) {
223                         ast_log(LOG_WARNING, "Failed to write frame\n");
224                         s->owner->streamid = -1;
225                         return 0;
226                 }
227         }
228         return retval;
229 }
230
231 static int g723_apply(struct ast_channel *c, struct ast_filestream *s)
232 {
233         /* Select our owner for this stream, and get the ball rolling. */
234         s->owner = c;
235         return 0;
236 }
237
238 static int g723_play(struct ast_filestream *s)
239 {
240         u_int32_t delay;
241         /* Read and ignore the first delay */
242         if (read(s->fd, &delay, 4) != 4) {
243                 /* Empty file */
244                 return 0;
245         }
246         ast_read_callback(s);
247         return 0;
248 }
249
250 static int g723_write(struct ast_filestream *fs, struct ast_frame *f)
251 {
252         struct timeval now;
253         u_int32_t delay;
254         u_int16_t size;
255         int res;
256         if (fs->fr) {
257                 ast_log(LOG_WARNING, "Asked to write on a read stream??\n");
258                 return -1;
259         }
260         if (f->frametype != AST_FRAME_VOICE) {
261                 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
262                 return -1;
263         }
264         if (f->subclass != AST_FORMAT_G723_1) {
265                 ast_log(LOG_WARNING, "Asked to write non-g723 frame!\n");
266                 return -1;
267         }
268         if (!(fs->orig.tv_usec || fs->orig.tv_sec)) {
269                 /* First frame should have zeros for delay */
270                 delay = 0;
271                 if (gettimeofday(&fs->orig, NULL)) {
272                         ast_log(LOG_WARNING, "gettimeofday() failed??  What is this?  Y2k?\n");
273                         return -1;
274                 }
275         } else {
276                 if (gettimeofday(&now, NULL)) {
277                         ast_log(LOG_WARNING, "gettimeofday() failed??  What is this?  Y2k?\n");
278                         return -1;
279                 }
280                 delay = (now.tv_sec - fs->orig.tv_sec) * 1000 + (now.tv_usec - fs->orig.tv_usec) / 1000;
281                 delay = htonl(delay);
282                 fs->orig.tv_sec = now.tv_sec;
283                 fs->orig.tv_usec = now.tv_usec;
284         }
285         if (f->datalen <= 0) {
286                 ast_log(LOG_WARNING, "Short frame ignored (%d bytes long?)\n", f->datalen);
287                 return 0;
288         }
289         if ((res = write(fs->fd, &delay, 4)) != 4) {
290                 ast_log(LOG_WARNING, "Unable to write delay: res=%d (%s)\n", res, strerror(errno));
291                 return -1;
292         }
293         size = htons(f->datalen);
294         if ((res =write(fs->fd, &size, 2)) != 2) {
295                 ast_log(LOG_WARNING, "Unable to write size: res=%d (%s)\n", res, strerror(errno));
296                 return -1;
297         }
298         if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
299                 ast_log(LOG_WARNING, "Unable to write frame: res=%d (%s)\n", res, strerror(errno));
300                 return -1;
301         }       
302         return 0;
303 }
304
305 static int g723_seek(struct ast_filestream *fs, long sample_offset, int whence)
306 {
307         return -1;
308 }
309
310 static int g723_trunc(struct ast_filestream *fs)
311 {
312         return -1;
313 }
314
315 static long g723_tell(struct ast_filestream *fs)
316 {
317         return -1;
318 }
319
320 static char *g723_getcomment(struct ast_filestream *s)
321 {
322         return NULL;
323 }
324
325 int load_module()
326 {
327         return ast_format_register(name, exts, AST_FORMAT_G723_1,
328                                                                 g723_open,
329                                                                 g723_rewrite,
330                                                                 g723_apply,
331                                                                 g723_play,
332                                                                 g723_write,
333                                                                 g723_seek,
334                                                                 g723_trunc,
335                                                                 g723_tell,
336                                                                 g723_read,
337                                                                 g723_close,
338                                                                 g723_getcomment);
339                                                                 
340                                                                 
341 }
342
343 int unload_module()
344 {
345         struct ast_filestream *tmp, *tmpl;
346         if (ast_pthread_mutex_lock(&g723_lock)) {
347                 ast_log(LOG_WARNING, "Unable to lock g723 list\n");
348                 return -1;
349         }
350         tmp = glist;
351         while(tmp) {
352                 if (tmp->owner)
353                         ast_softhangup(tmp->owner, AST_SOFTHANGUP_APPUNLOAD);
354                 tmpl = tmp;
355                 tmp = tmp->next;
356                 free(tmpl);
357         }
358         ast_pthread_mutex_unlock(&g723_lock);
359         return ast_format_unregister(name);
360 }       
361
362 int usecount()
363 {
364         int res;
365         if (ast_pthread_mutex_lock(&g723_lock)) {
366                 ast_log(LOG_WARNING, "Unable to lock g723 list\n");
367                 return -1;
368         }
369         res = glistcnt;
370         ast_pthread_mutex_unlock(&g723_lock);
371         return res;
372 }
373
374 char *description()
375 {
376         return desc;
377 }
378
379
380 char *key()
381 {
382         return ASTERISK_GPL_KEY;
383 }