blob: 38eceaca373694a402a2fa39deb9c9be696de803 [file] [log] [blame]
Benny Prijono7ffd7752008-03-17 14:07:53 +00001/* $Id$ */
2/*
3 * Copyright (C)2003-2008 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/g722.h>
20#include <pjmedia/codec.h>
21#include <pjmedia/errno.h>
22#include <pjmedia/endpoint.h>
23#include <pjmedia/plc.h>
24#include <pjmedia/port.h>
25#include <pjmedia/silencedet.h>
26#include <pj/assert.h>
27#include <pj/log.h>
28#include <pj/pool.h>
29#include <pj/string.h>
30#include <pj/os.h>
31
32#if defined(PJMEDIA_HAS_G722_CODEC) && (PJMEDIA_HAS_G722_CODEC != 0)
33
34#include "g722/g722_enc.h"
35#include "g722/g722_dec.h"
36
37#define THIS_FILE "g722.c"
38
39/* Defines */
40#define PTIME (20)
41#define SAMPLES_PER_FRAME (16000 * PTIME /1000)
42#define FRAME_LEN (160)
43#define PLC_DISABLED 1
44
45/* Tracing */
46#ifndef PJ_TRACE
47# define PJ_TRACE 0
48#endif
49
50#if PJ_TRACE
51# define TRACE_(expr) PJ_LOG(4,expr)
52#else
53# define TRACE_(expr)
54#endif
55
56
57/* Prototypes for G722 factory */
58static pj_status_t g722_test_alloc(pjmedia_codec_factory *factory,
59 const pjmedia_codec_info *id );
60static pj_status_t g722_default_attr(pjmedia_codec_factory *factory,
61 const pjmedia_codec_info *id,
62 pjmedia_codec_param *attr );
63static pj_status_t g722_enum_codecs(pjmedia_codec_factory *factory,
64 unsigned *count,
65 pjmedia_codec_info codecs[]);
66static pj_status_t g722_alloc_codec(pjmedia_codec_factory *factory,
67 const pjmedia_codec_info *id,
68 pjmedia_codec **p_codec);
69static pj_status_t g722_dealloc_codec(pjmedia_codec_factory *factory,
70 pjmedia_codec *codec );
71
72/* Prototypes for G722 implementation. */
73static pj_status_t g722_codec_init(pjmedia_codec *codec,
74 pj_pool_t *pool );
75static pj_status_t g722_codec_open(pjmedia_codec *codec,
76 pjmedia_codec_param *attr );
77static pj_status_t g722_codec_close(pjmedia_codec *codec );
78static pj_status_t g722_codec_modify(pjmedia_codec *codec,
79 const pjmedia_codec_param *attr );
80static pj_status_t g722_codec_parse(pjmedia_codec *codec,
81 void *pkt,
82 pj_size_t pkt_size,
83 const pj_timestamp *ts,
84 unsigned *frame_cnt,
85 pjmedia_frame frames[]);
86static pj_status_t g722_codec_encode(pjmedia_codec *codec,
87 const struct pjmedia_frame *input,
88 unsigned output_buf_len,
89 struct pjmedia_frame *output);
90static pj_status_t g722_codec_decode(pjmedia_codec *codec,
91 const struct pjmedia_frame *input,
92 unsigned output_buf_len,
93 struct pjmedia_frame *output);
94#if !PLC_DISABLED
95static pj_status_t g722_codec_recover(pjmedia_codec *codec,
96 unsigned output_buf_len,
97 struct pjmedia_frame *output);
98#endif
99
100/* Definition for G722 codec operations. */
101static pjmedia_codec_op g722_op =
102{
103 &g722_codec_init,
104 &g722_codec_open,
105 &g722_codec_close,
106 &g722_codec_modify,
107 &g722_codec_parse,
108 &g722_codec_encode,
109 &g722_codec_decode,
110#if !PLC_DISABLED
111 &g722_codec_recover
112#else
113 NULL
114#endif
115};
116
117/* Definition for G722 codec factory operations. */
118static pjmedia_codec_factory_op g722_factory_op =
119{
120 &g722_test_alloc,
121 &g722_default_attr,
122 &g722_enum_codecs,
123 &g722_alloc_codec,
124 &g722_dealloc_codec
125};
126
127/* G722 factory */
128static struct g722_codec_factory
129{
130 pjmedia_codec_factory base;
131 pjmedia_endpt *endpt;
132 pj_pool_t *pool;
133 pj_mutex_t *mutex;
134 pjmedia_codec codec_list;
135} g722_codec_factory;
136
137
138/* G722 codec private data. */
139struct g722_data
140{
141 g722_enc_t encoder;
142 g722_dec_t decoder;
143 pj_bool_t plc_enabled;
144 pj_bool_t vad_enabled;
145 pjmedia_silence_det *vad;
146 pj_timestamp last_tx;
147#if !PLC_DISABLED
148 pjmedia_plc *plc;
149#endif
150};
151
152
153
154/*
155 * Initialize and register G722 codec factory to pjmedia endpoint.
156 */
157PJ_DEF(pj_status_t) pjmedia_codec_g722_init( pjmedia_endpt *endpt )
158{
159 pjmedia_codec_mgr *codec_mgr;
160 pj_status_t status;
161
162 if (g722_codec_factory.pool != NULL)
163 return PJ_SUCCESS;
164
165 /* Create G722 codec factory. */
166 g722_codec_factory.base.op = &g722_factory_op;
167 g722_codec_factory.base.factory_data = NULL;
168 g722_codec_factory.endpt = endpt;
169
170 g722_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "g722", 1000,
171 1000);
172 if (!g722_codec_factory.pool)
173 return PJ_ENOMEM;
174
175 pj_list_init(&g722_codec_factory.codec_list);
176
177 /* Create mutex. */
178 status = pj_mutex_create_simple(g722_codec_factory.pool, "g722",
179 &g722_codec_factory.mutex);
180 if (status != PJ_SUCCESS)
181 goto on_error;
182
183 /* Get the codec manager. */
184 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
185 if (!codec_mgr) {
186 status = PJ_EINVALIDOP;
187 goto on_error;
188 }
189
190 /* Register codec factory to endpoint. */
191 status = pjmedia_codec_mgr_register_factory(codec_mgr,
192 &g722_codec_factory.base);
193 if (status != PJ_SUCCESS)
194 goto on_error;
195
196 TRACE_((THIS_FILE, "G722 codec factory initialized"));
197
198 /* Done. */
199 return PJ_SUCCESS;
200
201on_error:
202 pj_pool_release(g722_codec_factory.pool);
203 g722_codec_factory.pool = NULL;
204 return status;
205}
206
207/*
208 * Unregister G722 codec factory from pjmedia endpoint and deinitialize
209 * the G722 codec library.
210 */
211PJ_DEF(pj_status_t) pjmedia_codec_g722_deinit(void)
212{
213 pjmedia_codec_mgr *codec_mgr;
214 pj_status_t status;
215
216 if (g722_codec_factory.pool == NULL)
217 return PJ_SUCCESS;
218
219 /* Get the codec manager. */
220 codec_mgr = pjmedia_endpt_get_codec_mgr(g722_codec_factory.endpt);
221 if (!codec_mgr) {
222 pj_pool_release(g722_codec_factory.pool);
223 g722_codec_factory.pool = NULL;
224 return PJ_EINVALIDOP;
225 }
226
227 /* Unregister G722 codec factory. */
228 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
229 &g722_codec_factory.base);
230
231 /* Destroy mutex. */
232 pj_mutex_destroy(g722_codec_factory.mutex);
233
234 /* Destroy pool. */
235 pj_pool_release(g722_codec_factory.pool);
236 g722_codec_factory.pool = NULL;
237
238 TRACE_((THIS_FILE, "G722 codec factory shutdown"));
239 return status;
240}
241
242/*
243 * Check if factory can allocate the specified codec.
244 */
245static pj_status_t g722_test_alloc(pjmedia_codec_factory *factory,
246 const pjmedia_codec_info *info )
247{
248 PJ_UNUSED_ARG(factory);
249
250 /* Check payload type. */
251 if (info->pt != PJMEDIA_RTP_PT_G722)
252 return PJMEDIA_CODEC_EUNSUP;
253
254 /* Ignore the rest, since it's static payload type. */
255
256 return PJ_SUCCESS;
257}
258
259/*
260 * Generate default attribute.
261 */
262static pj_status_t g722_default_attr( pjmedia_codec_factory *factory,
263 const pjmedia_codec_info *id,
264 pjmedia_codec_param *attr )
265{
266 PJ_UNUSED_ARG(factory);
267 PJ_UNUSED_ARG(id);
268
269 pj_bzero(attr, sizeof(pjmedia_codec_param));
270 attr->info.clock_rate = 16000;
271 attr->info.channel_cnt = 1;
272 attr->info.avg_bps = 64000;
Nanang Izzuddine4b4b7d2008-06-06 12:15:23 +0000273 attr->info.max_bps = 64000;
Benny Prijono7ffd7752008-03-17 14:07:53 +0000274 attr->info.pcm_bits_per_sample = 16;
275 attr->info.frm_ptime = PTIME;
276 attr->info.pt = PJMEDIA_RTP_PT_G722;
277
278 attr->setting.frm_per_pkt = 1;
279 attr->setting.vad = 1;
280 attr->setting.plc = 0;
281
282 /* Default all other flag bits disabled. */
283
284 return PJ_SUCCESS;
285}
286
287/*
288 * Enum codecs supported by this factory (i.e. only G722!).
289 */
290static pj_status_t g722_enum_codecs(pjmedia_codec_factory *factory,
291 unsigned *count,
292 pjmedia_codec_info codecs[])
293{
294 PJ_UNUSED_ARG(factory);
295 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
296
297 pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
298 codecs[0].encoding_name = pj_str("G722");
299 codecs[0].pt = PJMEDIA_RTP_PT_G722;
300 codecs[0].type = PJMEDIA_TYPE_AUDIO;
301 codecs[0].clock_rate = 16000;
302 codecs[0].channel_cnt = 1;
303
304 *count = 1;
305
306 return PJ_SUCCESS;
307}
308
309/*
310 * Allocate a new G722 codec instance.
311 */
312static pj_status_t g722_alloc_codec(pjmedia_codec_factory *factory,
313 const pjmedia_codec_info *id,
314 pjmedia_codec **p_codec)
315{
316 pjmedia_codec *codec;
317 struct g722_data *g722_data;
318
319 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
320 PJ_ASSERT_RETURN(factory == &g722_codec_factory.base, PJ_EINVAL);
321
322 pj_mutex_lock(g722_codec_factory.mutex);
323
324 /* Get free nodes, if any. */
325 if (!pj_list_empty(&g722_codec_factory.codec_list)) {
326 codec = g722_codec_factory.codec_list.next;
327 pj_list_erase(codec);
328 } else {
329 pj_status_t status;
330
331 codec = PJ_POOL_ZALLOC_T(g722_codec_factory.pool, pjmedia_codec);
332 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
333 codec->op = &g722_op;
334 codec->factory = factory;
335
336 g722_data = PJ_POOL_ZALLOC_T(g722_codec_factory.pool, struct g722_data);
337 codec->codec_data = g722_data;
338
339#if !PLC_DISABLED
340 /* Create PLC */
341 status = pjmedia_plc_create(g722_codec_factory.pool, 16000,
342 SAMPLES_PER_FRAME, 0, &g722_data->plc);
343 if (status != PJ_SUCCESS) {
344 pj_mutex_unlock(g722_codec_factory.mutex);
345 return status;
346 }
347#endif
348
349 /* Create silence detector */
350 status = pjmedia_silence_det_create(g722_codec_factory.pool,
351 16000, SAMPLES_PER_FRAME,
352 &g722_data->vad);
353 if (status != PJ_SUCCESS) {
354 pj_mutex_unlock(g722_codec_factory.mutex);
355 TRACE_((THIS_FILE, "Create silence detector failed (status = %d)",
356 status));
357 return status;
358 }
359 }
360
361
362 pj_mutex_unlock(g722_codec_factory.mutex);
363
364 *p_codec = codec;
365 return PJ_SUCCESS;
366}
367
368/*
369 * Free codec.
370 */
371static pj_status_t g722_dealloc_codec(pjmedia_codec_factory *factory,
372 pjmedia_codec *codec )
373{
374 struct g722_data *g722_data;
375 int i;
376
377 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
378 PJ_ASSERT_RETURN(factory == &g722_codec_factory.base, PJ_EINVAL);
379
380 g722_data = (struct g722_data*) codec->codec_data;
381
382 /* Close codec, if it's not closed. */
383 g722_codec_close(codec);
384
385#if !PLC_DISABLED
386 /* Clear left samples in the PLC, since codec+plc will be reused
387 * next time.
388 */
389 for (i=0; i<2; ++i) {
390 pj_int16_t frame[SAMPLES_PER_FRAME];
391 pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame));
392 pjmedia_plc_save(g722_data->plc, frame);
393 }
394#else
395 PJ_UNUSED_ARG(i);
396#endif
397
398 /* Re-init silence_period */
399 pj_set_timestamp32(&g722_data->last_tx, 0, 0);
400
401 /* Put in the free list. */
402 pj_mutex_lock(g722_codec_factory.mutex);
403 pj_list_push_front(&g722_codec_factory.codec_list, codec);
404 pj_mutex_unlock(g722_codec_factory.mutex);
405
406 return PJ_SUCCESS;
407}
408
409/*
410 * Init codec.
411 */
412static pj_status_t g722_codec_init(pjmedia_codec *codec,
413 pj_pool_t *pool )
414{
415 PJ_UNUSED_ARG(codec);
416 PJ_UNUSED_ARG(pool);
417 return PJ_SUCCESS;
418}
419
420/*
421 * Open codec.
422 */
423static pj_status_t g722_codec_open(pjmedia_codec *codec,
424 pjmedia_codec_param *attr )
425{
426 struct g722_data *g722_data = (struct g722_data*) codec->codec_data;
427 pj_status_t status;
428
429 PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
430 PJ_ASSERT_RETURN(g722_data != NULL, PJ_EINVALIDOP);
431
432 status = g722_enc_init(&g722_data->encoder);
433 if (status != PJ_SUCCESS) {
434 TRACE_((THIS_FILE, "g722_enc_init() failed, status=%d", status));
435 pj_mutex_unlock(g722_codec_factory.mutex);
436 return PJMEDIA_CODEC_EFAILED;
437 }
438
439 status = g722_dec_init(&g722_data->decoder);
440 if (status != PJ_SUCCESS) {
441 TRACE_((THIS_FILE, "g722_dec_init() failed, status=%d", status));
442 pj_mutex_unlock(g722_codec_factory.mutex);
443 return PJMEDIA_CODEC_EFAILED;
444 }
445
446 g722_data->vad_enabled = (attr->setting.vad != 0);
447 g722_data->plc_enabled = (attr->setting.plc != 0);
448
449 TRACE_((THIS_FILE, "G722 codec opened: vad=%d, plc=%d",
450 g722_data->vad_enabled, g722_data->plc_enabled));
451 return PJ_SUCCESS;
452}
453
454/*
455 * Close codec.
456 */
457static pj_status_t g722_codec_close( pjmedia_codec *codec )
458{
459 /* The codec, encoder, and decoder will be reused, so there's
460 * nothing to do here
461 */
462
463 PJ_UNUSED_ARG(codec);
464
465 TRACE_((THIS_FILE, "G722 codec closed"));
466 return PJ_SUCCESS;
467}
468
469
470/*
471 * Modify codec settings.
472 */
473static pj_status_t g722_codec_modify(pjmedia_codec *codec,
474 const pjmedia_codec_param *attr )
475{
476 struct g722_data *g722_data = (struct g722_data*) codec->codec_data;
477
478 pj_assert(g722_data != NULL);
479
480 g722_data->vad_enabled = (attr->setting.vad != 0);
481 g722_data->plc_enabled = (attr->setting.plc != 0);
482
483 TRACE_((THIS_FILE, "G722 codec modified: vad=%d, plc=%d",
484 g722_data->vad_enabled, g722_data->plc_enabled));
485 return PJ_SUCCESS;
486}
487
488
489/*
490 * Get frames in the packet.
491 */
492static pj_status_t g722_codec_parse(pjmedia_codec *codec,
493 void *pkt,
494 pj_size_t pkt_size,
495 const pj_timestamp *ts,
496 unsigned *frame_cnt,
497 pjmedia_frame frames[])
498{
499 unsigned count = 0;
500
501 PJ_UNUSED_ARG(codec);
502
503 PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
504
505 TRACE_((THIS_FILE, "G722 parse(): input len=%d", pkt_size));
506
507 while (pkt_size >= FRAME_LEN && count < *frame_cnt) {
508 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
509 frames[count].buf = pkt;
510 frames[count].size = FRAME_LEN;
511 frames[count].timestamp.u64 = ts->u64 + count * SAMPLES_PER_FRAME;
512
513 pkt = ((char*)pkt) + FRAME_LEN;
514 pkt_size -= FRAME_LEN;
515
516 ++count;
517 }
518
519 TRACE_((THIS_FILE, "G722 parse(): got %d frames", count));
520
521 *frame_cnt = count;
522 return PJ_SUCCESS;
523}
524
525/*
526 * Encode frame.
527 */
528static pj_status_t g722_codec_encode(pjmedia_codec *codec,
529 const struct pjmedia_frame *input,
530 unsigned output_buf_len,
531 struct pjmedia_frame *output)
532{
533 struct g722_data *g722_data = (struct g722_data*) codec->codec_data;
534 pj_status_t status;
535
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000536 pj_assert(g722_data && input && output);
Benny Prijono7ffd7752008-03-17 14:07:53 +0000537
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000538 PJ_ASSERT_RETURN((input->size >> 2) <= output_buf_len,
539 PJMEDIA_CODEC_EFRMTOOSHORT);
Benny Prijono7ffd7752008-03-17 14:07:53 +0000540
541 /* Detect silence */
542 if (g722_data->vad_enabled) {
543 pj_bool_t is_silence;
544 pj_int32_t silence_duration;
545
546 silence_duration = pj_timestamp_diff32(&g722_data->last_tx,
547 &input->timestamp);
548
549 is_silence = pjmedia_silence_det_detect(g722_data->vad,
550 (const pj_int16_t*) input->buf,
551 (input->size >> 1),
552 NULL);
553 if (is_silence &&
554 PJMEDIA_CODEC_MAX_SILENCE_PERIOD != -1 &&
Benny Prijonodd3d0022008-06-14 16:52:04 +0000555 silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*16000/1000)
Benny Prijono7ffd7752008-03-17 14:07:53 +0000556 {
557 output->type = PJMEDIA_FRAME_TYPE_NONE;
558 output->buf = NULL;
559 output->size = 0;
560 output->timestamp = input->timestamp;
561 return PJ_SUCCESS;
562 } else {
563 g722_data->last_tx = input->timestamp;
564 }
565 }
566
567 /* Encode to temporary buffer */
Nanang Izzuddind4242972008-06-02 14:16:36 +0000568 output->size = output_buf_len;
Benny Prijono7ffd7752008-03-17 14:07:53 +0000569 status = g722_enc_encode(&g722_data->encoder, (pj_int16_t*)input->buf,
Nanang Izzuddin3aef5e12008-06-05 10:50:40 +0000570 (input->size >> 1), output->buf, &output->size);
Benny Prijono7ffd7752008-03-17 14:07:53 +0000571 if (status != PJ_SUCCESS) {
572 output->size = 0;
573 output->buf = NULL;
574 output->type = PJMEDIA_FRAME_TYPE_NONE;
575 TRACE_((THIS_FILE, "G722 encode() status: %d", status));
576 return PJMEDIA_CODEC_EFAILED;
577 }
578
579 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
580
581 TRACE_((THIS_FILE, "G722 encode(): size=%d", output->size));
582 return PJ_SUCCESS;
583}
584
585/*
586 * Decode frame.
587 */
588static pj_status_t g722_codec_decode(pjmedia_codec *codec,
589 const struct pjmedia_frame *input,
590 unsigned output_buf_len,
591 struct pjmedia_frame *output)
592{
593 struct g722_data *g722_data = (struct g722_data*) codec->codec_data;
594 pj_status_t status;
595
596 pj_assert(g722_data != NULL);
597 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
598
599 TRACE_((THIS_FILE, "G722 decode(): inbuf=%p, insize=%d, outbuf=%p,"
600 "outsize=%d",
601 input->buf, input->size, output->buf, output_buf_len));
602
603 if (output_buf_len < SAMPLES_PER_FRAME * 2) {
604 TRACE_((THIS_FILE, "G722 decode() ERROR: PJMEDIA_CODEC_EPCMTOOSHORT"));
605 return PJMEDIA_CODEC_EPCMTOOSHORT;
606 }
607
608 if (input->size != FRAME_LEN) {
609 TRACE_((THIS_FILE, "G722 decode() ERROR: PJMEDIA_CODEC_EFRMTOOSHORT"));
610 return PJMEDIA_CODEC_EFRMTOOSHORT;
611 }
612
613
614 /* Decode */
615 output->size = SAMPLES_PER_FRAME;
616 status = g722_dec_decode(&g722_data->decoder, input->buf, input->size,
617 (pj_int16_t*)output->buf, &output->size);
618 if (status != PJ_SUCCESS) {
619 TRACE_((THIS_FILE, "G722 decode() status: %d", status));
620 return PJMEDIA_CODEC_EFAILED;
621 }
622
623 pj_assert(output->size == SAMPLES_PER_FRAME);
624 output->size = SAMPLES_PER_FRAME * 2;
625 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
626
627#if !PLC_DISABLED
628 if (g722_data->plc_enabled)
629 pjmedia_plc_save(g722_data->plc, output->buf);
630#endif
631
632 TRACE_((THIS_FILE, "G722 decode done"));
633 return PJ_SUCCESS;
634}
635
636
637#if !PLC_DISABLED
638/*
639 * Recover lost frame.
640 */
641static pj_status_t g722_codec_recover(pjmedia_codec *codec,
642 unsigned output_buf_len,
643 struct pjmedia_frame *output)
644{
645 struct g722_data *g722_data = codec->codec_data;
646
647 PJ_ASSERT_RETURN(g722_data->plc_enabled, PJ_EINVALIDOP);
648
649 PJ_ASSERT_RETURN(output_buf_len >= SAMPLES_PER_FRAME * 2,
650 PJMEDIA_CODEC_EPCMTOOSHORT);
651
652 pjmedia_plc_generate(g722_data->plc, output->buf);
653
654 output->size = SAMPLES_PER_FRAME * 2;
655 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
656
657 return PJ_SUCCESS;
658}
659#endif
660
661#endif // PJMEDIA_HAS_G722_CODEC
662