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