Tue Mar 18 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 <asterisk/term.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <pthread.h>
25 #include "asterisk.h"
26
27 #ifdef TRACE_FRAMES
28 static int headers = 0;
29 static struct ast_frame *headerlist = NULL;
30 static pthread_mutex_t framelock = AST_MUTEX_INITIALIZER;
31 #endif
32
33 #define SMOOTHER_SIZE 8000
34
35 struct ast_smoother {
36         int size;
37         int format;
38         int readdata;
39         int optimizablestream;
40         float samplesperbyte;
41         struct ast_frame f;
42         char data[SMOOTHER_SIZE];
43         char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET];
44         struct ast_frame *opt;
45         int len;
46 };
47
48 void ast_smoother_reset(struct ast_smoother *s, int size)
49 {
50         memset(s, 0, sizeof(struct ast_smoother));
51         s->size = size;
52 }
53
54 struct ast_smoother *ast_smoother_new(int size)
55 {
56         struct ast_smoother *s;
57         if (size < 1)
58                 return NULL;
59         s = malloc(sizeof(struct ast_smoother));
60         if (s)
61                 ast_smoother_reset(s, size);
62         return s;
63 }
64
65 int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f)
66 {
67         if (f->frametype != AST_FRAME_VOICE) {
68                 ast_log(LOG_WARNING, "Huh?  Can't smooth a non-voice frame!\n");
69                 return -1;
70         }
71         if (!s->format) {
72                 s->format = f->subclass;
73                 s->samplesperbyte = (float)f->samples / (float)f->datalen;
74         } else if (s->format != f->subclass) {
75                 ast_log(LOG_WARNING, "Smoother was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass);
76                 return -1;
77         }
78         if (s->len + f->datalen > SMOOTHER_SIZE) {
79                 ast_log(LOG_WARNING, "Out of smoother space\n");
80                 return -1;
81         }
82         if ((f->datalen == s->size) && !s->opt) {
83                 if (!s->len) {
84                         /* Optimize by sending the frame we just got
85                            on the next read, thus eliminating the douple
86                            copy */
87                         s->opt = f;
88                         return 0;
89                 } else {
90                         s->optimizablestream++;
91                         if (s->optimizablestream > 10) {
92                                 /* For the past 10 rounds, we have input and output
93                                    frames of the correct size for this smoother, yet
94                                    we were unable to optimize because there was still
95                                    some cruft left over.  Lets just drop the cruft so
96                                    we can move to a fully optimized path */
97                                 s->len = 0;
98                                 s->opt = f;
99                                 return 0;
100                         }
101                 }
102         } else 
103                 s->optimizablestream = 0;
104         memcpy(s->data + s->len, f->data, f->datalen);
105         s->len += f->datalen;
106         return 0;
107 }
108
109 struct ast_frame *ast_smoother_read(struct ast_smoother *s)
110 {
111         struct ast_frame *opt;
112
113         /* IF we have an optimization frame, send it */
114         if (s->opt) {
115                 opt = s->opt;
116                 s->opt = NULL;
117                 return opt;
118         }
119
120         /* Make sure we have enough data */
121         if (s->len < s->size) {
122                 return NULL;
123         }
124         /* Make frame */
125         s->f.frametype = AST_FRAME_VOICE;
126         s->f.subclass = s->format;
127         s->f.data = s->framedata + AST_FRIENDLY_OFFSET;
128         s->f.offset = AST_FRIENDLY_OFFSET;
129         s->f.datalen = s->size;
130         s->f.samples = s->size * s->samplesperbyte;
131         /* Fill Data */
132         memcpy(s->f.data, s->data, s->size);
133         s->len -= s->size;
134         /* Move remaining data to the front if applicable */
135         if (s->len) 
136                 memmove(s->data, s->data + s->size, s->len);
137         /* Return frame */
138         return &s->f;
139 }
140
141 void ast_smoother_free(struct ast_smoother *s)
142 {
143         free(s);
144 }
145
146 static struct ast_frame *ast_frame_header_new(void)
147 {
148         struct ast_frame *f;
149         f = malloc(sizeof(struct ast_frame));
150         if (f)
151                 memset(f, 0, sizeof(struct ast_frame));
152 #ifdef TRACE_FRAMES
153         if (f) {
154                 headers++;
155                 f->prev = NULL;
156                 ast_pthread_mutex_lock(&framelock);
157                 f->next = headerlist;
158                 if (headerlist)
159                         headerlist->prev = f;
160                 headerlist = f;
161                 pthread_mutex_unlock(&framelock);
162         }
163 #endif  
164         return f;
165 }
166
167 /*
168  * Important: I should be made more efficient.  Frame headers should
169  * most definitely be cached
170  */
171
172 void ast_frfree(struct ast_frame *fr)
173 {
174         if (fr->mallocd & AST_MALLOCD_DATA) {
175                 if (fr->data) 
176                         free(fr->data - fr->offset);
177         }
178         if (fr->mallocd & AST_MALLOCD_SRC) {
179                 if (fr->src)
180                         free(fr->src);
181         }
182         if (fr->mallocd & AST_MALLOCD_HDR) {
183 #ifdef TRACE_FRAMES
184                 headers--;
185                 ast_pthread_mutex_lock(&framelock);
186                 if (fr->next)
187                         fr->next->prev = fr->prev;
188                 if (fr->prev)
189                         fr->prev->next = fr->next;
190                 else
191                         headerlist = fr->next;
192                 pthread_mutex_unlock(&framelock);
193 #endif                  
194                 free(fr);
195         }
196 }
197
198 struct ast_frame *ast_frisolate(struct ast_frame *fr)
199 {
200         struct ast_frame *out;
201         if (!(fr->mallocd & AST_MALLOCD_HDR)) {
202                 /* Allocate a new header if needed */
203                 out = ast_frame_header_new();
204                 if (!out) {
205                         ast_log(LOG_WARNING, "Out of memory\n");
206                         return NULL;
207                 }
208                 out->frametype = fr->frametype;
209                 out->subclass = fr->subclass;
210                 out->datalen = 0;
211                 out->samples = fr->samples;
212                 out->offset = 0;
213                 out->src = NULL;
214                 out->data = NULL;
215         } else {
216                 out = fr;
217         }
218         if (!(fr->mallocd & AST_MALLOCD_SRC)) {
219                 if (fr->src)
220                         out->src = strdup(fr->src);
221         } else
222                 out->src = fr->src;
223         if (!(fr->mallocd & AST_MALLOCD_DATA))  {
224                 out->data = malloc(fr->datalen + AST_FRIENDLY_OFFSET);
225                 if (!out->data) {
226                         free(out);
227                         ast_log(LOG_WARNING, "Out of memory\n");
228                         return NULL;
229                 }
230                 out->data += AST_FRIENDLY_OFFSET;
231                 out->offset = AST_FRIENDLY_OFFSET;
232                 out->datalen = fr->datalen;
233                 memcpy(out->data, fr->data, fr->datalen);
234         }
235         out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA;
236         return out;
237 }
238
239 struct ast_frame *ast_frdup(struct ast_frame *f)
240 {
241         struct ast_frame *out;
242         int len;
243         void *buf;
244         /* Start with standard stuff */
245         len = sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET + f->datalen;
246         /* If we have a source, add space for it */
247         if (f->src && strlen(f->src))
248                 len += strlen(f->src) + 1;
249         buf = malloc(len);
250         if (!buf)
251                 return NULL;
252         out = buf;
253         /* Set us as having malloc'd header only, so it will eventually
254            get freed. */
255         out->frametype = f->frametype;
256         out->subclass = f->subclass;
257         out->datalen = f->datalen;
258         out->samples = f->samples;
259         out->mallocd = AST_MALLOCD_HDR;
260         out->offset = AST_FRIENDLY_OFFSET;
261         out->data = buf + sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET;
262         if (f->src && strlen(f->src)) {
263                 out->src = out->data + f->datalen;
264                 /* Must have space since we allocated for it */
265                 strcpy(out->src, f->src);
266         } else
267                 out->src = NULL;
268         out->prev = NULL;
269         out->next = NULL;
270         memcpy(out->data, f->data, out->datalen);       
271         return out;
272 }
273
274 struct ast_frame *ast_fr_fdread(int fd)
275 {
276         char buf[65536];
277         int res;
278         int ttl = sizeof(struct ast_frame);
279         struct ast_frame *f = (struct ast_frame *)buf;
280         /* Read a frame directly from there.  They're always in the
281            right format. */
282         
283         while(ttl) {
284                 res = read(fd, buf, ttl);
285                 if (res < 0) {
286                         ast_log(LOG_WARNING, "Bad read on %d: %s\n", fd, strerror(errno));
287                         return NULL;
288                 }
289                 ttl -= res;
290         }
291         
292         /* read the frame header */
293         f->mallocd = 0;
294         /* Re-write data position */
295         f->data = buf + sizeof(struct ast_frame);
296         f->offset = 0;
297         /* Forget about being mallocd */
298         f->mallocd = 0;
299         /* Re-write the source */
300         f->src = __FUNCTION__;
301         if (f->datalen > sizeof(buf) - sizeof(struct ast_frame)) {
302                 /* Really bad read */
303                 ast_log(LOG_WARNING, "Strange read (%d bytes)\n", f->datalen);
304                 return NULL;
305         }
306         if (f->datalen) {
307                 if ((res = read(fd, f->data, f->datalen)) != f->datalen) {
308                         /* Bad read */
309                         ast_log(LOG_WARNING, "How very strange, expected %d, got %d\n", f->datalen, res);
310                         return NULL;
311                 }
312         }
313         if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
314                 return NULL;
315         }
316         return ast_frisolate(f);
317 }
318
319 /* Some convenient routines for sending frames to/from stream or datagram
320    sockets, pipes, etc (maybe even files) */
321
322 int ast_fr_fdwrite(int fd, struct ast_frame *frame)
323 {
324         /* Write the frame exactly */
325         if (write(fd, frame, sizeof(struct ast_frame)) != sizeof(struct ast_frame)) {
326                 ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
327                 return -1;
328         }
329         if (write(fd, frame->data, frame->datalen) != frame->datalen) {
330                 ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
331                 return -1;
332         }
333         return 0;
334 }
335
336 int ast_fr_fdhangup(int fd)
337 {
338         struct ast_frame hangup = {
339                 AST_FRAME_CONTROL,
340                 AST_CONTROL_HANGUP
341         };
342         return ast_fr_fdwrite(fd, &hangup);
343 }
344
345 int ast_getformatbyname(char *name)
346 {
347         if (!strcasecmp(name, "g723.1")) 
348                 return AST_FORMAT_G723_1;
349         else if (!strcasecmp(name, "gsm"))
350                 return AST_FORMAT_GSM;
351         else if (!strcasecmp(name, "ulaw"))
352                 return AST_FORMAT_ULAW;
353         else if (!strcasecmp(name, "alaw"))
354                 return AST_FORMAT_ALAW;
355         else if (!strcasecmp(name, "mp3"))
356                 return AST_FORMAT_MP3;
357         else if (!strcasecmp(name, "slinear"))
358                 return AST_FORMAT_SLINEAR;
359         else if (!strcasecmp(name, "lpc10"))
360                 return AST_FORMAT_LPC10;
361         else if (!strcasecmp(name, "adpcm"))
362                 return AST_FORMAT_ADPCM;
363         else if (!strcasecmp(name, "g729"))
364                 return AST_FORMAT_G729A;
365         else if (!strcasecmp(name, "speex"))
366                 return AST_FORMAT_SPEEX;
367         else if (!strcasecmp(name, "all"))
368                 return 0x7FFFFFFF;
369         return 0;
370 }
371
372 void ast_frame_dump(char *name, struct ast_frame *f, char *prefix)
373 {
374         char *n = "unknown";
375         char ftype[40] = "Unknown Frametype";
376         char cft[80];
377         char subclass[40] = "Unknown Subclass";
378         char csub[80];
379         char moreinfo[40] = "";
380         char cn[40];
381         char cp[40];
382         char cmn[40];
383         if (name)
384                 n = name;
385         if (!f) {
386                 ast_verbose("%s [ %s (NULL) ] [%s]\n", 
387                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
388                         term_color(cft, "HANGUP", COLOR_BRRED, COLOR_BLACK, sizeof(cft)), 
389                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
390                 return;
391         }
392         /* XXX We should probably print one each of voice and video when the format changes XXX */
393         if (f->frametype == AST_FRAME_VOICE)
394                 return;
395         if (f->frametype == AST_FRAME_VIDEO)
396                 return;
397         switch(f->frametype) {
398         case AST_FRAME_DTMF:
399                 strcpy(ftype, "DTMF");
400                 subclass[0] = f->subclass;
401                 subclass[1] = '\0';
402                 break;
403         case AST_FRAME_CONTROL:
404                 strcpy(ftype, "Control");
405                 switch(f->subclass) {
406                 case AST_CONTROL_HANGUP:
407                         strcpy(subclass, "Hangup");
408                         break;
409                 case AST_CONTROL_RING:
410                         strcpy(subclass, "Ring");
411                         break;
412                 case AST_CONTROL_RINGING:
413                         strcpy(subclass, "Ringing");
414                         break;
415                 case AST_CONTROL_ANSWER:
416                         strcpy(subclass, "Answer");
417                         break;
418                 case AST_CONTROL_BUSY:
419                         strcpy(subclass, "Busy");
420                         break;
421                 case AST_CONTROL_TAKEOFFHOOK:
422                         strcpy(subclass, "Take Off Hook");
423                         break;
424                 case AST_CONTROL_OFFHOOK:
425                         strcpy(subclass, "Line Off Hook");
426                         break;
427                 case AST_CONTROL_CONGESTION:
428                         strcpy(subclass, "Congestion");
429                         break;
430                 case AST_CONTROL_FLASH:
431                         strcpy(subclass, "Flash");
432                         break;
433                 case AST_CONTROL_WINK:
434                         strcpy(subclass, "Wink");
435                         break;
436                 case AST_CONTROL_OPTION:
437                         strcpy(subclass, "Option");
438                         break;
439                 case AST_CONTROL_RADIO_KEY:
440                         strcpy(subclass, "Key Radio");
441                         break;
442                 case AST_CONTROL_RADIO_UNKEY:
443                         strcpy(subclass, "Unkey Radio");
444                         break;
445                 default:
446                         snprintf(subclass, sizeof(subclass), "Unknown control '%d'", f->subclass);
447                 }
448         case AST_FRAME_NULL:
449                 strcpy(ftype, "Null Frame");
450                 strcpy(subclass, "N/A");
451                 break;
452         case AST_FRAME_IAX:
453                 /* Should never happen */
454                 strcpy(ftype, "IAX Specific");
455                 snprintf(subclass, sizeof(subclass), "IAX Frametype %d", f->subclass);
456                 break;
457         case AST_FRAME_TEXT:
458                 strcpy(ftype, "Text");
459                 strcpy(subclass, "N/A");
460                 strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
461                 break;
462         case AST_FRAME_IMAGE:
463                 strcpy(ftype, "Image");
464                 snprintf(subclass, sizeof(subclass), "Image format %d\n", f->subclass);
465                 break;
466         case AST_FRAME_HTML:
467                 strcpy(ftype, "HTML");
468                 switch(f->subclass) {
469                 case AST_HTML_URL:
470                         strcpy(subclass, "URL");
471                         strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
472                         break;
473                 case AST_HTML_DATA:
474                         strcpy(subclass, "Data");
475                         break;
476                 case AST_HTML_BEGIN:
477                         strcpy(subclass, "Begin");
478                         break;
479                 case AST_HTML_END:
480                         strcpy(subclass, "End");
481                         break;
482                 case AST_HTML_LDCOMPLETE:
483                         strcpy(subclass, "Load Complete");
484                         break;
485                 case AST_HTML_NOSUPPORT:
486                         strcpy(subclass, "No Support");
487                         break;
488                 case AST_HTML_LINKURL:
489                         strcpy(subclass, "Link URL");
490                         strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
491                         break;
492                 case AST_HTML_UNLINK:
493                         strcpy(subclass, "Unlink");
494                         break;
495                 case AST_HTML_LINKREJECT:
496                         strcpy(subclass, "Link Reject");
497                         break;
498                 default:
499                         snprintf(subclass, sizeof(subclass), "Unknown HTML frame '%d'\n", f->subclass);
500                         break;
501                 }
502                 break;
503         default:
504                 snprintf(ftype, sizeof(ftype), "Unknown Frametype '%d'", f->frametype);
505         }
506         if (strlen(moreinfo))
507                 ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) '%s' ] [%s]\n",  
508                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
509                         term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
510                         f->frametype, 
511                         term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
512                         f->subclass, 
513                         term_color(cmn, moreinfo, COLOR_BRGREEN, COLOR_BLACK, sizeof(cmn)),
514                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
515         else
516                 ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) ] [%s]\n",  
517                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
518                         term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
519                         f->frametype, 
520                         term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
521                         f->subclass, 
522                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
523
524 }
525
526
527 #ifdef TRACE_FRAMES
528 static int show_frame_stats(int fd, int argc, char *argv[])
529 {
530         struct ast_frame *f;
531         int x=1;
532         if (argc != 3)
533                 return RESULT_SHOWUSAGE;
534         ast_cli(fd, "     Framer Statistics     \n");
535         ast_cli(fd, "---------------------------\n");
536         ast_cli(fd, "Total allocated headers: %d\n", headers);
537         ast_cli(fd, "Queue Dump:\n");
538         ast_pthread_mutex_lock(&framelock);
539         for (f=headerlist; f; f = f->next) {
540                 ast_cli(fd, "%d.  Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
541         }
542         pthread_mutex_unlock(&framelock);
543         return RESULT_SUCCESS;
544 }
545
546 static char frame_stats_usage[] =
547 "Usage: show frame stats\n"
548 "       Displays debugging statistics from framer\n";
549
550 struct ast_cli_entry cli_frame_stats =
551 { { "show", "frame", "stats", NULL }, show_frame_stats, "Shows frame statistics", frame_stats_usage };
552 #endif
553
554 int init_framer(void)
555 {
556 #ifdef TRACE_FRAMES
557         ast_cli_register(&cli_frame_stats);
558 #endif
559         return 0;       
560 }