blob: ca8102a936855471e65622803b9705fc81a05e69 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjmedia-codec/l16.h>
21#include <pjmedia/codec.h>
22#include <pjmedia/errno.h>
23#include <pjmedia/endpoint.h>
24#include <pjmedia/plc.h>
25#include <pjmedia/silencedet.h>
26#include <pj/assert.h>
27#include <pj/pool.h>
28#include <pj/sock.h>
29#include <pj/string.h>
30
31
32/*
33 * Only build this file if PJMEDIA_HAS_L16_CODEC != 0
34 */
35#if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC != 0
36
37#define PLC_DISABLED 0
38
39
40static const pj_str_t STR_L16 = { "L16", 3 };
41
42/* To keep frame size below 1400 MTU, set ptime to 10ms for
43 * sampling rate > 35 KHz
44 */
45#define GET_PTIME(clock_rate) ((pj_uint16_t)(clock_rate > 35000 ? 10 : 20))
46
47
48/* Prototypes for L16 factory */
49static pj_status_t l16_test_alloc( pjmedia_codec_factory *factory,
50 const pjmedia_codec_info *id );
51static pj_status_t l16_default_attr( pjmedia_codec_factory *factory,
52 const pjmedia_codec_info *id,
53 pjmedia_codec_param *attr );
54static pj_status_t l16_enum_codecs (pjmedia_codec_factory *factory,
55 unsigned *count,
56 pjmedia_codec_info codecs[]);
57static pj_status_t l16_alloc_codec( pjmedia_codec_factory *factory,
58 const pjmedia_codec_info *id,
59 pjmedia_codec **p_codec);
60static pj_status_t l16_dealloc_codec( pjmedia_codec_factory *factory,
61 pjmedia_codec *codec );
62
63/* Prototypes for L16 implementation. */
64static pj_status_t l16_init( pjmedia_codec *codec,
65 pj_pool_t *pool );
66static pj_status_t l16_open( pjmedia_codec *codec,
67 pjmedia_codec_param *attr );
68static pj_status_t l16_close( pjmedia_codec *codec );
69static pj_status_t l16_modify(pjmedia_codec *codec,
70 const pjmedia_codec_param *attr );
71static pj_status_t l16_parse(pjmedia_codec *codec,
72 void *pkt,
73 pj_size_t pkt_size,
74 const pj_timestamp *ts,
75 unsigned *frame_cnt,
76 pjmedia_frame frames[]);
77static pj_status_t l16_encode( pjmedia_codec *codec,
78 const struct pjmedia_frame *input,
79 unsigned output_buf_len,
80 struct pjmedia_frame *output);
81static pj_status_t l16_decode( pjmedia_codec *codec,
82 const struct pjmedia_frame *input,
83 unsigned output_buf_len,
84 struct pjmedia_frame *output);
85#if !PLC_DISABLED
86static pj_status_t l16_recover(pjmedia_codec *codec,
87 unsigned output_buf_len,
88 struct pjmedia_frame *output);
89#endif
90
91/* Definition for L16 codec operations. */
92static pjmedia_codec_op l16_op =
93{
94 &l16_init,
95 &l16_open,
96 &l16_close,
97 &l16_modify,
98 &l16_parse,
99 &l16_encode,
100 &l16_decode,
101#if !PLC_DISABLED
102 &l16_recover
103#else
104 NULL
105#endif
106};
107
108/* Definition for L16 codec factory operations. */
109static pjmedia_codec_factory_op l16_factory_op =
110{
111 &l16_test_alloc,
112 &l16_default_attr,
113 &l16_enum_codecs,
114 &l16_alloc_codec,
115 &l16_dealloc_codec,
116 &pjmedia_codec_l16_deinit
117};
118
119/* L16 factory private data */
120static struct l16_factory
121{
122 pjmedia_codec_factory base;
123 pjmedia_endpt *endpt;
124 pj_pool_t *pool;
125 pj_mutex_t *mutex;
126} l16_factory;
127
128
129/* L16 codec private data. */
130struct l16_data
131{
132 pj_pool_t *pool;
133 unsigned frame_size; /* Frame size, in bytes */
134 unsigned clock_rate; /* Clock rate */
135
136#if !PLC_DISABLED
137 pj_bool_t plc_enabled;
138 pjmedia_plc *plc;
139#endif
140 pj_bool_t vad_enabled;
141 pjmedia_silence_det *vad;
142 pj_timestamp last_tx;
143};
144
145
146
147PJ_DEF(pj_status_t) pjmedia_codec_l16_init(pjmedia_endpt *endpt,
148 unsigned options)
149{
150 pjmedia_codec_mgr *codec_mgr;
151 pj_status_t status;
152
153
154 PJ_UNUSED_ARG(options);
155
156
157 if (l16_factory.endpt != NULL) {
158 /* Already initialized. */
159 return PJ_SUCCESS;
160 }
161
162 /* Init factory */
163 l16_factory.base.op = &l16_factory_op;
164 l16_factory.base.factory_data = NULL;
165 l16_factory.endpt = endpt;
166
167 /* Create pool */
168 l16_factory.pool = pjmedia_endpt_create_pool(endpt, "l16", 4000, 4000);
169 if (!l16_factory.pool)
170 return PJ_ENOMEM;
171
172 /* Create mutex. */
173 status = pj_mutex_create_simple(l16_factory.pool, "l16",
174 &l16_factory.mutex);
175 if (status != PJ_SUCCESS)
176 goto on_error;
177
178 /* Get the codec manager. */
179 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
180 if (!codec_mgr) {
181 return PJ_EINVALIDOP;
182 }
183
184 /* Register codec factory to endpoint. */
185 status = pjmedia_codec_mgr_register_factory(codec_mgr,
186 &l16_factory.base);
187 if (status != PJ_SUCCESS)
188 return status;
189
190
191 return PJ_SUCCESS;
192
193on_error:
194 if (l16_factory.mutex) {
195 pj_mutex_destroy(l16_factory.mutex);
196 l16_factory.mutex = NULL;
197 }
198 if (l16_factory.pool) {
199 pj_pool_release(l16_factory.pool);
200 l16_factory.pool = NULL;
201 }
202 return status;
203}
204
205PJ_DEF(pj_status_t) pjmedia_codec_l16_deinit(void)
206{
207 pjmedia_codec_mgr *codec_mgr;
208 pj_status_t status;
209
210 if (l16_factory.endpt == NULL) {
211 /* Not registered. */
212 return PJ_SUCCESS;
213 }
214
215 /* Lock mutex. */
216 pj_mutex_lock(l16_factory.mutex);
217
218 /* Get the codec manager. */
219 codec_mgr = pjmedia_endpt_get_codec_mgr(l16_factory.endpt);
220 if (!codec_mgr) {
221 l16_factory.endpt = NULL;
222 pj_mutex_unlock(l16_factory.mutex);
223 return PJ_EINVALIDOP;
224 }
225
226 /* Unregister L16 codec factory. */
227 status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
228 &l16_factory.base);
229 l16_factory.endpt = NULL;
230
231 /* Destroy mutex. */
232 pj_mutex_destroy(l16_factory.mutex);
233 l16_factory.mutex = NULL;
234
235
236 /* Release pool. */
237 pj_pool_release(l16_factory.pool);
238 l16_factory.pool = NULL;
239
240
241 return status;
242}
243
244static pj_status_t l16_test_alloc(pjmedia_codec_factory *factory,
245 const pjmedia_codec_info *id )
246{
247 PJ_UNUSED_ARG(factory);
248
249 if (pj_stricmp(&id->encoding_name, &STR_L16)==0) {
250 /* Match! */
251 return PJ_SUCCESS;
252 }
253
254 return -1;
255}
256
257static pj_status_t l16_default_attr( pjmedia_codec_factory *factory,
258 const pjmedia_codec_info *id,
259 pjmedia_codec_param *attr )
260{
261 PJ_UNUSED_ARG(factory);
262
263 pj_bzero(attr, sizeof(pjmedia_codec_param));
264 attr->info.pt = (pj_uint8_t)id->pt;
265 attr->info.clock_rate = id->clock_rate;
266 attr->info.channel_cnt = id->channel_cnt;
267 attr->info.avg_bps = id->clock_rate * id->channel_cnt * 16;
268 attr->info.max_bps = attr->info.avg_bps;
269 attr->info.pcm_bits_per_sample = 16;
270
271 /* To keep frame size below 1400 MTU, set ptime to 10ms for
272 * sampling rate > 35 KHz
273 */
274 attr->info.frm_ptime = GET_PTIME(id->clock_rate);
275
276 attr->setting.frm_per_pkt = 1;
277
278 attr->setting.vad = 1;
279#if !PLC_DISABLED
280 attr->setting.plc = 1;
281#endif
282
283 return PJ_SUCCESS;
284}
285
286static pj_status_t l16_enum_codecs( pjmedia_codec_factory *factory,
287 unsigned *max_count,
288 pjmedia_codec_info codecs[])
289{
290 unsigned count = 0;
291
292 PJ_UNUSED_ARG(factory);
293
294 if (count < *max_count) {
295 /* Register 44100Hz 1 channel L16 codec */
296 codecs[count].type = PJMEDIA_TYPE_AUDIO;
297 codecs[count].pt = PJMEDIA_RTP_PT_L16_1;
298 codecs[count].encoding_name = STR_L16;
299 codecs[count].clock_rate = 44100;
300 codecs[count].channel_cnt = 1;
301 ++count;
302 }
303
304 if (count < *max_count) {
305 /* Register 44100Hz 2 channels L16 codec */
306 codecs[count].type = PJMEDIA_TYPE_AUDIO;
307 codecs[count].pt = PJMEDIA_RTP_PT_L16_2;
308 codecs[count].encoding_name = STR_L16;
309 codecs[count].clock_rate = 44100;
310 codecs[count].channel_cnt = 2;
311 ++count;
312 }
313
314 if (count < *max_count) {
315 /* 8KHz mono */
316 codecs[count].type = PJMEDIA_TYPE_AUDIO;
317 codecs[count].pt = PJMEDIA_RTP_PT_L16_8KHZ_MONO;
318 codecs[count].encoding_name = STR_L16;
319 codecs[count].clock_rate = 8000;
320 codecs[count].channel_cnt = 1;
321 ++count;
322 }
323
324 if (count < *max_count) {
325 /* 8KHz stereo */
326 codecs[count].type = PJMEDIA_TYPE_AUDIO;
327 codecs[count].pt = PJMEDIA_RTP_PT_L16_8KHZ_STEREO;
328 codecs[count].encoding_name = STR_L16;
329 codecs[count].clock_rate = 8000;
330 codecs[count].channel_cnt = 2;
331 ++count;
332 }
333
334// disable some L16 modes
335#if 0
336 if (count < *max_count) {
337 /* 11025 Hz mono */
338 codecs[count].type = PJMEDIA_TYPE_AUDIO;
339 codecs[count].pt = PJMEDIA_RTP_PT_L16_11KHZ_MONO;
340 codecs[count].encoding_name = STR_L16;
341 codecs[count].clock_rate = 11025;
342 codecs[count].channel_cnt = 1;
343 ++count;
344 }
345
346 if (count < *max_count) {
347 /* 11025 Hz stereo */
348 codecs[count].type = PJMEDIA_TYPE_AUDIO;
349 codecs[count].pt = PJMEDIA_RTP_PT_L16_11KHZ_STEREO;
350 codecs[count].encoding_name = STR_L16;
351 codecs[count].clock_rate = 11025;
352 codecs[count].channel_cnt = 2;
353 ++count;
354 }
355#endif
356
357 if (count < *max_count) {
358 /* 16000 Hz mono */
359 codecs[count].type = PJMEDIA_TYPE_AUDIO;
360 codecs[count].pt = PJMEDIA_RTP_PT_L16_16KHZ_MONO;
361 codecs[count].encoding_name = STR_L16;
362 codecs[count].clock_rate = 16000;
363 codecs[count].channel_cnt = 1;
364 ++count;
365 }
366
367
368 if (count < *max_count) {
369 /* 16000 Hz stereo */
370 codecs[count].type = PJMEDIA_TYPE_AUDIO;
371 codecs[count].pt = PJMEDIA_RTP_PT_L16_16KHZ_STEREO;
372 codecs[count].encoding_name = STR_L16;
373 codecs[count].clock_rate = 16000;
374 codecs[count].channel_cnt = 2;
375 ++count;
376 }
377
378// disable some L16 modes
379#if 0
380 if (count < *max_count) {
381 /* 22050 Hz mono */
382 codecs[count].type = PJMEDIA_TYPE_AUDIO;
383 codecs[count].pt = PJMEDIA_RTP_PT_L16_22KHZ_MONO;
384 codecs[count].encoding_name = STR_L16;
385 codecs[count].clock_rate = 22050;
386 codecs[count].channel_cnt = 1;
387 ++count;
388 }
389
390
391 if (count < *max_count) {
392 /* 22050 Hz stereo */
393 codecs[count].type = PJMEDIA_TYPE_AUDIO;
394 codecs[count].pt = PJMEDIA_RTP_PT_L16_22KHZ_STEREO;
395 codecs[count].encoding_name = STR_L16;
396 codecs[count].clock_rate = 22050;
397 codecs[count].channel_cnt = 2;
398 ++count;
399 }
400
401 if (count < *max_count) {
402 /* 32000 Hz mono */
403 codecs[count].type = PJMEDIA_TYPE_AUDIO;
404 codecs[count].pt = PJMEDIA_RTP_PT_L16_32KHZ_MONO;
405 codecs[count].encoding_name = STR_L16;
406 codecs[count].clock_rate = 32000;
407 codecs[count].channel_cnt = 1;
408 ++count;
409 }
410
411 if (count < *max_count) {
412 /* 32000 Hz stereo */
413 codecs[count].type = PJMEDIA_TYPE_AUDIO;
414 codecs[count].pt = PJMEDIA_RTP_PT_L16_32KHZ_STEREO;
415 codecs[count].encoding_name = STR_L16;
416 codecs[count].clock_rate = 32000;
417 codecs[count].channel_cnt = 2;
418 ++count;
419 }
420
421 if (count < *max_count) {
422 /* 48KHz mono */
423 codecs[count].type = PJMEDIA_TYPE_AUDIO;
424 codecs[count].pt = PJMEDIA_RTP_PT_L16_48KHZ_MONO;
425 codecs[count].encoding_name = STR_L16;
426 codecs[count].clock_rate = 48000;
427 codecs[count].channel_cnt = 1;
428 ++count;
429 }
430
431 if (count < *max_count) {
432 /* 48KHz stereo */
433 codecs[count].type = PJMEDIA_TYPE_AUDIO;
434 codecs[count].pt = PJMEDIA_RTP_PT_L16_48KHZ_STEREO;
435 codecs[count].encoding_name = STR_L16;
436 codecs[count].clock_rate = 48000;
437 codecs[count].channel_cnt = 2;
438 ++count;
439 }
440#endif
441
442
443 *max_count = count;
444
445 return PJ_SUCCESS;
446}
447
448static pj_status_t l16_alloc_codec( pjmedia_codec_factory *factory,
449 const pjmedia_codec_info *id,
450 pjmedia_codec **p_codec)
451{
452 pjmedia_codec *codec = NULL;
453 struct l16_data *data;
454 unsigned ptime;
455 pj_pool_t *pool;
456
457 pj_status_t status;
458
459 PJ_ASSERT_RETURN(factory==&l16_factory.base, PJ_EINVAL);
460
461 /* Lock mutex. */
462 pj_mutex_lock(l16_factory.mutex);
463
464
465 pool = pjmedia_endpt_create_pool(l16_factory.endpt, "l16", 4000, 4000);
466 codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
467 codec->codec_data = pj_pool_alloc(pool, sizeof(struct l16_data));
468 codec->factory = factory;
469 codec->op = &l16_op;
470
471 /* Init private data */
472 ptime = GET_PTIME(id->clock_rate);
473 data = (struct l16_data*) codec->codec_data;
474 data->frame_size = ptime * id->clock_rate * id->channel_cnt * 2 / 1000;
475 data->clock_rate = id->clock_rate;
476 data->pool = pool;
477
478#if !PLC_DISABLED
479 /* Create PLC */
480 status = pjmedia_plc_create(pool, id->clock_rate,
481 data->frame_size >> 1, 0,
482 &data->plc);
483 if (status != PJ_SUCCESS) {
484 pj_mutex_unlock(l16_factory.mutex);
485 return status;
486 }
487#endif
488
489 /* Create silence detector */
490 status = pjmedia_silence_det_create(pool, id->clock_rate,
491 data->frame_size >> 1,
492 &data->vad);
493 if (status != PJ_SUCCESS) {
494 pj_mutex_unlock(l16_factory.mutex);
495 return status;
496 }
497
498 *p_codec = codec;
499
500 /* Unlock mutex. */
501 pj_mutex_unlock(l16_factory.mutex);
502
503 return PJ_SUCCESS;
504}
505
506static pj_status_t l16_dealloc_codec(pjmedia_codec_factory *factory,
507 pjmedia_codec *codec )
508{
509 struct l16_data *data;
510
511 PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
512 PJ_ASSERT_RETURN(factory==&l16_factory.base, PJ_EINVAL);
513
514 /* Lock mutex. */
515 pj_mutex_lock(l16_factory.mutex);
516
517 /* Just release codec data pool */
518 data = (struct l16_data*) codec->codec_data;
519 pj_assert(data);
520 pj_pool_release(data->pool);
521
522 /* Unlock mutex. */
523 pj_mutex_unlock(l16_factory.mutex);
524
525 return PJ_SUCCESS;
526}
527
528static pj_status_t l16_init( pjmedia_codec *codec, pj_pool_t *pool )
529{
530 /* There's nothing to do here really */
531 PJ_UNUSED_ARG(codec);
532 PJ_UNUSED_ARG(pool);
533
534 return PJ_SUCCESS;
535}
536
537static pj_status_t l16_open(pjmedia_codec *codec,
538 pjmedia_codec_param *attr )
539{
540 struct l16_data *data = NULL;
541
542 PJ_ASSERT_RETURN(codec && codec->codec_data && attr, PJ_EINVAL);
543
544 data = (struct l16_data*) codec->codec_data;
545
546 data->vad_enabled = (attr->setting.vad != 0);
547#if !PLC_DISABLED
548 data->plc_enabled = (attr->setting.plc != 0);
549#endif
550
551 return PJ_SUCCESS;
552}
553
554static pj_status_t l16_close( pjmedia_codec *codec )
555{
556 PJ_UNUSED_ARG(codec);
557 /* Nothing to do */
558 return PJ_SUCCESS;
559}
560
561static pj_status_t l16_modify(pjmedia_codec *codec,
562 const pjmedia_codec_param *attr )
563{
564 struct l16_data *data = (struct l16_data*) codec->codec_data;
565
566 pj_assert(data != NULL);
567
568 data->vad_enabled = (attr->setting.vad != 0);
569#if !PLC_DISABLED
570 data->plc_enabled = (attr->setting.plc != 0);
571#endif
572
573 return PJ_SUCCESS;
574}
575
576static pj_status_t l16_parse( pjmedia_codec *codec,
577 void *pkt,
578 pj_size_t pkt_size,
579 const pj_timestamp *ts,
580 unsigned *frame_cnt,
581 pjmedia_frame frames[])
582{
583 unsigned count = 0;
584 struct l16_data *data = (struct l16_data*) codec->codec_data;
585
586 PJ_UNUSED_ARG(codec);
587 PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
588
589 while (pkt_size >= data->frame_size && count < *frame_cnt) {
590 frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
591 frames[count].buf = pkt;
592 frames[count].size = data->frame_size;
593 frames[count].timestamp.u64 = ts->u64 + (count * data->frame_size);
594
595 pkt = ((char*)pkt) + data->frame_size;
596 pkt_size -= data->frame_size;
597
598 ++count;
599 }
600
601 *frame_cnt = count;
602 return PJ_SUCCESS;
603}
604
605static pj_status_t l16_encode(pjmedia_codec *codec,
606 const struct pjmedia_frame *input,
607 unsigned output_buf_len,
608 struct pjmedia_frame *output)
609{
610 struct l16_data *data = (struct l16_data*) codec->codec_data;
611 const pj_int16_t *samp = (const pj_int16_t*) input->buf;
612 const pj_int16_t *samp_end = samp + input->size/sizeof(pj_int16_t);
613 pj_int16_t *samp_out = (pj_int16_t*) output->buf;
614
615 pj_assert(data && input && output);
616
617 /* Check output buffer length */
618 if (output_buf_len < input->size)
619 return PJMEDIA_CODEC_EFRMTOOSHORT;
620
621 /* Detect silence */
622 if (data->vad_enabled) {
623 pj_bool_t is_silence;
624 pj_int32_t silence_duration;
625
626 silence_duration = pj_timestamp_diff32(&data->last_tx,
627 &input->timestamp);
628
629 is_silence = pjmedia_silence_det_detect(data->vad,
630 (const pj_int16_t*) input->buf,
631 (input->size >> 1),
632 NULL);
633 if (is_silence &&
634 (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
635 silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*
636 (int)data->clock_rate/1000))
637 {
638 output->type = PJMEDIA_FRAME_TYPE_NONE;
639 output->buf = NULL;
640 output->size = 0;
641 output->timestamp = input->timestamp;
642 return PJ_SUCCESS;
643 } else {
644 data->last_tx = input->timestamp;
645 }
646 }
647
648 /* Encode */
649#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
650 while (samp!=samp_end)
651 *samp_out++ = pj_htons(*samp++);
652#else
653 pjmedia_copy_samples(samp_out, samp, input->size >> 1);
654#endif
655
656
657 /* Done */
658 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
659 output->size = input->size;
660 output->timestamp = input->timestamp;
661
662 return PJ_SUCCESS;
663}
664
665static pj_status_t l16_decode(pjmedia_codec *codec,
666 const struct pjmedia_frame *input,
667 unsigned output_buf_len,
668 struct pjmedia_frame *output)
669{
670 struct l16_data *l16_data = (struct l16_data*) codec->codec_data;
671 const pj_int16_t *samp = (const pj_int16_t*) input->buf;
672 const pj_int16_t *samp_end = samp + input->size/sizeof(pj_int16_t);
673 pj_int16_t *samp_out = (pj_int16_t*) output->buf;
674
675 pj_assert(l16_data != NULL);
676 PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
677
678
679 /* Check output buffer length */
680 if (output_buf_len < input->size)
681 return PJMEDIA_CODEC_EPCMTOOSHORT;
682
683
684 /* Decode */
685#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
686 while (samp!=samp_end)
687 *samp_out++ = pj_ntohs(*samp++);
688#else
689 pjmedia_copy_samples(samp_out, samp, input->size >> 1);
690#endif
691
692
693 output->type = PJMEDIA_FRAME_TYPE_AUDIO;
694 output->size = input->size;
695 output->timestamp = input->timestamp;
696
697#if !PLC_DISABLED
698 if (l16_data->plc_enabled)
699 pjmedia_plc_save( l16_data->plc, (pj_int16_t*)output->buf);
700#endif
701
702 return PJ_SUCCESS;
703}
704
705#if !PLC_DISABLED
706/*
707 * Recover lost frame.
708 */
709static pj_status_t l16_recover(pjmedia_codec *codec,
710 unsigned output_buf_len,
711 struct pjmedia_frame *output)
712{
713 struct l16_data *data = (struct l16_data*) codec->codec_data;
714
715 PJ_ASSERT_RETURN(data->plc_enabled, PJ_EINVALIDOP);
716
717 PJ_ASSERT_RETURN(output_buf_len >= data->frame_size,
718 PJMEDIA_CODEC_EPCMTOOSHORT);
719
720 pjmedia_plc_generate(data->plc, (pj_int16_t*)output->buf);
721 output->size = data->frame_size;
722
723 return PJ_SUCCESS;
724}
725#endif
726
727#endif /* PJMEDIA_HAS_L16_CODEC */
728
729