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