update MANY more files with proper copyright/license info (thanks Ian!)
[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         int fd; /* 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(int fd)
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->fd = fd;
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(int fd, 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->fd = fd;
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         close(s->fd);
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 = read(s->fd, s->gsm, 33)) != 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 = write(fs->fd, gsm, 66)) != 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 = write(fs->fd, f->data, f->datalen)) != 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 = lseek(fs->fd, 0, SEEK_CUR);
203         max = lseek(fs->fd, 0, SEEK_END);
204         /* have to fudge to frame here, so not fully to sample */
205         distance = (sample_offset/160) * 33;
206         if(whence == SEEK_SET)
207                 offset = distance;
208         else if(whence == SEEK_CUR || whence == SEEK_FORCECUR)
209                 offset = distance + cur;
210         else if(whence == SEEK_END)
211                 offset = max - distance;
212         /* Always protect against seeking past the begining. */
213         offset = (offset < min)?min:offset;
214         if (whence != SEEK_FORCECUR) {
215                 offset = (offset > max)?max:offset;
216         } else if (offset > max) {
217                 int i;
218                 lseek(fs->fd, 0, SEEK_END);
219                 for (i=0; i< (offset - max) / 33; i++) {
220                         write(fs->fd, gsm_silence, 33);
221                 }
222         }
223         return lseek(fs->fd, offset, SEEK_SET);
224 }
225
226 static int gsm_trunc(struct ast_filestream *fs)
227 {
228         return ftruncate(fs->fd, lseek(fs->fd,0,SEEK_CUR));
229 }
230
231 static long gsm_tell(struct ast_filestream *fs)
232 {
233         off_t offset;
234         offset = lseek(fs->fd, 0, SEEK_CUR);
235         return (offset/33)*160;
236 }
237
238 static char *gsm_getcomment(struct ast_filestream *s)
239 {
240         return NULL;
241 }
242
243 int load_module()
244 {
245         return ast_format_register(name, exts, AST_FORMAT_GSM,
246                                                                 gsm_open,
247                                                                 gsm_rewrite,
248                                                                 gsm_write,
249                                                                 gsm_seek,
250                                                                 gsm_trunc,
251                                                                 gsm_tell,
252                                                                 gsm_read,
253                                                                 gsm_close,
254                                                                 gsm_getcomment);
255                                                                 
256                                                                 
257 }
258
259 int unload_module()
260 {
261         return ast_format_unregister(name);
262 }       
263
264 int usecount()
265 {
266         return glistcnt;
267 }
268
269 char *description()
270 {
271         return desc;
272 }
273
274
275 char *key()
276 {
277         return ASTERISK_GPL_KEY;
278 }