blob: 7bb4d729b65898da1324847693a525570f15a0c5 [file] [log] [blame]
Benny Prijonoa4bf0212006-02-10 15:57:08 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C)2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijonoa4bf0212006-02-10 15:57:08 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjmedia-codec/gsm.h>
20#include <pjmedia/codec.h>
21#include <pjmedia/errno.h>
22#include <pjmedia/endpoint.h>
Benny Prijono70c68912006-05-19 15:58:13 +000023#include <pjmedia/plc.h>
Benny Prijonof04ffdd2006-02-21 00:11:18 +000024#include <pjmedia/port.h>
Benny Prijono70c68912006-05-19 15:58:13 +000025#include <pjmedia/silencedet.h>
Benny Prijonoa4bf0212006-02-10 15:57:08 +000026#include <pj/assert.h>
27#include <pj/pool.h>
28#include <pj/string.h>
29#include <pj/os.h>
Benny Prijono7f1c90f2007-04-07 12:29:46 +000030#include "../../third_party/gsm/inc/gsm.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 Prijono00d15a52008-03-04 16:23:42 +000037/* We removed PLC in 0.6 (and re-enabled it again in 0.9!) */
38#define PLC_DISABLED 0
Benny Prijonoc95a0f02007-04-09 07:06:08 +000039
Benny Prijono4381efe2006-03-16 14:24:26 +000040
Benny Prijonoa4bf0212006-02-10 15:57:08 +000041/* Prototypes for GSM factory */
42static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
43 const pjmedia_codec_info *id );
44static pj_status_t gsm_default_attr( pjmedia_codec_factory *factory,
45 const pjmedia_codec_info *id,
46 pjmedia_codec_param *attr );
47static pj_status_t gsm_enum_codecs( pjmedia_codec_factory *factory,
48 unsigned *count,
49 pjmedia_codec_info codecs[]);
50static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
51 const pjmedia_codec_info *id,
52 pjmedia_codec **p_codec);
53static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
54 pjmedia_codec *codec );
55
56/* Prototypes for GSM implementation. */
Benny Prijonoa4bf0212006-02-10 15:57:08 +000057static pj_status_t gsm_codec_init( pjmedia_codec *codec,
58 pj_pool_t *pool );
59static pj_status_t gsm_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +000060 pjmedia_codec_param *attr );
Benny Prijonoa4bf0212006-02-10 15:57:08 +000061static pj_status_t gsm_codec_close( pjmedia_codec *codec );
Benny Prijonob94a6ab2006-12-26 21:18:11 +000062static pj_status_t gsm_codec_modify(pjmedia_codec *codec,
63 const pjmedia_codec_param *attr );
Benny Prijono8befd9f2006-05-13 22:46:23 +000064static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
65 void *pkt,
66 pj_size_t pkt_size,
67 const pj_timestamp *ts,
68 unsigned *frame_cnt,
69 pjmedia_frame frames[]);
Benny Prijonoa4bf0212006-02-10 15:57:08 +000070static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
71 const struct pjmedia_frame *input,
72 unsigned output_buf_len,
73 struct pjmedia_frame *output);
74static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
75 const struct pjmedia_frame *input,
76 unsigned output_buf_len,
77 struct pjmedia_frame *output);
Benny Prijonoc95a0f02007-04-09 07:06:08 +000078#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +000079static pj_status_t gsm_codec_recover(pjmedia_codec *codec,
80 unsigned output_buf_len,
81 struct pjmedia_frame *output);
Benny Prijonoc95a0f02007-04-09 07:06:08 +000082#endif
Benny Prijonoa4bf0212006-02-10 15:57:08 +000083
84/* Definition for GSM codec operations. */
85static pjmedia_codec_op gsm_op =
86{
Benny Prijonoa4bf0212006-02-10 15:57:08 +000087 &gsm_codec_init,
88 &gsm_codec_open,
89 &gsm_codec_close,
Benny Prijonob94a6ab2006-12-26 21:18:11 +000090 &gsm_codec_modify,
Benny Prijono8befd9f2006-05-13 22:46:23 +000091 &gsm_codec_parse,
Benny Prijonoa4bf0212006-02-10 15:57:08 +000092 &gsm_codec_encode,
Benny Prijono70c68912006-05-19 15:58:13 +000093 &gsm_codec_decode,
Benny Prijonoc95a0f02007-04-09 07:06:08 +000094#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +000095 &gsm_codec_recover
Benny Prijonoc95a0f02007-04-09 07:06:08 +000096#else
97 NULL
98#endif
Benny Prijonoa4bf0212006-02-10 15:57:08 +000099};
100
101/* Definition for GSM codec factory operations. */
102static pjmedia_codec_factory_op gsm_factory_op =
103{
104 &gsm_test_alloc,
105 &gsm_default_attr,
106 &gsm_enum_codecs,
107 &gsm_alloc_codec,
108 &gsm_dealloc_codec
109};
110
111/* GSM factory */
112static struct gsm_codec_factory
113{
114 pjmedia_codec_factory base;
115 pjmedia_endpt *endpt;
116 pj_pool_t *pool;
117 pj_mutex_t *mutex;
118 pjmedia_codec codec_list;
119} gsm_codec_factory;
120
Benny Prijono70c68912006-05-19 15:58:13 +0000121
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000122/* GSM codec private data. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000123struct gsm_data
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000124{
Benny Prijono8334ea12007-05-11 18:48:23 +0000125 struct gsm_state *encoder;
126 struct gsm_state *decoder;
Benny Prijono70c68912006-05-19 15:58:13 +0000127 pj_bool_t plc_enabled;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000128#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000129 pjmedia_plc *plc;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000130#endif
Benny Prijono70c68912006-05-19 15:58:13 +0000131 pj_bool_t vad_enabled;
132 pjmedia_silence_det *vad;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000133 pj_timestamp last_tx;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000134};
135
136
137
138/*
139 * Initialize and register GSM codec factory to pjmedia endpoint.
140 */
141PJ_DEF(pj_status_t) pjmedia_codec_gsm_init( pjmedia_endpt *endpt )
142{
143 pjmedia_codec_mgr *codec_mgr;
144 pj_status_t status;
145
146 if (gsm_codec_factory.pool != NULL)
147 return PJ_SUCCESS;
148
149 /* Create GSM codec factory. */
150 gsm_codec_factory.base.op = &gsm_factory_op;
151 gsm_codec_factory.base.factory_data = NULL;
152 gsm_codec_factory.endpt = endpt;
153
154 gsm_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "gsm", 4000,
155 4000);
156 if (!gsm_codec_factory.pool)
157 return PJ_ENOMEM;
158
159 pj_list_init(&gsm_codec_factory.codec_list);
160
161 /* Create mutex. */
162 status = pj_mutex_create_simple(gsm_codec_factory.pool, "gsm",
163 &gsm_codec_factory.mutex);
164 if (status != PJ_SUCCESS)
165 goto on_error;
166
167 /* Get the codec manager. */
168 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
169 if (!codec_mgr) {
170 status = PJ_EINVALIDOP;
171 goto on_error;
172 }
173
174 /* Register codec factory to endpoint. */
175 status = pjmedia_codec_mgr_register_factory(codec_mgr,
176 &gsm_codec_factory.base);
177 if (status != PJ_SUCCESS)
178 goto on_error;
179
180 /* Done. */
181 return PJ_SUCCESS;
182
183on_error:
184 pj_pool_release(gsm_codec_factory.pool);
185 gsm_codec_factory.pool = NULL;
186 return status;
187}
188
189
190
191/*
192 * Unregister GSM codec factory from pjmedia endpoint and deinitialize
193 * the GSM codec library.
194 */
195PJ_DEF(pj_status_t) pjmedia_codec_gsm_deinit(void)
196{
197 pjmedia_codec_mgr *codec_mgr;
198 pj_status_t status;
199
200 if (gsm_codec_factory.pool == NULL)
201 return PJ_SUCCESS;
202
203 /* We don't want to deinit if there's outstanding codec. */
Benny Prijonod7036a12007-03-15 10:12:57 +0000204 /* This is silly, as we'll always have codec in the list if
205 we ever allocate a codec! A better behavior maybe is to
206 deallocate all codecs in the list.
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000207 pj_mutex_lock(gsm_codec_factory.mutex);
208 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
209 pj_mutex_unlock(gsm_codec_factory.mutex);
210 return PJ_EBUSY;
211 }
Benny Prijonod7036a12007-03-15 10:12:57 +0000212 */
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000213
214 /* Get the codec manager. */
215 codec_mgr = pjmedia_endpt_get_codec_mgr(gsm_codec_factory.endpt);
216 if (!codec_mgr) {
217 pj_pool_release(gsm_codec_factory.pool);
218 gsm_codec_factory.pool = NULL;
219 return PJ_EINVALIDOP;
220 }
221
222 /* Unregister GSM codec factory. */
223 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
224 &gsm_codec_factory.base);
225
226 /* Destroy mutex. */
227 pj_mutex_destroy(gsm_codec_factory.mutex);
228
229 /* Destroy pool. */
230 pj_pool_release(gsm_codec_factory.pool);
231 gsm_codec_factory.pool = NULL;
232
233 return status;
234}
235
236/*
237 * Check if factory can allocate the specified codec.
238 */
239static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
240 const pjmedia_codec_info *info )
241{
242 PJ_UNUSED_ARG(factory);
243
244 /* Check payload type. */
245 if (info->pt != PJMEDIA_RTP_PT_GSM)
246 return PJMEDIA_CODEC_EUNSUP;
247
248 /* Ignore the rest, since it's static payload type. */
249
250 return PJ_SUCCESS;
251}
252
253/*
254 * Generate default attribute.
255 */
256static pj_status_t gsm_default_attr (pjmedia_codec_factory *factory,
257 const pjmedia_codec_info *id,
258 pjmedia_codec_param *attr )
259{
260 PJ_UNUSED_ARG(factory);
261 PJ_UNUSED_ARG(id);
262
Benny Prijonoac623b32006-07-03 15:19:31 +0000263 pj_bzero(attr, sizeof(pjmedia_codec_param));
Benny Prijono8befd9f2006-05-13 22:46:23 +0000264 attr->info.clock_rate = 8000;
265 attr->info.channel_cnt = 1;
266 attr->info.avg_bps = 13200;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000267 attr->info.max_bps = 13200;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000268 attr->info.pcm_bits_per_sample = 16;
269 attr->info.frm_ptime = 20;
270 attr->info.pt = PJMEDIA_RTP_PT_GSM;
271
272 attr->setting.frm_per_pkt = 1;
Benny Prijono70c68912006-05-19 15:58:13 +0000273 attr->setting.vad = 1;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000274#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000275 attr->setting.plc = 1;
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000276#endif
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000277
Benny Prijono70c68912006-05-19 15:58:13 +0000278 /* Default all other flag bits disabled. */
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000279
280 return PJ_SUCCESS;
281}
282
283/*
284 * Enum codecs supported by this factory (i.e. only GSM!).
285 */
286static pj_status_t gsm_enum_codecs(pjmedia_codec_factory *factory,
287 unsigned *count,
288 pjmedia_codec_info codecs[])
289{
290 PJ_UNUSED_ARG(factory);
291 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
292
Benny Prijonoac623b32006-07-03 15:19:31 +0000293 pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000294 codecs[0].encoding_name = pj_str("GSM");
295 codecs[0].pt = PJMEDIA_RTP_PT_GSM;
296 codecs[0].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000297 codecs[0].clock_rate = 8000;
298 codecs[0].channel_cnt = 1;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000299
300 *count = 1;
301
302 return PJ_SUCCESS;
303}
304
305/*
306 * Allocate a new GSM codec instance.
307 */
308static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
309 const pjmedia_codec_info *id,
310 pjmedia_codec **p_codec)
311{
312 pjmedia_codec *codec;
Benny Prijonof93895c2006-03-05 11:50:53 +0000313 struct gsm_data *gsm_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000314 pj_status_t status;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000315
316 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
317 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
318
319
320 pj_mutex_lock(gsm_codec_factory.mutex);
321
322 /* Get free nodes, if any. */
323 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
324 codec = gsm_codec_factory.codec_list.next;
325 pj_list_erase(codec);
326 } else {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000327 codec = PJ_POOL_ZALLOC_T(gsm_codec_factory.pool, pjmedia_codec);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000328 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000329 codec->op = &gsm_op;
330 codec->factory = factory;
Benny Prijonof93895c2006-03-05 11:50:53 +0000331
Benny Prijonoa1e69682007-05-11 15:14:34 +0000332 gsm_data = PJ_POOL_ZALLOC_T(gsm_codec_factory.pool, struct gsm_data);
Benny Prijonof93895c2006-03-05 11:50:53 +0000333 codec->codec_data = gsm_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000334
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000335#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000336 /* Create PLC */
337 status = pjmedia_plc_create(gsm_codec_factory.pool, 8000,
338 160, 0, &gsm_data->plc);
339 if (status != PJ_SUCCESS) {
340 pj_mutex_unlock(gsm_codec_factory.mutex);
341 return status;
342 }
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000343#endif
Benny Prijono70c68912006-05-19 15:58:13 +0000344
345 /* Create silence detector */
346 status = pjmedia_silence_det_create(gsm_codec_factory.pool,
347 8000, 160,
348 &gsm_data->vad);
349 if (status != PJ_SUCCESS) {
350 pj_mutex_unlock(gsm_codec_factory.mutex);
351 return status;
352 }
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000353 }
354
355 pj_mutex_unlock(gsm_codec_factory.mutex);
356
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000357 *p_codec = codec;
358 return PJ_SUCCESS;
359}
360
361/*
362 * Free codec.
363 */
364static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
365 pjmedia_codec *codec )
366{
Benny Prijonof93895c2006-03-05 11:50:53 +0000367 struct gsm_data *gsm_data;
Benny Prijono87445bc2006-07-26 11:23:07 +0000368 int i;
Benny Prijonof93895c2006-03-05 11:50:53 +0000369
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000370 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
371 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
372
Benny Prijonoa1e69682007-05-11 15:14:34 +0000373 gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonof93895c2006-03-05 11:50:53 +0000374
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000375 /* Close codec, if it's not closed. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000376 gsm_codec_close(codec);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000377
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000378#if !PLC_DISABLED
Benny Prijono87445bc2006-07-26 11:23:07 +0000379 /* Clear left samples in the PLC, since codec+plc will be reused
380 * next time.
381 */
382 for (i=0; i<2; ++i) {
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000383 pj_int16_t frame[160];
Benny Prijono87445bc2006-07-26 11:23:07 +0000384 pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame));
385 pjmedia_plc_save(gsm_data->plc, frame);
386 }
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000387#else
388 PJ_UNUSED_ARG(i);
389#endif
Benny Prijono87445bc2006-07-26 11:23:07 +0000390
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000391 /* Re-init silence_period */
392 pj_set_timestamp32(&gsm_data->last_tx, 0, 0);
393
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000394 /* Put in the free list. */
395 pj_mutex_lock(gsm_codec_factory.mutex);
396 pj_list_push_front(&gsm_codec_factory.codec_list, codec);
397 pj_mutex_unlock(gsm_codec_factory.mutex);
398
399 return PJ_SUCCESS;
400}
401
402/*
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000403 * Init codec.
404 */
405static pj_status_t gsm_codec_init( pjmedia_codec *codec,
406 pj_pool_t *pool )
407{
408 PJ_UNUSED_ARG(codec);
409 PJ_UNUSED_ARG(pool);
410 return PJ_SUCCESS;
411}
412
413/*
414 * Open codec.
415 */
416static pj_status_t gsm_codec_open( pjmedia_codec *codec,
Benny Prijono6865a3f2006-12-30 02:46:57 +0000417 pjmedia_codec_param *attr )
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000418{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000419 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonof93895c2006-03-05 11:50:53 +0000420
421 pj_assert(gsm_data != NULL);
422 pj_assert(gsm_data->encoder == NULL && gsm_data->decoder == NULL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000423
Benny Prijonof93895c2006-03-05 11:50:53 +0000424 gsm_data->encoder = gsm_create();
425 if (!gsm_data->encoder)
426 return PJMEDIA_CODEC_EFAILED;
427
428 gsm_data->decoder = gsm_create();
429 if (!gsm_data->decoder)
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000430 return PJMEDIA_CODEC_EFAILED;
431
Benny Prijono70c68912006-05-19 15:58:13 +0000432 gsm_data->vad_enabled = (attr->setting.vad != 0);
433 gsm_data->plc_enabled = (attr->setting.plc != 0);
434
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000435 return PJ_SUCCESS;
436}
437
438/*
439 * Close codec.
440 */
441static pj_status_t gsm_codec_close( pjmedia_codec *codec )
442{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000443 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000444
Benny Prijonof93895c2006-03-05 11:50:53 +0000445 pj_assert(gsm_data != NULL);
446
447 if (gsm_data->encoder) {
448 gsm_destroy(gsm_data->encoder);
449 gsm_data->encoder = NULL;
450 }
451 if (gsm_data->decoder) {
452 gsm_destroy(gsm_data->decoder);
453 gsm_data->decoder = NULL;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000454 }
455
456 return PJ_SUCCESS;
457}
458
459
460/*
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000461 * Modify codec settings.
462 */
463static pj_status_t gsm_codec_modify(pjmedia_codec *codec,
464 const pjmedia_codec_param *attr )
465{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000466 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000467
468 pj_assert(gsm_data != NULL);
Benny Prijono6865a3f2006-12-30 02:46:57 +0000469 pj_assert(gsm_data->encoder != NULL && gsm_data->decoder != NULL);
Benny Prijonob94a6ab2006-12-26 21:18:11 +0000470
471 gsm_data->vad_enabled = (attr->setting.vad != 0);
472 gsm_data->plc_enabled = (attr->setting.plc != 0);
473
474 return PJ_SUCCESS;
475}
476
477
478/*
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000479 * Get frames in the packet.
480 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000481static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
482 void *pkt,
483 pj_size_t pkt_size,
484 const pj_timestamp *ts,
485 unsigned *frame_cnt,
486 pjmedia_frame frames[])
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000487{
488 unsigned count = 0;
489
490 PJ_UNUSED_ARG(codec);
491
492 PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
493
494 while (pkt_size >= 33 && count < *frame_cnt) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000495 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
496 frames[count].buf = pkt;
497 frames[count].size = 33;
498 frames[count].timestamp.u64 = ts->u64 + count * 160;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000499
500 pkt = ((char*)pkt) + 33;
501 pkt_size -= 33;
502
503 ++count;
504 }
505
506 *frame_cnt = count;
507 return PJ_SUCCESS;
508}
509
510/*
511 * Encode frame.
512 */
513static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
514 const struct pjmedia_frame *input,
515 unsigned output_buf_len,
516 struct pjmedia_frame *output)
517{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000518 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000519 pj_int16_t *pcm_in;
520 unsigned in_size;
Benny Prijonof93895c2006-03-05 11:50:53 +0000521
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000522 pj_assert(gsm_data && input && output);
523
524 pcm_in = (pj_int16_t*)input->buf;
525 in_size = input->size;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000526
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000527 PJ_ASSERT_RETURN(in_size % 320 == 0, PJMEDIA_CODEC_EPCMFRMINLEN);
528 PJ_ASSERT_RETURN(output_buf_len >= 33 * in_size/320,
529 PJMEDIA_CODEC_EFRMTOOSHORT);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000530
Benny Prijono70c68912006-05-19 15:58:13 +0000531 /* Detect silence */
532 if (gsm_data->vad_enabled) {
533 pj_bool_t is_silence;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000534 pj_int32_t silence_duration;
535
536 silence_duration = pj_timestamp_diff32(&gsm_data->last_tx,
537 &input->timestamp);
Benny Prijono70c68912006-05-19 15:58:13 +0000538
539 is_silence = pjmedia_silence_det_detect(gsm_data->vad,
Benny Prijonoa1e69682007-05-11 15:14:34 +0000540 (const pj_int16_t*) input->buf,
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000541 (input->size >> 1),
Benny Prijono70c68912006-05-19 15:58:13 +0000542 NULL);
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000543 if (is_silence &&
544 PJMEDIA_CODEC_MAX_SILENCE_PERIOD != -1 &&
545 silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD)
546 {
Benny Prijono70c68912006-05-19 15:58:13 +0000547 output->type = PJMEDIA_FRAME_TYPE_NONE;
548 output->buf = NULL;
549 output->size = 0;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000550 output->timestamp = input->timestamp;
Benny Prijono70c68912006-05-19 15:58:13 +0000551 return PJ_SUCCESS;
Benny Prijonoc6c0ed02007-01-20 05:18:14 +0000552 } else {
553 gsm_data->last_tx = input->timestamp;
Benny Prijono70c68912006-05-19 15:58:13 +0000554 }
555 }
556
557 /* Encode */
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000558 output->size = 0;
559 while (in_size >= 320) {
560 gsm_encode(gsm_data->encoder, pcm_in,
561 (unsigned char*)output->buf + output->size);
562 pcm_in += 160;
563 output->size += 33;
564 in_size -= 320;
565 }
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000566
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000567 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
568
569 return PJ_SUCCESS;
570}
571
572/*
573 * Decode frame.
574 */
575static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
576 const struct pjmedia_frame *input,
577 unsigned output_buf_len,
578 struct pjmedia_frame *output)
579{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000580 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijonof93895c2006-03-05 11:50:53 +0000581
582 pj_assert(gsm_data != NULL);
583 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000584
585 if (output_buf_len < 320)
586 return PJMEDIA_CODEC_EPCMTOOSHORT;
587
588 if (input->size < 33)
589 return PJMEDIA_CODEC_EFRMTOOSHORT;
590
Benny Prijonof93895c2006-03-05 11:50:53 +0000591 gsm_decode(gsm_data->decoder,
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000592 (unsigned char*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000593 (short*)output->buf);
594
595 output->size = 320;
596 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
597
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000598#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000599 if (gsm_data->plc_enabled)
Benny Prijono89ac2b42008-06-13 12:52:56 +0000600 pjmedia_plc_save( gsm_data->plc, (pj_int16_t*)output->buf);
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000601#endif
Benny Prijono70c68912006-05-19 15:58:13 +0000602
603 return PJ_SUCCESS;
604}
605
606
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000607#if !PLC_DISABLED
Benny Prijono70c68912006-05-19 15:58:13 +0000608/*
609 * Recover lost frame.
610 */
611static pj_status_t gsm_codec_recover(pjmedia_codec *codec,
612 unsigned output_buf_len,
613 struct pjmedia_frame *output)
614{
Benny Prijono89ac2b42008-06-13 12:52:56 +0000615 struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000616
617 PJ_ASSERT_RETURN(gsm_data->plc_enabled, PJ_EINVALIDOP);
618
619 PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT);
620
Benny Prijono89ac2b42008-06-13 12:52:56 +0000621 pjmedia_plc_generate(gsm_data->plc, (pj_int16_t*)output->buf);
Benny Prijono70c68912006-05-19 15:58:13 +0000622 output->size = 320;
623
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000624 return PJ_SUCCESS;
625}
Benny Prijonoc95a0f02007-04-09 07:06:08 +0000626#endif
Benny Prijono4381efe2006-03-16 14:24:26 +0000627
628
629#endif /* PJMEDIA_HAS_GSM_CODEC */
630