Use FILE * instead of fd for files to support buffering
[asterisk/asterisk.git] / formats / format_gsm.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*
20  *
21  * Save to raw, headerless GSM data.
22  * 
23  */
24  
25 #include <unistd.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <stdlib.h>
29 #include <sys/time.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <string.h>
33
34 #include "asterisk.h"
35
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
38 #include "asterisk/lock.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/file.h"
41 #include "asterisk/logger.h"
42 #include "asterisk/sched.h"
43 #include "asterisk/module.h"
44 #include "asterisk/endian.h"
45
46 #include "msgsm.h"
47
48 /* Some Ideas for this code came from makegsme.c by Jeffrey Chilton */
49
50 /* Portions of the conversion code are by guido@sienanet.it */
51
52 /* silent gsm frame */
53 /* begin binary data: */
54 char gsm_silence[] = /* 33 */
55 {0xD8,0x20,0xA2,0xE1,0x5A,0x50,0x00,0x49,0x24,0x92,0x49,0x24,0x50,0x00,0x49
56 ,0x24,0x92,0x49,0x24,0x50,0x00,0x49,0x24,0x92,0x49,0x24,0x50,0x00,0x49,0x24
57 ,0x92,0x49,0x24};
58 /* end binary data. size = 33 bytes */
59
60 struct ast_filestream {
61         void *reserved[AST_RESERVED_POINTERS];
62         /* Believe it or not, we must decode/recode to account for the
63            weird MS format */
64         /* This is what a filestream means to us */
65         FILE *f; /* Descriptor */
66         struct ast_frame fr;                            /* Frame information */
67         char waste[AST_FRIENDLY_OFFSET];        /* Buffer for sending frames, etc */
68         char empty;                                                     /* Empty character */
69         unsigned char gsm[66];                          /* Two Real GSM Frames */
70 };
71
72
73 AST_MUTEX_DEFINE_STATIC(gsm_lock);
74 static int glistcnt = 0;
75
76 static char *name = "gsm";
77 static char *desc = "Raw GSM data";
78 static char *exts = "gsm";
79
80 static struct ast_filestream *gsm_open(FILE *f)
81 {
82         /* We don't have any header to read or anything really, but
83            if we did, it would go here.  We also might want to check
84            and be sure it's a valid file.  */
85         struct ast_filestream *tmp;
86         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
87                 memset(tmp, 0, sizeof(struct ast_filestream));
88                 if (ast_mutex_lock(&gsm_lock)) {
89                         ast_log(LOG_WARNING, "Unable to lock gsm list\n");
90                         free(tmp);
91                         return NULL;
92                 }
93                 tmp->f = f;
94                 tmp->fr.data = tmp->gsm;
95                 tmp->fr.frametype = AST_FRAME_VOICE;
96                 tmp->fr.subclass = AST_FORMAT_GSM;
97                 /* datalen will vary for each frame */
98                 tmp->fr.src = name;
99                 tmp->fr.mallocd = 0;
100                 glistcnt++;
101                 ast_mutex_unlock(&gsm_lock);
102                 ast_update_use_count();
103         }
104         return tmp;
105 }
106
107 static struct ast_filestream *gsm_rewrite(FILE *f, const char *comment)
108 {
109         /* We don't have any header to read or anything really, but
110            if we did, it would go here.  We also might want to check
111            and be sure it's a valid file.  */
112         struct ast_filestream *tmp;
113         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
114                 memset(tmp, 0, sizeof(struct ast_filestream));
115                 if (ast_mutex_lock(&gsm_lock)) {
116                         ast_log(LOG_WARNING, "Unable to lock gsm list\n");
117                         free(tmp);
118                         return NULL;
119                 }
120                 tmp->f = f;
121                 glistcnt++;
122                 ast_mutex_unlock(&gsm_lock);
123                 ast_update_use_count();
124         } else
125                 ast_log(LOG_WARNING, "Out of memory\n");
126         return tmp;
127 }
128
129 static void gsm_close(struct ast_filestream *s)
130 {
131         if (ast_mutex_lock(&gsm_lock)) {
132                 ast_log(LOG_WARNING, "Unable to lock gsm list\n");
133                 return;
134         }
135         glistcnt--;
136         ast_mutex_unlock(&gsm_lock);
137         ast_update_use_count();
138         fclose(s->f);
139         free(s);
140 }
141
142 static struct ast_frame *gsm_read(struct ast_filestream *s, int *whennext)
143 {
144         int res;
145         s->fr.frametype = AST_FRAME_VOICE;
146         s->fr.subclass = AST_FORMAT_GSM;
147         s->fr.offset = AST_FRIENDLY_OFFSET;
148         s->fr.samples = 160;
149         s->fr.datalen = 33;
150         s->fr.mallocd = 0;
151         s->fr.data = s->gsm;
152         if ((res = fread(s->gsm, 1, 33, s->f)) != 33) {
153                 if (res)
154                         ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
155                 return NULL;
156         }
157         *whennext = 160;
158         return &s->fr;
159 }
160
161 static int gsm_write(struct ast_filestream *fs, struct ast_frame *f)
162 {
163         int res;
164         unsigned char gsm[66];
165         if (f->frametype != AST_FRAME_VOICE) {
166                 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
167                 return -1;
168         }
169         if (f->subclass != AST_FORMAT_GSM) {
170                 ast_log(LOG_WARNING, "Asked to write non-GSM frame (%d)!\n", f->subclass);
171                 return -1;
172         }
173         if (!(f->datalen % 65)) {
174                 /* This is in MSGSM format, need to be converted */
175                 int len=0;
176                 while(len < f->datalen) {
177                         conv65(f->data + len, gsm);
178                         if ((res = fwrite(gsm, 1, 66, fs->f)) != 66) {
179                                 ast_log(LOG_WARNING, "Bad write (%d/66): %s\n", res, strerror(errno));
180                                 return -1;
181                         }
182                         len += 65;
183                 }
184         } else {
185                 if (f->datalen % 33) {
186                         ast_log(LOG_WARNING, "Invalid data length, %d, should be multiple of 33\n", f->datalen);
187                         return -1;
188                 }
189                 if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) {
190                                 ast_log(LOG_WARNING, "Bad write (%d/33): %s\n", res, strerror(errno));
191                                 return -1;
192                 }
193         }
194         return 0;
195 }
196
197 static int gsm_seek(struct ast_filestream *fs, long sample_offset, int whence)
198 {
199         off_t offset=0,min,cur,max,distance;
200         
201         min = 0;
202         cur = ftell(fs->f);
203         fseek(fs->f, 0, SEEK_END);
204         max = ftell(fs->f);
205         /* have to fudge to frame here, so not fully to sample */
206         distance = (sample_offset/160) * 33;
207         if(whence == SEEK_SET)
208                 offset = distance;
209         else if(whence == SEEK_CUR || whence == SEEK_FORCECUR)
210                 offset = distance + cur;
211         else if(whence == SEEK_END)
212                 offset = max - distance;
213         /* Always protect against seeking past the begining. */
214         offset = (offset < min)?min:offset;
215         if (whence != SEEK_FORCECUR) {
216                 offset = (offset > max)?max:offset;
217         } else if (offset > max) {
218                 int i;
219                 fseek(fs->f, 0, SEEK_END);
220                 for (i=0; i< (offset - max) / 33; i++) {
221                         fwrite(gsm_silence, 1, 33, fs->f);
222                 }
223         }
224         return fseek(fs->f, offset, SEEK_SET);
225 }
226
227 static int gsm_trunc(struct ast_filestream *fs)
228 {
229         return ftruncate(fileno(fs->f), ftell(fs->f));
230 }
231
232 static long gsm_tell(struct ast_filestream *fs)
233 {
234         off_t offset;
235         offset = ftell(fs->f);
236         return (offset/33)*160;
237 }
238
239 static char *gsm_getcomment(struct ast_filestream *s)
240 {
241         return NULL;
242 }
243
244 int load_module()
245 {
246         return ast_format_register(name, exts, AST_FORMAT_GSM,
247                                                                 gsm_open,
248                                                                 gsm_rewrite,
249                                                                 gsm_write,
250                                                                 gsm_seek,
251                                                                 gsm_trunc,
252                                                                 gsm_tell,
253                                                                 gsm_read,
254                                                                 gsm_close,
255                                                                 gsm_getcomment);
256                                                                 
257                                                                 
258 }
259
260 int unload_module()
261 {
262         return ast_format_unregister(name);
263 }       
264
265 int usecount()
266 {
267         return glistcnt;
268 }
269
270 char *description()
271 {
272         return desc;
273 }
274
275
276 char *key()
277 {
278         return ASTERISK_GPL_KEY;
279 }