Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjmedia / src / pjmedia-codec / opencore_amrnb.c
1 /* $Id$ */
2 /* 
3  * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2011 Dan Arrhenius <dan@keystream.se>
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
21 /* 
22  * AMR-NB codec implementation with OpenCORE AMRNB library
23  */
24 #include <pjmedia-codec/g722.h>
25 #include <pjmedia-codec/amr_sdp_match.h>
26 #include <pjmedia/codec.h>
27 #include <pjmedia/errno.h>
28 #include <pjmedia/endpoint.h>
29 #include <pjmedia/plc.h>
30 #include <pjmedia/port.h>
31 #include <pjmedia/silencedet.h>
32 #include <pj/assert.h>
33 #include <pj/log.h>
34 #include <pj/pool.h>
35 #include <pj/string.h>
36 #include <pj/os.h>
37 #include <pj/math.h>
38
39 #if defined(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC) && \
40     (PJMEDIA_HAS_OPENCORE_AMRNB_CODEC != 0)
41
42 #include <opencore-amrnb/interf_enc.h>
43 #include <opencore-amrnb/interf_dec.h>
44 #include <pjmedia-codec/amr_helper.h>
45 #include <pjmedia-codec/opencore_amrnb.h>
46
47 #define THIS_FILE "opencore_amrnb.c"
48
49 /* Tracing */
50 #define PJ_TRACE    0
51
52 #if PJ_TRACE
53 #   define TRACE_(expr) PJ_LOG(4,expr)
54 #else
55 #   define TRACE_(expr)
56 #endif
57
58 /* Use PJMEDIA PLC */
59 #define USE_PJMEDIA_PLC     1
60
61
62
63 /* Prototypes for AMR-NB factory */
64 static pj_status_t amr_test_alloc(pjmedia_codec_factory *factory, 
65                                    const pjmedia_codec_info *id );
66 static pj_status_t amr_default_attr(pjmedia_codec_factory *factory, 
67                                      const pjmedia_codec_info *id, 
68                                      pjmedia_codec_param *attr );
69 static pj_status_t amr_enum_codecs(pjmedia_codec_factory *factory, 
70                                     unsigned *count, 
71                                     pjmedia_codec_info codecs[]);
72 static pj_status_t amr_alloc_codec(pjmedia_codec_factory *factory, 
73                                     const pjmedia_codec_info *id, 
74                                     pjmedia_codec **p_codec);
75 static pj_status_t amr_dealloc_codec(pjmedia_codec_factory *factory, 
76                                       pjmedia_codec *codec );
77
78 /* Prototypes for AMR-NB implementation. */
79 static pj_status_t  amr_codec_init(pjmedia_codec *codec, 
80                                     pj_pool_t *pool );
81 static pj_status_t  amr_codec_open(pjmedia_codec *codec, 
82                                     pjmedia_codec_param *attr );
83 static pj_status_t  amr_codec_close(pjmedia_codec *codec );
84 static pj_status_t  amr_codec_modify(pjmedia_codec *codec, 
85                                       const pjmedia_codec_param *attr );
86 static pj_status_t  amr_codec_parse(pjmedia_codec *codec,
87                                      void *pkt,
88                                      pj_size_t pkt_size,
89                                      const pj_timestamp *ts,
90                                      unsigned *frame_cnt,
91                                      pjmedia_frame frames[]);
92 static pj_status_t  amr_codec_encode(pjmedia_codec *codec, 
93                                       const struct pjmedia_frame *input,
94                                       unsigned output_buf_len, 
95                                       struct pjmedia_frame *output);
96 static pj_status_t  amr_codec_decode(pjmedia_codec *codec, 
97                                       const struct pjmedia_frame *input,
98                                       unsigned output_buf_len, 
99                                       struct pjmedia_frame *output);
100 static pj_status_t  amr_codec_recover(pjmedia_codec *codec,
101                                       unsigned output_buf_len,
102                                       struct pjmedia_frame *output);
103
104
105
106 /* Definition for AMR-NB codec operations. */
107 static pjmedia_codec_op amr_op = 
108 {
109     &amr_codec_init,
110     &amr_codec_open,
111     &amr_codec_close,
112     &amr_codec_modify,
113     &amr_codec_parse,
114     &amr_codec_encode,
115     &amr_codec_decode,
116     &amr_codec_recover
117 };
118
119 /* Definition for AMR-NB codec factory operations. */
120 static pjmedia_codec_factory_op amr_factory_op =
121 {
122     &amr_test_alloc,
123     &amr_default_attr,
124     &amr_enum_codecs,
125     &amr_alloc_codec,
126     &amr_dealloc_codec,
127     &pjmedia_codec_opencore_amrnb_deinit
128 };
129
130
131 /* AMR-NB factory */
132 static struct amr_codec_factory
133 {
134     pjmedia_codec_factory    base;
135     pjmedia_endpt           *endpt;
136     pj_pool_t               *pool;
137 } amr_codec_factory;
138
139
140 /* AMR-NB codec private data. */
141 struct amr_data
142 {
143     pj_pool_t           *pool;
144     void                *encoder;
145     void                *decoder;
146     pj_bool_t            plc_enabled;
147     pj_bool_t            vad_enabled;
148     int                  enc_mode;
149     pjmedia_codec_amr_pack_setting enc_setting;
150     pjmedia_codec_amr_pack_setting dec_setting;
151 #if USE_PJMEDIA_PLC
152     pjmedia_plc         *plc;
153 #endif
154     pj_timestamp         last_tx;
155 };
156
157 static pjmedia_codec_amrnb_config def_config =
158 {
159     PJ_FALSE,       /* octet align      */
160     5900            /* bitrate          */
161 };
162
163
164
165 /*
166  * Initialize and register AMR-NB codec factory to pjmedia endpoint.
167  */
168 PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_init( pjmedia_endpt *endpt )
169 {
170     pjmedia_codec_mgr *codec_mgr;
171     pj_str_t codec_name;
172     pj_status_t status;
173
174     if (amr_codec_factory.pool != NULL)
175         return PJ_SUCCESS;
176
177     /* Create AMR-NB codec factory. */
178     amr_codec_factory.base.op = &amr_factory_op;
179     amr_codec_factory.base.factory_data = NULL;
180     amr_codec_factory.endpt = endpt;
181
182     amr_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "amrnb", 1000, 
183                                                        1000);
184     if (!amr_codec_factory.pool)
185         return PJ_ENOMEM;
186
187     /* Get the codec manager. */
188     codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
189     if (!codec_mgr) {
190         status = PJ_EINVALIDOP;
191         goto on_error;
192     }
193
194     /* Register format match callback. */
195     pj_cstr(&codec_name, "AMR");
196     status = pjmedia_sdp_neg_register_fmt_match_cb(
197                                         &codec_name,
198                                         &pjmedia_codec_amr_match_sdp);
199     if (status != PJ_SUCCESS)
200         goto on_error;
201
202     /* Register codec factory to endpoint. */
203     status = pjmedia_codec_mgr_register_factory(codec_mgr, 
204                                                 &amr_codec_factory.base);
205     if (status != PJ_SUCCESS)
206         goto on_error;
207
208     /* Done. */
209     return PJ_SUCCESS;
210
211 on_error:
212     pj_pool_release(amr_codec_factory.pool);
213     amr_codec_factory.pool = NULL;
214     return status;
215 }
216
217
218 /*
219  * Unregister AMR-NB codec factory from pjmedia endpoint and deinitialize
220  * the AMR-NB codec library.
221  */
222 PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void)
223 {
224     pjmedia_codec_mgr *codec_mgr;
225     pj_status_t status;
226
227     if (amr_codec_factory.pool == NULL)
228         return PJ_SUCCESS;
229
230     /* Get the codec manager. */
231     codec_mgr = pjmedia_endpt_get_codec_mgr(amr_codec_factory.endpt);
232     if (!codec_mgr) {
233         pj_pool_release(amr_codec_factory.pool);
234         amr_codec_factory.pool = NULL;
235         return PJ_EINVALIDOP;
236     }
237
238     /* Unregister AMR-NB codec factory. */
239     status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
240                                                   &amr_codec_factory.base);
241     
242     /* Destroy pool. */
243     pj_pool_release(amr_codec_factory.pool);
244     amr_codec_factory.pool = NULL;
245     
246     return status;
247 }
248
249
250 PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_set_config(
251                             const pjmedia_codec_amrnb_config *config)
252 {
253     unsigned nbitrates;
254
255
256     def_config = *config;
257
258     /* Normalize bitrate. */
259     nbitrates = PJ_ARRAY_SIZE(pjmedia_codec_amrnb_bitrates);
260     if (def_config.bitrate < pjmedia_codec_amrnb_bitrates[0])
261         def_config.bitrate = pjmedia_codec_amrnb_bitrates[0];
262     else if (def_config.bitrate > pjmedia_codec_amrnb_bitrates[nbitrates-1])
263         def_config.bitrate = pjmedia_codec_amrnb_bitrates[nbitrates-1];
264     else
265     {
266         unsigned i;
267         
268         for (i = 0; i < nbitrates; ++i) {
269             if (def_config.bitrate <= pjmedia_codec_amrnb_bitrates[i])
270                 break;
271         }
272         def_config.bitrate = pjmedia_codec_amrnb_bitrates[i];
273     }
274
275     return PJ_SUCCESS;
276 }
277
278 /* 
279  * Check if factory can allocate the specified codec. 
280  */
281 static pj_status_t amr_test_alloc( pjmedia_codec_factory *factory, 
282                                    const pjmedia_codec_info *info )
283 {
284     PJ_UNUSED_ARG(factory);
285
286     /* Check payload type. */
287     if (info->pt != PJMEDIA_RTP_PT_AMR)
288         return PJMEDIA_CODEC_EUNSUP;
289
290     /* Ignore the rest, since it's static payload type. */
291
292     return PJ_SUCCESS;
293 }
294
295 /*
296  * Generate default attribute.
297  */
298 static pj_status_t amr_default_attr( pjmedia_codec_factory *factory, 
299                                      const pjmedia_codec_info *id, 
300                                      pjmedia_codec_param *attr )
301 {
302     PJ_UNUSED_ARG(factory);
303     PJ_UNUSED_ARG(id);
304
305     pj_bzero(attr, sizeof(pjmedia_codec_param));
306     attr->info.clock_rate = 8000;
307     attr->info.channel_cnt = 1;
308     attr->info.avg_bps = def_config.bitrate;
309     attr->info.max_bps = pjmedia_codec_amrnb_bitrates[7];
310     attr->info.pcm_bits_per_sample = 16;
311     attr->info.frm_ptime = 20;
312     attr->info.pt = PJMEDIA_RTP_PT_AMR;
313
314     attr->setting.frm_per_pkt = 2;
315     attr->setting.vad = 1;
316     attr->setting.plc = 1;
317
318     if (def_config.octet_align) {
319         attr->setting.dec_fmtp.cnt = 1;
320         attr->setting.dec_fmtp.param[0].name = pj_str("octet-align");
321         attr->setting.dec_fmtp.param[0].val = pj_str("1");
322     }
323
324     /* Default all other flag bits disabled. */
325
326     return PJ_SUCCESS;
327 }
328
329
330 /*
331  * Enum codecs supported by this factory (i.e. only AMR-NB!).
332  */
333 static pj_status_t amr_enum_codecs( pjmedia_codec_factory *factory, 
334                                     unsigned *count, 
335                                     pjmedia_codec_info codecs[])
336 {
337     PJ_UNUSED_ARG(factory);
338     PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
339
340     pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
341     codecs[0].encoding_name = pj_str("AMR");
342     codecs[0].pt = PJMEDIA_RTP_PT_AMR;
343     codecs[0].type = PJMEDIA_TYPE_AUDIO;
344     codecs[0].clock_rate = 8000;
345     codecs[0].channel_cnt = 1;
346
347     *count = 1;
348
349     return PJ_SUCCESS;
350 }
351
352
353 /*
354  * Allocate a new AMR-NB codec instance.
355  */
356 static pj_status_t amr_alloc_codec( pjmedia_codec_factory *factory, 
357                                     const pjmedia_codec_info *id,
358                                     pjmedia_codec **p_codec)
359 {
360     pj_pool_t *pool;
361     pjmedia_codec *codec;
362     struct amr_data *amr_data;
363     pj_status_t status;
364
365     PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
366     PJ_ASSERT_RETURN(factory == &amr_codec_factory.base, PJ_EINVAL);
367
368     pool = pjmedia_endpt_create_pool(amr_codec_factory.endpt, "amrnb-inst", 
369                                      512, 512);
370
371     codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
372     PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
373     codec->op = &amr_op;
374     codec->factory = factory;
375
376     amr_data = PJ_POOL_ZALLOC_T(pool, struct amr_data);
377     codec->codec_data = amr_data;
378     amr_data->pool = pool;
379
380 #if USE_PJMEDIA_PLC
381     /* Create PLC */
382     status = pjmedia_plc_create(pool, 8000, 160, 0, &amr_data->plc);
383     if (status != PJ_SUCCESS) {
384         return status;
385     }
386 #else
387     PJ_UNUSED_ARG(status);
388 #endif
389     *p_codec = codec;
390     return PJ_SUCCESS;
391 }
392
393
394 /*
395  * Free codec.
396  */
397 static pj_status_t amr_dealloc_codec( pjmedia_codec_factory *factory, 
398                                       pjmedia_codec *codec )
399 {
400     struct amr_data *amr_data;
401
402     PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
403     PJ_ASSERT_RETURN(factory == &amr_codec_factory.base, PJ_EINVAL);
404
405     amr_data = (struct amr_data*) codec->codec_data;
406
407     /* Close codec, if it's not closed. */
408     amr_codec_close(codec);
409
410     pj_pool_release(amr_data->pool);
411     amr_data = NULL;
412
413     return PJ_SUCCESS;
414 }
415
416 /*
417  * Init codec.
418  */
419 static pj_status_t amr_codec_init( pjmedia_codec *codec, 
420                                    pj_pool_t *pool )
421 {
422     PJ_UNUSED_ARG(codec);
423     PJ_UNUSED_ARG(pool);
424     return PJ_SUCCESS;
425 }
426
427
428 /*
429  * Open codec.
430  */
431 static pj_status_t amr_codec_open( pjmedia_codec *codec, 
432                                    pjmedia_codec_param *attr )
433 {
434     struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
435     pjmedia_codec_amr_pack_setting *setting;
436     unsigned i;
437     pj_uint8_t octet_align = 0;
438     pj_int8_t enc_mode;
439     const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11};
440
441     PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
442     PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP);
443
444     enc_mode = pjmedia_codec_amr_get_mode(attr->info.avg_bps);
445     pj_assert(enc_mode >= 0 && enc_mode <= 7);
446
447     /* Check octet-align */
448     for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
449         if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name, 
450                        &STR_FMTP_OCTET_ALIGN) == 0)
451         {
452             octet_align = (pj_uint8_t)
453                           (pj_strtoul(&attr->setting.dec_fmtp.param[i].val));
454             break;
455         }
456     }
457
458     /* Check mode-set */
459     for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
460         const pj_str_t STR_FMTP_MODE_SET = {"mode-set", 8};
461         
462         if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, 
463                        &STR_FMTP_MODE_SET) == 0)
464         {
465             const char *p;
466             pj_size_t l;
467             pj_int8_t diff = 99;
468
469             /* Encoding mode is chosen based on local default mode setting:
470              * - if local default mode is included in the mode-set, use it
471              * - otherwise, find the closest mode to local default mode;
472              *   if there are two closest modes, prefer to use the higher
473              *   one, e.g: local default mode is 4, the mode-set param
474              *   contains '2,3,5,6', then 5 will be chosen.
475              */
476             p = pj_strbuf(&attr->setting.enc_fmtp.param[i].val);
477             l = pj_strlen(&attr->setting.enc_fmtp.param[i].val);
478             while (l--) {
479                 if (*p>='0' && *p<='7') {
480                     pj_int8_t tmp = *p - '0' - enc_mode;
481
482                     if (PJ_ABS(diff) > PJ_ABS(tmp) || 
483                         (PJ_ABS(diff) == PJ_ABS(tmp) && tmp > diff))
484                     {
485                         diff = tmp;
486                         if (diff == 0) break;
487                     }
488                 }
489                 ++p;
490             }
491             PJ_ASSERT_RETURN(diff != 99, PJMEDIA_CODEC_EFAILED);
492
493             enc_mode = enc_mode + diff;
494
495             break;
496         }
497     }
498
499     amr_data->vad_enabled = (attr->setting.vad != 0);
500     amr_data->plc_enabled = (attr->setting.plc != 0);
501     amr_data->enc_mode = enc_mode;
502
503     amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
504     if (amr_data->encoder == NULL) {
505         TRACE_((THIS_FILE, "Encoder_Interface_init() failed"));
506         amr_codec_close(codec);
507         return PJMEDIA_CODEC_EFAILED;
508     }
509     setting = &amr_data->enc_setting;
510     pj_bzero(setting, sizeof(pjmedia_codec_amr_pack_setting));
511     setting->amr_nb = 1;
512     setting->reorder = 0;
513     setting->octet_aligned = octet_align;
514     setting->cmr = 15;
515
516     amr_data->decoder = Decoder_Interface_init();
517     if (amr_data->decoder == NULL) {
518         TRACE_((THIS_FILE, "Decoder_Interface_init() failed"));
519         amr_codec_close(codec);
520         return PJMEDIA_CODEC_EFAILED;
521     }
522     setting = &amr_data->dec_setting;
523     pj_bzero(setting, sizeof(pjmedia_codec_amr_pack_setting));
524     setting->amr_nb = 1;
525     setting->reorder = 0;
526     setting->octet_aligned = octet_align;
527
528     TRACE_((THIS_FILE, "AMR-NB codec allocated: vad=%d, plc=%d, bitrate=%d",
529                         amr_data->vad_enabled, amr_data->plc_enabled, 
530                         pjmedia_codec_amrnb_bitrates[amr_data->enc_mode]));
531     return PJ_SUCCESS;
532 }
533
534
535 /*
536  * Close codec.
537  */
538 static pj_status_t amr_codec_close( pjmedia_codec *codec )
539 {
540     struct amr_data *amr_data;
541
542     PJ_ASSERT_RETURN(codec, PJ_EINVAL);
543
544     amr_data = (struct amr_data*) codec->codec_data;
545     PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP);
546
547     if (amr_data->encoder) {
548         Encoder_Interface_exit(amr_data->encoder);
549         amr_data->encoder = NULL;
550     }
551
552     if (amr_data->decoder) {
553         Decoder_Interface_exit(amr_data->decoder);
554         amr_data->decoder = NULL;
555     }
556     
557     TRACE_((THIS_FILE, "AMR-NB codec closed"));
558     return PJ_SUCCESS;
559 }
560
561
562 /*
563  * Modify codec settings.
564  */
565 static pj_status_t amr_codec_modify( pjmedia_codec *codec, 
566                                      const pjmedia_codec_param *attr )
567 {
568     struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
569     pj_bool_t prev_vad_state;
570
571     pj_assert(amr_data != NULL);
572     pj_assert(amr_data->encoder != NULL && amr_data->decoder != NULL);
573
574     prev_vad_state = amr_data->vad_enabled;
575     amr_data->vad_enabled = (attr->setting.vad != 0);
576     amr_data->plc_enabled = (attr->setting.plc != 0);
577
578     if (prev_vad_state != amr_data->vad_enabled) {
579         /* Reinit AMR encoder to update VAD setting */
580         TRACE_((THIS_FILE, "Reiniting AMR encoder to update VAD setting."));
581         Encoder_Interface_exit(amr_data->encoder);
582         amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
583         if (amr_data->encoder == NULL) {
584             TRACE_((THIS_FILE, "Encoder_Interface_init() failed"));
585             amr_codec_close(codec);
586             return PJMEDIA_CODEC_EFAILED;
587         }
588     }
589
590     TRACE_((THIS_FILE, "AMR-NB codec modified: vad=%d, plc=%d",
591                         amr_data->vad_enabled, amr_data->plc_enabled));
592     return PJ_SUCCESS;
593 }
594
595
596 /*
597  * Get frames in the packet.
598  */
599 static pj_status_t amr_codec_parse( pjmedia_codec *codec,
600                                     void *pkt,
601                                     pj_size_t pkt_size,
602                                     const pj_timestamp *ts,
603                                     unsigned *frame_cnt,
604                                     pjmedia_frame frames[])
605 {
606     struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
607     pj_uint8_t cmr;
608     pj_status_t status;
609
610     status = pjmedia_codec_amr_parse(pkt, pkt_size, ts, &amr_data->dec_setting,
611                                      frames, frame_cnt, &cmr);
612     if (status != PJ_SUCCESS)
613         return status;
614
615     /* Check for Change Mode Request. */
616     if (cmr <= 7 && amr_data->enc_mode != cmr) {
617         amr_data->enc_mode = cmr;
618         TRACE_((THIS_FILE, "AMR-NB encoder switched mode to %d (%dbps)",
619                             amr_data->enc_mode, 
620                             pjmedia_codec_amrnb_bitrates[amr_data->enc_mode]));
621     }
622
623     return PJ_SUCCESS;
624 }
625
626
627 /*
628  * Encode frame.
629  */
630 static pj_status_t amr_codec_encode( pjmedia_codec *codec, 
631                                      const struct pjmedia_frame *input,
632                                      unsigned output_buf_len, 
633                                      struct pjmedia_frame *output)
634 {
635     struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
636     unsigned char *bitstream;
637     pj_int16_t *speech;
638     unsigned nsamples, samples_per_frame;
639     enum {MAX_FRAMES_PER_PACKET = 16};
640     pjmedia_frame frames[MAX_FRAMES_PER_PACKET];
641     pj_uint8_t *p;
642     unsigned i, out_size = 0, nframes = 0;
643     pj_size_t payload_len;
644     unsigned dtx_cnt, sid_cnt;
645     pj_status_t status;
646     int size;
647
648     pj_assert(amr_data != NULL);
649     PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
650
651     nsamples = input->size >> 1;
652     samples_per_frame = 160;
653     PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0, 
654                      PJMEDIA_CODEC_EPCMFRMINLEN);
655
656     nframes = nsamples / samples_per_frame;
657     PJ_ASSERT_RETURN(nframes <= MAX_FRAMES_PER_PACKET, 
658                      PJMEDIA_CODEC_EFRMTOOSHORT);
659
660     /* Encode the frames */
661     speech = (pj_int16_t*)input->buf;
662     bitstream = (unsigned char*)output->buf;
663     while (nsamples >= samples_per_frame) {
664         size = Encoder_Interface_Encode (amr_data->encoder, amr_data->enc_mode,
665                                          speech, bitstream, 0);
666         if (size == 0) {
667             output->size = 0;
668             output->buf = NULL;
669             output->type = PJMEDIA_FRAME_TYPE_NONE;
670             TRACE_((THIS_FILE, "AMR-NB encode() failed"));
671             return PJMEDIA_CODEC_EFAILED;
672         }
673         nsamples -= 160;
674         speech += samples_per_frame;
675         bitstream += size;
676         out_size += size;
677         TRACE_((THIS_FILE, "AMR-NB encode(): mode=%d, size=%d",
678                 amr_data->enc_mode, out_size));
679     }
680
681     /* Pack payload */
682     p = (pj_uint8_t*)output->buf + output_buf_len - out_size;
683     pj_memmove(p, output->buf, out_size);
684     dtx_cnt = sid_cnt = 0;
685     for (i = 0; i < nframes; ++i) {
686         pjmedia_codec_amr_bit_info *info = (pjmedia_codec_amr_bit_info*)
687                                            &frames[i].bit_info;
688         info->frame_type = (pj_uint8_t)((*p >> 3) & 0x0F);
689         info->good_quality = (pj_uint8_t)((*p >> 2) & 0x01);
690         info->mode = (pj_int8_t)amr_data->enc_mode;
691         info->start_bit = 0;
692         frames[i].buf = p + 1;
693         frames[i].size = (info->frame_type <= 8)? 
694                          pjmedia_codec_amrnb_framelen[info->frame_type] : 0;
695         p += frames[i].size + 1;
696
697         /* Count the number of SID and DTX frames */
698         if (info->frame_type == 15) /* DTX*/
699             ++dtx_cnt;
700         else if (info->frame_type == 8) /* SID */
701             ++sid_cnt;
702     }
703
704     /* VA generates DTX frames as DTX+SID frames switching quickly and it
705      * seems that the SID frames occur too often (assuming the purpose is 
706      * only for keeping NAT alive?). So let's modify the behavior a bit.
707      * Only an SID frame will be sent every PJMEDIA_CODEC_MAX_SILENCE_PERIOD
708      * milliseconds.
709      */
710     if (sid_cnt + dtx_cnt == nframes) {
711         pj_int32_t dtx_duration;
712
713         dtx_duration = pj_timestamp_diff32(&amr_data->last_tx, 
714                                            &input->timestamp);
715         if (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
716             dtx_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000) 
717         {
718             output->size = 0;
719             output->type = PJMEDIA_FRAME_TYPE_NONE;
720             output->timestamp = input->timestamp;
721             return PJ_SUCCESS;
722         }
723     }
724
725     payload_len = output_buf_len;
726
727     status = pjmedia_codec_amr_pack(frames, nframes, &amr_data->enc_setting,
728                                     output->buf, &payload_len);
729     if (status != PJ_SUCCESS) {
730         output->size = 0;
731         output->buf = NULL;
732         output->type = PJMEDIA_FRAME_TYPE_NONE;
733         TRACE_((THIS_FILE, "Failed to pack AMR payload, status=%d", status));
734         return status;
735     }
736
737     output->size = payload_len;
738     output->type = PJMEDIA_FRAME_TYPE_AUDIO;
739     output->timestamp = input->timestamp;
740
741     amr_data->last_tx = input->timestamp;
742
743     return PJ_SUCCESS;
744 }
745
746
747 /*
748  * Decode frame.
749  */
750 static pj_status_t amr_codec_decode( pjmedia_codec *codec, 
751                                      const struct pjmedia_frame *input,
752                                      unsigned output_buf_len, 
753                                      struct pjmedia_frame *output)
754 {
755     struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
756     pjmedia_frame input_;
757     pjmedia_codec_amr_bit_info *info;
758     /* VA AMR-NB decoding buffer: AMR-NB max frame size + 1 byte header. */
759     unsigned char bitstream[32];
760
761     pj_assert(amr_data != NULL);
762     PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
763
764     if (output_buf_len < 320)
765         return PJMEDIA_CODEC_EPCMTOOSHORT;
766
767     input_.buf = &bitstream[1];
768     input_.size = 31; /* AMR-NB max frame size */
769     pjmedia_codec_amr_predecode(input, &amr_data->dec_setting, &input_);
770     info = (pjmedia_codec_amr_bit_info*)&input_.bit_info;
771
772     /* VA AMRNB decoder requires frame info in the first byte. */
773     bitstream[0] = (info->frame_type << 3) | (info->good_quality << 2);
774
775     TRACE_((THIS_FILE, "AMR-NB decode(): mode=%d, ft=%d, size=%d",
776             info->mode, info->frame_type, input_.size));
777
778     /* Decode */
779     Decoder_Interface_Decode(amr_data->decoder, bitstream,
780                              (pj_int16_t*)output->buf, 0);
781
782     output->size = 320;
783     output->type = PJMEDIA_FRAME_TYPE_AUDIO;
784     output->timestamp = input->timestamp;
785
786 #if USE_PJMEDIA_PLC
787     if (amr_data->plc_enabled)
788         pjmedia_plc_save(amr_data->plc, (pj_int16_t*)output->buf);
789 #endif
790
791     return PJ_SUCCESS;
792 }
793
794
795 /*
796  * Recover lost frame.
797  */
798 #if USE_PJMEDIA_PLC
799 /*
800  * Recover lost frame.
801  */
802 static pj_status_t  amr_codec_recover( pjmedia_codec *codec,
803                                        unsigned output_buf_len,
804                                        struct pjmedia_frame *output)
805 {
806     struct amr_data *amr_data = codec->codec_data;
807
808     TRACE_((THIS_FILE, "amr_codec_recover"));
809
810     PJ_ASSERT_RETURN(amr_data->plc_enabled, PJ_EINVALIDOP);
811
812     PJ_ASSERT_RETURN(output_buf_len >= 320,  PJMEDIA_CODEC_EPCMTOOSHORT);
813
814     pjmedia_plc_generate(amr_data->plc, (pj_int16_t*)output->buf);
815
816     output->size = 320;
817     output->type = PJMEDIA_FRAME_TYPE_AUDIO;
818     
819     return PJ_SUCCESS;
820 }
821 #endif
822
823 #if defined(_MSC_VER) && PJMEDIA_AUTO_LINK_OPENCORE_AMR_LIBS
824 #  if PJMEDIA_OPENCORE_AMR_BUILT_WITH_GCC
825 #   pragma comment( lib, "libopencore-amrnb.a")
826 #  else
827 #   error Unsupported OpenCORE AMR library, fix here
828 #  endif
829 #endif
830
831 #endif