blob: ece73d870aca09847623b0a68fdad900f53081f8 [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;
273 attr->info.pcm_bits_per_sample = 16;
274 attr->info.frm_ptime = PTIME;
275 attr->info.pt = PJMEDIA_RTP_PT_G722;
276
277 attr->setting.frm_per_pkt = 1;
278 attr->setting.vad = 1;
279 attr->setting.plc = 0;
280
281 /* Default all other flag bits disabled. */
282
283 return PJ_SUCCESS;
284}
285
286/*
287 * Enum codecs supported by this factory (i.e. only G722!).
288 */
289static pj_status_t g722_enum_codecs(pjmedia_codec_factory *factory,
290 unsigned *count,
291 pjmedia_codec_info codecs[])
292{
293 PJ_UNUSED_ARG(factory);
294 PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
295
296 pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
297 codecs[0].encoding_name = pj_str("G722");
298 codecs[0].pt = PJMEDIA_RTP_PT_G722;
299 codecs[0].type = PJMEDIA_TYPE_AUDIO;
300 codecs[0].clock_rate = 16000;
301 codecs[0].channel_cnt = 1;
302
303 *count = 1;
304
305 return PJ_SUCCESS;
306}
307
308/*
309 * Allocate a new G722 codec instance.
310 */
311static pj_status_t g722_alloc_codec(pjmedia_codec_factory *factory,
312 const pjmedia_codec_info *id,
313 pjmedia_codec **p_codec)
314{
315 pjmedia_codec *codec;
316 struct g722_data *g722_data;
317
318 PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
319 PJ_ASSERT_RETURN(factory == &g722_codec_factory.base, PJ_EINVAL);
320
321 pj_mutex_lock(g722_codec_factory.mutex);
322
323 /* Get free nodes, if any. */
324 if (!pj_list_empty(&g722_codec_factory.codec_list)) {
325 codec = g722_codec_factory.codec_list.next;
326 pj_list_erase(codec);
327 } else {
328 pj_status_t status;
329
330 codec = PJ_POOL_ZALLOC_T(g722_codec_factory.pool, pjmedia_codec);
331 PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
332 codec->op = &g722_op;
333 codec->factory = factory;
334
335 g722_data = PJ_POOL_ZALLOC_T(g722_codec_factory.pool, struct g722_data);
336 codec->codec_data = g722_data;
337
338#if !PLC_DISABLED
339 /* Create PLC */
340 status = pjmedia_plc_create(g722_codec_factory.pool, 16000,
341 SAMPLES_PER_FRAME, 0, &g722_data->plc);
342 if (status != PJ_SUCCESS) {
343 pj_mutex_unlock(g722_codec_factory.mutex);
344 return status;
345 }
346#endif
347
348 /* Create silence detector */
349 status = pjmedia_silence_det_create(g722_codec_factory.pool,
350 16000, SAMPLES_PER_FRAME,
351 &g722_data->vad);
352 if (status != PJ_SUCCESS) {
353 pj_mutex_unlock(g722_codec_factory.mutex);
354 TRACE_((THIS_FILE, "Create silence detector failed (status = %d)",
355 status));
356 return status;
357 }
358 }
359
360
361 pj_mutex_unlock(g722_codec_factory.mutex);
362
363 *p_codec = codec;
364 return PJ_SUCCESS;
365}
366
367/*
368 * Free codec.
369 */
370static pj_status_t g722_dealloc_codec(pjmedia_codec_factory *factory,
371 pjmedia_codec *codec )
372{
373 struct g722_data *g722_data;
374 int i;
375
376 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
377 PJ_ASSERT_RETURN(factory == &g722_codec_factory.base, PJ_EINVAL);
378
379 g722_data = (struct g722_data*) codec->codec_data;
380
381 /* Close codec, if it's not closed. */
382 g722_codec_close(codec);
383
384#if !PLC_DISABLED
385 /* Clear left samples in the PLC, since codec+plc will be reused
386 * next time.
387 */
388 for (i=0; i<2; ++i) {
389 pj_int16_t frame[SAMPLES_PER_FRAME];
390 pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame));
391 pjmedia_plc_save(g722_data->plc, frame);
392 }
393#else
394 PJ_UNUSED_ARG(i);
395#endif
396
397 /* Re-init silence_period */
398 pj_set_timestamp32(&g722_data->last_tx, 0, 0);
399
400 /* Put in the free list. */
401 pj_mutex_lock(g722_codec_factory.mutex);
402 pj_list_push_front(&g722_codec_factory.codec_list, codec);
403 pj_mutex_unlock(g722_codec_factory.mutex);
404
405 return PJ_SUCCESS;
406}
407
408/*
409 * Init codec.
410 */
411static pj_status_t g722_codec_init(pjmedia_codec *codec,
412 pj_pool_t *pool )
413{
414 PJ_UNUSED_ARG(codec);
415 PJ_UNUSED_ARG(pool);
416 return PJ_SUCCESS;
417}
418
419/*
420 * Open codec.
421 */
422static pj_status_t g722_codec_open(pjmedia_codec *codec,
423 pjmedia_codec_param *attr )
424{
425 struct g722_data *g722_data = (struct g722_data*) codec->codec_data;
426 pj_status_t status;
427
428 PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
429 PJ_ASSERT_RETURN(g722_data != NULL, PJ_EINVALIDOP);
430
431 status = g722_enc_init(&g722_data->encoder);
432 if (status != PJ_SUCCESS) {
433 TRACE_((THIS_FILE, "g722_enc_init() failed, status=%d", status));
434 pj_mutex_unlock(g722_codec_factory.mutex);
435 return PJMEDIA_CODEC_EFAILED;
436 }
437
438 status = g722_dec_init(&g722_data->decoder);
439 if (status != PJ_SUCCESS) {
440 TRACE_((THIS_FILE, "g722_dec_init() failed, status=%d", status));
441 pj_mutex_unlock(g722_codec_factory.mutex);
442 return PJMEDIA_CODEC_EFAILED;
443 }
444
445 g722_data->vad_enabled = (attr->setting.vad != 0);
446 g722_data->plc_enabled = (attr->setting.plc != 0);
447
448 TRACE_((THIS_FILE, "G722 codec opened: vad=%d, plc=%d",
449 g722_data->vad_enabled, g722_data->plc_enabled));
450 return PJ_SUCCESS;
451}
452
453/*
454 * Close codec.
455 */
456static pj_status_t g722_codec_close( pjmedia_codec *codec )
457{
458 /* The codec, encoder, and decoder will be reused, so there's
459 * nothing to do here
460 */
461
462 PJ_UNUSED_ARG(codec);
463
464 TRACE_((THIS_FILE, "G722 codec closed"));
465 return PJ_SUCCESS;
466}
467
468
469/*
470 * Modify codec settings.
471 */
472static pj_status_t g722_codec_modify(pjmedia_codec *codec,
473 const pjmedia_codec_param *attr )
474{
475 struct g722_data *g722_data = (struct g722_data*) codec->codec_data;
476
477 pj_assert(g722_data != NULL);
478
479 g722_data->vad_enabled = (attr->setting.vad != 0);
480 g722_data->plc_enabled = (attr->setting.plc != 0);
481
482 TRACE_((THIS_FILE, "G722 codec modified: vad=%d, plc=%d",
483 g722_data->vad_enabled, g722_data->plc_enabled));
484 return PJ_SUCCESS;
485}
486
487
488/*
489 * Get frames in the packet.
490 */
491static pj_status_t g722_codec_parse(pjmedia_codec *codec,
492 void *pkt,
493 pj_size_t pkt_size,
494 const pj_timestamp *ts,
495 unsigned *frame_cnt,
496 pjmedia_frame frames[])
497{
498 unsigned count = 0;
499
500 PJ_UNUSED_ARG(codec);
501
502 PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
503
504 TRACE_((THIS_FILE, "G722 parse(): input len=%d", pkt_size));
505
506 while (pkt_size >= FRAME_LEN && count < *frame_cnt) {
507 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
508 frames[count].buf = pkt;
509 frames[count].size = FRAME_LEN;
510 frames[count].timestamp.u64 = ts->u64 + count * SAMPLES_PER_FRAME;
511
512 pkt = ((char*)pkt) + FRAME_LEN;
513 pkt_size -= FRAME_LEN;
514
515 ++count;
516 }
517
518 TRACE_((THIS_FILE, "G722 parse(): got %d frames", count));
519
520 *frame_cnt = count;
521 return PJ_SUCCESS;
522}
523
524/*
525 * Encode frame.
526 */
527static pj_status_t g722_codec_encode(pjmedia_codec *codec,
528 const struct pjmedia_frame *input,
529 unsigned output_buf_len,
530 struct pjmedia_frame *output)
531{
532 struct g722_data *g722_data = (struct g722_data*) codec->codec_data;
533 pj_status_t status;
534
535 pj_assert(g722_data != NULL);
536 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
537
538 if (output_buf_len < FRAME_LEN)
539 return PJMEDIA_CODEC_EFRMTOOSHORT;
540
541 PJ_ASSERT_RETURN(input->size/2 == SAMPLES_PER_FRAME,
542 PJMEDIA_CODEC_EPCMFRMINLEN);
543
544 /* Detect silence */
545 if (g722_data->vad_enabled) {
546 pj_bool_t is_silence;
547 pj_int32_t silence_duration;
548
549 silence_duration = pj_timestamp_diff32(&g722_data->last_tx,
550 &input->timestamp);
551
552 is_silence = pjmedia_silence_det_detect(g722_data->vad,
553 (const pj_int16_t*) input->buf,
554 (input->size >> 1),
555 NULL);
556 if (is_silence &&
557 PJMEDIA_CODEC_MAX_SILENCE_PERIOD != -1 &&
558 silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD)
559 {
560 output->type = PJMEDIA_FRAME_TYPE_NONE;
561 output->buf = NULL;
562 output->size = 0;
563 output->timestamp = input->timestamp;
564 return PJ_SUCCESS;
565 } else {
566 g722_data->last_tx = input->timestamp;
567 }
568 }
569
570 /* Encode to temporary buffer */
571 status = g722_enc_encode(&g722_data->encoder, (pj_int16_t*)input->buf,
572 SAMPLES_PER_FRAME, output->buf, &output->size);
573 if (status != PJ_SUCCESS) {
574 output->size = 0;
575 output->buf = NULL;
576 output->type = PJMEDIA_FRAME_TYPE_NONE;
577 TRACE_((THIS_FILE, "G722 encode() status: %d", status));
578 return PJMEDIA_CODEC_EFAILED;
579 }
580
581 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
582
583 TRACE_((THIS_FILE, "G722 encode(): size=%d", output->size));
584 return PJ_SUCCESS;
585}
586
587/*
588 * Decode frame.
589 */
590static pj_status_t g722_codec_decode(pjmedia_codec *codec,
591 const struct pjmedia_frame *input,
592 unsigned output_buf_len,
593 struct pjmedia_frame *output)
594{
595 struct g722_data *g722_data = (struct g722_data*) codec->codec_data;
596 pj_status_t status;
597
598 pj_assert(g722_data != NULL);
599 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
600
601 TRACE_((THIS_FILE, "G722 decode(): inbuf=%p, insize=%d, outbuf=%p,"
602 "outsize=%d",
603 input->buf, input->size, output->buf, output_buf_len));
604
605 if (output_buf_len < SAMPLES_PER_FRAME * 2) {
606 TRACE_((THIS_FILE, "G722 decode() ERROR: PJMEDIA_CODEC_EPCMTOOSHORT"));
607 return PJMEDIA_CODEC_EPCMTOOSHORT;
608 }
609
610 if (input->size != FRAME_LEN) {
611 TRACE_((THIS_FILE, "G722 decode() ERROR: PJMEDIA_CODEC_EFRMTOOSHORT"));
612 return PJMEDIA_CODEC_EFRMTOOSHORT;
613 }
614
615
616 /* Decode */
617 output->size = SAMPLES_PER_FRAME;
618 status = g722_dec_decode(&g722_data->decoder, input->buf, input->size,
619 (pj_int16_t*)output->buf, &output->size);
620 if (status != PJ_SUCCESS) {
621 TRACE_((THIS_FILE, "G722 decode() status: %d", status));
622 return PJMEDIA_CODEC_EFAILED;
623 }
624
625 pj_assert(output->size == SAMPLES_PER_FRAME);
626 output->size = SAMPLES_PER_FRAME * 2;
627 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
628
629#if !PLC_DISABLED
630 if (g722_data->plc_enabled)
631 pjmedia_plc_save(g722_data->plc, output->buf);
632#endif
633
634 TRACE_((THIS_FILE, "G722 decode done"));
635 return PJ_SUCCESS;
636}
637
638
639#if !PLC_DISABLED
640/*
641 * Recover lost frame.
642 */
643static pj_status_t g722_codec_recover(pjmedia_codec *codec,
644 unsigned output_buf_len,
645 struct pjmedia_frame *output)
646{
647 struct g722_data *g722_data = codec->codec_data;
648
649 PJ_ASSERT_RETURN(g722_data->plc_enabled, PJ_EINVALIDOP);
650
651 PJ_ASSERT_RETURN(output_buf_len >= SAMPLES_PER_FRAME * 2,
652 PJMEDIA_CODEC_EPCMTOOSHORT);
653
654 pjmedia_plc_generate(g722_data->plc, output->buf);
655
656 output->size = SAMPLES_PER_FRAME * 2;
657 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
658
659 return PJ_SUCCESS;
660}
661#endif
662
663#endif // PJMEDIA_HAS_G722_CODEC
664