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