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