Handle transitions in delivery time and non-delivery time
[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;
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 && strlen(f->src))
291                 len += strlen(f->src) + 1;
292         buf = malloc(len);
293         if (!buf)
294                 return NULL;
295         out = buf;
296         /* Set us as having malloc'd header only, so it will eventually
297            get freed. */
298         out->frametype = f->frametype;
299         out->subclass = f->subclass;
300         out->datalen = f->datalen;
301         out->samples = f->samples;
302         out->delivery = f->delivery;
303         out->mallocd = AST_MALLOCD_HDR;
304         out->offset = AST_FRIENDLY_OFFSET;
305         out->data = buf + sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET;
306         if (f->src && strlen(f->src)) {
307                 out->src = out->data + f->datalen;
308                 /* Must have space since we allocated for it */
309                 strcpy(out->src, f->src);
310         } else
311                 out->src = NULL;
312         out->prev = NULL;
313         out->next = NULL;
314         memcpy(out->data, f->data, out->datalen);       
315         return out;
316 }
317
318 struct ast_frame *ast_fr_fdread(int fd)
319 {
320         char buf[65536];
321         int res;
322         int ttl = sizeof(struct ast_frame);
323         struct ast_frame *f = (struct ast_frame *)buf;
324         /* Read a frame directly from there.  They're always in the
325            right format. */
326         
327         while(ttl) {
328                 res = read(fd, buf, ttl);
329                 if (res < 0) {
330                         ast_log(LOG_WARNING, "Bad read on %d: %s\n", fd, strerror(errno));
331                         return NULL;
332                 }
333                 ttl -= res;
334         }
335         
336         /* read the frame header */
337         f->mallocd = 0;
338         /* Re-write data position */
339         f->data = buf + sizeof(struct ast_frame);
340         f->offset = 0;
341         /* Forget about being mallocd */
342         f->mallocd = 0;
343         /* Re-write the source */
344         f->src = __FUNCTION__;
345         if (f->datalen > sizeof(buf) - sizeof(struct ast_frame)) {
346                 /* Really bad read */
347                 ast_log(LOG_WARNING, "Strange read (%d bytes)\n", f->datalen);
348                 return NULL;
349         }
350         if (f->datalen) {
351                 if ((res = read(fd, f->data, f->datalen)) != f->datalen) {
352                         /* Bad read */
353                         ast_log(LOG_WARNING, "How very strange, expected %d, got %d\n", f->datalen, res);
354                         return NULL;
355                 }
356         }
357         if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
358                 return NULL;
359         }
360         return ast_frisolate(f);
361 }
362
363 /* Some convenient routines for sending frames to/from stream or datagram
364    sockets, pipes, etc (maybe even files) */
365
366 int ast_fr_fdwrite(int fd, struct ast_frame *frame)
367 {
368         /* Write the frame exactly */
369         if (write(fd, frame, sizeof(struct ast_frame)) != sizeof(struct ast_frame)) {
370                 ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
371                 return -1;
372         }
373         if (write(fd, frame->data, frame->datalen) != frame->datalen) {
374                 ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
375                 return -1;
376         }
377         return 0;
378 }
379
380 int ast_fr_fdhangup(int fd)
381 {
382         struct ast_frame hangup = {
383                 AST_FRAME_CONTROL,
384                 AST_CONTROL_HANGUP
385         };
386         return ast_fr_fdwrite(fd, &hangup);
387 }
388
389 char* ast_getformatname(int format)
390 {
391         if (format == AST_FORMAT_G723_1) 
392                 return "G723";
393         else if (format == AST_FORMAT_GSM)
394                 return "GSM";
395         else if (format == AST_FORMAT_ULAW)
396                 return "ULAW";
397         else if (format == AST_FORMAT_ALAW)
398                 return "ALAW";
399         else if (format == AST_FORMAT_G726)
400                 return "G726";
401         else if (format == AST_FORMAT_SLINEAR)
402                 return "SLINR";
403         else if (format == AST_FORMAT_LPC10)
404                 return "LPC10";
405         else if (format == AST_FORMAT_ADPCM)
406                 return "ADPCM";
407         else if (format == AST_FORMAT_G729A)
408                 return "G729A";
409         else if (format == AST_FORMAT_SPEEX)
410                 return "SPEEX";
411         else if (format == AST_FORMAT_ILBC)
412                 return "ILBC";
413         else if (format == AST_FORMAT_JPEG)
414                 return "JPEG";
415         else if (format == AST_FORMAT_PNG)
416                 return "PNG";
417         else if (format == AST_FORMAT_H261)
418                 return "H261";
419         else if (format == AST_FORMAT_H263)
420                 return "H263";
421         return "UNKN";
422 }
423
424 int ast_getformatbyname(char *name)
425 {
426         if (!strcasecmp(name, "g723.1")) 
427                 return AST_FORMAT_G723_1;
428         else if (!strcasecmp(name, "gsm"))
429                 return AST_FORMAT_GSM;
430         else if (!strcasecmp(name, "ulaw"))
431                 return AST_FORMAT_ULAW;
432         else if (!strcasecmp(name, "alaw"))
433                 return AST_FORMAT_ALAW;
434         else if (!strcasecmp(name, "g726"))
435                 return AST_FORMAT_G726;
436         else if (!strcasecmp(name, "slinear"))
437                 return AST_FORMAT_SLINEAR;
438         else if (!strcasecmp(name, "lpc10"))
439                 return AST_FORMAT_LPC10;
440         else if (!strcasecmp(name, "adpcm"))
441                 return AST_FORMAT_ADPCM;
442         else if (!strcasecmp(name, "g729"))
443                 return AST_FORMAT_G729A;
444         else if (!strcasecmp(name, "speex"))
445                 return AST_FORMAT_SPEEX;
446         else if (!strcasecmp(name, "ilbc"))
447                 return AST_FORMAT_ILBC;
448         else if (!strcasecmp(name, "h261"))
449                 return AST_FORMAT_H261;
450         else if (!strcasecmp(name, "h263"))
451                 return AST_FORMAT_H263;
452         else if (!strcasecmp(name, "all"))
453                 return 0x7FFFFFFF;
454         return 0;
455 }
456
457 char *ast_codec2str(int codec) {
458         static char codecs[25][30] = {
459                 /* Audio formats */
460                 "G.723.1",                    /*  0 */
461                 "GSM",                        /*  1 */
462                 "G.711 u-law",                /*  2 */
463                 "G.711 A-law",                /*  3 */
464                 "G.726",                      /*  4 */
465                 "ADPCM",                      /*  5 */
466                 "16 bit Signed Linear PCM",   /*  6 */
467                 "LPC10",                      /*  7 */
468                 "G.729A audio",               /*  8 */
469                 "SpeeX",                      /*  9 */
470                 "iLBC",                       /* 10 */
471                 "undefined",                  /* 11 */
472                 "undefined",                  /* 12 */
473                 "undefined",                  /* 13 */
474                 "undefined",                  /* 14 */
475                 "Maximum audio format",       /* 15 */
476         /* Image formats */
477                 "JPEG image",                 /* 16 */
478                 "PNG image",                  /* 17 */
479                 "H.261 Video",                /* 18 */
480                 "H.263 Video",                /* 19 */
481                 "undefined",                  /* 20 */
482                 "undefined",                  /* 21 */
483                 "undefined",                  /* 22 */
484                 "undefined",                  /* 23 */
485         "Maximum video format",       /* 24 */
486                 };
487         if ((codec >= 0) && (codec <= 24))
488                 return codecs[codec];
489         else
490                 return "unknown";
491 }
492
493 static int show_codecs(int fd, int argc, char *argv[])
494 {
495         int i, found=0;
496
497         if ((argc < 2) || (argc > 3))
498                 return RESULT_SHOWUSAGE;
499
500         if (getenv("I_AM_NOT_AN_IDIOT") == NULL)
501                 ast_cli(fd, "Disclaimer: this command is for informational purposes only.\n"
502                                 "\tIt does not indicate anything about your configuration.\n");
503
504         if ((argc == 2) || (!strcasecmp(argv[1],"audio"))) {
505                 found = 1;
506                 for (i=0;i<11;i++)
507                         ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(i));
508         }
509
510         if ((argc == 2) || (!strcasecmp(argv[1],"image"))) {
511                 found = 1;
512                 for (i=16;i<18;i++)
513                         ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(i));
514         }
515
516         if ((argc == 2) || (!strcasecmp(argv[1],"video"))) {
517                 found = 1;
518                 for (i=18;i<20;i++)
519                         ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(i));
520         }
521
522         if (! found)
523                 return RESULT_SHOWUSAGE;
524         else
525                 return RESULT_SUCCESS;
526 }
527
528 static char frame_show_codecs_usage[] =
529 "Usage: show [audio|video|image] codecs\n"
530 "       Displays codec mapping\n";
531
532 struct ast_cli_entry cli_show_codecs =
533 { { "show", "codecs", NULL }, show_codecs, "Shows codecs", frame_show_codecs_usage };
534 struct ast_cli_entry cli_show_codecs_audio =
535 { { "show", "audio", "codecs", NULL }, show_codecs, "Shows audio codecs", frame_show_codecs_usage };
536 struct ast_cli_entry cli_show_codecs_video =
537 { { "show", "video", "codecs", NULL }, show_codecs, "Shows video codecs", frame_show_codecs_usage };
538 struct ast_cli_entry cli_show_codecs_image =
539 { { "show", "image", "codecs", NULL }, show_codecs, "Shows image codecs", frame_show_codecs_usage };
540
541 static int show_codec_n(int fd, int argc, char *argv[])
542 {
543         int codec, i, found=0;
544
545         if (argc != 3)
546                 return RESULT_SHOWUSAGE;
547
548         if (sscanf(argv[2],"%d",&codec) != 1)
549                 return RESULT_SHOWUSAGE;
550
551         for (i=0;i<32;i++)
552                 if (codec & (1 << i)) {
553                         found = 1;
554                         ast_cli(fd, "%11u (1 << %2d)  %s\n",1 << i,i,ast_codec2str(i));
555                 }
556
557         if (! found)
558                 ast_cli(fd, "Codec %d not found\n", codec);
559
560         return RESULT_SUCCESS;
561 }
562
563 static char frame_show_codec_n_usage[] =
564 "Usage: show codec <number>\n"
565 "       Displays codec mapping\n";
566
567 struct ast_cli_entry cli_show_codec_n =
568 { { "show", "codec", NULL }, show_codec_n, "Shows a specific codec", frame_show_codec_n_usage };
569
570 void ast_frame_dump(char *name, struct ast_frame *f, char *prefix)
571 {
572         char *n = "unknown";
573         char ftype[40] = "Unknown Frametype";
574         char cft[80];
575         char subclass[40] = "Unknown Subclass";
576         char csub[80];
577         char moreinfo[40] = "";
578         char cn[40];
579         char cp[40];
580         char cmn[40];
581         if (name)
582                 n = name;
583         if (!f) {
584                 ast_verbose("%s [ %s (NULL) ] [%s]\n", 
585                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
586                         term_color(cft, "HANGUP", COLOR_BRRED, COLOR_BLACK, sizeof(cft)), 
587                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
588                 return;
589         }
590         /* XXX We should probably print one each of voice and video when the format changes XXX */
591         if (f->frametype == AST_FRAME_VOICE)
592                 return;
593         if (f->frametype == AST_FRAME_VIDEO)
594                 return;
595         switch(f->frametype) {
596         case AST_FRAME_DTMF:
597                 strcpy(ftype, "DTMF");
598                 subclass[0] = f->subclass;
599                 subclass[1] = '\0';
600                 break;
601         case AST_FRAME_CONTROL:
602                 strcpy(ftype, "Control");
603                 switch(f->subclass) {
604                 case AST_CONTROL_HANGUP:
605                         strcpy(subclass, "Hangup");
606                         break;
607                 case AST_CONTROL_RING:
608                         strcpy(subclass, "Ring");
609                         break;
610                 case AST_CONTROL_RINGING:
611                         strcpy(subclass, "Ringing");
612                         break;
613                 case AST_CONTROL_ANSWER:
614                         strcpy(subclass, "Answer");
615                         break;
616                 case AST_CONTROL_BUSY:
617                         strcpy(subclass, "Busy");
618                         break;
619                 case AST_CONTROL_TAKEOFFHOOK:
620                         strcpy(subclass, "Take Off Hook");
621                         break;
622                 case AST_CONTROL_OFFHOOK:
623                         strcpy(subclass, "Line Off Hook");
624                         break;
625                 case AST_CONTROL_CONGESTION:
626                         strcpy(subclass, "Congestion");
627                         break;
628                 case AST_CONTROL_FLASH:
629                         strcpy(subclass, "Flash");
630                         break;
631                 case AST_CONTROL_WINK:
632                         strcpy(subclass, "Wink");
633                         break;
634                 case AST_CONTROL_OPTION:
635                         strcpy(subclass, "Option");
636                         break;
637                 case AST_CONTROL_RADIO_KEY:
638                         strcpy(subclass, "Key Radio");
639                         break;
640                 case AST_CONTROL_RADIO_UNKEY:
641                         strcpy(subclass, "Unkey Radio");
642                         break;
643                 default:
644                         snprintf(subclass, sizeof(subclass), "Unknown control '%d'", f->subclass);
645                 }
646         case AST_FRAME_NULL:
647                 strcpy(ftype, "Null Frame");
648                 strcpy(subclass, "N/A");
649                 break;
650         case AST_FRAME_IAX:
651                 /* Should never happen */
652                 strcpy(ftype, "IAX Specific");
653                 snprintf(subclass, sizeof(subclass), "IAX Frametype %d", f->subclass);
654                 break;
655         case AST_FRAME_TEXT:
656                 strcpy(ftype, "Text");
657                 strcpy(subclass, "N/A");
658                 strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
659                 break;
660         case AST_FRAME_IMAGE:
661                 strcpy(ftype, "Image");
662                 snprintf(subclass, sizeof(subclass), "Image format %s\n", ast_getformatname(f->subclass));
663                 break;
664         case AST_FRAME_HTML:
665                 strcpy(ftype, "HTML");
666                 switch(f->subclass) {
667                 case AST_HTML_URL:
668                         strcpy(subclass, "URL");
669                         strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
670                         break;
671                 case AST_HTML_DATA:
672                         strcpy(subclass, "Data");
673                         break;
674                 case AST_HTML_BEGIN:
675                         strcpy(subclass, "Begin");
676                         break;
677                 case AST_HTML_END:
678                         strcpy(subclass, "End");
679                         break;
680                 case AST_HTML_LDCOMPLETE:
681                         strcpy(subclass, "Load Complete");
682                         break;
683                 case AST_HTML_NOSUPPORT:
684                         strcpy(subclass, "No Support");
685                         break;
686                 case AST_HTML_LINKURL:
687                         strcpy(subclass, "Link URL");
688                         strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
689                         break;
690                 case AST_HTML_UNLINK:
691                         strcpy(subclass, "Unlink");
692                         break;
693                 case AST_HTML_LINKREJECT:
694                         strcpy(subclass, "Link Reject");
695                         break;
696                 default:
697                         snprintf(subclass, sizeof(subclass), "Unknown HTML frame '%d'\n", f->subclass);
698                         break;
699                 }
700                 break;
701         default:
702                 snprintf(ftype, sizeof(ftype), "Unknown Frametype '%d'", f->frametype);
703         }
704         if (strlen(moreinfo))
705                 ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) '%s' ] [%s]\n",  
706                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
707                         term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
708                         f->frametype, 
709                         term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
710                         f->subclass, 
711                         term_color(cmn, moreinfo, COLOR_BRGREEN, COLOR_BLACK, sizeof(cmn)),
712                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
713         else
714                 ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) ] [%s]\n",  
715                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
716                         term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
717                         f->frametype, 
718                         term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
719                         f->subclass, 
720                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
721
722 }
723
724
725 #ifdef TRACE_FRAMES
726 static int show_frame_stats(int fd, int argc, char *argv[])
727 {
728         struct ast_frame *f;
729         int x=1;
730         if (argc != 3)
731                 return RESULT_SHOWUSAGE;
732         ast_cli(fd, "     Framer Statistics     \n");
733         ast_cli(fd, "---------------------------\n");
734         ast_cli(fd, "Total allocated headers: %d\n", headers);
735         ast_cli(fd, "Queue Dump:\n");
736         ast_mutex_lock(&framelock);
737         for (f=headerlist; f; f = f->next) {
738                 ast_cli(fd, "%d.  Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
739         }
740         ast_mutex_unlock(&framelock);
741         return RESULT_SUCCESS;
742 }
743
744 static char frame_stats_usage[] =
745 "Usage: show frame stats\n"
746 "       Displays debugging statistics from framer\n";
747
748 struct ast_cli_entry cli_frame_stats =
749 { { "show", "frame", "stats", NULL }, show_frame_stats, "Shows frame statistics", frame_stats_usage };
750 #endif
751
752 int init_framer(void)
753 {
754 #ifdef TRACE_FRAMES
755         ast_cli_register(&cli_frame_stats);
756 #endif
757         ast_cli_register(&cli_show_codecs);
758         ast_cli_register(&cli_show_codecs_audio);
759         ast_cli_register(&cli_show_codecs_video);
760         ast_cli_register(&cli_show_codecs_image);
761         ast_cli_register(&cli_show_codec_n);
762         return 0;       
763 }