blob: 83593d6c197e365baf09478c50763bb6bd95840e [file] [log] [blame]
Benny Prijonoa4bf0212006-02-10 15:57:08 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijonoa4bf0212006-02-10 15:57:08 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjmedia-codec/gsm.h>
21#include <pjmedia/codec.h>
22#include <pjmedia/errno.h>
23#include <pjmedia/endpoint.h>
Benny Prijono70c68912006-05-19 15:58:13 +000024#include <pjmedia/plc.h>
Benny Prijonof04ffdd2006-02-21 00:11:18 +000025#include <pjmedia/port.h>
Benny Prijono70c68912006-05-19 15:58:13 +000026#include <pjmedia/silencedet.h>
Benny Prijonoa4bf0212006-02-10 15:57:08 +000027#include <pj/assert.h>
28#include <pj/pool.h>
29#include <pj/string.h>
30#include <pj/os.h>
Benny Prijonoa4bf0212006-02-10 15:57:08 +000031
Benny Prijono4381efe2006-03-16 14:24:26 +000032/*
33 * Only build this file if PJMEDIA_HAS_GSM_CODEC != 0
34 */
35#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC != 0
36
Benny Prijono8a9b3b22010-01-14 14:46:54 +000037#if defined(PJMEDIA_EXTERNAL_GSM_CODEC) && PJMEDIA_EXTERNAL_GSM_CODEC
Benny Prijonod4306432010-05-01 22:05:41 +000038# if PJMEDIA_EXTERNAL_GSM_GSM_H
Benny Prijono8a9b3b22010-01-14 14:46:54 +000039# include <gsm/gsm.h>
Benny Prijonod4306432010-05-01 22:05:41 +000040# elif PJMEDIA_EXTERNAL_GSM_H
41# include <gsm.h>
42# else
43# error Please set the location of gsm.h
44# endif
Benny Prijono8a9b3b22010-01-14 14:46:54 +000045#else
46# include "../../third_party/gsm/inc/gsm.h"
47#endif
48
Benny Prijono00d15a52008-03-04 16:23:42 +000049/* We removed PLC in 0.6 (and re-enabled it again in 0.9!) */
50#define PLC_DISABLED 0
Benny Prijonoc95a0f02007-04-09 07:06:08 +000051
Benny Prijono4381efe2006-03-16 14:24:26 +000052
Benny Prijonoa4bf0212006-02-10 15:57:08 +000053/* Prototypes for GSM factory */
54static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
55 const pjmedia_codec_info *id );
56static pj_status_t gsm_default_attr( pjmedia_codec_factory *factory,
57 const pjmedia_codec_info *id,
58 pjmedia_codec_param *attr );
59static pj_status_t gsm_enum_codecs( pjmedia_codec_factory *factory,
60 unsigned *count,
61 pjmedia_codec_info codecs[]);
62static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
63 const pjmedia_codec_info *id,
64 pjmedia_codec **p_codec);
65static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
66 pjmedia_codec *codec );
67
68/* Prototypes for GSM implementation. */
Benny Prijonoa4bf0212006-02-10 15:57:08 +000069static pj_status_t gsm_codec_init( pjmedia_codec *codec,
70 pj_pool_t *pool );
71static pj_status_t gsm_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +000072 pjmedia_codec_param *attr );
Benny Prijonoa4bf0212006-02-10 15:57:08 +000073static pj_status_t gsm_codec_close( pjmedia_codec *codec );
Benny Prijonob94a6ab2006-12-26 21:18:11 +000074static pj_status_t gsm_codec_modify(pjmedia_codec *codec,
75 const pjmedia_codec_param *attr );
Benny Prijono8befd9f2006-05-13 22:46:23 +000076static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
77 void *pkt,
78 pj_size_t pkt_size,
79 const pj_timestamp *ts,
80 unsigned *frame_cnt,
81 pjmedia_frame frames[]);
Benny Prijonoa4bf0212006-02-10 15:57:08 +000082static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
83 const struct pjmedia_frame *input,
84 unsigned output_buf_len,
85 struct pjmedia_frame *output);
86static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
87 const struct pjmedia_frame *input,
88 unsigned output_buf_len,
89 struct pjmedia_frame *output);
Benny Prijonoc95a0f02007-04-09 07:06:08 +000090#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +000091static pj_status_t gsm_codec_recover(pjmedia_codec *codec,
92 unsigned output_buf_len,
93 struct pjmedia_frame *output);
Benny Prijonoc95a0f02007-04-09 07:06:08 +000094#endif
Benny Prijonoa4bf0212006-02-10 15:57:08 +000095
96/* Definition for GSM codec operations. */
97static pjmedia_codec_op gsm_op =
98{
Benny Prijonoa4bf0212006-02-10 15:57:08 +000099 &gsm_codec_init,
100 &gsm_codec_open,
101 &gsm_codec_close,
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000102 &gsm_codec_modify,
Benny Prijono8befd9f2006-05-13 22:46:23 +0000103 &gsm_codec_parse,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000104 &gsm_codec_encode,
Benny Prijono70c68912006-05-19 15:58:13 +0000105 &gsm_codec_decode,
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000106#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000107 &gsm_codec_recover
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000108#else
109 NULL
110#endif
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000111};
112
113/* Definition for GSM codec factory operations. */
114static pjmedia_codec_factory_op gsm_factory_op =
115{
116 &gsm_test_alloc,
117 &gsm_default_attr,
118 &gsm_enum_codecs,
119 &gsm_alloc_codec,
120 &gsm_dealloc_codec
121};
122
123/* GSM factory */
124static struct gsm_codec_factory
125{
126 pjmedia_codec_factory base;
127 pjmedia_endpt *endpt;
128 pj_pool_t *pool;
129 pj_mutex_t *mutex;
130 pjmedia_codec codec_list;
131} gsm_codec_factory;
132
Benny Prijono70c68912006-05-19 15:58:13 +0000133
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000134/* GSM codec private data. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000135struct gsm_data
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000136{
Benny Prijono8334ea12007-05-11 18:48:23 +0000137 struct gsm_state *encoder;
138 struct gsm_state *decoder;
Benny Prijono70c68912006-05-19 15:58:13 +0000139 pj_bool_t plc_enabled;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000140#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000141 pjmedia_plc *plc;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000142#endif
Benny Prijono70c68912006-05-19 15:58:13 +0000143 pj_bool_t vad_enabled;
144 pjmedia_silence_det *vad;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000145 pj_timestamp last_tx;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000146};
147
148
149
150/*
151 * Initialize and register GSM codec factory to pjmedia endpoint.
152 */
153PJ_DEF(pj_status_t) pjmedia_codec_gsm_init( pjmedia_endpt *endpt )
154{
155 pjmedia_codec_mgr *codec_mgr;
156 pj_status_t status;
157
158 if (gsm_codec_factory.pool != NULL)
159 return PJ_SUCCESS;
160
161 /* Create GSM codec factory. */
162 gsm_codec_factory.base.op = &gsm_factory_op;
163 gsm_codec_factory.base.factory_data = NULL;
164 gsm_codec_factory.endpt = endpt;
165
166 gsm_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "gsm", 4000,
167 4000);
168 if (!gsm_codec_factory.pool)
169 return PJ_ENOMEM;
170
171 pj_list_init(&gsm_codec_factory.codec_list);
172
173 /* Create mutex. */
174 status = pj_mutex_create_simple(gsm_codec_factory.pool, "gsm",
175 &gsm_codec_factory.mutex);
176 if (status != PJ_SUCCESS)
177 goto on_error;
178
179 /* Get the codec manager. */
180 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
181 if (!codec_mgr) {
182 status = PJ_EINVALIDOP;
183 goto on_error;
184 }
185
186 /* Register codec factory to endpoint. */
187 status = pjmedia_codec_mgr_register_factory(codec_mgr,
188 &gsm_codec_factory.base);
189 if (status != PJ_SUCCESS)
190 goto on_error;
191
192 /* Done. */
193 return PJ_SUCCESS;
194
195on_error:
196 pj_pool_release(gsm_codec_factory.pool);
197 gsm_codec_factory.pool = NULL;
198 return status;
199}
200
201
202
203/*
204 * Unregister GSM codec factory from pjmedia endpoint and deinitialize
205 * the GSM codec library.
206 */
207PJ_DEF(pj_status_t) pjmedia_codec_gsm_deinit(void)
208{
209 pjmedia_codec_mgr *codec_mgr;
210 pj_status_t status;
211
212 if (gsm_codec_factory.pool == NULL)
213 return PJ_SUCCESS;
214
215 /* We don't want to deinit if there's outstanding codec. */
Benny Prijonod7036a12007-03-15 10:12:57 +0000216 /* This is silly, as we'll always have codec in the list if
217 we ever allocate a codec! A better behavior maybe is to
218 deallocate all codecs in the list.
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000219 pj_mutex_lock(gsm_codec_factory.mutex);
220 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
221 pj_mutex_unlock(gsm_codec_factory.mutex);
222 return PJ_EBUSY;
223 }
Benny Prijonod7036a12007-03-15 10:12:57 +0000224 */
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000225
226 /* Get the codec manager. */
227 codec_mgr = pjmedia_endpt_get_codec_mgr(gsm_codec_factory.endpt);
228 if (!codec_mgr) {
229 pj_pool_release(gsm_codec_factory.pool);
230 gsm_codec_factory.pool = NULL;
231 return PJ_EINVALIDOP;
232 }
233
234 /* Unregister GSM codec factory. */
235 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
236 &gsm_codec_factory.base);
237
238 /* Destroy mutex. */
239 pj_mutex_destroy(gsm_codec_factory.mutex);
240
241 /* Destroy pool. */
242 pj_pool_release(gsm_codec_factory.pool);
243 gsm_codec_factory.pool = NULL;
244
245 return status;
246}
247
248/*
249 * Check if factory can allocate the specified codec.
250 */
251static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
252 const pjmedia_codec_info *info )
253{
254 PJ_UNUSED_ARG(factory);
255
256 /* Check payload type. */
257 if (info->pt != PJMEDIA_RTP_PT_GSM)
258 return PJMEDIA_CODEC_EUNSUP;
259
260 /* Ignore the rest, since it's static payload type. */
261
262 return PJ_SUCCESS;
263}
264
265/*
266 * Generate default attribute.
267 */
268static pj_status_t gsm_default_attr (pjmedia_codec_factory *factory,
269 const pjmedia_codec_info *id,
270 pjmedia_codec_param *attr )
271{
272 PJ_UNUSED_ARG(factory);
273 PJ_UNUSED_ARG(id);
274
Benny Prijonoac623b32006-07-03 15:19:31 +0000275 pj_bzero(attr, sizeof(pjmedia_codec_param));
Benny Prijono8befd9f2006-05-13 22:46:23 +0000276 attr->info.clock_rate = 8000;
277 attr->info.channel_cnt = 1;
278 attr->info.avg_bps = 13200;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000279 attr->info.max_bps = 13200;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000280 attr->info.pcm_bits_per_sample = 16;
281 attr->info.frm_ptime = 20;
282 attr->info.pt = PJMEDIA_RTP_PT_GSM;
283
284 attr->setting.frm_per_pkt = 1;
Benny Prijono70c68912006-05-19 15:58:13 +0000285 attr->setting.vad = 1;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000286#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000287 attr->setting.plc = 1;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000288#endif
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000289
Benny Prijono70c68912006-05-19 15:58:13 +0000290 /* Default all other flag bits disabled. */
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000291
292 return PJ_SUCCESS;
293}
294
295/*
296 * Enum codecs supported by this factory (i.e. only GSM!).
297 */
298static pj_status_t gsm_enum_codecs(pjmedia_codec_factory *factory,
299 unsigned *count,
300 pjmedia_codec_info codecs[])
301{
302 PJ_UNUSED_ARG(factory);
303 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
304
Benny Prijonoac623b32006-07-03 15:19:31 +0000305 pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000306 codecs[0].encoding_name = pj_str("GSM");
307 codecs[0].pt = PJMEDIA_RTP_PT_GSM;
308 codecs[0].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000309 codecs[0].clock_rate = 8000;
310 codecs[0].channel_cnt = 1;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000311
312 *count = 1;
313
314 return PJ_SUCCESS;
315}
316
317/*
318 * Allocate a new GSM codec instance.
319 */
320static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
321 const pjmedia_codec_info *id,
322 pjmedia_codec **p_codec)
323{
324 pjmedia_codec *codec;
Benny Prijonof93895c2006-03-05 11:50:53 +0000325 struct gsm_data *gsm_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000326 pj_status_t status;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000327
328 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
329 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
330
331
332 pj_mutex_lock(gsm_codec_factory.mutex);
333
334 /* Get free nodes, if any. */
335 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
336 codec = gsm_codec_factory.codec_list.next;
337 pj_list_erase(codec);
338 } else {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000339 codec = PJ_POOL_ZALLOC_T(gsm_codec_factory.pool, pjmedia_codec);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000340 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000341 codec->op = &gsm_op;
342 codec->factory = factory;
Benny Prijonof93895c2006-03-05 11:50:53 +0000343
Benny Prijonoa1e69682007-05-11 15:14:34 +0000344 gsm_data = PJ_POOL_ZALLOC_T(gsm_codec_factory.pool, struct gsm_data);
Benny Prijonof93895c2006-03-05 11:50:53 +0000345 codec->codec_data = gsm_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000346
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000347#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000348 /* Create PLC */
349 status = pjmedia_plc_create(gsm_codec_factory.pool, 8000,
350 160, 0, &gsm_data->plc);
351 if (status != PJ_SUCCESS) {
352 pj_mutex_unlock(gsm_codec_factory.mutex);
353 return status;
354 }
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000355#endif
Benny Prijono70c68912006-05-19 15:58:13 +0000356
357 /* Create silence detector */
358 status = pjmedia_silence_det_create(gsm_codec_factory.pool,
359 8000, 160,
360 &gsm_data->vad);
361 if (status != PJ_SUCCESS) {
362 pj_mutex_unlock(gsm_codec_factory.mutex);
363 return status;
364 }
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000365 }
366
367 pj_mutex_unlock(gsm_codec_factory.mutex);
368
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000369 *p_codec = codec;
370 return PJ_SUCCESS;
371}
372
373/*
374 * Free codec.
375 */
376static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
377 pjmedia_codec *codec )
378{
Benny Prijonof93895c2006-03-05 11:50:53 +0000379 struct gsm_data *gsm_data;
Benny Prijono87445bc2006-07-26 11:23:07 +0000380 int i;
Benny Prijonof93895c2006-03-05 11:50:53 +0000381
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000382 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
383 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
384
Benny Prijonoa1e69682007-05-11 15:14:34 +0000385 gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonof93895c2006-03-05 11:50:53 +0000386
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000387 /* Close codec, if it's not closed. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000388 gsm_codec_close(codec);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000389
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000390#if !PLC_DISABLED
Benny Prijono87445bc2006-07-26 11:23:07 +0000391 /* Clear left samples in the PLC, since codec+plc will be reused
392 * next time.
393 */
394 for (i=0; i<2; ++i) {
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000395 pj_int16_t frame[160];
Benny Prijono87445bc2006-07-26 11:23:07 +0000396 pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame));
397 pjmedia_plc_save(gsm_data->plc, frame);
398 }
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000399#else
400 PJ_UNUSED_ARG(i);
401#endif
Benny Prijono87445bc2006-07-26 11:23:07 +0000402
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000403 /* Re-init silence_period */
404 pj_set_timestamp32(&gsm_data->last_tx, 0, 0);
405
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000406 /* Put in the free list. */
407 pj_mutex_lock(gsm_codec_factory.mutex);
408 pj_list_push_front(&gsm_codec_factory.codec_list, codec);
409 pj_mutex_unlock(gsm_codec_factory.mutex);
410
411 return PJ_SUCCESS;
412}
413
414/*
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000415 * Init codec.
416 */
417static pj_status_t gsm_codec_init( pjmedia_codec *codec,
418 pj_pool_t *pool )
419{
420 PJ_UNUSED_ARG(codec);
421 PJ_UNUSED_ARG(pool);
422 return PJ_SUCCESS;
423}
424
425/*
426 * Open codec.
427 */
428static pj_status_t gsm_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +0000429 pjmedia_codec_param *attr )
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000430{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000431 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonof93895c2006-03-05 11:50:53 +0000432
433 pj_assert(gsm_data != NULL);
434 pj_assert(gsm_data->encoder == NULL && gsm_data->decoder == NULL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000435
Benny Prijonof93895c2006-03-05 11:50:53 +0000436 gsm_data->encoder = gsm_create();
437 if (!gsm_data->encoder)
438 return PJMEDIA_CODEC_EFAILED;
439
440 gsm_data->decoder = gsm_create();
441 if (!gsm_data->decoder)
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000442 return PJMEDIA_CODEC_EFAILED;
443
Benny Prijono70c68912006-05-19 15:58:13 +0000444 gsm_data->vad_enabled = (attr->setting.vad != 0);
445 gsm_data->plc_enabled = (attr->setting.plc != 0);
446
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000447 return PJ_SUCCESS;
448}
449
450/*
451 * Close codec.
452 */
453static pj_status_t gsm_codec_close( pjmedia_codec *codec )
454{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000455 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000456
Benny Prijonof93895c2006-03-05 11:50:53 +0000457 pj_assert(gsm_data != NULL);
458
459 if (gsm_data->encoder) {
460 gsm_destroy(gsm_data->encoder);
461 gsm_data->encoder = NULL;
462 }
463 if (gsm_data->decoder) {
464 gsm_destroy(gsm_data->decoder);
465 gsm_data->decoder = NULL;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000466 }
467
468 return PJ_SUCCESS;
469}
470
471
472/*
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000473 * Modify codec settings.
474 */
475static pj_status_t gsm_codec_modify(pjmedia_codec *codec,
476 const pjmedia_codec_param *attr )
477{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000478 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000479
480 pj_assert(gsm_data != NULL);
Benny Prijono6865a3f2006-12-30 02:46:57 +0000481 pj_assert(gsm_data->encoder != NULL && gsm_data->decoder != NULL);
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000482
483 gsm_data->vad_enabled = (attr->setting.vad != 0);
484 gsm_data->plc_enabled = (attr->setting.plc != 0);
485
486 return PJ_SUCCESS;
487}
488
489
490/*
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000491 * Get frames in the packet.
492 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000493static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
494 void *pkt,
495 pj_size_t pkt_size,
496 const pj_timestamp *ts,
497 unsigned *frame_cnt,
498 pjmedia_frame frames[])
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000499{
500 unsigned count = 0;
501
502 PJ_UNUSED_ARG(codec);
503
504 PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
505
506 while (pkt_size >= 33 && count < *frame_cnt) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000507 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
508 frames[count].buf = pkt;
509 frames[count].size = 33;
510 frames[count].timestamp.u64 = ts->u64 + count * 160;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000511
512 pkt = ((char*)pkt) + 33;
513 pkt_size -= 33;
514
515 ++count;
516 }
517
518 *frame_cnt = count;
519 return PJ_SUCCESS;
520}
521
522/*
523 * Encode frame.
524 */
525static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
526 const struct pjmedia_frame *input,
527 unsigned output_buf_len,
528 struct pjmedia_frame *output)
529{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000530 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000531 pj_int16_t *pcm_in;
532 unsigned in_size;
Benny Prijonof93895c2006-03-05 11:50:53 +0000533
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000534 pj_assert(gsm_data && input && output);
535
536 pcm_in = (pj_int16_t*)input->buf;
537 in_size = input->size;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000538
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000539 PJ_ASSERT_RETURN(in_size % 320 == 0, PJMEDIA_CODEC_EPCMFRMINLEN);
540 PJ_ASSERT_RETURN(output_buf_len >= 33 * in_size/320,
541 PJMEDIA_CODEC_EFRMTOOSHORT);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000542
Benny Prijono70c68912006-05-19 15:58:13 +0000543 /* Detect silence */
544 if (gsm_data->vad_enabled) {
545 pj_bool_t is_silence;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000546 pj_int32_t silence_duration;
547
548 silence_duration = pj_timestamp_diff32(&gsm_data->last_tx,
549 &input->timestamp);
Benny Prijono70c68912006-05-19 15:58:13 +0000550
551 is_silence = pjmedia_silence_det_detect(gsm_data->vad,
Benny Prijonoa1e69682007-05-11 15:14:34 +0000552 (const pj_int16_t*) input->buf,
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000553 (input->size >> 1),
Benny Prijono70c68912006-05-19 15:58:13 +0000554 NULL);
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000555 if (is_silence &&
Nanang Izzuddin4ff93f42009-06-13 15:28:37 +0000556 (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
557 silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000))
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000558 {
Benny Prijono70c68912006-05-19 15:58:13 +0000559 output->type = PJMEDIA_FRAME_TYPE_NONE;
560 output->buf = NULL;
561 output->size = 0;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000562 output->timestamp = input->timestamp;
Benny Prijono70c68912006-05-19 15:58:13 +0000563 return PJ_SUCCESS;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000564 } else {
565 gsm_data->last_tx = input->timestamp;
Benny Prijono70c68912006-05-19 15:58:13 +0000566 }
567 }
568
569 /* Encode */
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000570 output->size = 0;
571 while (in_size >= 320) {
572 gsm_encode(gsm_data->encoder, pcm_in,
573 (unsigned char*)output->buf + output->size);
574 pcm_in += 160;
575 output->size += 33;
576 in_size -= 320;
577 }
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000578
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000579 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
Nanang Izzuddin5fe03142009-06-02 18:01:49 +0000580 output->timestamp = input->timestamp;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000581
582 return PJ_SUCCESS;
583}
584
585/*
586 * Decode frame.
587 */
588static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
589 const struct pjmedia_frame *input,
590 unsigned output_buf_len,
591 struct pjmedia_frame *output)
592{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000593 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonof93895c2006-03-05 11:50:53 +0000594
595 pj_assert(gsm_data != NULL);
596 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000597
598 if (output_buf_len < 320)
599 return PJMEDIA_CODEC_EPCMTOOSHORT;
600
601 if (input->size < 33)
602 return PJMEDIA_CODEC_EFRMTOOSHORT;
603
Benny Prijonof93895c2006-03-05 11:50:53 +0000604 gsm_decode(gsm_data->decoder,
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000605 (unsigned char*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000606 (short*)output->buf);
607
608 output->size = 320;
609 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
Nanang Izzuddin5fe03142009-06-02 18:01:49 +0000610 output->timestamp = input->timestamp;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000611
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000612#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000613 if (gsm_data->plc_enabled)
Benny Prijono89ac2b42008-06-13 12:52:56 +0000614 pjmedia_plc_save( gsm_data->plc, (pj_int16_t*)output->buf);
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000615#endif
Benny Prijono70c68912006-05-19 15:58:13 +0000616
617 return PJ_SUCCESS;
618}
619
620
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000621#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000622/*
623 * Recover lost frame.
624 */
625static pj_status_t gsm_codec_recover(pjmedia_codec *codec,
626 unsigned output_buf_len,
627 struct pjmedia_frame *output)
628{
Benny Prijono89ac2b42008-06-13 12:52:56 +0000629 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000630
631 PJ_ASSERT_RETURN(gsm_data->plc_enabled, PJ_EINVALIDOP);
632
633 PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT);
634
Benny Prijono89ac2b42008-06-13 12:52:56 +0000635 pjmedia_plc_generate(gsm_data->plc, (pj_int16_t*)output->buf);
Benny Prijono70c68912006-05-19 15:58:13 +0000636 output->size = 320;
637
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000638 return PJ_SUCCESS;
639}
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000640#endif
Benny Prijono4381efe2006-03-16 14:24:26 +0000641
642
643#endif /* PJMEDIA_HAS_GSM_CODEC */
644