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