show correct codec in verbose messages (bug #4008)
[asterisk/asterisk.git] / codecs / codec_gsm.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Translate between signed linear and Global System for Mobile Communications (GSM)
5  *
6  * The GSM code is from TOAST.  Copyright information for that package is available
7  * in  the GSM directory.
8  * 
9  * Copyright (C) 1999, Mark Spencer
10  *
11  * Mark Spencer <markster@linux-support.net>
12  *
13  * This program is free software, distributed under the terms of
14  * the GNU General Public License
15  */
16
17 #include <asterisk/lock.h>
18 #include <asterisk/translate.h>
19 #include <asterisk/config.h>
20 #include <asterisk/options.h>
21 #include <asterisk/module.h>
22 #include <asterisk/logger.h>
23 #include <asterisk/channel.h>
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <netinet/in.h>
28 #include <string.h>
29 #include <stdio.h>
30
31 #include "gsm/inc/gsm.h"
32 #include "../formats/msgsm.h"
33
34 /* Sample frame data */
35 #include "slin_gsm_ex.h"
36 #include "gsm_slin_ex.h"
37
38 AST_MUTEX_DEFINE_STATIC(localuser_lock);
39 static int localusecnt=0;
40
41 static char *tdesc = "GSM/PCM16 (signed linear) Codec Translator";
42
43 static int useplc = 0;
44
45 struct ast_translator_pvt {
46         gsm gsm;
47         struct ast_frame f;
48         /* Space to build offset */
49         char offset[AST_FRIENDLY_OFFSET];
50         /* Buffer for our outgoing frame */
51         short outbuf[8000];
52         /* Enough to store a full second */
53         short buf[8000];
54         int tail;
55         plc_state_t plc;
56 };
57
58 #define gsm_coder_pvt ast_translator_pvt
59
60 static struct ast_translator_pvt *gsm_new(void)
61 {
62         struct gsm_coder_pvt *tmp;
63         tmp = malloc(sizeof(struct gsm_coder_pvt));
64         if (tmp) {
65                 if (!(tmp->gsm = gsm_create())) {
66                         free(tmp);
67                         tmp = NULL;
68                 }
69                 tmp->tail = 0;
70                 plc_init(&tmp->plc);
71                 localusecnt++;
72         }
73         return tmp;
74 }
75
76 static struct ast_frame *lintogsm_sample(void)
77 {
78         static struct ast_frame f;
79         f.frametype = AST_FRAME_VOICE;
80         f.subclass = AST_FORMAT_SLINEAR;
81         f.datalen = sizeof(slin_gsm_ex);
82         /* Assume 8000 Hz */
83         f.samples = sizeof(slin_gsm_ex)/2;
84         f.mallocd = 0;
85         f.offset = 0;
86         f.src = __PRETTY_FUNCTION__;
87         f.data = slin_gsm_ex;
88         return &f;
89 }
90
91 static struct ast_frame *gsmtolin_sample(void)
92 {
93         static struct ast_frame f;
94         f.frametype = AST_FRAME_VOICE;
95         f.subclass = AST_FORMAT_GSM;
96         f.datalen = sizeof(gsm_slin_ex);
97         /* All frames are 20 ms long */
98         f.samples = 160;
99         f.mallocd = 0;
100         f.offset = 0;
101         f.src = __PRETTY_FUNCTION__;
102         f.data = gsm_slin_ex;
103         return &f;
104 }
105
106 static struct ast_frame *gsmtolin_frameout(struct ast_translator_pvt *tmp)
107 {
108         if (!tmp->tail)
109                 return NULL;
110         /* Signed linear is no particular frame size, so just send whatever
111            we have in the buffer in one lump sum */
112         tmp->f.frametype = AST_FRAME_VOICE;
113         tmp->f.subclass = AST_FORMAT_SLINEAR;
114         tmp->f.datalen = tmp->tail * 2;
115         /* Assume 8000 Hz */
116         tmp->f.samples = tmp->tail;
117         tmp->f.mallocd = 0;
118         tmp->f.offset = AST_FRIENDLY_OFFSET;
119         tmp->f.src = __PRETTY_FUNCTION__;
120         tmp->f.data = tmp->buf;
121         /* Reset tail pointer */
122         tmp->tail = 0;
123
124         return &tmp->f; 
125 }
126
127 static int gsmtolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
128 {
129         /* Assuming there's space left, decode into the current buffer at
130            the tail location.  Read in as many frames as there are */
131         int x;
132         unsigned char data[66];
133         int msgsm=0;
134         
135         if(f->datalen == 0) { /* perform PLC with nominal framesize of 20ms/160 samples */
136               if((tmp->tail + 160) > sizeof(tmp->buf) / 2) {
137                   ast_log(LOG_WARNING, "Out of buffer space\n");
138                   return -1;
139               }
140               if(useplc) {
141                   plc_fillin(&tmp->plc, tmp->buf+tmp->tail, 160);
142                   tmp->tail += 160;
143               }
144               return 0;
145         }
146
147         if ((f->datalen % 33) && (f->datalen % 65)) {
148                 ast_log(LOG_WARNING, "Huh?  A GSM frame that isn't a multiple of 33 or 65 bytes long from %s (%d)?\n", f->src, f->datalen);
149                 return -1;
150         }
151         
152         if (f->datalen % 65 == 0) 
153                 msgsm = 1;
154                 
155         for (x=0;x<f->datalen;x+=(msgsm ? 65 : 33)) {
156                 if (msgsm) {
157                         /* Translate MSGSM format to Real GSM format before feeding in */
158                         conv65(f->data + x, data);
159                         if (tmp->tail + 320 < sizeof(tmp->buf)/2) {     
160                                 if (gsm_decode(tmp->gsm, data, tmp->buf + tmp->tail)) {
161                                         ast_log(LOG_WARNING, "Invalid GSM data (1)\n");
162                                         return -1;
163                                 }
164                                 tmp->tail+=160;
165                                 if (gsm_decode(tmp->gsm, data + 33, tmp->buf + tmp->tail)) {
166                                         ast_log(LOG_WARNING, "Invalid GSM data (2)\n");
167                                         return -1;
168                                 }
169                                 tmp->tail+=160;
170                         } else {
171                                 ast_log(LOG_WARNING, "Out of (MS) buffer space\n");
172                                 return -1;
173                         }
174                 } else {
175                         if (tmp->tail + 160 < sizeof(tmp->buf)/2) {     
176                                 if (gsm_decode(tmp->gsm, f->data + x, tmp->buf + tmp->tail)) {
177                                         ast_log(LOG_WARNING, "Invalid GSM data\n");
178                                         return -1;
179                                 }
180                                 tmp->tail+=160;
181                         } else {
182                                 ast_log(LOG_WARNING, "Out of buffer space\n");
183                                 return -1;
184                         }
185                 }
186         }
187
188         /* just add the last 20ms frame; there must have been at least one */
189         if(useplc) plc_rx(&tmp->plc, tmp->buf+tmp->tail-160, 160);
190
191         return 0;
192 }
193
194 static int lintogsm_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
195 {
196         /* Just add the frames to our stream */
197         /* XXX We should look at how old the rest of our stream is, and if it
198            is too old, then we should overwrite it entirely, otherwise we can
199            get artifacts of earlier talk that do not belong */
200         if (tmp->tail + f->datalen/2 < sizeof(tmp->buf) / 2) {
201                 memcpy((tmp->buf + tmp->tail), f->data, f->datalen);
202                 tmp->tail += f->datalen/2;
203         } else {
204                 ast_log(LOG_WARNING, "Out of buffer space\n");
205                 return -1;
206         }
207         return 0;
208 }
209
210 static struct ast_frame *lintogsm_frameout(struct ast_translator_pvt *tmp)
211 {
212         int x=0;
213         /* We can't work on anything less than a frame in size */
214         if (tmp->tail < 160)
215                 return NULL;
216         tmp->f.frametype = AST_FRAME_VOICE;
217         tmp->f.subclass = AST_FORMAT_GSM;
218         tmp->f.mallocd = 0;
219         tmp->f.offset = AST_FRIENDLY_OFFSET;
220         tmp->f.src = __PRETTY_FUNCTION__;
221         tmp->f.data = tmp->outbuf;
222         while(tmp->tail >= 160) {
223                 if ((x+1) * 33 >= sizeof(tmp->outbuf)) {
224                         ast_log(LOG_WARNING, "Out of buffer space\n");
225                         break;
226                 }
227                 /* Encode a frame of data */
228                 gsm_encode(tmp->gsm, tmp->buf, ((gsm_byte *) tmp->outbuf) + (x * 33));
229                 /* Assume 8000 Hz -- 20 ms */
230                 tmp->tail -= 160;
231                 /* Move the data at the end of the buffer to the front */
232                 if (tmp->tail)
233                         memmove(tmp->buf, tmp->buf + 160, tmp->tail * 2);
234                 x++;
235         }
236         tmp->f.datalen = x * 33;
237         tmp->f.samples = x * 160;
238         return &tmp->f; 
239 }
240
241 static void gsm_destroy_stuff(struct ast_translator_pvt *pvt)
242 {
243         if (pvt->gsm)
244                 gsm_destroy(pvt->gsm);
245         free(pvt);
246         localusecnt--;
247 }
248
249 static struct ast_translator gsmtolin =
250         { "gsmtolin", 
251            AST_FORMAT_GSM, AST_FORMAT_SLINEAR,
252            gsm_new,
253            gsmtolin_framein,
254            gsmtolin_frameout,
255            gsm_destroy_stuff,
256            gsmtolin_sample
257            };
258
259 static struct ast_translator lintogsm =
260         { "lintogsm", 
261            AST_FORMAT_SLINEAR, AST_FORMAT_GSM,
262            gsm_new,
263            lintogsm_framein,
264            lintogsm_frameout,
265            gsm_destroy_stuff,
266            lintogsm_sample
267            };
268
269
270 static void parse_config(void)
271 {
272         struct ast_config *cfg;
273         struct ast_variable *var;
274         if ((cfg = ast_config_load("codecs.conf"))) {
275                 if ((var = ast_variable_browse(cfg, "plc"))) {
276                         while (var) {
277                                if (!strcasecmp(var->name, "genericplc")) {
278                                        useplc = ast_true(var->value) ? 1 : 0;
279                                        if (option_verbose > 2)
280                                                ast_verbose(VERBOSE_PREFIX_3 "codec_gsm: %susing generic PLC\n", useplc ? "" : "not ");
281                                }
282                                var = var->next;
283                         }
284                 }
285         }
286 }
287
288 int reload(void)
289 {
290         parse_config();
291         return 0;
292 }
293
294 int unload_module(void)
295 {
296         int res;
297         ast_mutex_lock(&localuser_lock);
298         res = ast_unregister_translator(&lintogsm);
299         if (!res)
300                 res = ast_unregister_translator(&gsmtolin);
301         if (localusecnt)
302                 res = -1;
303         ast_mutex_unlock(&localuser_lock);
304         return res;
305 }
306
307 int load_module(void)
308 {
309         int res;
310         parse_config();
311         res=ast_register_translator(&gsmtolin);
312         if (!res) 
313                 res=ast_register_translator(&lintogsm);
314         else
315                 ast_unregister_translator(&gsmtolin);
316         return res;
317 }
318
319 char *description(void)
320 {
321         return tdesc;
322 }
323
324 int usecount(void)
325 {
326         int res;
327         STANDARD_USECOUNT(res);
328         return res;
329 }
330
331 char *key()
332 {
333         return ASTERISK_GPL_KEY;
334 }