Version 0.3.0 from FTP
[asterisk/asterisk.git] / frame.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Frame manipulation routines
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/frame.h>
15 #include <asterisk/logger.h>
16 #include <asterisk/options.h>
17 #include <asterisk/cli.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <pthread.h>
23 #include "asterisk.h"
24
25 #ifdef TRACE_FRAMES
26 static int headers = 0;
27 static struct ast_frame *headerlist = NULL;
28 static pthread_mutex_t framelock = AST_MUTEX_INITIALIZER;
29 #endif
30
31 #define SMOOTHER_SIZE 8000
32
33 struct ast_smoother {
34         int size;
35         int format;
36         int readdata;
37         float timeperbyte;
38         struct ast_frame f;
39         char data[SMOOTHER_SIZE];
40         char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET];
41         int len;
42 };
43
44 struct ast_smoother *ast_smoother_new(int size)
45 {
46         struct ast_smoother *s;
47         if (size < 1)
48                 return NULL;
49         s = malloc(sizeof(struct ast_smoother));
50         if (s) {
51                 memset(s, 0, sizeof(struct ast_smoother));
52                 s->size = size;
53         }
54         return s;
55 }
56
57 int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f)
58 {
59         if (f->frametype != AST_FRAME_VOICE) {
60                 ast_log(LOG_WARNING, "Huh?  Can't smooth a non-voice frame!\n");
61                 return -1;
62         }
63         if (!s->format) {
64                 s->format = f->subclass;
65                 s->timeperbyte = (float)f->timelen / (float)f->datalen;
66         } else if (s->format != f->subclass) {
67                 ast_log(LOG_WARNING, "Smoother was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass);
68                 return -1;
69         }
70         if (s->len + f->datalen > SMOOTHER_SIZE) {
71                 ast_log(LOG_WARNING, "Out of smoother space\n");
72                 return -1;
73         }
74         memcpy(s->data + s->len, f->data, f->datalen);
75         s->len += f->datalen;
76         return 0;
77 }
78
79 struct ast_frame *ast_smoother_read(struct ast_smoother *s)
80 {
81         /* Make sure we have enough data */
82         if (s->len < s->size) {
83                 return NULL;
84         }
85         /* Make frame */
86         s->f.frametype = AST_FRAME_VOICE;
87         s->f.subclass = s->format;
88         s->f.data = s->framedata + AST_FRIENDLY_OFFSET;
89         s->f.offset = AST_FRIENDLY_OFFSET;
90         s->f.datalen = s->size;
91         s->f.timelen = s->size * s->timeperbyte;
92         /* Fill Data */
93         memcpy(s->f.data, s->data, s->size);
94         s->len -= s->size;
95         /* Move remaining data to the front if applicable */
96         if (s->len) 
97                 memmove(s->data, s->data + s->size, s->len);
98         /* Return frame */
99         return &s->f;
100 }
101
102 void ast_smoother_free(struct ast_smoother *s)
103 {
104         free(s);
105 }
106
107 static struct ast_frame *ast_frame_header_new(void)
108 {
109         struct ast_frame *f;
110         f = malloc(sizeof(struct ast_frame));
111         if (f)
112                 memset(f, 0, sizeof(struct ast_frame));
113 #ifdef TRACE_FRAMES
114         if (f) {
115                 headers++;
116                 f->prev = NULL;
117                 ast_pthread_mutex_lock(&framelock);
118                 f->next = headerlist;
119                 if (headerlist)
120                         headerlist->prev = f;
121                 headerlist = f;
122                 pthread_mutex_unlock(&framelock);
123         }
124 #endif  
125         return f;
126 }
127
128 /*
129  * Important: I should be made more efficient.  Frame headers should
130  * most definitely be cached
131  */
132
133 void ast_frfree(struct ast_frame *fr)
134 {
135         if (fr->mallocd & AST_MALLOCD_DATA) {
136                 if (fr->data) 
137                         free(fr->data - fr->offset);
138         }
139         if (fr->mallocd & AST_MALLOCD_SRC) {
140                 if (fr->src)
141                         free(fr->src);
142         }
143         if (fr->mallocd & AST_MALLOCD_HDR) {
144 #ifdef TRACE_FRAMES
145                 headers--;
146                 ast_pthread_mutex_lock(&framelock);
147                 if (fr->next)
148                         fr->next->prev = fr->prev;
149                 if (fr->prev)
150                         fr->prev->next = fr->next;
151                 else
152                         headerlist = fr->next;
153                 pthread_mutex_unlock(&framelock);
154 #endif                  
155                 free(fr);
156         }
157 }
158
159 struct ast_frame *ast_frisolate(struct ast_frame *fr)
160 {
161         struct ast_frame *out;
162         if (!(fr->mallocd & AST_MALLOCD_HDR)) {
163                 /* Allocate a new header if needed */
164                 out = ast_frame_header_new();
165                 if (!out) {
166                         ast_log(LOG_WARNING, "Out of memory\n");
167                         return NULL;
168                 }
169                 out->frametype = fr->frametype;
170                 out->subclass = fr->subclass;
171                 out->datalen = 0;
172                 out->timelen = fr->timelen;
173                 out->offset = 0;
174                 out->src = NULL;
175                 out->data = NULL;
176         } else {
177                 out = fr;
178         }
179         if (!(fr->mallocd & AST_MALLOCD_SRC)) {
180                 if (fr->src)
181                         out->src = strdup(fr->src);
182         } else
183                 out->src = fr->src;
184         if (!(fr->mallocd & AST_MALLOCD_DATA))  {
185                 out->data = malloc(fr->datalen + AST_FRIENDLY_OFFSET);
186                 if (!out->data) {
187                         free(out);
188                         ast_log(LOG_WARNING, "Out of memory\n");
189                         return NULL;
190                 }
191                 out->data += AST_FRIENDLY_OFFSET;
192                 out->offset = AST_FRIENDLY_OFFSET;
193                 out->datalen = fr->datalen;
194                 memcpy(out->data, fr->data, fr->datalen);
195         }
196         out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA;
197         return out;
198 }
199
200 struct ast_frame *ast_frdup(struct ast_frame *f)
201 {
202         struct ast_frame *ret;
203         int p;
204         p = f->mallocd;
205         f->mallocd = 0;
206         /* Make frisolate think this is a 100% static frame, and make a duplicate */
207         ret = ast_frisolate(f);
208         /* Restore its true malloc status */
209         f->mallocd = p;
210         return ret;
211 }
212
213 struct ast_frame *ast_fr_fdread(int fd)
214 {
215         char buf[65536];
216         int res;
217         int ttl = sizeof(struct ast_frame);
218         struct ast_frame *f = (struct ast_frame *)buf;
219         /* Read a frame directly from there.  They're always in the
220            right format. */
221         
222         while(ttl) {
223                 res = read(fd, buf, ttl);
224                 if (res < 0) {
225                         ast_log(LOG_WARNING, "Bad read on %d: %s\n", fd, strerror(errno));
226                         return NULL;
227                 }
228                 ttl -= res;
229         }
230         
231         /* read the frame header */
232         f->mallocd = 0;
233         /* Re-write data position */
234         f->data = buf + sizeof(struct ast_frame);
235         f->offset = 0;
236         /* Forget about being mallocd */
237         f->mallocd = 0;
238         /* Re-write the source */
239         f->src = __FUNCTION__;
240         if (f->datalen > sizeof(buf) - sizeof(struct ast_frame)) {
241                 /* Really bad read */
242                 ast_log(LOG_WARNING, "Strange read (%d bytes)\n", f->datalen);
243                 return NULL;
244         }
245         if (f->datalen) {
246                 if ((res = read(fd, f->data, f->datalen)) != f->datalen) {
247                         /* Bad read */
248                         ast_log(LOG_WARNING, "How very strange, expected %d, got %d\n", f->datalen, res);
249                         return NULL;
250                 }
251         }
252         if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
253                 return NULL;
254         }
255         return ast_frisolate(f);
256 }
257
258 /* Some convenient routines for sending frames to/from stream or datagram
259    sockets, pipes, etc (maybe even files) */
260
261 int ast_fr_fdwrite(int fd, struct ast_frame *frame)
262 {
263         /* Write the frame exactly */
264         if (write(fd, frame, sizeof(struct ast_frame)) != sizeof(struct ast_frame)) {
265                 ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
266                 return -1;
267         }
268         if (write(fd, frame->data, frame->datalen) != frame->datalen) {
269                 ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
270                 return -1;
271         }
272         return 0;
273 }
274
275 int ast_fr_fdhangup(int fd)
276 {
277         struct ast_frame hangup = {
278                 AST_FRAME_CONTROL,
279                 AST_CONTROL_HANGUP
280         };
281         return ast_fr_fdwrite(fd, &hangup);
282 }
283
284 int ast_getformatbyname(char *name)
285 {
286         if (!strcasecmp(name, "g723.1")) 
287                 return AST_FORMAT_G723_1;
288         else if (!strcasecmp(name, "gsm"))
289                 return AST_FORMAT_GSM;
290         else if (!strcasecmp(name, "ulaw"))
291                 return AST_FORMAT_ULAW;
292         else if (!strcasecmp(name, "alaw"))
293                 return AST_FORMAT_ALAW;
294         else if (!strcasecmp(name, "mp3"))
295                 return AST_FORMAT_MP3;
296         else if (!strcasecmp(name, "slinear"))
297                 return AST_FORMAT_SLINEAR;
298         else if (!strcasecmp(name, "lpc10"))
299                 return AST_FORMAT_LPC10;
300         else if (!strcasecmp(name, "adpcm"))
301                 return AST_FORMAT_ADPCM;
302         else if (!strcasecmp(name, "speex"))
303                 return AST_FORMAT_SPEEX;
304         else if (!strcasecmp(name, "all"))
305                 return 0x7FFFFFFF;
306         return 0;
307 }
308
309 #ifdef TRACE_FRAMES
310 static int show_frame_stats(int fd, int argc, char *argv[])
311 {
312         struct ast_frame *f;
313         int x=1;
314         if (argc != 3)
315                 return RESULT_SHOWUSAGE;
316         ast_cli(fd, "     Framer Statistics     \n");
317         ast_cli(fd, "---------------------------\n");
318         ast_cli(fd, "Total allocated headers: %d\n", headers);
319         ast_cli(fd, "Queue Dump:\n");
320         ast_pthread_mutex_lock(&framelock);
321         for (f=headerlist; f; f = f->next) {
322                 ast_cli(fd, "%d.  Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
323         }
324         pthread_mutex_unlock(&framelock);
325         return RESULT_SUCCESS;
326 }
327
328 static char frame_stats_usage[] =
329 "Usage: show frame stats\n"
330 "       Displays debugging statistics from framer\n";
331
332 struct ast_cli_entry cli_frame_stats =
333 { { "show", "frame", "stats", NULL }, show_frame_stats, "Shows frame statistics", frame_stats_usage };
334 #endif
335
336 int init_framer(void)
337 {
338 #ifdef TRACE_FRAMES
339         ast_cli_register(&cli_frame_stats);
340 #endif
341         return 0;       
342 }