blob: 48e8ae139f9a5c03e1ffdc72087cd99e1801fa84 [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
244 pj_memset(attr, 0, 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
271 pj_memset(&codecs[0], 0, sizeof(pjmedia_codec_info));
272 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;
346
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000347 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
348 PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);
349
Benny Prijonof93895c2006-03-05 11:50:53 +0000350 gsm_data = codec->codec_data;
351
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000352 /* Close codec, if it's not closed. */
Benny Prijonof93895c2006-03-05 11:50:53 +0000353 gsm_codec_close(codec);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000354
355 /* Put in the free list. */
356 pj_mutex_lock(gsm_codec_factory.mutex);
357 pj_list_push_front(&gsm_codec_factory.codec_list, codec);
358 pj_mutex_unlock(gsm_codec_factory.mutex);
359
360 return PJ_SUCCESS;
361}
362
363/*
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000364 * Init codec.
365 */
366static pj_status_t gsm_codec_init( pjmedia_codec *codec,
367 pj_pool_t *pool )
368{
369 PJ_UNUSED_ARG(codec);
370 PJ_UNUSED_ARG(pool);
371 return PJ_SUCCESS;
372}
373
374/*
375 * Open codec.
376 */
377static pj_status_t gsm_codec_open( pjmedia_codec *codec,
378 pjmedia_codec_param *attr )
379{
Benny Prijonof93895c2006-03-05 11:50:53 +0000380 struct gsm_data *gsm_data = codec->codec_data;
381
382 pj_assert(gsm_data != NULL);
383 pj_assert(gsm_data->encoder == NULL && gsm_data->decoder == NULL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000384
385 PJ_UNUSED_ARG(attr);
386
Benny Prijonof93895c2006-03-05 11:50:53 +0000387 gsm_data->encoder = gsm_create();
388 if (!gsm_data->encoder)
389 return PJMEDIA_CODEC_EFAILED;
390
391 gsm_data->decoder = gsm_create();
392 if (!gsm_data->decoder)
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000393 return PJMEDIA_CODEC_EFAILED;
394
Benny Prijono70c68912006-05-19 15:58:13 +0000395 gsm_data->vad_enabled = (attr->setting.vad != 0);
396 gsm_data->plc_enabled = (attr->setting.plc != 0);
397
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000398 return PJ_SUCCESS;
399}
400
401/*
402 * Close codec.
403 */
404static pj_status_t gsm_codec_close( pjmedia_codec *codec )
405{
Benny Prijonof93895c2006-03-05 11:50:53 +0000406 struct gsm_data *gsm_data = codec->codec_data;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000407
Benny Prijonof93895c2006-03-05 11:50:53 +0000408 pj_assert(gsm_data != NULL);
409
410 if (gsm_data->encoder) {
411 gsm_destroy(gsm_data->encoder);
412 gsm_data->encoder = NULL;
413 }
414 if (gsm_data->decoder) {
415 gsm_destroy(gsm_data->decoder);
416 gsm_data->decoder = NULL;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000417 }
418
419 return PJ_SUCCESS;
420}
421
422
423/*
424 * Get frames in the packet.
425 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000426static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
427 void *pkt,
428 pj_size_t pkt_size,
429 const pj_timestamp *ts,
430 unsigned *frame_cnt,
431 pjmedia_frame frames[])
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000432{
433 unsigned count = 0;
434
435 PJ_UNUSED_ARG(codec);
436
437 PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
438
439 while (pkt_size >= 33 && count < *frame_cnt) {
Benny Prijono8befd9f2006-05-13 22:46:23 +0000440 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
441 frames[count].buf = pkt;
442 frames[count].size = 33;
443 frames[count].timestamp.u64 = ts->u64 + count * 160;
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000444
445 pkt = ((char*)pkt) + 33;
446 pkt_size -= 33;
447
448 ++count;
449 }
450
451 *frame_cnt = count;
452 return PJ_SUCCESS;
453}
454
455/*
456 * Encode frame.
457 */
458static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
459 const struct pjmedia_frame *input,
460 unsigned output_buf_len,
461 struct pjmedia_frame *output)
462{
Benny Prijonof93895c2006-03-05 11:50:53 +0000463 struct gsm_data *gsm_data = codec->codec_data;
464
465 pj_assert(gsm_data != NULL);
466 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000467
468 if (output_buf_len < 33)
469 return PJMEDIA_CODEC_EFRMTOOSHORT;
470
471 if (input->size < 320)
472 return PJMEDIA_CODEC_EPCMTOOSHORT;
473
Benny Prijono70c68912006-05-19 15:58:13 +0000474 /* Detect silence */
475 if (gsm_data->vad_enabled) {
476 pj_bool_t is_silence;
477
478 is_silence = pjmedia_silence_det_detect(gsm_data->vad,
479 input->buf,
480 input->size / 2,
481 NULL);
482 if (is_silence) {
483 output->type = PJMEDIA_FRAME_TYPE_NONE;
484 output->buf = NULL;
485 output->size = 0;
486 output->timestamp.u64 = input->timestamp.u64;
487 return PJ_SUCCESS;
488 }
489 }
490
491 /* Encode */
Benny Prijonof93895c2006-03-05 11:50:53 +0000492 gsm_encode(gsm_data->encoder, (short*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000493 (unsigned char*)output->buf);
494
495 output->size = 33;
496 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
497
498 return PJ_SUCCESS;
499}
500
501/*
502 * Decode frame.
503 */
504static pj_status_t gsm_codec_decode( pjmedia_codec *codec,
505 const struct pjmedia_frame *input,
506 unsigned output_buf_len,
507 struct pjmedia_frame *output)
508{
Benny Prijonof93895c2006-03-05 11:50:53 +0000509 struct gsm_data *gsm_data = codec->codec_data;
510
511 pj_assert(gsm_data != NULL);
512 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000513
514 if (output_buf_len < 320)
515 return PJMEDIA_CODEC_EPCMTOOSHORT;
516
517 if (input->size < 33)
518 return PJMEDIA_CODEC_EFRMTOOSHORT;
519
Benny Prijonof93895c2006-03-05 11:50:53 +0000520 gsm_decode(gsm_data->decoder,
Benny Prijonof80b1bf2006-02-19 02:24:27 +0000521 (unsigned char*)input->buf,
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000522 (short*)output->buf);
523
524 output->size = 320;
525 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
526
Benny Prijono70c68912006-05-19 15:58:13 +0000527 if (gsm_data->plc_enabled)
528 pjmedia_plc_save( gsm_data->plc, output->buf);
529
530 return PJ_SUCCESS;
531}
532
533
534/*
535 * Recover lost frame.
536 */
537static pj_status_t gsm_codec_recover(pjmedia_codec *codec,
538 unsigned output_buf_len,
539 struct pjmedia_frame *output)
540{
541 struct gsm_data *gsm_data = codec->codec_data;
542
543 PJ_ASSERT_RETURN(gsm_data->plc_enabled, PJ_EINVALIDOP);
544
545 PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT);
546
547 pjmedia_plc_generate(gsm_data->plc, output->buf);
548 output->size = 320;
549
Benny Prijonoa4bf0212006-02-10 15:57:08 +0000550 return PJ_SUCCESS;
551}
Benny Prijono4381efe2006-03-16 14:24:26 +0000552
553
554#endif /* PJMEDIA_HAS_GSM_CODEC */
555