blob: 965c936d20f452f4ce56f1162ebfcea071f48474 [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 Prijono7f1c90f2007-04-07 12:29:46 +000031#include "../../third_party/gsm/inc/gsm.h"
Benny Prijonoa4bf0212006-02-10 15:57:08 +000032
Benny Prijono4381efe2006-03-16 14:24:26 +000033/*
34 * Only build this file if PJMEDIA_HAS_GSM_CODEC != 0
35 */
36#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC != 0
37
Benny Prijono00d15a52008-03-04 16:23:42 +000038/* We removed PLC in 0.6 (and re-enabled it again in 0.9!) */
39#define PLC_DISABLED 0
Benny Prijonoc95a0f02007-04-09 07:06:08 +000040
Benny Prijono4381efe2006-03-16 14:24:26 +000041
Benny Prijonoa4bf0212006-02-10 15:57:08 +000042/* Prototypes for GSM factory */
43static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
44 const pjmedia_codec_info *id );
45static pj_status_t gsm_default_attr( pjmedia_codec_factory *factory,
46 const pjmedia_codec_info *id,
47 pjmedia_codec_param *attr );
48static pj_status_t gsm_enum_codecs( pjmedia_codec_factory *factory,
49 unsigned *count,
50 pjmedia_codec_info codecs[]);
51static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
52 const pjmedia_codec_info *id,
53 pjmedia_codec **p_codec);
54static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
55 pjmedia_codec *codec );
56
57/* Prototypes for GSM implementation. */
Benny Prijonoa4bf0212006-02-10 15:57:08 +000058static pj_status_t gsm_codec_init( pjmedia_codec *codec,
59 pj_pool_t *pool );
60static pj_status_t gsm_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +000061 pjmedia_codec_param *attr );
Benny Prijonoa4bf0212006-02-10 15:57:08 +000062static pj_status_t gsm_codec_close( pjmedia_codec *codec );
Benny Prijonob94a6ab2006-12-26 21:18:11 +000063static pj_status_t gsm_codec_modify(pjmedia_codec *codec,
64 const pjmedia_codec_param *attr );
Benny Prijono8befd9f2006-05-13 22:46:23 +000065static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
66 void *pkt,
67 pj_size_t pkt_size,
68 const pj_timestamp *ts,
69 unsigned *frame_cnt,
70 pjmedia_frame frames[]);
Benny Prijonoa4bf0212006-02-10 15:57:08 +000071static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
72 const struct pjmedia_frame *input,
73 unsigned output_buf_len,
74 struct pjmedia_frame *output);
75static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
76 const struct pjmedia_frame *input,
77 unsigned output_buf_len,
78 struct pjmedia_frame *output);
Benny Prijonoc95a0f02007-04-09 07:06:08 +000079#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +000080static pj_status_t gsm_codec_recover(pjmedia_codec *codec,
81 unsigned output_buf_len,
82 struct pjmedia_frame *output);
Benny Prijonoc95a0f02007-04-09 07:06:08 +000083#endif
Benny Prijonoa4bf0212006-02-10 15:57:08 +000084
85/* Definition for GSM codec operations. */
86static pjmedia_codec_op gsm_op =
87{
Benny Prijonoa4bf0212006-02-10 15:57:08 +000088 &gsm_codec_init,
89 &gsm_codec_open,
90 &gsm_codec_close,
Benny Prijonob94a6ab2006-12-26 21:18:11 +000091 &gsm_codec_modify,
Benny Prijono8befd9f2006-05-13 22:46:23 +000092 &gsm_codec_parse,
Benny Prijonoa4bf0212006-02-10 15:57:08 +000093 &gsm_codec_encode,
Benny Prijono70c68912006-05-19 15:58:13 +000094 &gsm_codec_decode,
Benny Prijonoc95a0f02007-04-09 07:06:08 +000095#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +000096 &gsm_codec_recover
Benny Prijonoc95a0f02007-04-09 07:06:08 +000097#else
98 NULL
99#endif
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000100};
101
102/* Definition for GSM codec factory operations. */
103static pjmedia_codec_factory_op gsm_factory_op =
104{
105 &gsm_test_alloc,
106 &gsm_default_attr,
107 &gsm_enum_codecs,
108 &gsm_alloc_codec,
109 &gsm_dealloc_codec
110};
111
112/* GSM factory */
113static struct gsm_codec_factory
114{
115 pjmedia_codec_factory base;
116 pjmedia_endpt *endpt;
117 pj_pool_t *pool;
118 pj_mutex_t *mutex;
119 pjmedia_codec codec_list;
120} gsm_codec_factory;
121
Benny Prijono70c68912006-05-19 15:58:13 +0000122
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000123/* GSM codec private data. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000124struct gsm_data
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000125{
Benny Prijono8334ea12007-05-11 18:48:23 +0000126 struct gsm_state *encoder;
127 struct gsm_state *decoder;
Benny Prijono70c68912006-05-19 15:58:13 +0000128 pj_bool_t plc_enabled;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000129#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000130 pjmedia_plc *plc;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000131#endif
Benny Prijono70c68912006-05-19 15:58:13 +0000132 pj_bool_t vad_enabled;
133 pjmedia_silence_det *vad;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000134 pj_timestamp last_tx;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000135};
136
137
138
139/*
140 * Initialize and register GSM codec factory to pjmedia endpoint.
141 */
142PJ_DEF(pj_status_t) pjmedia_codec_gsm_init( pjmedia_endpt *endpt )
143{
144 pjmedia_codec_mgr *codec_mgr;
145 pj_status_t status;
146
147 if (gsm_codec_factory.pool != NULL)
148 return PJ_SUCCESS;
149
150 /* Create GSM codec factory. */
151 gsm_codec_factory.base.op = &gsm_factory_op;
152 gsm_codec_factory.base.factory_data = NULL;
153 gsm_codec_factory.endpt = endpt;
154
155 gsm_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "gsm", 4000,
156 4000);
157 if (!gsm_codec_factory.pool)
158 return PJ_ENOMEM;
159
160 pj_list_init(&gsm_codec_factory.codec_list);
161
162 /* Create mutex. */
163 status = pj_mutex_create_simple(gsm_codec_factory.pool, "gsm",
164 &gsm_codec_factory.mutex);
165 if (status != PJ_SUCCESS)
166 goto on_error;
167
168 /* Get the codec manager. */
169 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
170 if (!codec_mgr) {
171 status = PJ_EINVALIDOP;
172 goto on_error;
173 }
174
175 /* Register codec factory to endpoint. */
176 status = pjmedia_codec_mgr_register_factory(codec_mgr,
177 &gsm_codec_factory.base);
178 if (status != PJ_SUCCESS)
179 goto on_error;
180
181 /* Done. */
182 return PJ_SUCCESS;
183
184on_error:
185 pj_pool_release(gsm_codec_factory.pool);
186 gsm_codec_factory.pool = NULL;
187 return status;
188}
189
190
191
192/*
193 * Unregister GSM codec factory from pjmedia endpoint and deinitialize
194 * the GSM codec library.
195 */
196PJ_DEF(pj_status_t) pjmedia_codec_gsm_deinit(void)
197{
198 pjmedia_codec_mgr *codec_mgr;
199 pj_status_t status;
200
201 if (gsm_codec_factory.pool == NULL)
202 return PJ_SUCCESS;
203
204 /* We don't want to deinit if there's outstanding codec. */
Benny Prijonod7036a12007-03-15 10:12:57 +0000205 /* This is silly, as we'll always have codec in the list if
206 we ever allocate a codec! A better behavior maybe is to
207 deallocate all codecs in the list.
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000208 pj_mutex_lock(gsm_codec_factory.mutex);
209 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
210 pj_mutex_unlock(gsm_codec_factory.mutex);
211 return PJ_EBUSY;
212 }
Benny Prijonod7036a12007-03-15 10:12:57 +0000213 */
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000214
215 /* Get the codec manager. */
216 codec_mgr = pjmedia_endpt_get_codec_mgr(gsm_codec_factory.endpt);
217 if (!codec_mgr) {
218 pj_pool_release(gsm_codec_factory.pool);
219 gsm_codec_factory.pool = NULL;
220 return PJ_EINVALIDOP;
221 }
222
223 /* Unregister GSM codec factory. */
224 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
225 &gsm_codec_factory.base);
226
227 /* Destroy mutex. */
228 pj_mutex_destroy(gsm_codec_factory.mutex);
229
230 /* Destroy pool. */
231 pj_pool_release(gsm_codec_factory.pool);
232 gsm_codec_factory.pool = NULL;
233
234 return status;
235}
236
237/*
238 * Check if factory can allocate the specified codec.
239 */
240static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
241 const pjmedia_codec_info *info )
242{
243 PJ_UNUSED_ARG(factory);
244
245 /* Check payload type. */
246 if (info->pt != PJMEDIA_RTP_PT_GSM)
247 return PJMEDIA_CODEC_EUNSUP;
248
249 /* Ignore the rest, since it's static payload type. */
250
251 return PJ_SUCCESS;
252}
253
254/*
255 * Generate default attribute.
256 */
257static pj_status_t gsm_default_attr (pjmedia_codec_factory *factory,
258 const pjmedia_codec_info *id,
259 pjmedia_codec_param *attr )
260{
261 PJ_UNUSED_ARG(factory);
262 PJ_UNUSED_ARG(id);
263
Benny Prijonoac623b32006-07-03 15:19:31 +0000264 pj_bzero(attr, sizeof(pjmedia_codec_param));
Benny Prijono8befd9f2006-05-13 22:46:23 +0000265 attr->info.clock_rate = 8000;
266 attr->info.channel_cnt = 1;
267 attr->info.avg_bps = 13200;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000268 attr->info.max_bps = 13200;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000269 attr->info.pcm_bits_per_sample = 16;
270 attr->info.frm_ptime = 20;
271 attr->info.pt = PJMEDIA_RTP_PT_GSM;
272
273 attr->setting.frm_per_pkt = 1;
Benny Prijono70c68912006-05-19 15:58:13 +0000274 attr->setting.vad = 1;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000275#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000276 attr->setting.plc = 1;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000277#endif
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000278
Benny Prijono70c68912006-05-19 15:58:13 +0000279 /* Default all other flag bits disabled. */
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000280
281 return PJ_SUCCESS;
282}
283
284/*
285 * Enum codecs supported by this factory (i.e. only GSM!).
286 */
287static pj_status_t gsm_enum_codecs(pjmedia_codec_factory *factory,
288 unsigned *count,
289 pjmedia_codec_info codecs[])
290{
291 PJ_UNUSED_ARG(factory);
292 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
293
Benny Prijonoac623b32006-07-03 15:19:31 +0000294 pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000295 codecs[0].encoding_name = pj_str("GSM");
296 codecs[0].pt = PJMEDIA_RTP_PT_GSM;
297 codecs[0].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000298 codecs[0].clock_rate = 8000;
299 codecs[0].channel_cnt = 1;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000300
301 *count = 1;
302
303 return PJ_SUCCESS;
304}
305
306/*
307 * Allocate a new GSM codec instance.
308 */
309static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
310 const pjmedia_codec_info *id,
311 pjmedia_codec **p_codec)
312{
313 pjmedia_codec *codec;
Benny Prijonof93895c2006-03-05 11:50:53 +0000314 struct gsm_data *gsm_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000315 pj_status_t status;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000316
317 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
318 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
319
320
321 pj_mutex_lock(gsm_codec_factory.mutex);
322
323 /* Get free nodes, if any. */
324 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
325 codec = gsm_codec_factory.codec_list.next;
326 pj_list_erase(codec);
327 } else {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000328 codec = PJ_POOL_ZALLOC_T(gsm_codec_factory.pool, pjmedia_codec);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000329 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000330 codec->op = &gsm_op;
331 codec->factory = factory;
Benny Prijonof93895c2006-03-05 11:50:53 +0000332
Benny Prijonoa1e69682007-05-11 15:14:34 +0000333 gsm_data = PJ_POOL_ZALLOC_T(gsm_codec_factory.pool, struct gsm_data);
Benny Prijonof93895c2006-03-05 11:50:53 +0000334 codec->codec_data = gsm_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000335
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000336#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000337 /* Create PLC */
338 status = pjmedia_plc_create(gsm_codec_factory.pool, 8000,
339 160, 0, &gsm_data->plc);
340 if (status != PJ_SUCCESS) {
341 pj_mutex_unlock(gsm_codec_factory.mutex);
342 return status;
343 }
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000344#endif
Benny Prijono70c68912006-05-19 15:58:13 +0000345
346 /* Create silence detector */
347 status = pjmedia_silence_det_create(gsm_codec_factory.pool,
348 8000, 160,
349 &gsm_data->vad);
350 if (status != PJ_SUCCESS) {
351 pj_mutex_unlock(gsm_codec_factory.mutex);
352 return status;
353 }
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000354 }
355
356 pj_mutex_unlock(gsm_codec_factory.mutex);
357
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000358 *p_codec = codec;
359 return PJ_SUCCESS;
360}
361
362/*
363 * Free codec.
364 */
365static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
366 pjmedia_codec *codec )
367{
Benny Prijonof93895c2006-03-05 11:50:53 +0000368 struct gsm_data *gsm_data;
Benny Prijono87445bc2006-07-26 11:23:07 +0000369 int i;
Benny Prijonof93895c2006-03-05 11:50:53 +0000370
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000371 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
372 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
373
Benny Prijonoa1e69682007-05-11 15:14:34 +0000374 gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonof93895c2006-03-05 11:50:53 +0000375
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000376 /* Close codec, if it's not closed. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000377 gsm_codec_close(codec);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000378
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000379#if !PLC_DISABLED
Benny Prijono87445bc2006-07-26 11:23:07 +0000380 /* Clear left samples in the PLC, since codec+plc will be reused
381 * next time.
382 */
383 for (i=0; i<2; ++i) {
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000384 pj_int16_t frame[160];
Benny Prijono87445bc2006-07-26 11:23:07 +0000385 pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame));
386 pjmedia_plc_save(gsm_data->plc, frame);
387 }
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000388#else
389 PJ_UNUSED_ARG(i);
390#endif
Benny Prijono87445bc2006-07-26 11:23:07 +0000391
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000392 /* Re-init silence_period */
393 pj_set_timestamp32(&gsm_data->last_tx, 0, 0);
394
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000395 /* Put in the free list. */
396 pj_mutex_lock(gsm_codec_factory.mutex);
397 pj_list_push_front(&gsm_codec_factory.codec_list, codec);
398 pj_mutex_unlock(gsm_codec_factory.mutex);
399
400 return PJ_SUCCESS;
401}
402
403/*
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000404 * Init codec.
405 */
406static pj_status_t gsm_codec_init( pjmedia_codec *codec,
407 pj_pool_t *pool )
408{
409 PJ_UNUSED_ARG(codec);
410 PJ_UNUSED_ARG(pool);
411 return PJ_SUCCESS;
412}
413
414/*
415 * Open codec.
416 */
417static pj_status_t gsm_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +0000418 pjmedia_codec_param *attr )
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000419{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000420 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonof93895c2006-03-05 11:50:53 +0000421
422 pj_assert(gsm_data != NULL);
423 pj_assert(gsm_data->encoder == NULL && gsm_data->decoder == NULL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000424
Benny Prijonof93895c2006-03-05 11:50:53 +0000425 gsm_data->encoder = gsm_create();
426 if (!gsm_data->encoder)
427 return PJMEDIA_CODEC_EFAILED;
428
429 gsm_data->decoder = gsm_create();
430 if (!gsm_data->decoder)
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000431 return PJMEDIA_CODEC_EFAILED;
432
Benny Prijono70c68912006-05-19 15:58:13 +0000433 gsm_data->vad_enabled = (attr->setting.vad != 0);
434 gsm_data->plc_enabled = (attr->setting.plc != 0);
435
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000436 return PJ_SUCCESS;
437}
438
439/*
440 * Close codec.
441 */
442static pj_status_t gsm_codec_close( pjmedia_codec *codec )
443{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000444 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000445
Benny Prijonof93895c2006-03-05 11:50:53 +0000446 pj_assert(gsm_data != NULL);
447
448 if (gsm_data->encoder) {
449 gsm_destroy(gsm_data->encoder);
450 gsm_data->encoder = NULL;
451 }
452 if (gsm_data->decoder) {
453 gsm_destroy(gsm_data->decoder);
454 gsm_data->decoder = NULL;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000455 }
456
457 return PJ_SUCCESS;
458}
459
460
461/*
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000462 * Modify codec settings.
463 */
464static pj_status_t gsm_codec_modify(pjmedia_codec *codec,
465 const pjmedia_codec_param *attr )
466{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000467 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000468
469 pj_assert(gsm_data != NULL);
Benny Prijono6865a3f2006-12-30 02:46:57 +0000470 pj_assert(gsm_data->encoder != NULL && gsm_data->decoder != NULL);
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000471
472 gsm_data->vad_enabled = (attr->setting.vad != 0);
473 gsm_data->plc_enabled = (attr->setting.plc != 0);
474
475 return PJ_SUCCESS;
476}
477
478
479/*
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000480 * Get frames in the packet.
481 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000482static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
483 void *pkt,
484 pj_size_t pkt_size,
485 const pj_timestamp *ts,
486 unsigned *frame_cnt,
487 pjmedia_frame frames[])
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000488{
489 unsigned count = 0;
490
491 PJ_UNUSED_ARG(codec);
492
493 PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
494
495 while (pkt_size >= 33 && count < *frame_cnt) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000496 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
497 frames[count].buf = pkt;
498 frames[count].size = 33;
499 frames[count].timestamp.u64 = ts->u64 + count * 160;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000500
501 pkt = ((char*)pkt) + 33;
502 pkt_size -= 33;
503
504 ++count;
505 }
506
507 *frame_cnt = count;
508 return PJ_SUCCESS;
509}
510
511/*
512 * Encode frame.
513 */
514static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
515 const struct pjmedia_frame *input,
516 unsigned output_buf_len,
517 struct pjmedia_frame *output)
518{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000519 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000520 pj_int16_t *pcm_in;
521 unsigned in_size;
Benny Prijonof93895c2006-03-05 11:50:53 +0000522
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000523 pj_assert(gsm_data && input && output);
524
525 pcm_in = (pj_int16_t*)input->buf;
526 in_size = input->size;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000527
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000528 PJ_ASSERT_RETURN(in_size % 320 == 0, PJMEDIA_CODEC_EPCMFRMINLEN);
529 PJ_ASSERT_RETURN(output_buf_len >= 33 * in_size/320,
530 PJMEDIA_CODEC_EFRMTOOSHORT);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000531
Benny Prijono70c68912006-05-19 15:58:13 +0000532 /* Detect silence */
533 if (gsm_data->vad_enabled) {
534 pj_bool_t is_silence;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000535 pj_int32_t silence_duration;
536
537 silence_duration = pj_timestamp_diff32(&gsm_data->last_tx,
538 &input->timestamp);
Benny Prijono70c68912006-05-19 15:58:13 +0000539
540 is_silence = pjmedia_silence_det_detect(gsm_data->vad,
Benny Prijonoa1e69682007-05-11 15:14:34 +0000541 (const pj_int16_t*) input->buf,
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000542 (input->size >> 1),
Benny Prijono70c68912006-05-19 15:58:13 +0000543 NULL);
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000544 if (is_silence &&
Nanang Izzuddin4ff93f42009-06-13 15:28:37 +0000545 (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
546 silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000))
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000547 {
Benny Prijono70c68912006-05-19 15:58:13 +0000548 output->type = PJMEDIA_FRAME_TYPE_NONE;
549 output->buf = NULL;
550 output->size = 0;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000551 output->timestamp = input->timestamp;
Benny Prijono70c68912006-05-19 15:58:13 +0000552 return PJ_SUCCESS;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000553 } else {
554 gsm_data->last_tx = input->timestamp;
Benny Prijono70c68912006-05-19 15:58:13 +0000555 }
556 }
557
558 /* Encode */
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000559 output->size = 0;
560 while (in_size >= 320) {
561 gsm_encode(gsm_data->encoder, pcm_in,
562 (unsigned char*)output->buf + output->size);
563 pcm_in += 160;
564 output->size += 33;
565 in_size -= 320;
566 }
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000567
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000568 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
Nanang Izzuddin5fe03142009-06-02 18:01:49 +0000569 output->timestamp = input->timestamp;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000570
571 return PJ_SUCCESS;
572}
573
574/*
575 * Decode frame.
576 */
577static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
578 const struct pjmedia_frame *input,
579 unsigned output_buf_len,
580 struct pjmedia_frame *output)
581{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000582 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonof93895c2006-03-05 11:50:53 +0000583
584 pj_assert(gsm_data != NULL);
585 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000586
587 if (output_buf_len < 320)
588 return PJMEDIA_CODEC_EPCMTOOSHORT;
589
590 if (input->size < 33)
591 return PJMEDIA_CODEC_EFRMTOOSHORT;
592
Benny Prijonof93895c2006-03-05 11:50:53 +0000593 gsm_decode(gsm_data->decoder,
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000594 (unsigned char*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000595 (short*)output->buf);
596
597 output->size = 320;
598 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
Nanang Izzuddin5fe03142009-06-02 18:01:49 +0000599 output->timestamp = input->timestamp;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000600
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000601#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000602 if (gsm_data->plc_enabled)
Benny Prijono89ac2b42008-06-13 12:52:56 +0000603 pjmedia_plc_save( gsm_data->plc, (pj_int16_t*)output->buf);
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000604#endif
Benny Prijono70c68912006-05-19 15:58:13 +0000605
606 return PJ_SUCCESS;
607}
608
609
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000610#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000611/*
612 * Recover lost frame.
613 */
614static pj_status_t gsm_codec_recover(pjmedia_codec *codec,
615 unsigned output_buf_len,
616 struct pjmedia_frame *output)
617{
Benny Prijono89ac2b42008-06-13 12:52:56 +0000618 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000619
620 PJ_ASSERT_RETURN(gsm_data->plc_enabled, PJ_EINVALIDOP);
621
622 PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT);
623
Benny Prijono89ac2b42008-06-13 12:52:56 +0000624 pjmedia_plc_generate(gsm_data->plc, (pj_int16_t*)output->buf);
Benny Prijono70c68912006-05-19 15:58:13 +0000625 output->size = 320;
626
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000627 return PJ_SUCCESS;
628}
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000629#endif
Benny Prijono4381efe2006-03-16 14:24:26 +0000630
631
632#endif /* PJMEDIA_HAS_GSM_CODEC */
633