f92018cf25b5a466feddfe255f69471503709ca7
[asterisk/asterisk.git] / formats / format_ilbc.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Save to raw, headerless iLBC data.
5  *
6  * Brian K. West <brian@bkw.org>
7  * 
8  * Copyright (C) 1999, Mark Spencer
9  *
10  * Mark Spencer <markster@linux-support.net>
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License
14  */
15  
16 #include <asterisk/lock.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/file.h>
19 #include <asterisk/logger.h>
20 #include <asterisk/sched.h>
21 #include <asterisk/module.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <stdlib.h>
25 #include <sys/time.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <string.h>
30 #ifdef __linux__
31 #include <endian.h>
32 #else
33 #ifdef SOLARIS
34 #include "solaris-compat/compat.h"
35 #else
36 #include <machine/endian.h>
37 #endif
38 #endif
39
40 /* Some Ideas for this code came from makeg729e.c by Jeffrey Chilton */
41
42 /* Portions of the conversion code are by guido@sienanet.it */
43
44 struct ast_filestream {
45         void *reserved[AST_RESERVED_POINTERS];
46         /* Believe it or not, we must decode/recode to account for the
47            weird MS format */
48         /* This is what a filestream means to us */
49         int fd; /* Descriptor */
50         struct ast_frame fr;                            /* Frame information */
51         char waste[AST_FRIENDLY_OFFSET];        /* Buffer for sending frames, etc */
52         char empty;                                                     /* Empty character */
53         unsigned char ilbc[50];                         /* One Real iLBC Frame */
54 };
55
56
57 AST_MUTEX_DEFINE_STATIC(ilbc_lock);
58 static int glistcnt = 0;
59
60 static char *name = "iLBC";
61 static char *desc = "Raw iLBC data";
62 static char *exts = "ilbc";
63
64 static struct ast_filestream *ilbc_open(int fd)
65 {
66         /* We don't have any header to read or anything really, but
67            if we did, it would go here.  We also might want to check
68            and be sure it's a valid file.  */
69         struct ast_filestream *tmp;
70         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
71                 memset(tmp, 0, sizeof(struct ast_filestream));
72                 if (ast_mutex_lock(&ilbc_lock)) {
73                         ast_log(LOG_WARNING, "Unable to lock ilbc list\n");
74                         free(tmp);
75                         return NULL;
76                 }
77                 tmp->fd = fd;
78                 tmp->fr.data = tmp->ilbc;
79                 tmp->fr.frametype = AST_FRAME_VOICE;
80                 tmp->fr.subclass = AST_FORMAT_ILBC;
81                 /* datalen will vary for each frame */
82                 tmp->fr.src = name;
83                 tmp->fr.mallocd = 0;
84                 glistcnt++;
85                 ast_mutex_unlock(&ilbc_lock);
86                 ast_update_use_count();
87         }
88         return tmp;
89 }
90
91 static struct ast_filestream *ilbc_rewrite(int fd, const char *comment)
92 {
93         /* We don't have any header to read or anything really, but
94            if we did, it would go here.  We also might want to check
95            and be sure it's a valid file.  */
96         struct ast_filestream *tmp;
97         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
98                 memset(tmp, 0, sizeof(struct ast_filestream));
99                 if (ast_mutex_lock(&ilbc_lock)) {
100                         ast_log(LOG_WARNING, "Unable to lock ilbc list\n");
101                         free(tmp);
102                         return NULL;
103                 }
104                 tmp->fd = fd;
105                 glistcnt++;
106                 ast_mutex_unlock(&ilbc_lock);
107                 ast_update_use_count();
108         } else
109                 ast_log(LOG_WARNING, "Out of memory\n");
110         return tmp;
111 }
112
113 static void ilbc_close(struct ast_filestream *s)
114 {
115         if (ast_mutex_lock(&ilbc_lock)) {
116                 ast_log(LOG_WARNING, "Unable to lock ilbc list\n");
117                 return;
118         }
119         glistcnt--;
120         ast_mutex_unlock(&ilbc_lock);
121         ast_update_use_count();
122         close(s->fd);
123         free(s);
124         s = NULL;
125 }
126
127 static struct ast_frame *ilbc_read(struct ast_filestream *s, int *whennext)
128 {
129         int res;
130         /* Send a frame from the file to the appropriate channel */
131         s->fr.frametype = AST_FRAME_VOICE;
132         s->fr.subclass = AST_FORMAT_ILBC;
133         s->fr.offset = AST_FRIENDLY_OFFSET;
134         s->fr.samples = 240;
135         s->fr.datalen = 50;
136         s->fr.mallocd = 0;
137         s->fr.data = s->ilbc;
138         if ((res = read(s->fd, s->ilbc, 50)) != 50) {
139                 if (res)
140                         ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
141                 return NULL;
142         }
143         *whennext = s->fr.samples;
144         return &s->fr;
145 }
146
147 static int ilbc_write(struct ast_filestream *fs, struct ast_frame *f)
148 {
149         int res;
150         if (f->frametype != AST_FRAME_VOICE) {
151                 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
152                 return -1;
153         }
154         if (f->subclass != AST_FORMAT_ILBC) {
155                 ast_log(LOG_WARNING, "Asked to write non-iLBC frame (%d)!\n", f->subclass);
156                 return -1;
157         }
158         if (f->datalen % 50) {
159                 ast_log(LOG_WARNING, "Invalid data length, %d, should be multiple of 50\n", f->datalen);
160                 return -1;
161         }
162         if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
163                         ast_log(LOG_WARNING, "Bad write (%d/50): %s\n", res, strerror(errno));
164                         return -1;
165         }
166         return 0;
167 }
168
169 static char *ilbc_getcomment(struct ast_filestream *s)
170 {
171         return NULL;
172 }
173
174 static int ilbc_seek(struct ast_filestream *fs, long sample_offset, int whence)
175 {
176         long bytes;
177         off_t min,cur,max,offset=0;
178         min = 0;
179         cur = lseek(fs->fd, 0, SEEK_CUR);
180         max = lseek(fs->fd, 0, SEEK_END);
181         
182         bytes = 50 * (sample_offset / 240);
183         if (whence == SEEK_SET)
184                 offset = bytes;
185         else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
186                 offset = cur + bytes;
187         else if (whence == SEEK_END)
188                 offset = max - bytes;
189         if (whence != SEEK_FORCECUR) {
190                 offset = (offset > max)?max:offset;
191         }
192         // protect against seeking beyond begining.
193         offset = (offset < min)?min:offset;
194         if (lseek(fs->fd, offset, SEEK_SET) < 0)
195                 return -1;
196         return 0;
197 }
198
199 static int ilbc_trunc(struct ast_filestream *fs)
200 {
201         /* Truncate file to current length */
202         if (ftruncate(fs->fd, lseek(fs->fd, 0, SEEK_CUR)) < 0)
203                 return -1;
204         return 0;
205 }
206
207 static long ilbc_tell(struct ast_filestream *fs)
208 {
209         off_t offset;
210         offset = lseek(fs->fd, 0, SEEK_CUR);
211         return (offset/50)*240;
212 }
213
214 int load_module()
215 {
216         return ast_format_register(name, exts, AST_FORMAT_ILBC,
217                                                                 ilbc_open,
218                                                                 ilbc_rewrite,
219                                                                 ilbc_write,
220                                                                 ilbc_seek,
221                                                                 ilbc_trunc,
222                                                                 ilbc_tell,
223                                                                 ilbc_read,
224                                                                 ilbc_close,
225                                                                 ilbc_getcomment);
226                                                                 
227                                                                 
228 }
229
230 int unload_module()
231 {
232         return ast_format_unregister(name);
233 }       
234
235 int usecount()
236 {
237         int res;
238         if (ast_mutex_lock(&ilbc_lock)) {
239                 ast_log(LOG_WARNING, "Unable to lock ilbc list\n");
240                 return -1;
241         }
242         res = glistcnt;
243         ast_mutex_unlock(&ilbc_lock);
244         return res;
245 }
246
247 char *description()
248 {
249         return desc;
250 }
251
252
253 char *key()
254 {
255         return ASTERISK_GPL_KEY;
256 }