Version 0.2.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 }
154
155 static int ast_read_callback(void *data)
156 {
157         u_int16_t size;
158         u_int32_t delay = -1;
159         int looper = 1;
160         int retval = 0;
161         int res;
162         struct ast_filestream *s = data;
163         /* Send a frame from the file to the appropriate channel */
164         while(looper) {
165                 if (read(s->fd, &size, 2) != 2) {
166                         /* Out of data, or the file is no longer valid.  In any case
167                            go ahead and stop the stream */
168                         s->owner->streamid = -1;
169                         return 0;
170                 }
171                 /* Looks like we have a frame to read from here */
172                 size = ntohs(size);
173                 if (size > G723_MAX_SIZE - sizeof(struct ast_frame)) {
174                         ast_log(LOG_WARNING, "Size %d is invalid\n", size);
175                         /* The file is apparently no longer any good, as we
176                            shouldn't ever get frames even close to this 
177                            size.  */
178                         s->owner->streamid = -1;
179                         return 0;
180                 }
181                 /* Read the data into the buffer */
182                 s->fr->offset = AST_FRIENDLY_OFFSET;
183                 s->fr->datalen = size;
184                 s->fr->data = s->buf + sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET;
185                 if ((res = read(s->fd, s->fr->data , size)) != size) {
186                         ast_log(LOG_WARNING, "Short read (%d of %d bytes) (%s)!\n", res, size, strerror(errno));
187                         s->owner->streamid = -1;
188                         return 0;
189                 }
190                 /* Read the delay for the next packet, and schedule again if necessary */
191                 if (read(s->fd, &delay, 4) == 4) 
192                         delay = ntohl(delay);
193                 else
194                         delay = -1;
195 #if 0
196                 /* Average out frames <= 50 ms */
197                 if (delay < 50)
198                         s->fr->timelen = 30;
199                 else
200                         s->fr->timelen = delay;
201 #else
202                 s->fr->timelen = 30;
203 #endif
204                 /* Unless there is no delay, we're going to exit out as soon as we
205                    have processed the current frame. */
206                 if (delay > VOFR_FUDGE) {
207                         looper = 0;
208                         /* If there is a delay, lets schedule the next event */
209                         if (delay != s->lasttimeout) {
210                                 /* We'll install the next timeout now. */
211                                 s->owner->streamid = ast_sched_add(s->owner->sched, 
212                                                                                                           delay - VOFR_FUDGE, 
213                                                                                                           ast_read_callback, s);
214                                 
215                                 s->lasttimeout = delay;
216                         } else
217                                 /* Just come back again at the same time */
218                                 retval = -1;
219                 }
220                 /* Lastly, process the frame */
221                 if (ast_write(s->owner, s->fr)) {
222                         ast_log(LOG_WARNING, "Failed to write frame\n");
223                         s->owner->streamid = -1;
224                         return 0;
225                 }
226         }
227         return retval;
228 }
229
230 static int g723_apply(struct ast_channel *c, struct ast_filestream *s)
231 {
232         u_int32_t delay;
233         /* Select our owner for this stream, and get the ball rolling. */
234         s->owner = c;
235         /* Read and ignore the first delay */
236         if (read(s->fd, &delay, 4) != 4) {
237                 /* Empty file */
238                 return 0;
239         }
240         ast_read_callback(s);
241         return 0;
242 }
243
244 static int g723_write(struct ast_filestream *fs, struct ast_frame *f)
245 {
246         struct timeval now;
247         u_int32_t delay;
248         u_int16_t size;
249         int res;
250         if (fs->fr) {
251                 ast_log(LOG_WARNING, "Asked to write on a read stream??\n");
252                 return -1;
253         }
254         if (f->frametype != AST_FRAME_VOICE) {
255                 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
256                 return -1;
257         }
258         if (f->subclass != AST_FORMAT_G723_1) {
259                 ast_log(LOG_WARNING, "Asked to write non-g723 frame!\n");
260                 return -1;
261         }
262         if (!(fs->orig.tv_usec || fs->orig.tv_sec)) {
263                 /* First frame should have zeros for delay */
264                 delay = 0;
265                 if (gettimeofday(&fs->orig, NULL)) {
266                         ast_log(LOG_WARNING, "gettimeofday() failed??  What is this?  Y2k?\n");
267                         return -1;
268                 }
269         } else {
270                 if (gettimeofday(&now, NULL)) {
271                         ast_log(LOG_WARNING, "gettimeofday() failed??  What is this?  Y2k?\n");
272                         return -1;
273                 }
274                 delay = (now.tv_sec - fs->orig.tv_sec) * 1000 + (now.tv_usec - fs->orig.tv_usec) / 1000;
275                 delay = htonl(delay);
276                 fs->orig.tv_sec = now.tv_sec;
277                 fs->orig.tv_usec = now.tv_usec;
278         }
279         if (f->datalen <= 0) {
280                 ast_log(LOG_WARNING, "Short frame ignored (%d bytes long?)\n", f->datalen);
281                 return 0;
282         }
283         if ((res = write(fs->fd, &delay, 4)) != 4) {
284                 ast_log(LOG_WARNING, "Unable to write delay: res=%d (%s)\n", res, strerror(errno));
285                 return -1;
286         }
287         size = htons(f->datalen);
288         if ((res =write(fs->fd, &size, 2)) != 2) {
289                 ast_log(LOG_WARNING, "Unable to write size: res=%d (%s)\n", res, strerror(errno));
290                 return -1;
291         }
292         if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
293                 ast_log(LOG_WARNING, "Unable to write frame: res=%d (%s)\n", res, strerror(errno));
294                 return -1;
295         }       
296         return 0;
297 }
298
299 static char *g723_getcomment(struct ast_filestream *s)
300 {
301         return NULL;
302 }
303
304 int load_module()
305 {
306         return ast_format_register(name, exts, AST_FORMAT_G723_1,
307                                                                 g723_open,
308                                                                 g723_rewrite,
309                                                                 g723_apply,
310                                                                 g723_write,
311                                                                 g723_read,
312                                                                 g723_close,
313                                                                 g723_getcomment);
314                                                                 
315                                                                 
316 }
317
318 int unload_module()
319 {
320         struct ast_filestream *tmp, *tmpl;
321         if (ast_pthread_mutex_lock(&g723_lock)) {
322                 ast_log(LOG_WARNING, "Unable to lock g723 list\n");
323                 return -1;
324         }
325         tmp = glist;
326         while(tmp) {
327                 if (tmp->owner)
328                         ast_softhangup(tmp->owner, AST_SOFTHANGUP_APPUNLOAD);
329                 tmpl = tmp;
330                 tmp = tmp->next;
331                 free(tmpl);
332         }
333         ast_pthread_mutex_unlock(&g723_lock);
334         return ast_format_unregister(name);
335 }       
336
337 int usecount()
338 {
339         int res;
340         if (ast_pthread_mutex_lock(&g723_lock)) {
341                 ast_log(LOG_WARNING, "Unable to lock g723 list\n");
342                 return -1;
343         }
344         res = glistcnt;
345         ast_pthread_mutex_unlock(&g723_lock);
346         return res;
347 }
348
349 char *description()
350 {
351         return desc;
352 }
353
354
355 char *key()
356 {
357         return ASTERISK_GPL_KEY;
358 }