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