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