blob: 633ddc1bf3b04688c85c07b621d30a213709d229 [file] [log] [blame]
Benny Prijonoa4bf0212006-02-10 15:57:08 +00001/* $Id$ */
2/*
3 * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
4 *
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>
30#include "gsm/gsm.h"
31
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
37
Benny Prijonoa4bf0212006-02-10 15:57:08 +000038/* Prototypes for GSM factory */
39static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
40 const pjmedia_codec_info *id );
41static pj_status_t gsm_default_attr( pjmedia_codec_factory *factory,
42 const pjmedia_codec_info *id,
43 pjmedia_codec_param *attr );
44static pj_status_t gsm_enum_codecs( pjmedia_codec_factory *factory,
45 unsigned *count,
46 pjmedia_codec_info codecs[]);
47static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
48 const pjmedia_codec_info *id,
49 pjmedia_codec **p_codec);
50static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
51 pjmedia_codec *codec );
52
53/* Prototypes for GSM implementation. */
Benny Prijonoa4bf0212006-02-10 15:57:08 +000054static pj_status_t gsm_codec_init( pjmedia_codec *codec,
55 pj_pool_t *pool );
56static pj_status_t gsm_codec_open( pjmedia_codec *codec,
57 pjmedia_codec_param *attr );
58static pj_status_t gsm_codec_close( pjmedia_codec *codec );
Benny Prijono8befd9f2006-05-13 22:46:23 +000059static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
60 void *pkt,
61 pj_size_t pkt_size,
62 const pj_timestamp *ts,
63 unsigned *frame_cnt,
64 pjmedia_frame frames[]);
Benny Prijonoa4bf0212006-02-10 15:57:08 +000065static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
66 const struct pjmedia_frame *input,
67 unsigned output_buf_len,
68 struct pjmedia_frame *output);
69static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
70 const struct pjmedia_frame *input,
71 unsigned output_buf_len,
72 struct pjmedia_frame *output);
Benny Prijono70c68912006-05-19 15:58:13 +000073static pj_status_t gsm_codec_recover(pjmedia_codec *codec,
74 unsigned output_buf_len,
75 struct pjmedia_frame *output);
Benny Prijonoa4bf0212006-02-10 15:57:08 +000076
77/* Definition for GSM codec operations. */
78static pjmedia_codec_op gsm_op =
79{
Benny Prijonoa4bf0212006-02-10 15:57:08 +000080 &gsm_codec_init,
81 &gsm_codec_open,
82 &gsm_codec_close,
Benny Prijono8befd9f2006-05-13 22:46:23 +000083 &gsm_codec_parse,
Benny Prijonoa4bf0212006-02-10 15:57:08 +000084 &gsm_codec_encode,
Benny Prijono70c68912006-05-19 15:58:13 +000085 &gsm_codec_decode,
86 &gsm_codec_recover
Benny Prijonoa4bf0212006-02-10 15:57:08 +000087};
88
89/* Definition for GSM codec factory operations. */
90static pjmedia_codec_factory_op gsm_factory_op =
91{
92 &gsm_test_alloc,
93 &gsm_default_attr,
94 &gsm_enum_codecs,
95 &gsm_alloc_codec,
96 &gsm_dealloc_codec
97};
98
99/* GSM factory */
100static struct gsm_codec_factory
101{
102 pjmedia_codec_factory base;
103 pjmedia_endpt *endpt;
104 pj_pool_t *pool;
105 pj_mutex_t *mutex;
106 pjmedia_codec codec_list;
107} gsm_codec_factory;
108
Benny Prijono70c68912006-05-19 15:58:13 +0000109
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000110/* GSM codec private data. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000111struct gsm_data
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000112{
Benny Prijono70c68912006-05-19 15:58:13 +0000113 void *encoder;
114 void *decoder;
115 pj_bool_t plc_enabled;
116 pjmedia_plc *plc;
117 pj_bool_t vad_enabled;
118 pjmedia_silence_det *vad;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000119};
120
121
122
123/*
124 * Initialize and register GSM codec factory to pjmedia endpoint.
125 */
126PJ_DEF(pj_status_t) pjmedia_codec_gsm_init( pjmedia_endpt *endpt )
127{
128 pjmedia_codec_mgr *codec_mgr;
129 pj_status_t status;
130
131 if (gsm_codec_factory.pool != NULL)
132 return PJ_SUCCESS;
133
134 /* Create GSM codec factory. */
135 gsm_codec_factory.base.op = &gsm_factory_op;
136 gsm_codec_factory.base.factory_data = NULL;
137 gsm_codec_factory.endpt = endpt;
138
139 gsm_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "gsm", 4000,
140 4000);
141 if (!gsm_codec_factory.pool)
142 return PJ_ENOMEM;
143
144 pj_list_init(&gsm_codec_factory.codec_list);
145
146 /* Create mutex. */
147 status = pj_mutex_create_simple(gsm_codec_factory.pool, "gsm",
148 &gsm_codec_factory.mutex);
149 if (status != PJ_SUCCESS)
150 goto on_error;
151
152 /* Get the codec manager. */
153 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
154 if (!codec_mgr) {
155 status = PJ_EINVALIDOP;
156 goto on_error;
157 }
158
159 /* Register codec factory to endpoint. */
160 status = pjmedia_codec_mgr_register_factory(codec_mgr,
161 &gsm_codec_factory.base);
162 if (status != PJ_SUCCESS)
163 goto on_error;
164
165 /* Done. */
166 return PJ_SUCCESS;
167
168on_error:
169 pj_pool_release(gsm_codec_factory.pool);
170 gsm_codec_factory.pool = NULL;
171 return status;
172}
173
174
175
176/*
177 * Unregister GSM codec factory from pjmedia endpoint and deinitialize
178 * the GSM codec library.
179 */
180PJ_DEF(pj_status_t) pjmedia_codec_gsm_deinit(void)
181{
182 pjmedia_codec_mgr *codec_mgr;
183 pj_status_t status;
184
185 if (gsm_codec_factory.pool == NULL)
186 return PJ_SUCCESS;
187
188 /* We don't want to deinit if there's outstanding codec. */
189 pj_mutex_lock(gsm_codec_factory.mutex);
190 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
191 pj_mutex_unlock(gsm_codec_factory.mutex);
192 return PJ_EBUSY;
193 }
194
195 /* Get the codec manager. */
196 codec_mgr = pjmedia_endpt_get_codec_mgr(gsm_codec_factory.endpt);
197 if (!codec_mgr) {
198 pj_pool_release(gsm_codec_factory.pool);
199 gsm_codec_factory.pool = NULL;
200 return PJ_EINVALIDOP;
201 }
202
203 /* Unregister GSM codec factory. */
204 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
205 &gsm_codec_factory.base);
206
207 /* Destroy mutex. */
208 pj_mutex_destroy(gsm_codec_factory.mutex);
209
210 /* Destroy pool. */
211 pj_pool_release(gsm_codec_factory.pool);
212 gsm_codec_factory.pool = NULL;
213
214 return status;
215}
216
217/*
218 * Check if factory can allocate the specified codec.
219 */
220static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory,
221 const pjmedia_codec_info *info )
222{
223 PJ_UNUSED_ARG(factory);
224
225 /* Check payload type. */
226 if (info->pt != PJMEDIA_RTP_PT_GSM)
227 return PJMEDIA_CODEC_EUNSUP;
228
229 /* Ignore the rest, since it's static payload type. */
230
231 return PJ_SUCCESS;
232}
233
234/*
235 * Generate default attribute.
236 */
237static pj_status_t gsm_default_attr (pjmedia_codec_factory *factory,
238 const pjmedia_codec_info *id,
239 pjmedia_codec_param *attr )
240{
241 PJ_UNUSED_ARG(factory);
242 PJ_UNUSED_ARG(id);
243
Benny Prijonoac623b32006-07-03 15:19:31 +0000244 pj_bzero(attr, sizeof(pjmedia_codec_param));
Benny Prijono8befd9f2006-05-13 22:46:23 +0000245 attr->info.clock_rate = 8000;
246 attr->info.channel_cnt = 1;
247 attr->info.avg_bps = 13200;
248 attr->info.pcm_bits_per_sample = 16;
249 attr->info.frm_ptime = 20;
250 attr->info.pt = PJMEDIA_RTP_PT_GSM;
251
252 attr->setting.frm_per_pkt = 1;
Benny Prijono70c68912006-05-19 15:58:13 +0000253 attr->setting.vad = 1;
254 attr->setting.plc = 1;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000255
Benny Prijono70c68912006-05-19 15:58:13 +0000256 /* Default all other flag bits disabled. */
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000257
258 return PJ_SUCCESS;
259}
260
261/*
262 * Enum codecs supported by this factory (i.e. only GSM!).
263 */
264static pj_status_t gsm_enum_codecs(pjmedia_codec_factory *factory,
265 unsigned *count,
266 pjmedia_codec_info codecs[])
267{
268 PJ_UNUSED_ARG(factory);
269 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
270
Benny Prijonoac623b32006-07-03 15:19:31 +0000271 pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000272 codecs[0].encoding_name = pj_str("GSM");
273 codecs[0].pt = PJMEDIA_RTP_PT_GSM;
274 codecs[0].type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoa837c302006-04-27 22:36:40 +0000275 codecs[0].clock_rate = 8000;
276 codecs[0].channel_cnt = 1;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000277
278 *count = 1;
279
280 return PJ_SUCCESS;
281}
282
283/*
284 * Allocate a new GSM codec instance.
285 */
286static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory,
287 const pjmedia_codec_info *id,
288 pjmedia_codec **p_codec)
289{
290 pjmedia_codec *codec;
Benny Prijonof93895c2006-03-05 11:50:53 +0000291 struct gsm_data *gsm_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000292 pj_status_t status;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000293
294 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
295 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
296
297
298 pj_mutex_lock(gsm_codec_factory.mutex);
299
300 /* Get free nodes, if any. */
301 if (!pj_list_empty(&gsm_codec_factory.codec_list)) {
302 codec = gsm_codec_factory.codec_list.next;
303 pj_list_erase(codec);
304 } else {
305 codec = pj_pool_zalloc(gsm_codec_factory.pool,
306 sizeof(pjmedia_codec));
307 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000308 codec->op = &gsm_op;
309 codec->factory = factory;
Benny Prijonof93895c2006-03-05 11:50:53 +0000310
311 gsm_data = pj_pool_zalloc(gsm_codec_factory.pool,
312 sizeof(struct gsm_data));
313 codec->codec_data = gsm_data;
Benny Prijono70c68912006-05-19 15:58:13 +0000314
315 /* Create PLC */
316 status = pjmedia_plc_create(gsm_codec_factory.pool, 8000,
317 160, 0, &gsm_data->plc);
318 if (status != PJ_SUCCESS) {
319 pj_mutex_unlock(gsm_codec_factory.mutex);
320 return status;
321 }
322
323 /* Create silence detector */
324 status = pjmedia_silence_det_create(gsm_codec_factory.pool,
325 8000, 160,
326 &gsm_data->vad);
327 if (status != PJ_SUCCESS) {
328 pj_mutex_unlock(gsm_codec_factory.mutex);
329 return status;
330 }
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000331 }
332
333 pj_mutex_unlock(gsm_codec_factory.mutex);
334
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000335 *p_codec = codec;
336 return PJ_SUCCESS;
337}
338
339/*
340 * Free codec.
341 */
342static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory,
343 pjmedia_codec *codec )
344{
Benny Prijonof93895c2006-03-05 11:50:53 +0000345 struct gsm_data *gsm_data;
Benny Prijono87445bc2006-07-26 11:23:07 +0000346 pj_int16_t frame[160];
347 int i;
Benny Prijonof93895c2006-03-05 11:50:53 +0000348
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000349 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
350 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
351
Benny Prijonof93895c2006-03-05 11:50:53 +0000352 gsm_data = codec->codec_data;
353
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000354 /* Close codec, if it's not closed. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000355 gsm_codec_close(codec);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000356
Benny Prijono87445bc2006-07-26 11:23:07 +0000357 /* Clear left samples in the PLC, since codec+plc will be reused
358 * next time.
359 */
360 for (i=0; i<2; ++i) {
361 pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame));
362 pjmedia_plc_save(gsm_data->plc, frame);
363 }
364
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000365 /* Put in the free list. */
366 pj_mutex_lock(gsm_codec_factory.mutex);
367 pj_list_push_front(&gsm_codec_factory.codec_list, codec);
368 pj_mutex_unlock(gsm_codec_factory.mutex);
369
370 return PJ_SUCCESS;
371}
372
373/*
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000374 * Init codec.
375 */
376static pj_status_t gsm_codec_init( pjmedia_codec *codec,
377 pj_pool_t *pool )
378{
379 PJ_UNUSED_ARG(codec);
380 PJ_UNUSED_ARG(pool);
381 return PJ_SUCCESS;
382}
383
384/*
385 * Open codec.
386 */
387static pj_status_t gsm_codec_open( pjmedia_codec *codec,
388 pjmedia_codec_param *attr )
389{
Benny Prijonof93895c2006-03-05 11:50:53 +0000390 struct gsm_data *gsm_data = codec->codec_data;
391
392 pj_assert(gsm_data != NULL);
393 pj_assert(gsm_data->encoder == NULL && gsm_data->decoder == NULL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000394
395 PJ_UNUSED_ARG(attr);
396
Benny Prijonof93895c2006-03-05 11:50:53 +0000397 gsm_data->encoder = gsm_create();
398 if (!gsm_data->encoder)
399 return PJMEDIA_CODEC_EFAILED;
400
401 gsm_data->decoder = gsm_create();
402 if (!gsm_data->decoder)
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000403 return PJMEDIA_CODEC_EFAILED;
404
Benny Prijono70c68912006-05-19 15:58:13 +0000405 gsm_data->vad_enabled = (attr->setting.vad != 0);
406 gsm_data->plc_enabled = (attr->setting.plc != 0);
407
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000408 return PJ_SUCCESS;
409}
410
411/*
412 * Close codec.
413 */
414static pj_status_t gsm_codec_close( pjmedia_codec *codec )
415{
Benny Prijonof93895c2006-03-05 11:50:53 +0000416 struct gsm_data *gsm_data = codec->codec_data;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000417
Benny Prijonof93895c2006-03-05 11:50:53 +0000418 pj_assert(gsm_data != NULL);
419
420 if (gsm_data->encoder) {
421 gsm_destroy(gsm_data->encoder);
422 gsm_data->encoder = NULL;
423 }
424 if (gsm_data->decoder) {
425 gsm_destroy(gsm_data->decoder);
426 gsm_data->decoder = NULL;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000427 }
428
429 return PJ_SUCCESS;
430}
431
432
433/*
434 * Get frames in the packet.
435 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000436static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
437 void *pkt,
438 pj_size_t pkt_size,
439 const pj_timestamp *ts,
440 unsigned *frame_cnt,
441 pjmedia_frame frames[])
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000442{
443 unsigned count = 0;
444
445 PJ_UNUSED_ARG(codec);
446
447 PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
448
449 while (pkt_size >= 33 && count < *frame_cnt) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000450 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
451 frames[count].buf = pkt;
452 frames[count].size = 33;
453 frames[count].timestamp.u64 = ts->u64 + count * 160;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000454
455 pkt = ((char*)pkt) + 33;
456 pkt_size -= 33;
457
458 ++count;
459 }
460
461 *frame_cnt = count;
462 return PJ_SUCCESS;
463}
464
465/*
466 * Encode frame.
467 */
468static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
469 const struct pjmedia_frame *input,
470 unsigned output_buf_len,
471 struct pjmedia_frame *output)
472{
Benny Prijonof93895c2006-03-05 11:50:53 +0000473 struct gsm_data *gsm_data = codec->codec_data;
474
475 pj_assert(gsm_data != NULL);
476 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000477
478 if (output_buf_len < 33)
479 return PJMEDIA_CODEC_EFRMTOOSHORT;
480
481 if (input->size < 320)
482 return PJMEDIA_CODEC_EPCMTOOSHORT;
483
Benny Prijono70c68912006-05-19 15:58:13 +0000484 /* Detect silence */
485 if (gsm_data->vad_enabled) {
486 pj_bool_t is_silence;
487
488 is_silence = pjmedia_silence_det_detect(gsm_data->vad,
489 input->buf,
490 input->size / 2,
491 NULL);
492 if (is_silence) {
493 output->type = PJMEDIA_FRAME_TYPE_NONE;
494 output->buf = NULL;
495 output->size = 0;
496 output->timestamp.u64 = input->timestamp.u64;
497 return PJ_SUCCESS;
498 }
499 }
500
501 /* Encode */
Benny Prijonof93895c2006-03-05 11:50:53 +0000502 gsm_encode(gsm_data->encoder, (short*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000503 (unsigned char*)output->buf);
504
505 output->size = 33;
506 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
507
508 return PJ_SUCCESS;
509}
510
511/*
512 * Decode frame.
513 */
514static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
515 const struct pjmedia_frame *input,
516 unsigned output_buf_len,
517 struct pjmedia_frame *output)
518{
Benny Prijonof93895c2006-03-05 11:50:53 +0000519 struct gsm_data *gsm_data = codec->codec_data;
520
521 pj_assert(gsm_data != NULL);
522 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000523
524 if (output_buf_len < 320)
525 return PJMEDIA_CODEC_EPCMTOOSHORT;
526
527 if (input->size < 33)
528 return PJMEDIA_CODEC_EFRMTOOSHORT;
529
Benny Prijonof93895c2006-03-05 11:50:53 +0000530 gsm_decode(gsm_data->decoder,
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000531 (unsigned char*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000532 (short*)output->buf);
533
534 output->size = 320;
535 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
536
Benny Prijono70c68912006-05-19 15:58:13 +0000537 if (gsm_data->plc_enabled)
538 pjmedia_plc_save( gsm_data->plc, output->buf);
539
540 return PJ_SUCCESS;
541}
542
543
544/*
545 * Recover lost frame.
546 */
547static pj_status_t gsm_codec_recover(pjmedia_codec *codec,
548 unsigned output_buf_len,
549 struct pjmedia_frame *output)
550{
551 struct gsm_data *gsm_data = codec->codec_data;
552
553 PJ_ASSERT_RETURN(gsm_data->plc_enabled, PJ_EINVALIDOP);
554
555 PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT);
556
557 pjmedia_plc_generate(gsm_data->plc, output->buf);
558 output->size = 320;
559
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000560 return PJ_SUCCESS;
561}
Benny Prijono4381efe2006-03-16 14:24:26 +0000562
563
564#endif /* PJMEDIA_HAS_GSM_CODEC */
565