Wed Mar 12 07:00:01 CET 2003
[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 "asterisk.h"
26
27 #ifdef TRACE_FRAMES
28 static int headers = 0;
29 static struct ast_frame *headerlist = NULL;
30 static pthread_mutex_t framelock = AST_MUTEX_INITIALIZER;
31 #endif
32
33 #define SMOOTHER_SIZE 8000
34
35 struct ast_smoother {
36         int size;
37         int format;
38         int readdata;
39         int optimizablestream;
40         float samplesperbyte;
41         struct ast_frame f;
42         char data[SMOOTHER_SIZE];
43         char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET];
44         struct ast_frame *opt;
45         int len;
46 };
47
48 void ast_smoother_reset(struct ast_smoother *s, int size)
49 {
50         memset(s, 0, sizeof(struct ast_smoother));
51         s->size = size;
52 }
53
54 struct ast_smoother *ast_smoother_new(int size)
55 {
56         struct ast_smoother *s;
57         if (size < 1)
58                 return NULL;
59         s = malloc(sizeof(struct ast_smoother));
60         if (s)
61                 ast_smoother_reset(s, size);
62         return s;
63 }
64
65 int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f)
66 {
67         if (f->frametype != AST_FRAME_VOICE) {
68                 ast_log(LOG_WARNING, "Huh?  Can't smooth a non-voice frame!\n");
69                 return -1;
70         }
71         if (!s->format) {
72                 s->format = f->subclass;
73                 s->samplesperbyte = (float)f->samples / (float)f->datalen;
74         } else if (s->format != f->subclass) {
75                 ast_log(LOG_WARNING, "Smoother was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass);
76                 return -1;
77         }
78         if (s->len + f->datalen > SMOOTHER_SIZE) {
79                 ast_log(LOG_WARNING, "Out of smoother space\n");
80                 return -1;
81         }
82         if ((f->datalen == s->size) && !s->opt) {
83                 if (!s->len) {
84                         /* Optimize by sending the frame we just got
85                            on the next read, thus eliminating the douple
86                            copy */
87                         s->opt = f;
88                         return 0;
89                 } else {
90                         s->optimizablestream++;
91                         if (s->optimizablestream > 10) {
92                                 /* For the past 10 rounds, we have input and output
93                                    frames of the correct size for this smoother, yet
94                                    we were unable to optimize because there was still
95                                    some cruft left over.  Lets just drop the cruft so
96                                    we can move to a fully optimized path */
97                                 s->len = 0;
98                                 s->opt = f;
99                                 return 0;
100                         }
101                 }
102         } else 
103                 s->optimizablestream = 0;
104         memcpy(s->data + s->len, f->data, f->datalen);
105         s->len += f->datalen;
106         return 0;
107 }
108
109 struct ast_frame *ast_smoother_read(struct ast_smoother *s)
110 {
111         struct ast_frame *opt;
112
113         /* IF we have an optimization frame, send it */
114         if (s->opt) {
115                 opt = s->opt;
116                 s->opt = NULL;
117                 return opt;
118         }
119
120         /* Make sure we have enough data */
121         if (s->len < s->size) {
122                 return NULL;
123         }
124         /* Make frame */
125         s->f.frametype = AST_FRAME_VOICE;
126         s->f.subclass = s->format;
127         s->f.data = s->framedata + AST_FRIENDLY_OFFSET;
128         s->f.offset = AST_FRIENDLY_OFFSET;
129         s->f.datalen = s->size;
130         s->f.samples = s->size * s->samplesperbyte;
131         /* Fill Data */
132         memcpy(s->f.data, s->data, s->size);
133         s->len -= s->size;
134         /* Move remaining data to the front if applicable */
135         if (s->len) 
136                 memmove(s->data, s->data + s->size, s->len);
137         /* Return frame */
138         return &s->f;
139 }
140
141 void ast_smoother_free(struct ast_smoother *s)
142 {
143         free(s);
144 }
145
146 static struct ast_frame *ast_frame_header_new(void)
147 {
148         struct ast_frame *f;
149         f = malloc(sizeof(struct ast_frame));
150         if (f)
151                 memset(f, 0, sizeof(struct ast_frame));
152 #ifdef TRACE_FRAMES
153         if (f) {
154                 headers++;
155                 f->prev = NULL;
156                 ast_pthread_mutex_lock(&framelock);
157                 f->next = headerlist;
158                 if (headerlist)
159                         headerlist->prev = f;
160                 headerlist = f;
161                 pthread_mutex_unlock(&framelock);
162         }
163 #endif  
164         return f;
165 }
166
167 /*
168  * Important: I should be made more efficient.  Frame headers should
169  * most definitely be cached
170  */
171
172 void ast_frfree(struct ast_frame *fr)
173 {
174         if (fr->mallocd & AST_MALLOCD_DATA) {
175                 if (fr->data) 
176                         free(fr->data - fr->offset);
177         }
178         if (fr->mallocd & AST_MALLOCD_SRC) {
179                 if (fr->src)
180                         free(fr->src);
181         }
182         if (fr->mallocd & AST_MALLOCD_HDR) {
183 #ifdef TRACE_FRAMES
184                 headers--;
185                 ast_pthread_mutex_lock(&framelock);
186                 if (fr->next)
187                         fr->next->prev = fr->prev;
188                 if (fr->prev)
189                         fr->prev->next = fr->next;
190                 else
191                         headerlist = fr->next;
192                 pthread_mutex_unlock(&framelock);
193 #endif                  
194                 free(fr);
195         }
196 }
197
198 struct ast_frame *ast_frisolate(struct ast_frame *fr)
199 {
200         struct ast_frame *out;
201         if (!(fr->mallocd & AST_MALLOCD_HDR)) {
202                 /* Allocate a new header if needed */
203                 out = ast_frame_header_new();
204                 if (!out) {
205                         ast_log(LOG_WARNING, "Out of memory\n");
206                         return NULL;
207                 }
208                 out->frametype = fr->frametype;
209                 out->subclass = fr->subclass;
210                 out->datalen = 0;
211                 out->samples = fr->samples;
212                 out->offset = 0;
213                 out->src = NULL;
214                 out->data = NULL;
215         } else {
216                 out = fr;
217         }
218         if (!(fr->mallocd & AST_MALLOCD_SRC)) {
219                 if (fr->src)
220                         out->src = strdup(fr->src);
221         } else
222                 out->src = fr->src;
223         if (!(fr->mallocd & AST_MALLOCD_DATA))  {
224                 out->data = malloc(fr->datalen + AST_FRIENDLY_OFFSET);
225                 if (!out->data) {
226                         free(out);
227                         ast_log(LOG_WARNING, "Out of memory\n");
228                         return NULL;
229                 }
230                 out->data += AST_FRIENDLY_OFFSET;
231                 out->offset = AST_FRIENDLY_OFFSET;
232                 out->datalen = fr->datalen;
233                 memcpy(out->data, fr->data, fr->datalen);
234         }
235         out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA;
236         return out;
237 }
238
239 struct ast_frame *ast_frdup(struct ast_frame *f)
240 {
241         struct ast_frame *ret;
242         int p;
243         p = f->mallocd;
244         f->mallocd = 0;
245         /* Make frisolate think this is a 100% static frame, and make a duplicate */
246         ret = ast_frisolate(f);
247         /* Restore its true malloc status */
248         f->mallocd = p;
249         return ret;
250 }
251
252 struct ast_frame *ast_fr_fdread(int fd)
253 {
254         char buf[65536];
255         int res;
256         int ttl = sizeof(struct ast_frame);
257         struct ast_frame *f = (struct ast_frame *)buf;
258         /* Read a frame directly from there.  They're always in the
259            right format. */
260         
261         while(ttl) {
262                 res = read(fd, buf, ttl);
263                 if (res < 0) {
264                         ast_log(LOG_WARNING, "Bad read on %d: %s\n", fd, strerror(errno));
265                         return NULL;
266                 }
267                 ttl -= res;
268         }
269         
270         /* read the frame header */
271         f->mallocd = 0;
272         /* Re-write data position */
273         f->data = buf + sizeof(struct ast_frame);
274         f->offset = 0;
275         /* Forget about being mallocd */
276         f->mallocd = 0;
277         /* Re-write the source */
278         f->src = __FUNCTION__;
279         if (f->datalen > sizeof(buf) - sizeof(struct ast_frame)) {
280                 /* Really bad read */
281                 ast_log(LOG_WARNING, "Strange read (%d bytes)\n", f->datalen);
282                 return NULL;
283         }
284         if (f->datalen) {
285                 if ((res = read(fd, f->data, f->datalen)) != f->datalen) {
286                         /* Bad read */
287                         ast_log(LOG_WARNING, "How very strange, expected %d, got %d\n", f->datalen, res);
288                         return NULL;
289                 }
290         }
291         if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
292                 return NULL;
293         }
294         return ast_frisolate(f);
295 }
296
297 /* Some convenient routines for sending frames to/from stream or datagram
298    sockets, pipes, etc (maybe even files) */
299
300 int ast_fr_fdwrite(int fd, struct ast_frame *frame)
301 {
302         /* Write the frame exactly */
303         if (write(fd, frame, sizeof(struct ast_frame)) != sizeof(struct ast_frame)) {
304                 ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
305                 return -1;
306         }
307         if (write(fd, frame->data, frame->datalen) != frame->datalen) {
308                 ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
309                 return -1;
310         }
311         return 0;
312 }
313
314 int ast_fr_fdhangup(int fd)
315 {
316         struct ast_frame hangup = {
317                 AST_FRAME_CONTROL,
318                 AST_CONTROL_HANGUP
319         };
320         return ast_fr_fdwrite(fd, &hangup);
321 }
322
323 int ast_getformatbyname(char *name)
324 {
325         if (!strcasecmp(name, "g723.1")) 
326                 return AST_FORMAT_G723_1;
327         else if (!strcasecmp(name, "gsm"))
328                 return AST_FORMAT_GSM;
329         else if (!strcasecmp(name, "ulaw"))
330                 return AST_FORMAT_ULAW;
331         else if (!strcasecmp(name, "alaw"))
332                 return AST_FORMAT_ALAW;
333         else if (!strcasecmp(name, "mp3"))
334                 return AST_FORMAT_MP3;
335         else if (!strcasecmp(name, "slinear"))
336                 return AST_FORMAT_SLINEAR;
337         else if (!strcasecmp(name, "lpc10"))
338                 return AST_FORMAT_LPC10;
339         else if (!strcasecmp(name, "adpcm"))
340                 return AST_FORMAT_ADPCM;
341         else if (!strcasecmp(name, "g729"))
342                 return AST_FORMAT_G729A;
343         else if (!strcasecmp(name, "speex"))
344                 return AST_FORMAT_SPEEX;
345         else if (!strcasecmp(name, "all"))
346                 return 0x7FFFFFFF;
347         return 0;
348 }
349
350 void ast_frame_dump(char *name, struct ast_frame *f, char *prefix)
351 {
352         char *n = "unknown";
353         char ftype[40] = "Unknown Frametype";
354         char cft[80];
355         char subclass[40] = "Unknown Subclass";
356         char csub[80];
357         char moreinfo[40] = "";
358         char cn[40];
359         char cp[40];
360         char cmn[40];
361         if (name)
362                 n = name;
363         if (!f) {
364                 ast_verbose("%s [ %s (NULL) ] [%s]\n", 
365                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
366                         term_color(cft, "HANGUP", COLOR_BRRED, COLOR_BLACK, sizeof(cft)), 
367                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
368                 return;
369         }
370         /* XXX We should probably print one each of voice and video when the format changes XXX */
371         if (f->frametype == AST_FRAME_VOICE)
372                 return;
373         if (f->frametype == AST_FRAME_VIDEO)
374                 return;
375         switch(f->frametype) {
376         case AST_FRAME_DTMF:
377                 strcpy(ftype, "DTMF");
378                 subclass[0] = f->subclass;
379                 subclass[1] = '\0';
380                 break;
381         case AST_FRAME_CONTROL:
382                 strcpy(ftype, "Control");
383                 switch(f->subclass) {
384                 case AST_CONTROL_HANGUP:
385                         strcpy(subclass, "Hangup");
386                         break;
387                 case AST_CONTROL_RING:
388                         strcpy(subclass, "Ring");
389                         break;
390                 case AST_CONTROL_RINGING:
391                         strcpy(subclass, "Ringing");
392                         break;
393                 case AST_CONTROL_ANSWER:
394                         strcpy(subclass, "Answer");
395                         break;
396                 case AST_CONTROL_BUSY:
397                         strcpy(subclass, "Busy");
398                         break;
399                 case AST_CONTROL_TAKEOFFHOOK:
400                         strcpy(subclass, "Take Off Hook");
401                         break;
402                 case AST_CONTROL_OFFHOOK:
403                         strcpy(subclass, "Line Off Hook");
404                         break;
405                 case AST_CONTROL_CONGESTION:
406                         strcpy(subclass, "Congestion");
407                         break;
408                 case AST_CONTROL_FLASH:
409                         strcpy(subclass, "Flash");
410                         break;
411                 case AST_CONTROL_WINK:
412                         strcpy(subclass, "Wink");
413                         break;
414                 case AST_CONTROL_OPTION:
415                         strcpy(subclass, "Option");
416                         break;
417                 case AST_CONTROL_RADIO_KEY:
418                         strcpy(subclass, "Key Radio");
419                         break;
420                 case AST_CONTROL_RADIO_UNKEY:
421                         strcpy(subclass, "Unkey Radio");
422                         break;
423                 default:
424                         snprintf(subclass, sizeof(subclass), "Unknown control '%d'", f->subclass);
425                 }
426         case AST_FRAME_NULL:
427                 strcpy(ftype, "Null Frame");
428                 strcpy(subclass, "N/A");
429                 break;
430         case AST_FRAME_IAX:
431                 /* Should never happen */
432                 strcpy(ftype, "IAX Specific");
433                 snprintf(subclass, sizeof(subclass), "IAX Frametype %d", f->subclass);
434                 break;
435         case AST_FRAME_TEXT:
436                 strcpy(ftype, "Text");
437                 strcpy(subclass, "N/A");
438                 strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
439                 break;
440         case AST_FRAME_IMAGE:
441                 strcpy(ftype, "Image");
442                 snprintf(subclass, sizeof(subclass), "Image format %d\n", f->subclass);
443                 break;
444         case AST_FRAME_HTML:
445                 strcpy(ftype, "HTML");
446                 switch(f->subclass) {
447                 case AST_HTML_URL:
448                         strcpy(subclass, "URL");
449                         strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
450                         break;
451                 case AST_HTML_DATA:
452                         strcpy(subclass, "Data");
453                         break;
454                 case AST_HTML_BEGIN:
455                         strcpy(subclass, "Begin");
456                         break;
457                 case AST_HTML_END:
458                         strcpy(subclass, "End");
459                         break;
460                 case AST_HTML_LDCOMPLETE:
461                         strcpy(subclass, "Load Complete");
462                         break;
463                 case AST_HTML_NOSUPPORT:
464                         strcpy(subclass, "No Support");
465                         break;
466                 case AST_HTML_LINKURL:
467                         strcpy(subclass, "Link URL");
468                         strncpy(moreinfo, f->data, sizeof(moreinfo) - 1);
469                         break;
470                 case AST_HTML_UNLINK:
471                         strcpy(subclass, "Unlink");
472                         break;
473                 case AST_HTML_LINKREJECT:
474                         strcpy(subclass, "Link Reject");
475                         break;
476                 default:
477                         snprintf(subclass, sizeof(subclass), "Unknown HTML frame '%d'\n", f->subclass);
478                         break;
479                 }
480                 break;
481         default:
482                 snprintf(ftype, sizeof(ftype), "Unknown Frametype '%d'", f->frametype);
483         }
484         if (strlen(moreinfo))
485                 ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) '%s' ] [%s]\n",  
486                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
487                         term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
488                         f->frametype, 
489                         term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
490                         f->subclass, 
491                         term_color(cmn, moreinfo, COLOR_BRGREEN, COLOR_BLACK, sizeof(cmn)),
492                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
493         else
494                 ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) ] [%s]\n",  
495                         term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
496                         term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
497                         f->frametype, 
498                         term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
499                         f->subclass, 
500                         term_color(cn, n, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
501
502 }
503
504
505 #ifdef TRACE_FRAMES
506 static int show_frame_stats(int fd, int argc, char *argv[])
507 {
508         struct ast_frame *f;
509         int x=1;
510         if (argc != 3)
511                 return RESULT_SHOWUSAGE;
512         ast_cli(fd, "     Framer Statistics     \n");
513         ast_cli(fd, "---------------------------\n");
514         ast_cli(fd, "Total allocated headers: %d\n", headers);
515         ast_cli(fd, "Queue Dump:\n");
516         ast_pthread_mutex_lock(&framelock);
517         for (f=headerlist; f; f = f->next) {
518                 ast_cli(fd, "%d.  Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
519         }
520         pthread_mutex_unlock(&framelock);
521         return RESULT_SUCCESS;
522 }
523
524 static char frame_stats_usage[] =
525 "Usage: show frame stats\n"
526 "       Displays debugging statistics from framer\n";
527
528 struct ast_cli_entry cli_frame_stats =
529 { { "show", "frame", "stats", NULL }, show_frame_stats, "Shows frame statistics", frame_stats_usage };
530 #endif
531
532 int init_framer(void)
533 {
534 #ifdef TRACE_FRAMES
535         ast_cli_register(&cli_frame_stats);
536 #endif
537         return 0;       
538 }