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