Version 0.3.0 from FTP
[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 <stdlib.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <pthread.h>
24 #include "asterisk.h"
25
26 #ifdef TRACE_FRAMES
27 static int headers = 0;
28 static struct ast_frame *headerlist = NULL;
29 static pthread_mutex_t framelock = AST_MUTEX_INITIALIZER;
30 #endif
31
32 #define SMOOTHER_SIZE 8000
33
34 struct ast_smoother {
35         int size;
36         int format;
37         int readdata;
38         float samplesperbyte;
39         struct ast_frame f;
40         char data[SMOOTHER_SIZE];
41         char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET];
42         int len;
43 };
44
45 void ast_smoother_reset(struct ast_smoother *s, int size)
46 {
47         memset(s, 0, sizeof(struct ast_smoother));
48         s->size = size;
49 }
50
51 struct ast_smoother *ast_smoother_new(int size)
52 {
53         struct ast_smoother *s;
54         if (size < 1)
55                 return NULL;
56         s = malloc(sizeof(struct ast_smoother));
57         if (s)
58                 ast_smoother_reset(s, size);
59         return s;
60 }
61
62 int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f)
63 {
64         if (f->frametype != AST_FRAME_VOICE) {
65                 ast_log(LOG_WARNING, "Huh?  Can't smooth a non-voice frame!\n");
66                 return -1;
67         }
68         if (!s->format) {
69                 s->format = f->subclass;
70                 s->samplesperbyte = (float)f->samples / (float)f->datalen;
71         } else if (s->format != f->subclass) {
72                 ast_log(LOG_WARNING, "Smoother was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass);
73                 return -1;
74         }
75         if (s->len + f->datalen > SMOOTHER_SIZE) {
76                 ast_log(LOG_WARNING, "Out of smoother space\n");
77                 return -1;
78         }
79         memcpy(s->data + s->len, f->data, f->datalen);
80         s->len += f->datalen;
81         return 0;
82 }
83
84 struct ast_frame *ast_smoother_read(struct ast_smoother *s)
85 {
86         /* Make sure we have enough data */
87         if (s->len < s->size) {
88                 return NULL;
89         }
90         /* Make frame */
91         s->f.frametype = AST_FRAME_VOICE;
92         s->f.subclass = s->format;
93         s->f.data = s->framedata + AST_FRIENDLY_OFFSET;
94         s->f.offset = AST_FRIENDLY_OFFSET;
95         s->f.datalen = s->size;
96         s->f.samples = s->size * s->samplesperbyte;
97         /* Fill Data */
98         memcpy(s->f.data, s->data, s->size);
99         s->len -= s->size;
100         /* Move remaining data to the front if applicable */
101         if (s->len) 
102                 memmove(s->data, s->data + s->size, s->len);
103         /* Return frame */
104         return &s->f;
105 }
106
107 void ast_smoother_free(struct ast_smoother *s)
108 {
109         free(s);
110 }
111
112 static struct ast_frame *ast_frame_header_new(void)
113 {
114         struct ast_frame *f;
115         f = malloc(sizeof(struct ast_frame));
116         if (f)
117                 memset(f, 0, sizeof(struct ast_frame));
118 #ifdef TRACE_FRAMES
119         if (f) {
120                 headers++;
121                 f->prev = NULL;
122                 ast_pthread_mutex_lock(&framelock);
123                 f->next = headerlist;
124                 if (headerlist)
125                         headerlist->prev = f;
126                 headerlist = f;
127                 pthread_mutex_unlock(&framelock);
128         }
129 #endif  
130         return f;
131 }
132
133 /*
134  * Important: I should be made more efficient.  Frame headers should
135  * most definitely be cached
136  */
137
138 void ast_frfree(struct ast_frame *fr)
139 {
140         if (fr->mallocd & AST_MALLOCD_DATA) {
141                 if (fr->data) 
142                         free(fr->data - fr->offset);
143         }
144         if (fr->mallocd & AST_MALLOCD_SRC) {
145                 if (fr->src)
146                         free(fr->src);
147         }
148         if (fr->mallocd & AST_MALLOCD_HDR) {
149 #ifdef TRACE_FRAMES
150                 headers--;
151                 ast_pthread_mutex_lock(&framelock);
152                 if (fr->next)
153                         fr->next->prev = fr->prev;
154                 if (fr->prev)
155                         fr->prev->next = fr->next;
156                 else
157                         headerlist = fr->next;
158                 pthread_mutex_unlock(&framelock);
159 #endif                  
160                 free(fr);
161         }
162 }
163
164 struct ast_frame *ast_frisolate(struct ast_frame *fr)
165 {
166         struct ast_frame *out;
167         if (!(fr->mallocd & AST_MALLOCD_HDR)) {
168                 /* Allocate a new header if needed */
169                 out = ast_frame_header_new();
170                 if (!out) {
171                         ast_log(LOG_WARNING, "Out of memory\n");
172                         return NULL;
173                 }
174                 out->frametype = fr->frametype;
175                 out->subclass = fr->subclass;
176                 out->datalen = 0;
177                 out->samples = fr->samples;
178                 out->offset = 0;
179                 out->src = NULL;
180                 out->data = NULL;
181         } else {
182                 out = fr;
183         }
184         if (!(fr->mallocd & AST_MALLOCD_SRC)) {
185                 if (fr->src)
186                         out->src = strdup(fr->src);
187         } else
188                 out->src = fr->src;
189         if (!(fr->mallocd & AST_MALLOCD_DATA))  {
190                 out->data = malloc(fr->datalen + AST_FRIENDLY_OFFSET);
191                 if (!out->data) {
192                         free(out);
193                         ast_log(LOG_WARNING, "Out of memory\n");
194                         return NULL;
195                 }
196                 out->data += AST_FRIENDLY_OFFSET;
197                 out->offset = AST_FRIENDLY_OFFSET;
198                 out->datalen = fr->datalen;
199                 memcpy(out->data, fr->data, fr->datalen);
200         }
201         out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA;
202         return out;
203 }
204
205 struct ast_frame *ast_frdup(struct ast_frame *f)
206 {
207         struct ast_frame *ret;
208         int p;
209         p = f->mallocd;
210         f->mallocd = 0;
211         /* Make frisolate think this is a 100% static frame, and make a duplicate */
212         ret = ast_frisolate(f);
213         /* Restore its true malloc status */
214         f->mallocd = p;
215         return ret;
216 }
217
218 struct ast_frame *ast_fr_fdread(int fd)
219 {
220         char buf[65536];
221         int res;
222         int ttl = sizeof(struct ast_frame);
223         struct ast_frame *f = (struct ast_frame *)buf;
224         /* Read a frame directly from there.  They're always in the
225            right format. */
226         
227         while(ttl) {
228                 res = read(fd, buf, ttl);
229                 if (res < 0) {
230                         ast_log(LOG_WARNING, "Bad read on %d: %s\n", fd, strerror(errno));
231                         return NULL;
232                 }
233                 ttl -= res;
234         }
235         
236         /* read the frame header */
237         f->mallocd = 0;
238         /* Re-write data position */
239         f->data = buf + sizeof(struct ast_frame);
240         f->offset = 0;
241         /* Forget about being mallocd */
242         f->mallocd = 0;
243         /* Re-write the source */
244         f->src = __FUNCTION__;
245         if (f->datalen > sizeof(buf) - sizeof(struct ast_frame)) {
246                 /* Really bad read */
247                 ast_log(LOG_WARNING, "Strange read (%d bytes)\n", f->datalen);
248                 return NULL;
249         }
250         if (f->datalen) {
251                 if ((res = read(fd, f->data, f->datalen)) != f->datalen) {
252                         /* Bad read */
253                         ast_log(LOG_WARNING, "How very strange, expected %d, got %d\n", f->datalen, res);
254                         return NULL;
255                 }
256         }
257         if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
258                 return NULL;
259         }
260         return ast_frisolate(f);
261 }
262
263 /* Some convenient routines for sending frames to/from stream or datagram
264    sockets, pipes, etc (maybe even files) */
265
266 int ast_fr_fdwrite(int fd, struct ast_frame *frame)
267 {
268         /* Write the frame exactly */
269         if (write(fd, frame, sizeof(struct ast_frame)) != sizeof(struct ast_frame)) {
270                 ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
271                 return -1;
272         }
273         if (write(fd, frame->data, frame->datalen) != frame->datalen) {
274                 ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno));
275                 return -1;
276         }
277         return 0;
278 }
279
280 int ast_fr_fdhangup(int fd)
281 {
282         struct ast_frame hangup = {
283                 AST_FRAME_CONTROL,
284                 AST_CONTROL_HANGUP
285         };
286         return ast_fr_fdwrite(fd, &hangup);
287 }
288
289 int ast_getformatbyname(char *name)
290 {
291         if (!strcasecmp(name, "g723.1")) 
292                 return AST_FORMAT_G723_1;
293         else if (!strcasecmp(name, "gsm"))
294                 return AST_FORMAT_GSM;
295         else if (!strcasecmp(name, "ulaw"))
296                 return AST_FORMAT_ULAW;
297         else if (!strcasecmp(name, "alaw"))
298                 return AST_FORMAT_ALAW;
299         else if (!strcasecmp(name, "mp3"))
300                 return AST_FORMAT_MP3;
301         else if (!strcasecmp(name, "slinear"))
302                 return AST_FORMAT_SLINEAR;
303         else if (!strcasecmp(name, "lpc10"))
304                 return AST_FORMAT_LPC10;
305         else if (!strcasecmp(name, "adpcm"))
306                 return AST_FORMAT_ADPCM;
307         else if (!strcasecmp(name, "g729"))
308                 return AST_FORMAT_G729A;
309         else if (!strcasecmp(name, "speex"))
310                 return AST_FORMAT_SPEEX;
311         else if (!strcasecmp(name, "all"))
312                 return 0x7FFFFFFF;
313         return 0;
314 }
315
316 #ifdef TRACE_FRAMES
317 static int show_frame_stats(int fd, int argc, char *argv[])
318 {
319         struct ast_frame *f;
320         int x=1;
321         if (argc != 3)
322                 return RESULT_SHOWUSAGE;
323         ast_cli(fd, "     Framer Statistics     \n");
324         ast_cli(fd, "---------------------------\n");
325         ast_cli(fd, "Total allocated headers: %d\n", headers);
326         ast_cli(fd, "Queue Dump:\n");
327         ast_pthread_mutex_lock(&framelock);
328         for (f=headerlist; f; f = f->next) {
329                 ast_cli(fd, "%d.  Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
330         }
331         pthread_mutex_unlock(&framelock);
332         return RESULT_SUCCESS;
333 }
334
335 static char frame_stats_usage[] =
336 "Usage: show frame stats\n"
337 "       Displays debugging statistics from framer\n";
338
339 struct ast_cli_entry cli_frame_stats =
340 { { "show", "frame", "stats", NULL }, show_frame_stats, "Shows frame statistics", frame_stats_usage };
341 #endif
342
343 int init_framer(void)
344 {
345 #ifdef TRACE_FRAMES
346         ast_cli_register(&cli_frame_stats);
347 #endif
348         return 0;       
349 }