include "logger.h" and errno.h from asterisk.h - usage shows that they
[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 "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <sys/time.h>
33
34 #include "asterisk/lock.h"
35 #include "asterisk/channel.h"
36 #include "asterisk/file.h"
37 #include "asterisk/sched.h"
38 #include "asterisk/module.h"
39 #include "asterisk/endian.h"
40
41 #include "msgsm.h"
42
43 /* Some Ideas for this code came from makegsme.c by Jeffrey Chilton */
44
45 /* Portions of the conversion code are by guido@sienanet.it */
46
47 #define GSM_FRAME_SIZE  33
48 #define GSM_SAMPLES     160
49
50 /* silent gsm frame */
51 /* begin binary data: */
52 char gsm_silence[] = /* 33 */
53 {0xD8,0x20,0xA2,0xE1,0x5A,0x50,0x00,0x49,0x24,0x92,0x49,0x24,0x50,0x00,0x49
54 ,0x24,0x92,0x49,0x24,0x50,0x00,0x49,0x24,0x92,0x49,0x24,0x50,0x00,0x49,0x24
55 ,0x92,0x49,0x24};
56 /* end binary data. size = 33 bytes */
57
58 static struct ast_frame *gsm_read(struct ast_filestream *s, int *whennext)
59 {
60         int res;
61
62         s->fr.frametype = AST_FRAME_VOICE;
63         s->fr.subclass = AST_FORMAT_GSM;
64         AST_FRAME_SET_BUFFER(&(s->fr), s->buf, AST_FRIENDLY_OFFSET, GSM_FRAME_SIZE)
65         s->fr.mallocd = 0;
66         if ((res = fread(s->fr.data, 1, GSM_FRAME_SIZE, s->f)) != GSM_FRAME_SIZE) {
67                 if (res)
68                         ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
69                 return NULL;
70         }
71         *whennext = s->fr.samples = GSM_SAMPLES;
72         return &s->fr;
73 }
74
75 static int gsm_write(struct ast_filestream *fs, struct ast_frame *f)
76 {
77         int res;
78         unsigned char gsm[2*GSM_FRAME_SIZE];
79
80         if (f->frametype != AST_FRAME_VOICE) {
81                 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
82                 return -1;
83         }
84         if (f->subclass != AST_FORMAT_GSM) {
85                 ast_log(LOG_WARNING, "Asked to write non-GSM frame (%d)!\n", f->subclass);
86                 return -1;
87         }
88         if (!(f->datalen % 65)) {
89                 /* This is in MSGSM format, need to be converted */
90                 int len=0;
91                 while(len < f->datalen) {
92                         conv65(f->data + len, gsm);
93                         if ((res = fwrite(gsm, 1, 2*GSM_FRAME_SIZE, fs->f)) != 2*GSM_FRAME_SIZE) {
94                                 ast_log(LOG_WARNING, "Bad write (%d/66): %s\n", res, strerror(errno));
95                                 return -1;
96                         }
97                         len += 65;
98                 }
99         } else {
100                 if (f->datalen % GSM_FRAME_SIZE) {
101                         ast_log(LOG_WARNING, "Invalid data length, %d, should be multiple of 33\n", f->datalen);
102                         return -1;
103                 }
104                 if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) {
105                                 ast_log(LOG_WARNING, "Bad write (%d/33): %s\n", res, strerror(errno));
106                                 return -1;
107                 }
108         }
109         return 0;
110 }
111
112 static int gsm_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
113 {
114         off_t offset=0,min,cur,max,distance;
115         
116         min = 0;
117         cur = ftello(fs->f);
118         fseeko(fs->f, 0, SEEK_END);
119         max = ftello(fs->f);
120         /* have to fudge to frame here, so not fully to sample */
121         distance = (sample_offset/GSM_SAMPLES) * GSM_FRAME_SIZE;
122         if(whence == SEEK_SET)
123                 offset = distance;
124         else if(whence == SEEK_CUR || whence == SEEK_FORCECUR)
125                 offset = distance + cur;
126         else if(whence == SEEK_END)
127                 offset = max - distance;
128         /* Always protect against seeking past the begining. */
129         offset = (offset < min)?min:offset;
130         if (whence != SEEK_FORCECUR) {
131                 offset = (offset > max)?max:offset;
132         } else if (offset > max) {
133                 int i;
134                 fseeko(fs->f, 0, SEEK_END);
135                 for (i=0; i< (offset - max) / GSM_FRAME_SIZE; i++) {
136                         fwrite(gsm_silence, 1, GSM_FRAME_SIZE, fs->f);
137                 }
138         }
139         return fseeko(fs->f, offset, SEEK_SET);
140 }
141
142 static int gsm_trunc(struct ast_filestream *fs)
143 {
144         return ftruncate(fileno(fs->f), ftello(fs->f));
145 }
146
147 static off_t gsm_tell(struct ast_filestream *fs)
148 {
149         off_t offset = ftello(fs->f);
150         return (offset/GSM_FRAME_SIZE)*GSM_SAMPLES;
151 }
152
153 static const struct ast_format gsm_f = {
154         .name = "gsm",
155         .exts = "gsm",
156         .format = AST_FORMAT_GSM,
157         .write = gsm_write,
158         .seek = gsm_seek,
159         .trunc = gsm_trunc,
160         .tell = gsm_tell,
161         .read = gsm_read,
162         .buf_size = 2*GSM_FRAME_SIZE + AST_FRIENDLY_OFFSET,     /* 2 gsm frames */
163 };
164
165 static int load_module(void)
166 {
167         if (ast_format_register(&gsm_f))
168                 return AST_MODULE_LOAD_FAILURE;
169         return AST_MODULE_LOAD_SUCCESS;
170 }
171
172 static int unload_module(void)
173 {
174         return ast_format_unregister(gsm_f.name);
175 }       
176
177 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Raw GSM data");