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