First pass at properly handling account codes in forwarding
[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 <asterisk/utils.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <stdio.h>
26 #include "asterisk.h"
27
28 #ifdef TRACE_FRAMES
29 static int headers = 0;
30 static struct ast_frame *headerlist = NULL;
31 AST_MUTEX_DEFINE_STATIC(framelock);
32 #endif
33
34 #define SMOOTHER_SIZE 8000
35
36 struct ast_smoother {
37         int size;
38         int format;
39         int readdata;
40         int optimizablestream;
41         int flags;
42         float samplesperbyte;
43         struct ast_frame f;
44         struct timeval delivery;
45         char data[SMOOTHER_SIZE];
46         char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET];
47         struct ast_frame *opt;
48         int len;
49 };
50
51 void ast_smoother_reset(struct ast_smoother *s, int size)
52 {
53         memset(s, 0, sizeof(struct ast_smoother));
54         s->size = size;
55 }
56
57 struct ast_smoother *ast_smoother_new(int size)
58 {
59         struct ast_smoother *s;
60         if (size < 1)
61                 return NULL;
62         s = malloc(sizeof(struct ast_smoother));
63         if (s)
64                 ast_smoother_reset(s, size);
65         return s;
66 }
67
68 int ast_smoother_get_flags(struct ast_smoother *s)
69 {
70         return s->flags;
71 }
72
73 void ast_smoother_set_flags(struct ast_smoother *s, int flags)
74 {
75         s->flags = flags;
76 }
77
78 int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f)
79 {
80         if (f->frametype != AST_FRAME_VOICE) {
81                 ast_log(LOG_WARNING, "Huh?  Can't smooth a non-voice frame!\n");
82                 return -1;
83         }
84         if (!s->format) {
85                 s->format = f->subclass;
86                 s->samplesperbyte = (float)f->samples / (float)f->datalen;
87         } else if (s->format != f->subclass) {
88                 ast_log(LOG_WARNING, "Smoother was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass);
89                 return -1;
90         }
91         if (s->len + f->datalen > SMOOTHER_SIZE) {
92                 ast_log(LOG_WARNING, "Out of smoother space\n");
93                 return -1;
94         }
95         if (((f->datalen == s->size) || ((f->datalen < 10) && (s->flags & AST_SMOOTHER_FLAG_G729)))
96                                  && !s->opt && (f->offset >= AST_MIN_OFFSET)) {
97                 if (!s->len) {
98                         /* Optimize by sending the frame we just got
99                            on the next read, thus eliminating the douple
100                            copy */
101                         s->opt = f;
102                         return 0;
103                 } else {
104                         s->optimizablestream++;
105                         if (s->optimizablestream > 10) {
106                                 /* For the past 10 rounds, we have input and output
107                                    frames of the correct size for this smoother, yet
108                                    we were unable to optimize because there was still
109                                    some cruft left over.  Lets just drop the cruft so
110                                    we can move to a fully optimized path */
111                                 s->len = 0;
112                                 s->opt = f;
113                                 return 0;
114                         }
115                 }
116         } else 
117                 s->optimizablestream = 0;
118         if (s->flags & AST_SMOOTHER_FLAG_G729) {
119                 if (s->len % 10) {
120                         ast_log(LOG_NOTICE, "Dropping extra frame of G.729 since we already have a VAD frame at the end\n");
121                         return 0;
122                 }
123         }
124         memcpy(s->data + s->len, f->data, f->datalen);
125         /* If either side is empty, reset the delivery time */
126         if (!s->len || (!f->delivery.tv_sec && !f->delivery.tv_usec) ||
127                         (!s->delivery.tv_sec && !s->delivery.tv_usec))
128                 s->delivery = f->delivery;
129         s->len += f->datalen;
130         return 0;
131 }
132
133 struct ast_frame *ast_smoother_read(struct ast_smoother *s)
134 {
135         struct ast_frame *opt;
136         int len;
137         /* IF we have an optimization frame, send it */
138         if (s->opt) {
139                 opt = s->opt;
140                 s->opt = NULL;
141                 return opt;
142         }
143
144         /* Make sure we have enough data */
145         if (s->len < s->size) {
146                 /* Or, if this is a G.729 frame with VAD on it, send it immediately anyway */
147                 if (!((s->flags & AST_SMOOTHER_FLAG_G729) && (s->size % 10)))
148                         return NULL;
149         }
150         len = s->size;
151         if (len > s->len)
152                 len = s->len;
153         /* Make frame */
154         s->f.frametype = AST_FRAME_VOICE;
155         s->f.subclass = s->format;
156         s->f.data = s->framedata + AST_FRIENDLY_OFFSET;
157         s->f.offset = AST_FRIENDLY_OFFSET;
158         s->f.datalen = len;
159         /* Samples will be improper given VAD, but with VAD the concept really doesn't even exist */
160         s->f.samples = len * s->samplesperbyte;
161         s->f.delivery = s->delivery;
162         /* Fill Data */
163         memcpy(s->f.data, s->data, len);
164         s->len -= len;
165         /* Move remaining data to the front if applicable */
166         if (s->len) {
167                 /* In principle this should all be fine because if we are sending
168                    G.729 VAD, the next timestamp will take over anyawy */
169                 memmove(s->data, s->data + len, s->len);
170                 if (s->delivery.tv_sec || s->delivery.tv_usec) {
171                         /* If we have delivery time, increment it, otherwise, leave it at 0 */
172                         s->delivery.tv_sec += (len * s->samplesperbyte) / 8000.0;
173                         s->delivery.tv_usec += (((int)(len * s->samplesperbyte)) % 8000) * 125;
174                         if (s->delivery.tv_usec > 1000000) {
175                                 s->delivery.tv_usec -= 1000000;
176                                 s->delivery.tv_sec += 1;
177                         }
178                 }
179         }
180         /* Return frame */
181         return &s->f;
182 }
183
184 void ast_smoother_free(struct ast_smoother *s)
185 {
186         free(s);
187 }
188
189 static struct ast_frame *ast_frame_header_new(void)
190 {
191         struct ast_frame *f;
192         f = malloc(sizeof(struct ast_frame));
193         if (f)
194                 memset(f, 0, sizeof(struct ast_frame));
195 #ifdef TRACE_FRAMES
196         if (f) {
197                 headers++;
198                 f->prev = NULL;
199                 ast_mutex_lock(&framelock);
200                 f->next = headerlist;
201                 if (headerlist)
202                         headerlist->prev = f;
203                 headerlist = f;
204                 ast_mutex_unlock(&framelock);
205         }
206 #endif  
207         return f;
208 }
209
210 /*
211  * Important: I should be made more efficient.  Frame headers should
212  * most definitely be cached
213  */
214
215 void ast_frfree(struct ast_frame *fr)
216 {
217         if (fr->mallocd & AST_MALLOCD_DATA) {
218                 if (fr->data) 
219                         free(fr->data - fr->offset);
220         }
221         if (fr->mallocd & AST_MALLOCD_SRC) {
222                 if (fr->src)
223                         free(fr->src);
224         }
225         if (fr->mallocd & AST_MALLOCD_HDR) {
226 #ifdef TRACE_FRAMES
227                 headers--;
228                 ast_mutex_lock(&framelock);
229                 if (fr->next)
230                         fr->next->prev = fr->prev;
231                 if (fr->prev)
232                         fr->prev->next = fr->next;
233                 else
234                         headerlist = fr->next;
235                 ast_mutex_unlock(&framelock);
236 #endif                  
237                 free(fr);
238         }
239 }
240
241 struct ast_frame *ast_frisolate(struct ast_frame *fr)
242 {
243         struct ast_frame *out;
244         if (!(fr->mallocd & AST_MALLOCD_HDR)) {
245                 /* Allocate a new header if needed */
246                 out = ast_frame_header_new();
247                 if (!out) {
248                         ast_log(LOG_WARNING, "Out of memory\n");
249                         return NULL;
250                 }
251                 out->frametype = fr->frametype;
252                 out->subclass = fr->subclass;
253                 out->datalen = 0;
254                 out->samples = fr->samples;
255                 out->offset = 0;
256                 out->src = NULL;
257                 out->data = NULL;
258         } else {
259                 out = fr;
260         }
261         if (!(fr->mallocd & AST_MALLOCD_SRC)) {
262                 if (fr->src)
263                         out->src = strdup(fr->src);
264         } else
265                 out->src = fr->src;
266         if (!(fr->mallocd & AST_MALLOCD_DATA))  {
267                 out->data = malloc(fr->datalen + AST_FRIENDLY_OFFSET);
268                 if (!out->data) {
269                         free(out);
270                         ast_log(LOG_WARNING, "Out of memory\n");
271                         return NULL;
272                 }
273                 out->data += AST_FRIENDLY_OFFSET;
274                 out->offset = AST_FRIENDLY_OFFSET;
275                 out->datalen = fr->datalen;
276                 memcpy(out->data, fr->data, fr->datalen);
277         }
278         out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA;
279         return out;
280 }
281
282 struct ast_frame *ast_frdup(struct ast_frame *f)
283 {
284         struct ast_frame *out;
285         int len, srclen = 0;
286         void *buf;
287         /* Start with standard stuff */
288         len = sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET + f->datalen;
289         /* If we have a source, add space for it */
290         if (f->src)
291                 srclen = strlen(f->src);
292         if (srclen > 0)
293                 len += srclen + 1;
294         buf = malloc(len);
295         if (!buf)
296                 return NULL;
297         out = buf;
298         /* Set us as having malloc'd header only, so it will eventually
299            get freed. */
300         out->frametype = f->frametype;
301         out->subclass = f->subclass;
302         out->datalen = f->datalen;
303         out->samples = f->samples;
304         out->delivery = f->delivery;
305         out->mallocd = AST_MALLOCD_HDR;
306         out->offset = AST_FRIENDLY_OFFSET;
307         out->data = buf + sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET;
308         if (srclen > 0) {
309                 out->src = out->data + f->datalen;
310                 /* Must have space since we allocated for it */
311                 strcpy(out->src, f->src);
312         } else
313                 out->src = NULL;
314         out->prev = NULL;
315         out->next = NULL;
316         memcpy(out->data, f->data, out->datalen);       
317         return out;
318 }
319
320 struct ast_frame *ast_fr_fdread(int fd)
321 {
322         char buf[65536];
323         int res;
324         int ttl = sizeof(struct ast_frame);
325         struct ast_frame *f = (struct ast_frame *)buf;
326         /* Read a frame directly from there.  They're always in the
327            right format. */
328         
329         while(ttl) {
330                 res = read(fd, buf, ttl);
331                 if (res < 0) {
332                         ast_log(LOG_WARNING, "Bad read on %d: %s\n", fd, strerror(errno));
333                         return NULL;
334                 }
335                 ttl -= res;
336         }
337         
338         /* read the frame header */
339         f->mallocd = 0;
340         /* Re-write data position */
341         f->data = buf + sizeof(struct ast_frame);
342         f->offset = 0;
343         /* Forget about being mallocd */
344         f->mallocd = 0;
345         /* Re-write the source */
346         f->src = __FUNCTION__;
347         if (f->datalen > sizeof(buf) - sizeof(struct ast_frame)) {
348                 /* Really bad read */
349                 ast_log(LOG_WARNING, "Strange read (%d bytes)\n", f->datalen);
350                 return NULL;
351         }
352         if (f->datalen) {
353                 if ((res = read(fd, f->data, f->datalen)) != f->datalen) {
354                         /* Bad read */
355                         ast_log(LOG_WARNING, "How very strange, expected %d, got %d\n", f->datalen, res);
356                         return NULL;
357                 }
358         }
359         if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
360                 return NULL;
361         }
362         return ast_frisolate(f);
363 }
364
365 /* Some convenient routines for sending frames to/from stream or datagram
366    sockets, pipes, etc (maybe even files) */
367
368 int ast_fr_fdwrite(int fd, struct ast_frame *frame)
369 {
370         /* Write the frame exactly */
371         if (write(fd, frame, sizeof(struct ast_frame)) != sizeof(struct ast_frame)) {
372                 ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
373                 return -1;
374         }
375         if (write(fd, frame->data, frame->datalen) != frame->datalen) {
376                 ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
377                 return -1;
378         }
379         return 0;
380 }
381
382 int ast_fr_fdhangup(int fd)
383 {
384         struct ast_frame hangup = {
385                 AST_FRAME_CONTROL,
386                 AST_CONTROL_HANGUP
387         };
388         return ast_fr_fdwrite(fd, &hangup);
389 }
390
391 char* ast_getformatname(int format)
392 {
393         if (format == AST_FORMAT_G723_1) 
394                 return "G723";
395         else if (format == AST_FORMAT_GSM)
396                 return "GSM";
397         else if (format == AST_FORMAT_ULAW)
398                 return "ULAW";
399         else if (format == AST_FORMAT_ALAW)
400                 return "ALAW";
401         else if (format == AST_FORMAT_G726)
402                 return "G726";
403         else if (format == AST_FORMAT_SLINEAR)
404                 return "SLINR";
405         else if (format == AST_FORMAT_LPC10)
406                 return "LPC10";
407         else if (format == AST_FORMAT_ADPCM)
408                 return "ADPCM";
409         else if (format == AST_FORMAT_G729A)
410                 return "G729A";
411         else if (format == AST_FORMAT_SPEEX)
412                 return "SPEEX";
413         else if (format == AST_FORMAT_ILBC)
414                 return "ILBC";
415         else if (format == AST_FORMAT_JPEG)
416                 return "JPEG";
417         else if (format == AST_FORMAT_PNG)
418                 return "PNG";
419         else if (format == AST_FORMAT_H261)
420                 return "H261";
421         else if (format == AST_FORMAT_H263)
422                 return "H263";
423         return "UNKN";
424 }
425
426 char* ast_getformatname_multiple(char *buf, unsigned n, int format) {
427         unsigned u=1;
428         unsigned len;
429         char *b = buf;
430         char *start = buf;
431         if (!n) return buf;
432         snprintf(b,n,"0x%x(",format);
433         len = strlen(b);
434         b += len;
435         n -= len;
436         start = b;
437         while (u) {
438                 if (u&format) {
439                         snprintf(b,n,"%s|",ast_getformatname(u));
440                         len = strlen(b);
441                         b += len;
442                         n -= len;
443                 }
444                 u *= 2;
445         }
446         if (start==b)
447                 snprintf(start,n,"EMPTY)");
448         else if (n>1)
449                 b[-1]=')';
450         return buf;
451 }
452
453 int ast_getformatbyname(char *name)
454 {
455         if (!strcasecmp(name, "g723.1")) 
456                 return AST_FORMAT_G723_1;
457         else if (!strcasecmp(name, "gsm"))
458                 return AST_FORMAT_GSM;
459         else if (!strcasecmp(name, "ulaw"))
460                 return AST_FORMAT_ULAW;
461         else if (!strcasecmp(name, "alaw"))
462                 return AST_FORMAT_ALAW;
463         else if (!strcasecmp(name, "g726"))
464                 return AST_FORMAT_G726;
465         else if (!strcasecmp(name, "slinear"))
466                 return AST_FORMAT_SLINEAR;
467         else if (!strcasecmp(name, "lpc10"))
468                 return AST_FORMAT_LPC10;
469         else if (!strcasecmp(name, "adpcm"))
470                 return AST_FORMAT_ADPCM;
471         else if (!strcasecmp(name, "g729"))
472                 return AST_FORMAT_G729A;
473         else if (!strcasecmp(name, "speex"))
474                 return AST_FORMAT_SPEEX;
475         else if (!strcasecmp(name, "ilbc"))
476                 return AST_FORMAT_ILBC;
477         else if (!strcasecmp(name, "h261"))
478                 return AST_FORMAT_H261;
479         else if (!strcasecmp(name, "h263"))
480                 return AST_FORMAT_H263;
481         else if (!strcasecmp(name, "all"))
482                 return 0x7FFFFFFF;
483         return 0;
484 }
485
486 char *ast_codec2str(int codec) {
487         static char codecs[25][30] = {
488                 /* Audio formats */
489                 "G.723.1",                    /*  0 */
490                 "GSM",                        /*  1 */
491                 "G.711 u-law",                /*  2 */
492                 "G.711 A-law",                /*  3 */
493                 "G.726",                      /*  4 */
494                 "ADPCM",                      /*  5 */
495                 "16 bit Signed Linear PCM",   /*  6 */
496                 "LPC10",                      /*  7 */
497                 "G.729A audio",               /*  8 */
498                 "SpeeX",                      /*  9 */
499                 "iLBC",                       /* 10 */
500                 "undefined",                  /* 11 */
501                 "undefined",                  /* 12 */
502                 "undefined",                  /* 13 */
503                 "undefined",                  /* 14 */
504                 "Maximum audio format",       /* 15 */
505                 /* Image formats */
506                 "JPEG image",                 /* 16 */
507                 "PNG image",                  /* 17 */
508                 "H.261 Video",                /* 18 */
509                 "H.263 Video",                /* 19 */
510                 "undefined",                  /* 20 */
511                 "undefined",                  /* 21 */
512                 "undefined",                  /* 22 */
513                 "undefined",                  /* 23 */
514                 "Maximum video format",       /* 24 */
515                 };
516         if ((codec >= 0) && (codec <= 24))
517                 return codecs[codec];
518         else
519                 return "unknown";
520 }
521
522 static int show_codecs(int fd, int argc, char *argv[])
523 {
524         int i, found=0;
525
526         if ((argc < 2) || (argc > 3))
527                 return RESULT_SHOWUSAGE;
528
529         if (getenv("I_AM_NOT_AN_IDIOT") == NULL)
530                 ast_cli(fd, "Disclaimer: this command is for informational purposes only.\n"
531                                 "\tIt does not indicate anything about your configuration.\n");
532
533         if ((argc == 2) || (!strcasecmp(argv[1],"audio"))) {
534                 found = 1;
535                 for (i=0;i<11;i++)
536                         ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(i));
537         }
538
539         if ((argc == 2) || (!strcasecmp(argv[1],"image"))) {
540                 found = 1;
541                 for (i=16;i<18;i++)
542                         ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(i));
543         }
544
545         if ((argc == 2) || (!strcasecmp(argv[1],"video"))) {
546                 found = 1;
547                 for (i=18;i<20;i++)
548                         ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(i));
549         }
550
551         if (! found)
552                 return RESULT_SHOWUSAGE;
553         else
554                 return RESULT_SUCCESS;
555 }
556
557 static char frame_show_codecs_usage[] =
558 "Usage: show [audio|video|image] codecs\n"
559 "       Displays codec mapping\n";
560
561 struct ast_cli_entry cli_show_codecs =
562 { { "show", "codecs", NULL }, show_codecs, "Shows codecs", frame_show_codecs_usage };
563 struct ast_cli_entry cli_show_codecs_audio =
564 { { "show", "audio", "codecs", NULL }, show_codecs, "Shows audio codecs", frame_show_codecs_usage };
565 struct ast_cli_entry cli_show_codecs_video =
566 { { "show", "video", "codecs", NULL }, show_codecs, "Shows video codecs", frame_show_codecs_usage };
567 struct ast_cli_entry cli_show_codecs_image =
568 { { "show", "image", "codecs", NULL }, show_codecs, "Shows image codecs", frame_show_codecs_usage };
569
570 static int show_codec_n(int fd, int argc, char *argv[])
571 {
572         int codec, i, found=0;
573
574         if (argc != 3)
575                 return RESULT_SHOWUSAGE;
576
577         if (sscanf(argv[2],"%d",&codec) != 1)
578                 return RESULT_SHOWUSAGE;
579
580         for (i=0;i<32;i++)
581                 if (codec & (1 << i)) {
582                         found = 1;
583                         ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(i));
584                 }
585
586         if (! found)
587                 ast_cli(fd, "Codec %d not found\n", codec);
588
589         return RESULT_SUCCESS;
590 }
591
592 static char frame_show_codec_n_usage[] =
593 "Usage: show codec <number>\n"
594 "       Displays codec mapping\n";
595
596 struct ast_cli_entry cli_show_codec_n =
597 { { "show", "codec", NULL }, show_codec_n, "Shows a specific codec", frame_show_codec_n_usage };
598
599 void ast_frame_dump(char *name, struct ast_frame *f, char *prefix)
600 {
601         char *n = "unknown";
602         char ftype[40] = "Unknown Frametype";
603         char cft[80];
604         char subclass[40] = "Unknown Subclass";
605         char csub[80];
606         char moreinfo[40] = "";
607         char cn[40];
608         char cp[40];
609         char cmn[40];
610         if (name)
611                 n = name;
612         if (!f) {
613                 ast_verbose("%s [ %s (NULL) ] [%s]\n", 
614                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
615                         term_color(cft, "HANGUP", COLOR_BRRED, COLOR_BLACK, sizeof(cft)), 
616                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
617                 return;
618         }
619         /* XXX We should probably print one each of voice and video when the format changes XXX */
620         if (f->frametype == AST_FRAME_VOICE)
621                 return;
622         if (f->frametype == AST_FRAME_VIDEO)
623                 return;
624         switch(f->frametype) {
625         case AST_FRAME_DTMF:
626                 strcpy(ftype, "DTMF");
627                 subclass[0] = f->subclass;
628                 subclass[1] = '\0';
629                 break;
630         case AST_FRAME_CONTROL:
631                 strcpy(ftype, "Control");
632                 switch(f->subclass) {
633                 case AST_CONTROL_HANGUP:
634                         strcpy(subclass, "Hangup");
635                         break;
636                 case AST_CONTROL_RING:
637                         strcpy(subclass, "Ring");
638                         break;
639                 case AST_CONTROL_RINGING:
640                         strcpy(subclass, "Ringing");
641                         break;
642                 case AST_CONTROL_ANSWER:
643                         strcpy(subclass, "Answer");
644                         break;
645                 case AST_CONTROL_BUSY:
646                         strcpy(subclass, "Busy");
647                         break;
648                 case AST_CONTROL_TAKEOFFHOOK:
649                         strcpy(subclass, "Take Off Hook");
650                         break;
651                 case AST_CONTROL_OFFHOOK:
652                         strcpy(subclass, "Line Off Hook");
653                         break;
654                 case AST_CONTROL_CONGESTION:
655                         strcpy(subclass, "Congestion");
656                         break;
657                 case AST_CONTROL_FLASH:
658                         strcpy(subclass, "Flash");
659                         break;
660                 case AST_CONTROL_WINK:
661                         strcpy(subclass, "Wink");
662                         break;
663                 case AST_CONTROL_OPTION:
664                         strcpy(subclass, "Option");
665                         break;
666                 case AST_CONTROL_RADIO_KEY:
667                         strcpy(subclass, "Key Radio");
668                         break;
669                 case AST_CONTROL_RADIO_UNKEY:
670                         strcpy(subclass, "Unkey Radio");
671                         break;
672                 default:
673                         snprintf(subclass, sizeof(subclass), "Unknown control '%d'", f->subclass);
674                 }
675         case AST_FRAME_NULL:
676                 strcpy(ftype, "Null Frame");
677                 strcpy(subclass, "N/A");
678                 break;
679         case AST_FRAME_IAX:
680                 /* Should never happen */
681                 strcpy(ftype, "IAX Specific");
682                 snprintf(subclass, sizeof(subclass), "IAX Frametype %d", f->subclass);
683                 break;
684         case AST_FRAME_TEXT:
685                 strcpy(ftype, "Text");
686                 strcpy(subclass, "N/A");
687                 strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
688                 break;
689         case AST_FRAME_IMAGE:
690                 strcpy(ftype, "Image");
691                 snprintf(subclass, sizeof(subclass), "Image format %s\n", ast_getformatname(f->subclass));
692                 break;
693         case AST_FRAME_HTML:
694                 strcpy(ftype, "HTML");
695                 switch(f->subclass) {
696                 case AST_HTML_URL:
697                         strcpy(subclass, "URL");
698                         strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
699                         break;
700                 case AST_HTML_DATA:
701                         strcpy(subclass, "Data");
702                         break;
703                 case AST_HTML_BEGIN:
704                         strcpy(subclass, "Begin");
705                         break;
706                 case AST_HTML_END:
707                         strcpy(subclass, "End");
708                         break;
709                 case AST_HTML_LDCOMPLETE:
710                         strcpy(subclass, "Load Complete");
711                         break;
712                 case AST_HTML_NOSUPPORT:
713                         strcpy(subclass, "No Support");
714                         break;
715                 case AST_HTML_LINKURL:
716                         strcpy(subclass, "Link URL");
717                         strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
718                         break;
719                 case AST_HTML_UNLINK:
720                         strcpy(subclass, "Unlink");
721                         break;
722                 case AST_HTML_LINKREJECT:
723                         strcpy(subclass, "Link Reject");
724                         break;
725                 default:
726                         snprintf(subclass, sizeof(subclass), "Unknown HTML frame '%d'\n", f->subclass);
727                         break;
728                 }
729                 break;
730         default:
731                 snprintf(ftype, sizeof(ftype), "Unknown Frametype '%d'", f->frametype);
732         }
733         if (!ast_strlen_zero(moreinfo))
734                 ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) '%s' ] [%s]\n",  
735                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
736                         term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
737                         f->frametype, 
738                         term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
739                         f->subclass, 
740                         term_color(cmn, moreinfo, COLOR_BRGREEN, COLOR_BLACK, sizeof(cmn)),
741                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
742         else
743                 ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) ] [%s]\n",  
744                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
745                         term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
746                         f->frametype, 
747                         term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
748                         f->subclass, 
749                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
750
751 }
752
753
754 #ifdef TRACE_FRAMES
755 static int show_frame_stats(int fd, int argc, char *argv[])
756 {
757         struct ast_frame *f;
758         int x=1;
759         if (argc != 3)
760                 return RESULT_SHOWUSAGE;
761         ast_cli(fd, "     Framer Statistics     \n");
762         ast_cli(fd, "---------------------------\n");
763         ast_cli(fd, "Total allocated headers: %d\n", headers);
764         ast_cli(fd, "Queue Dump:\n");
765         ast_mutex_lock(&framelock);
766         for (f=headerlist; f; f = f->next) {
767                 ast_cli(fd, "%d.  Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
768         }
769         ast_mutex_unlock(&framelock);
770         return RESULT_SUCCESS;
771 }
772
773 static char frame_stats_usage[] =
774 "Usage: show frame stats\n"
775 "       Displays debugging statistics from framer\n";
776
777 struct ast_cli_entry cli_frame_stats =
778 { { "show", "frame", "stats", NULL }, show_frame_stats, "Shows frame statistics", frame_stats_usage };
779 #endif
780
781 int init_framer(void)
782 {
783 #ifdef TRACE_FRAMES
784         ast_cli_register(&cli_frame_stats);
785 #endif
786         ast_cli_register(&cli_show_codecs);
787         ast_cli_register(&cli_show_codecs_audio);
788         ast_cli_register(&cli_show_codecs_video);
789         ast_cli_register(&cli_show_codecs_image);
790         ast_cli_register(&cli_show_codec_n);
791         return 0;       
792 }