ast_frdup optimization: only call strlen once and save the result
[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         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 int ast_getformatbyname(char *name)
427 {
428         if (!strcasecmp(name, "g723.1")) 
429                 return AST_FORMAT_G723_1;
430         else if (!strcasecmp(name, "gsm"))
431                 return AST_FORMAT_GSM;
432         else if (!strcasecmp(name, "ulaw"))
433                 return AST_FORMAT_ULAW;
434         else if (!strcasecmp(name, "alaw"))
435                 return AST_FORMAT_ALAW;
436         else if (!strcasecmp(name, "g726"))
437                 return AST_FORMAT_G726;
438         else if (!strcasecmp(name, "slinear"))
439                 return AST_FORMAT_SLINEAR;
440         else if (!strcasecmp(name, "lpc10"))
441                 return AST_FORMAT_LPC10;
442         else if (!strcasecmp(name, "adpcm"))
443                 return AST_FORMAT_ADPCM;
444         else if (!strcasecmp(name, "g729"))
445                 return AST_FORMAT_G729A;
446         else if (!strcasecmp(name, "speex"))
447                 return AST_FORMAT_SPEEX;
448         else if (!strcasecmp(name, "ilbc"))
449                 return AST_FORMAT_ILBC;
450         else if (!strcasecmp(name, "h261"))
451                 return AST_FORMAT_H261;
452         else if (!strcasecmp(name, "h263"))
453                 return AST_FORMAT_H263;
454         else if (!strcasecmp(name, "all"))
455                 return 0x7FFFFFFF;
456         return 0;
457 }
458
459 char *ast_codec2str(int codec) {
460         static char codecs[25][30] = {
461                 /* Audio formats */
462                 "G.723.1",                    /*  0 */
463                 "GSM",                        /*  1 */
464                 "G.711 u-law",                /*  2 */
465                 "G.711 A-law",                /*  3 */
466                 "G.726",                      /*  4 */
467                 "ADPCM",                      /*  5 */
468                 "16 bit Signed Linear PCM",   /*  6 */
469                 "LPC10",                      /*  7 */
470                 "G.729A audio",               /*  8 */
471                 "SpeeX",                      /*  9 */
472                 "iLBC",                       /* 10 */
473                 "undefined",                  /* 11 */
474                 "undefined",                  /* 12 */
475                 "undefined",                  /* 13 */
476                 "undefined",                  /* 14 */
477                 "Maximum audio format",       /* 15 */
478         /* Image formats */
479                 "JPEG image",                 /* 16 */
480                 "PNG image",                  /* 17 */
481                 "H.261 Video",                /* 18 */
482                 "H.263 Video",                /* 19 */
483                 "undefined",                  /* 20 */
484                 "undefined",                  /* 21 */
485                 "undefined",                  /* 22 */
486                 "undefined",                  /* 23 */
487         "Maximum video format",       /* 24 */
488                 };
489         if ((codec >= 0) && (codec <= 24))
490                 return codecs[codec];
491         else
492                 return "unknown";
493 }
494
495 static int show_codecs(int fd, int argc, char *argv[])
496 {
497         int i, found=0;
498
499         if ((argc < 2) || (argc > 3))
500                 return RESULT_SHOWUSAGE;
501
502         if (getenv("I_AM_NOT_AN_IDIOT") == NULL)
503                 ast_cli(fd, "Disclaimer: this command is for informational purposes only.\n"
504                                 "\tIt does not indicate anything about your configuration.\n");
505
506         if ((argc == 2) || (!strcasecmp(argv[1],"audio"))) {
507                 found = 1;
508                 for (i=0;i<11;i++)
509                         ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(i));
510         }
511
512         if ((argc == 2) || (!strcasecmp(argv[1],"image"))) {
513                 found = 1;
514                 for (i=16;i<18;i++)
515                         ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(i));
516         }
517
518         if ((argc == 2) || (!strcasecmp(argv[1],"video"))) {
519                 found = 1;
520                 for (i=18;i<20;i++)
521                         ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(i));
522         }
523
524         if (! found)
525                 return RESULT_SHOWUSAGE;
526         else
527                 return RESULT_SUCCESS;
528 }
529
530 static char frame_show_codecs_usage[] =
531 "Usage: show [audio|video|image] codecs\n"
532 "       Displays codec mapping\n";
533
534 struct ast_cli_entry cli_show_codecs =
535 { { "show", "codecs", NULL }, show_codecs, "Shows codecs", frame_show_codecs_usage };
536 struct ast_cli_entry cli_show_codecs_audio =
537 { { "show", "audio", "codecs", NULL }, show_codecs, "Shows audio codecs", frame_show_codecs_usage };
538 struct ast_cli_entry cli_show_codecs_video =
539 { { "show", "video", "codecs", NULL }, show_codecs, "Shows video codecs", frame_show_codecs_usage };
540 struct ast_cli_entry cli_show_codecs_image =
541 { { "show", "image", "codecs", NULL }, show_codecs, "Shows image codecs", frame_show_codecs_usage };
542
543 static int show_codec_n(int fd, int argc, char *argv[])
544 {
545         int codec, i, found=0;
546
547         if (argc != 3)
548                 return RESULT_SHOWUSAGE;
549
550         if (sscanf(argv[2],"%d",&codec) != 1)
551                 return RESULT_SHOWUSAGE;
552
553         for (i=0;i<32;i++)
554                 if (codec & (1 << i)) {
555                         found = 1;
556                         ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(i));
557                 }
558
559         if (! found)
560                 ast_cli(fd, "Codec %d not found\n", codec);
561
562         return RESULT_SUCCESS;
563 }
564
565 static char frame_show_codec_n_usage[] =
566 "Usage: show codec <number>\n"
567 "       Displays codec mapping\n";
568
569 struct ast_cli_entry cli_show_codec_n =
570 { { "show", "codec", NULL }, show_codec_n, "Shows a specific codec", frame_show_codec_n_usage };
571
572 void ast_frame_dump(char *name, struct ast_frame *f, char *prefix)
573 {
574         char *n = "unknown";
575         char ftype[40] = "Unknown Frametype";
576         char cft[80];
577         char subclass[40] = "Unknown Subclass";
578         char csub[80];
579         char moreinfo[40] = "";
580         char cn[40];
581         char cp[40];
582         char cmn[40];
583         if (name)
584                 n = name;
585         if (!f) {
586                 ast_verbose("%s [ %s (NULL) ] [%s]\n", 
587                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
588                         term_color(cft, "HANGUP", COLOR_BRRED, COLOR_BLACK, sizeof(cft)), 
589                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
590                 return;
591         }
592         /* XXX We should probably print one each of voice and video when the format changes XXX */
593         if (f->frametype == AST_FRAME_VOICE)
594                 return;
595         if (f->frametype == AST_FRAME_VIDEO)
596                 return;
597         switch(f->frametype) {
598         case AST_FRAME_DTMF:
599                 strcpy(ftype, "DTMF");
600                 subclass[0] = f->subclass;
601                 subclass[1] = '\0';
602                 break;
603         case AST_FRAME_CONTROL:
604                 strcpy(ftype, "Control");
605                 switch(f->subclass) {
606                 case AST_CONTROL_HANGUP:
607                         strcpy(subclass, "Hangup");
608                         break;
609                 case AST_CONTROL_RING:
610                         strcpy(subclass, "Ring");
611                         break;
612                 case AST_CONTROL_RINGING:
613                         strcpy(subclass, "Ringing");
614                         break;
615                 case AST_CONTROL_ANSWER:
616                         strcpy(subclass, "Answer");
617                         break;
618                 case AST_CONTROL_BUSY:
619                         strcpy(subclass, "Busy");
620                         break;
621                 case AST_CONTROL_TAKEOFFHOOK:
622                         strcpy(subclass, "Take Off Hook");
623                         break;
624                 case AST_CONTROL_OFFHOOK:
625                         strcpy(subclass, "Line Off Hook");
626                         break;
627                 case AST_CONTROL_CONGESTION:
628                         strcpy(subclass, "Congestion");
629                         break;
630                 case AST_CONTROL_FLASH:
631                         strcpy(subclass, "Flash");
632                         break;
633                 case AST_CONTROL_WINK:
634                         strcpy(subclass, "Wink");
635                         break;
636                 case AST_CONTROL_OPTION:
637                         strcpy(subclass, "Option");
638                         break;
639                 case AST_CONTROL_RADIO_KEY:
640                         strcpy(subclass, "Key Radio");
641                         break;
642                 case AST_CONTROL_RADIO_UNKEY:
643                         strcpy(subclass, "Unkey Radio");
644                         break;
645                 default:
646                         snprintf(subclass, sizeof(subclass), "Unknown control '%d'", f->subclass);
647                 }
648         case AST_FRAME_NULL:
649                 strcpy(ftype, "Null Frame");
650                 strcpy(subclass, "N/A");
651                 break;
652         case AST_FRAME_IAX:
653                 /* Should never happen */
654                 strcpy(ftype, "IAX Specific");
655                 snprintf(subclass, sizeof(subclass), "IAX Frametype %d", f->subclass);
656                 break;
657         case AST_FRAME_TEXT:
658                 strcpy(ftype, "Text");
659                 strcpy(subclass, "N/A");
660                 strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
661                 break;
662         case AST_FRAME_IMAGE:
663                 strcpy(ftype, "Image");
664                 snprintf(subclass, sizeof(subclass), "Image format %s\n", ast_getformatname(f->subclass));
665                 break;
666         case AST_FRAME_HTML:
667                 strcpy(ftype, "HTML");
668                 switch(f->subclass) {
669                 case AST_HTML_URL:
670                         strcpy(subclass, "URL");
671                         strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
672                         break;
673                 case AST_HTML_DATA:
674                         strcpy(subclass, "Data");
675                         break;
676                 case AST_HTML_BEGIN:
677                         strcpy(subclass, "Begin");
678                         break;
679                 case AST_HTML_END:
680                         strcpy(subclass, "End");
681                         break;
682                 case AST_HTML_LDCOMPLETE:
683                         strcpy(subclass, "Load Complete");
684                         break;
685                 case AST_HTML_NOSUPPORT:
686                         strcpy(subclass, "No Support");
687                         break;
688                 case AST_HTML_LINKURL:
689                         strcpy(subclass, "Link URL");
690                         strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
691                         break;
692                 case AST_HTML_UNLINK:
693                         strcpy(subclass, "Unlink");
694                         break;
695                 case AST_HTML_LINKREJECT:
696                         strcpy(subclass, "Link Reject");
697                         break;
698                 default:
699                         snprintf(subclass, sizeof(subclass), "Unknown HTML frame '%d'\n", f->subclass);
700                         break;
701                 }
702                 break;
703         default:
704                 snprintf(ftype, sizeof(ftype), "Unknown Frametype '%d'", f->frametype);
705         }
706         if (strlen(moreinfo))
707                 ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) '%s' ] [%s]\n",  
708                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
709                         term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
710                         f->frametype, 
711                         term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
712                         f->subclass, 
713                         term_color(cmn, moreinfo, COLOR_BRGREEN, COLOR_BLACK, sizeof(cmn)),
714                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
715         else
716                 ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) ] [%s]\n",  
717                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
718                         term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
719                         f->frametype, 
720                         term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
721                         f->subclass, 
722                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
723
724 }
725
726
727 #ifdef TRACE_FRAMES
728 static int show_frame_stats(int fd, int argc, char *argv[])
729 {
730         struct ast_frame *f;
731         int x=1;
732         if (argc != 3)
733                 return RESULT_SHOWUSAGE;
734         ast_cli(fd, "     Framer Statistics     \n");
735         ast_cli(fd, "---------------------------\n");
736         ast_cli(fd, "Total allocated headers: %d\n", headers);
737         ast_cli(fd, "Queue Dump:\n");
738         ast_mutex_lock(&framelock);
739         for (f=headerlist; f; f = f->next) {
740                 ast_cli(fd, "%d.  Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
741         }
742         ast_mutex_unlock(&framelock);
743         return RESULT_SUCCESS;
744 }
745
746 static char frame_stats_usage[] =
747 "Usage: show frame stats\n"
748 "       Displays debugging statistics from framer\n";
749
750 struct ast_cli_entry cli_frame_stats =
751 { { "show", "frame", "stats", NULL }, show_frame_stats, "Shows frame statistics", frame_stats_usage };
752 #endif
753
754 int init_framer(void)
755 {
756 #ifdef TRACE_FRAMES
757         ast_cli_register(&cli_frame_stats);
758 #endif
759         ast_cli_register(&cli_show_codecs);
760         ast_cli_register(&cli_show_codecs_audio);
761         ast_cli_register(&cli_show_codecs_video);
762         ast_cli_register(&cli_show_codecs_image);
763         ast_cli_register(&cli_show_codec_n);
764         return 0;       
765 }