123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050 |
- /*
- * Copyright (C) 2011-2013 Teluu Inc. (http://www.teluu.com)
- * Copyright (C) 2011 Dan Arrhenius <dan@keystream.se>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- /*
- * AMR codec implementation with OpenCORE AMR library
- */
- #include <pjmedia-codec/g722.h>
- #include <pjmedia-codec/amr_sdp_match.h>
- #include <pjmedia/codec.h>
- #include <pjmedia/errno.h>
- #include <pjmedia/endpoint.h>
- #include <pjmedia/plc.h>
- #include <pjmedia/port.h>
- #include <pjmedia/silencedet.h>
- #include <pj/assert.h>
- #include <pj/log.h>
- #include <pj/pool.h>
- #include <pj/string.h>
- #include <pj/os.h>
- #include <pj/math.h>
- #if defined(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC) && \
- (PJMEDIA_HAS_OPENCORE_AMRNB_CODEC != 0)
- #define USE_AMRNB
- #endif
- #if defined(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC) && \
- (PJMEDIA_HAS_OPENCORE_AMRWB_CODEC != 0)
- #define USE_AMRWB
- #endif
- #if defined(USE_AMRNB) || defined(USE_AMRWB)
- #ifdef USE_AMRNB
- #include <opencore-amrnb/interf_enc.h>
- #include <opencore-amrnb/interf_dec.h>
- #endif
- #ifdef USE_AMRWB
- #include <vo-amrwbenc/enc_if.h>
- #include <opencore-amrwb/dec_if.h>
- #endif
- #include <pjmedia-codec/amr_helper.h>
- #include <pjmedia-codec/opencore_amr.h>
- #define THIS_FILE "opencore_amr.c"
- /* Tracing */
- #define PJ_TRACE 0
- #if PJ_TRACE
- # define TRACE_(expr) PJ_LOG(4,expr)
- #else
- # define TRACE_(expr)
- #endif
- /* Use PJMEDIA PLC */
- #define USE_PJMEDIA_PLC 1
- #define FRAME_LENGTH_MS 20
- /* Prototypes for AMR factory */
- static pj_status_t amr_test_alloc(pjmedia_codec_factory *factory,
- const pjmedia_codec_info *id );
- static pj_status_t amr_default_attr(pjmedia_codec_factory *factory,
- const pjmedia_codec_info *id,
- pjmedia_codec_param *attr );
- static pj_status_t amr_enum_codecs(pjmedia_codec_factory *factory,
- unsigned *count,
- pjmedia_codec_info codecs[]);
- static pj_status_t amr_alloc_codec(pjmedia_codec_factory *factory,
- const pjmedia_codec_info *id,
- pjmedia_codec **p_codec);
- static pj_status_t amr_dealloc_codec(pjmedia_codec_factory *factory,
- pjmedia_codec *codec );
- /* Prototypes for AMR implementation. */
- static pj_status_t amr_codec_init(pjmedia_codec *codec,
- pj_pool_t *pool );
- static pj_status_t amr_codec_open(pjmedia_codec *codec,
- pjmedia_codec_param *attr );
- static pj_status_t amr_codec_close(pjmedia_codec *codec );
- static pj_status_t amr_codec_modify(pjmedia_codec *codec,
- const pjmedia_codec_param *attr );
- static pj_status_t amr_codec_parse(pjmedia_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- const pj_timestamp *ts,
- unsigned *frame_cnt,
- pjmedia_frame frames[]);
- static pj_status_t amr_codec_encode(pjmedia_codec *codec,
- const struct pjmedia_frame *input,
- unsigned output_buf_len,
- struct pjmedia_frame *output);
- static pj_status_t amr_codec_decode(pjmedia_codec *codec,
- const struct pjmedia_frame *input,
- unsigned output_buf_len,
- struct pjmedia_frame *output);
- static pj_status_t amr_codec_recover(pjmedia_codec *codec,
- unsigned output_buf_len,
- struct pjmedia_frame *output);
- /* Definition for AMR codec operations. */
- static pjmedia_codec_op amr_op =
- {
- &amr_codec_init,
- &amr_codec_open,
- &amr_codec_close,
- &amr_codec_modify,
- &amr_codec_parse,
- &amr_codec_encode,
- &amr_codec_decode,
- &amr_codec_recover
- };
- /* Definition for AMR codec factory operations. */
- static pjmedia_codec_factory_op amr_factory_op =
- {
- &amr_test_alloc,
- &amr_default_attr,
- &amr_enum_codecs,
- &amr_alloc_codec,
- &amr_dealloc_codec,
- &pjmedia_codec_opencore_amr_deinit
- };
- /* AMR factory */
- static struct amr_codec_factory
- {
- pjmedia_codec_factory base;
- pjmedia_endpt *endpt;
- pj_pool_t *pool;
- pj_bool_t init[2];
- } amr_codec_factory;
- /* AMR codec private data. */
- struct amr_data
- {
- pj_pool_t *pool;
- unsigned clock_rate;
- void *encoder;
- void *decoder;
- pj_bool_t plc_enabled;
- pj_bool_t vad_enabled;
- int enc_mode;
- pjmedia_codec_amr_pack_setting enc_setting;
- pjmedia_codec_amr_pack_setting dec_setting;
- #if USE_PJMEDIA_PLC
- pjmedia_plc *plc;
- #endif
- pj_timestamp last_tx;
- };
- /* Index for AMR tables. */
- enum
- {
- IDX_AMR_NB, /* Index for narrowband. */
- IDX_AMR_WB /* Index for wideband. */
- };
- static pjmedia_codec_amr_config def_config[2] =
- {{ /* AMR-NB */
- PJ_FALSE, /* octet align */
- 5900 /* bitrate */
- },
- { /* AMR-WB */
- PJ_FALSE, /* octet align */
- 12650 /* bitrate */
- }};
- static const pj_uint16_t* amr_bitrates[2] =
- {pjmedia_codec_amrnb_bitrates, pjmedia_codec_amrwb_bitrates};
- static const unsigned amr_bitrates_size[2] =
- {
- PJ_ARRAY_SIZE(pjmedia_codec_amrnb_bitrates),
- PJ_ARRAY_SIZE(pjmedia_codec_amrwb_bitrates)
- };
- /*
- * Initialize and register AMR codec factory to pjmedia endpoint.
- */
- PJ_DEF(pj_status_t) pjmedia_codec_opencore_amr_init( pjmedia_endpt *endpt,
- unsigned options)
- {
- pjmedia_codec_mgr *codec_mgr;
- pj_str_t codec_name;
- pj_status_t status;
- if (amr_codec_factory.pool != NULL)
- return PJ_SUCCESS;
- /* Create AMR codec factory. */
- amr_codec_factory.base.op = &amr_factory_op;
- amr_codec_factory.base.factory_data = NULL;
- amr_codec_factory.endpt = endpt;
- #ifdef USE_AMRNB
- amr_codec_factory.init[IDX_AMR_NB] = ((options & PJMEDIA_AMR_NO_NB) == 0);
- #else
- amr_codec_factory.init[IDX_AMR_NB] = PJ_FALSE;
- #endif
- #ifdef USE_AMRWB
- amr_codec_factory.init[IDX_AMR_WB] = ((options & PJMEDIA_AMR_NO_WB) == 0);
- #else
- amr_codec_factory.init[IDX_AMR_WB] = PJ_FALSE;
- #endif
- amr_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "amr", 1000,
- 1000);
- if (!amr_codec_factory.pool)
- return PJ_ENOMEM;
- /* Get the codec manager. */
- codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
- if (!codec_mgr) {
- status = PJ_EINVALIDOP;
- goto on_error;
- }
- /* Register format match callback. */
- #ifdef USE_AMRNB
- if ((options & PJMEDIA_AMR_NO_NB) == 0) {
- pj_cstr(&codec_name, "AMR");
- status = pjmedia_sdp_neg_register_fmt_match_cb(
- &codec_name,
- &pjmedia_codec_amr_match_sdp);
- if (status != PJ_SUCCESS)
- goto on_error;
- }
- #endif
- #ifdef USE_AMRWB
- if ((options & PJMEDIA_AMR_NO_WB) == 0) {
- pj_cstr(&codec_name, "AMR-WB");
- status = pjmedia_sdp_neg_register_fmt_match_cb(
- &codec_name,
- &pjmedia_codec_amr_match_sdp);
- if (status != PJ_SUCCESS)
- goto on_error;
- }
- #endif
- /* Register codec factory to endpoint. */
- status = pjmedia_codec_mgr_register_factory(codec_mgr,
- &amr_codec_factory.base);
- if (status != PJ_SUCCESS)
- goto on_error;
- /* Done. */
- return PJ_SUCCESS;
- on_error:
- pj_pool_release(amr_codec_factory.pool);
- amr_codec_factory.pool = NULL;
- return status;
- }
- PJ_DEF(pj_status_t)
- pjmedia_codec_opencore_amr_init_default( pjmedia_endpt *endpt )
- {
- return pjmedia_codec_opencore_amr_init(endpt, 0);
- }
- PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_init( pjmedia_endpt *endpt )
- {
- return pjmedia_codec_opencore_amr_init(endpt, PJMEDIA_AMR_NO_WB);
- }
- /*
- * Unregister AMR codec factory from pjmedia endpoint and deinitialize
- * the AMR codec library.
- */
- PJ_DEF(pj_status_t) pjmedia_codec_opencore_amr_deinit(void)
- {
- pjmedia_codec_mgr *codec_mgr;
- pj_status_t status;
- amr_codec_factory.init[IDX_AMR_NB] = PJ_FALSE;
- amr_codec_factory.init[IDX_AMR_WB] = PJ_FALSE;
-
- if (amr_codec_factory.pool == NULL)
- return PJ_SUCCESS;
- /* Get the codec manager. */
- codec_mgr = pjmedia_endpt_get_codec_mgr(amr_codec_factory.endpt);
- if (!codec_mgr) {
- pj_pool_release(amr_codec_factory.pool);
- amr_codec_factory.pool = NULL;
- return PJ_EINVALIDOP;
- }
- /* Unregister AMR codec factory. */
- status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
- &amr_codec_factory.base);
-
- /* Destroy pool. */
- pj_pool_release(amr_codec_factory.pool);
- amr_codec_factory.pool = NULL;
-
- return status;
- }
- PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void)
- {
- if (amr_codec_factory.init[IDX_AMR_NB] &&
- amr_codec_factory.init[IDX_AMR_WB])
- {
- PJ_LOG(4, (THIS_FILE, "Should call "
- "pjmedia_codec_opencore_amr_deinit() instead"));
-
- return PJ_EINVALIDOP;
- }
-
- return pjmedia_codec_opencore_amr_deinit();
- }
- static pj_status_t
- amr_set_config(unsigned idx, const pjmedia_codec_amr_config *config)
- {
- unsigned nbitrates;
- def_config[idx] = *config;
- /* Normalize bitrate. */
- nbitrates = amr_bitrates_size[idx];
- if (def_config[idx].bitrate < amr_bitrates[idx][0]) {
- def_config[idx].bitrate = amr_bitrates[idx][0];
- } else if (def_config[idx].bitrate > amr_bitrates[idx][nbitrates-1]) {
- def_config[idx].bitrate = amr_bitrates[idx][nbitrates-1];
- } else
- {
- unsigned i;
-
- for (i = 0; i < nbitrates; ++i) {
- if (def_config[idx].bitrate <= amr_bitrates[idx][i])
- break;
- }
- def_config[idx].bitrate = amr_bitrates[idx][i];
- }
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_set_config(
- const pjmedia_codec_amrnb_config *config)
- {
- return amr_set_config(IDX_AMR_NB, (const pjmedia_codec_amr_config *)config);
- }
- PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrwb_set_config(
- const pjmedia_codec_amrwb_config *config)
- {
- return amr_set_config(IDX_AMR_WB, (const pjmedia_codec_amr_config *)config);
- }
- /*
- * Check if factory can allocate the specified codec.
- */
- static pj_status_t amr_test_alloc( pjmedia_codec_factory *factory,
- const pjmedia_codec_info *info )
- {
- const pj_str_t amr_tag = { "AMR", 3};
- const pj_str_t amrwb_tag = { "AMR-WB", 6};
- PJ_UNUSED_ARG(factory);
- /* Type MUST be audio. */
- if (info->type != PJMEDIA_TYPE_AUDIO)
- return PJMEDIA_CODEC_EUNSUP;
-
- /* Check payload type. */
- if (info->pt != PJMEDIA_RTP_PT_AMR && info->pt != PJMEDIA_RTP_PT_AMRWB)
- return PJMEDIA_CODEC_EUNSUP;
-
- /* Check encoding name. */
- if (pj_stricmp(&info->encoding_name, &amr_tag) != 0 &&
- pj_stricmp(&info->encoding_name, &amrwb_tag) != 0)
- {
- return PJMEDIA_CODEC_EUNSUP;
- }
-
- /* Check clock-rate */
- if ((info->clock_rate == 8000 && amr_codec_factory.init[IDX_AMR_NB]) ||
- (info->clock_rate == 16000 && amr_codec_factory.init[IDX_AMR_WB]))
- {
- return PJ_SUCCESS;
- }
- /* Unsupported or disabled. */
- return PJMEDIA_CODEC_EUNSUP;
- }
- /*
- * Generate default attribute.
- */
- static pj_status_t amr_default_attr( pjmedia_codec_factory *factory,
- const pjmedia_codec_info *id,
- pjmedia_codec_param *attr )
- {
- unsigned idx;
-
- PJ_UNUSED_ARG(factory);
- idx = (id->clock_rate <= 8000? IDX_AMR_NB: IDX_AMR_WB);
- pj_bzero(attr, sizeof(pjmedia_codec_param));
- attr->info.clock_rate = (id->clock_rate <= 8000? 8000: 16000);
- attr->info.channel_cnt = 1;
- attr->info.avg_bps = def_config[idx].bitrate;
- attr->info.max_bps = amr_bitrates[idx][amr_bitrates_size[idx]-1];
- attr->info.pcm_bits_per_sample = 16;
- attr->info.frm_ptime = 20;
- attr->info.pt = (pj_uint8_t)id->pt;
- attr->setting.frm_per_pkt = 1;
- attr->setting.vad = 1;
- attr->setting.plc = 1;
- if (def_config[idx].octet_align) {
- attr->setting.dec_fmtp.cnt = 1;
- attr->setting.dec_fmtp.param[0].name = pj_str("octet-align");
- attr->setting.dec_fmtp.param[0].val = pj_str("1");
- }
- /* Default all other flag bits disabled. */
- return PJ_SUCCESS;
- }
- /*
- * Enum codecs supported by this factory (i.e. AMR-NB and AMR-WB).
- */
- static pj_status_t amr_enum_codecs( pjmedia_codec_factory *factory,
- unsigned *count,
- pjmedia_codec_info codecs[])
- {
- PJ_UNUSED_ARG(factory);
- PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
- *count = 0;
- if (amr_codec_factory.init[IDX_AMR_NB]) {
- pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
- codecs[*count].encoding_name = pj_str("AMR");
- codecs[*count].pt = PJMEDIA_RTP_PT_AMR;
- codecs[*count].type = PJMEDIA_TYPE_AUDIO;
- codecs[*count].clock_rate = 8000;
- codecs[*count].channel_cnt = 1;
- (*count)++;
- }
-
- if (amr_codec_factory.init[IDX_AMR_WB]) {
- pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
- codecs[*count].encoding_name = pj_str("AMR-WB");
- codecs[*count].pt = PJMEDIA_RTP_PT_AMRWB;
- codecs[*count].type = PJMEDIA_TYPE_AUDIO;
- codecs[*count].clock_rate = 16000;
- codecs[*count].channel_cnt = 1;
- (*count)++;
- }
- return PJ_SUCCESS;
- }
- /*
- * Allocate a new AMR codec instance.
- */
- static pj_status_t amr_alloc_codec( pjmedia_codec_factory *factory,
- const pjmedia_codec_info *id,
- pjmedia_codec **p_codec)
- {
- pj_pool_t *pool;
- pjmedia_codec *codec;
- struct amr_data *amr_data;
- pj_status_t status;
- PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
- PJ_ASSERT_RETURN(factory == &amr_codec_factory.base, PJ_EINVAL);
- pool = pjmedia_endpt_create_pool(amr_codec_factory.endpt, "amr-inst",
- 512, 512);
- codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
- PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
- codec->op = &amr_op;
- codec->factory = factory;
- amr_data = PJ_POOL_ZALLOC_T(pool, struct amr_data);
- codec->codec_data = amr_data;
- amr_data->pool = pool;
- #if USE_PJMEDIA_PLC
- /* Create PLC */
- status = pjmedia_plc_create(pool, id->clock_rate,
- id->clock_rate * FRAME_LENGTH_MS / 1000, 0,
- &amr_data->plc);
- if (status != PJ_SUCCESS) {
- return status;
- }
- #else
- PJ_UNUSED_ARG(status);
- #endif
- *p_codec = codec;
- return PJ_SUCCESS;
- }
- /*
- * Free codec.
- */
- static pj_status_t amr_dealloc_codec( pjmedia_codec_factory *factory,
- pjmedia_codec *codec )
- {
- struct amr_data *amr_data;
- PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
- PJ_ASSERT_RETURN(factory == &amr_codec_factory.base, PJ_EINVAL);
- amr_data = (struct amr_data*) codec->codec_data;
- /* Close codec, if it's not closed. */
- amr_codec_close(codec);
- pj_pool_release(amr_data->pool);
- amr_data = NULL;
- return PJ_SUCCESS;
- }
- /*
- * Init codec.
- */
- static pj_status_t amr_codec_init( pjmedia_codec *codec,
- pj_pool_t *pool )
- {
- PJ_UNUSED_ARG(codec);
- PJ_UNUSED_ARG(pool);
- return PJ_SUCCESS;
- }
- /*
- * Open codec.
- */
- static pj_status_t amr_codec_open( pjmedia_codec *codec,
- pjmedia_codec_param *attr )
- {
- struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
- pjmedia_codec_amr_pack_setting *setting;
- unsigned i;
- pj_uint8_t octet_align = 0;
- pj_int8_t enc_mode;
- const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11};
- unsigned idx;
- PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
- PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP);
- idx = (attr->info.clock_rate <= 8000? IDX_AMR_NB: IDX_AMR_WB);
- enc_mode = pjmedia_codec_amr_get_mode(attr->info.avg_bps);
- pj_assert(enc_mode >= 0 && (unsigned)enc_mode < amr_bitrates_size[idx]);
- /* Check octet-align */
- for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
- if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name,
- &STR_FMTP_OCTET_ALIGN) == 0)
- {
- octet_align = (pj_uint8_t)
- (pj_strtoul(&attr->setting.dec_fmtp.param[i].val));
- break;
- }
- }
- /* Check mode-set */
- for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
- const pj_str_t STR_FMTP_MODE_SET = {"mode-set", 8};
-
- if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name,
- &STR_FMTP_MODE_SET) == 0)
- {
- const char *p;
- pj_size_t l;
- pj_int8_t diff = 99;
- /* Encoding mode is chosen based on local default mode setting:
- * - if local default mode is included in the mode-set, use it
- * - otherwise, find the closest mode to local default mode;
- * if there are two closest modes, prefer to use the higher
- * one, e.g: local default mode is 4, the mode-set param
- * contains '2,3,5,6', then 5 will be chosen.
- */
- p = pj_strbuf(&attr->setting.enc_fmtp.param[i].val);
- l = pj_strlen(&attr->setting.enc_fmtp.param[i].val);
- while (l--) {
- if (*p>='0' && (unsigned)*p<=('0'+amr_bitrates_size[idx]-1)) {
- pj_int8_t tmp = *p - '0' - enc_mode;
- if (PJ_ABS(diff) > PJ_ABS(tmp) ||
- (PJ_ABS(diff) == PJ_ABS(tmp) && tmp > diff))
- {
- diff = tmp;
- if (diff == 0) break;
- }
- }
- ++p;
- }
- PJ_ASSERT_RETURN(diff != 99, PJMEDIA_CODEC_EFAILED);
- enc_mode = enc_mode + diff;
- break;
- }
- }
- amr_data->clock_rate = attr->info.clock_rate;
- amr_data->vad_enabled = (attr->setting.vad != 0);
- amr_data->plc_enabled = (attr->setting.plc != 0);
- amr_data->enc_mode = enc_mode;
- if (idx == IDX_AMR_NB) {
- #ifdef USE_AMRNB
- amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
- #endif
- } else {
- #ifdef USE_AMRWB
- amr_data->encoder = E_IF_init();
- #endif
- }
- if (amr_data->encoder == NULL) {
- TRACE_((THIS_FILE, "Encoder initialization failed"));
- amr_codec_close(codec);
- return PJMEDIA_CODEC_EFAILED;
- }
- setting = &amr_data->enc_setting;
- pj_bzero(setting, sizeof(pjmedia_codec_amr_pack_setting));
- setting->amr_nb = (idx == IDX_AMR_NB? 1: 0);
- setting->reorder = 0;
- setting->octet_aligned = octet_align;
- setting->cmr = 15;
- if (idx == IDX_AMR_NB) {
- #ifdef USE_AMRNB
- amr_data->decoder = Decoder_Interface_init();
- #endif
- } else {
- #ifdef USE_AMRWB
- amr_data->decoder = D_IF_init();
- #endif
- }
- if (amr_data->decoder == NULL) {
- TRACE_((THIS_FILE, "Decoder initialization failed"));
- amr_codec_close(codec);
- return PJMEDIA_CODEC_EFAILED;
- }
- setting = &amr_data->dec_setting;
- pj_bzero(setting, sizeof(pjmedia_codec_amr_pack_setting));
- setting->amr_nb = (idx == IDX_AMR_NB? 1: 0);
- setting->reorder = 0;
- setting->octet_aligned = octet_align;
- TRACE_((THIS_FILE, "AMR codec allocated: clockrate=%d vad=%d, plc=%d,"
- " bitrate=%d", amr_data->clock_rate,
- amr_data->vad_enabled, amr_data->plc_enabled,
- amr_bitrates[idx][amr_data->enc_mode]));
- return PJ_SUCCESS;
- }
- /*
- * Close codec.
- */
- static pj_status_t amr_codec_close( pjmedia_codec *codec )
- {
- struct amr_data *amr_data;
- PJ_ASSERT_RETURN(codec, PJ_EINVAL);
- amr_data = (struct amr_data*) codec->codec_data;
- PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP);
- if (amr_data->encoder) {
- if (amr_data->enc_setting.amr_nb) {
- #ifdef USE_AMRNB
- Encoder_Interface_exit(amr_data->encoder);
- #endif
- } else {
- #ifdef USE_AMRWB
- E_IF_exit(amr_data->encoder);
- #endif
- }
- amr_data->encoder = NULL;
- }
- if (amr_data->decoder) {
- if (amr_data->dec_setting.amr_nb) {
- #ifdef USE_AMRNB
- Decoder_Interface_exit(amr_data->decoder);
- #endif
- } else {
- #ifdef USE_AMRWB
- D_IF_exit(amr_data->decoder);
- #endif
- }
- amr_data->decoder = NULL;
- }
-
- TRACE_((THIS_FILE, "AMR codec closed"));
- return PJ_SUCCESS;
- }
- /*
- * Modify codec settings.
- */
- static pj_status_t amr_codec_modify( pjmedia_codec *codec,
- const pjmedia_codec_param *attr )
- {
- struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
- pj_bool_t prev_vad_state;
- pj_assert(amr_data != NULL);
- pj_assert(amr_data->encoder != NULL && amr_data->decoder != NULL);
- prev_vad_state = amr_data->vad_enabled;
- amr_data->vad_enabled = (attr->setting.vad != 0);
- amr_data->plc_enabled = (attr->setting.plc != 0);
- if (amr_data->enc_setting.amr_nb &&
- prev_vad_state != amr_data->vad_enabled)
- {
- /* Reinit AMR encoder to update VAD setting */
- TRACE_((THIS_FILE, "Reiniting AMR encoder to update VAD setting."));
- #ifdef USE_AMRNB
- Encoder_Interface_exit(amr_data->encoder);
- amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
- #endif
- if (amr_data->encoder == NULL) {
- TRACE_((THIS_FILE, "Encoder_Interface_init() failed"));
- amr_codec_close(codec);
- return PJMEDIA_CODEC_EFAILED;
- }
- }
- TRACE_((THIS_FILE, "AMR codec modified: vad=%d, plc=%d",
- amr_data->vad_enabled, amr_data->plc_enabled));
- return PJ_SUCCESS;
- }
- /*
- * Get frames in the packet.
- */
- static pj_status_t amr_codec_parse( pjmedia_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- const pj_timestamp *ts,
- unsigned *frame_cnt,
- pjmedia_frame frames[])
- {
- struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
- pj_uint8_t cmr;
- pj_status_t status;
- unsigned idx = (amr_data->enc_setting.amr_nb? 0: 1);
- status = pjmedia_codec_amr_parse(pkt, pkt_size, ts, &amr_data->dec_setting,
- frames, frame_cnt, &cmr);
- if (status != PJ_SUCCESS)
- return status;
- /* Check for Change Mode Request. */
- if (cmr < amr_bitrates_size[idx] && amr_data->enc_mode != cmr) {
- amr_data->enc_mode = cmr;
- TRACE_((THIS_FILE, "AMR encoder switched mode to %d (%dbps)",
- amr_data->enc_mode,
- amr_bitrates[idx][amr_data->enc_mode]));
- }
- return PJ_SUCCESS;
- }
- /*
- * Encode frame.
- */
- static pj_status_t amr_codec_encode( pjmedia_codec *codec,
- const struct pjmedia_frame *input,
- unsigned output_buf_len,
- struct pjmedia_frame *output)
- {
- struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
- unsigned char *bitstream;
- pj_int16_t *speech;
- unsigned nsamples, samples_per_frame;
- enum {MAX_FRAMES_PER_PACKET = 16};
- pjmedia_frame frames[MAX_FRAMES_PER_PACKET];
- pj_uint8_t *p;
- unsigned i, out_size = 0, nframes = 0;
- pj_size_t payload_len;
- unsigned dtx_cnt, sid_cnt;
- pj_status_t status;
- pj_assert(amr_data != NULL);
- PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
- nsamples = input->size >> 1;
- samples_per_frame = amr_data->clock_rate * FRAME_LENGTH_MS / 1000;
- PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0,
- PJMEDIA_CODEC_EPCMFRMINLEN);
- nframes = nsamples / samples_per_frame;
- PJ_ASSERT_RETURN(nframes <= MAX_FRAMES_PER_PACKET,
- PJMEDIA_CODEC_EFRMTOOSHORT);
- /* Encode the frames */
- speech = (pj_int16_t*)input->buf;
- bitstream = (unsigned char*)output->buf;
- while (nsamples >= samples_per_frame) {
- int size;
- if (amr_data->enc_setting.amr_nb) {
- #ifdef USE_AMRNB
- size = Encoder_Interface_Encode (amr_data->encoder,
- amr_data->enc_mode,
- speech, bitstream, 0);
- #else
- size = 0;
- #endif
- } else {
- #ifdef USE_AMRWB
- size = E_IF_encode (amr_data->encoder, amr_data->enc_mode,
- speech, bitstream, 0);
- #else
- size = 0;
- #endif
- }
- if (size == 0) {
- output->size = 0;
- output->buf = NULL;
- output->type = PJMEDIA_FRAME_TYPE_NONE;
- TRACE_((THIS_FILE, "AMR encode() failed"));
- return PJMEDIA_CODEC_EFAILED;
- }
- nsamples -= samples_per_frame;
- speech += samples_per_frame;
- bitstream += size;
- out_size += size;
- TRACE_((THIS_FILE, "AMR encode(): mode=%d, size=%d",
- amr_data->enc_mode, out_size));
- }
- /* Pack payload */
- p = (pj_uint8_t*)output->buf + output_buf_len - out_size;
- pj_memmove(p, output->buf, out_size);
- dtx_cnt = sid_cnt = 0;
- for (i = 0; i < nframes; ++i) {
- pjmedia_codec_amr_bit_info *info = (pjmedia_codec_amr_bit_info*)
- &frames[i].bit_info;
- info->frame_type = (pj_uint8_t)((*p >> 3) & 0x0F);
- info->good_quality = (pj_uint8_t)((*p >> 2) & 0x01);
- info->mode = (pj_int8_t)amr_data->enc_mode;
- info->start_bit = 0;
- frames[i].buf = p + 1;
- if (amr_data->enc_setting.amr_nb) {
- frames[i].size = (info->frame_type <= 8)?
- pjmedia_codec_amrnb_framelen[info->frame_type] : 0;
- } else {
- frames[i].size = (info->frame_type <= 9)?
- pjmedia_codec_amrwb_framelen[info->frame_type] : 0;
- }
- p += frames[i].size + 1;
- /* Count the number of SID and DTX frames */
- if (info->frame_type == 15) /* DTX*/
- ++dtx_cnt;
- else if (info->frame_type == 8 && amr_data->enc_setting.amr_nb) /* SID */
- ++sid_cnt;
- else if (info->frame_type == 9 && !amr_data->enc_setting.amr_nb) /* SID */
- ++sid_cnt;
- }
- /* VA generates DTX frames as DTX+SID frames switching quickly and it
- * seems that the SID frames occur too often (assuming the purpose is
- * only for keeping NAT alive?). So let's modify the behavior a bit.
- * Only an SID frame will be sent every PJMEDIA_CODEC_MAX_SILENCE_PERIOD
- * milliseconds.
- */
- if (sid_cnt + dtx_cnt == nframes) {
- pj_int32_t dtx_duration;
- dtx_duration = pj_timestamp_diff32(&amr_data->last_tx,
- &input->timestamp);
- if (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
- dtx_duration < (int)(PJMEDIA_CODEC_MAX_SILENCE_PERIOD*
- amr_data->clock_rate/1000))
- {
- output->size = 0;
- output->type = PJMEDIA_FRAME_TYPE_NONE;
- output->timestamp = input->timestamp;
- return PJ_SUCCESS;
- }
- }
- payload_len = output_buf_len;
- status = pjmedia_codec_amr_pack(frames, nframes, &amr_data->enc_setting,
- output->buf, &payload_len);
- if (status != PJ_SUCCESS) {
- output->size = 0;
- output->buf = NULL;
- output->type = PJMEDIA_FRAME_TYPE_NONE;
- TRACE_((THIS_FILE, "Failed to pack AMR payload, status=%d", status));
- return status;
- }
- output->size = payload_len;
- output->type = PJMEDIA_FRAME_TYPE_AUDIO;
- output->timestamp = input->timestamp;
- amr_data->last_tx = input->timestamp;
- return PJ_SUCCESS;
- }
- /*
- * Decode frame.
- */
- static pj_status_t amr_codec_decode( pjmedia_codec *codec,
- const struct pjmedia_frame *input,
- unsigned output_buf_len,
- struct pjmedia_frame *output)
- {
- struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
- pjmedia_frame input_;
- pjmedia_codec_amr_bit_info *info;
- unsigned out_size;
- /* AMR decoding buffer: AMR max frame size + 1 byte header. */
- unsigned char bitstream[61];
- pj_assert(amr_data != NULL);
- PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
- out_size = amr_data->clock_rate * FRAME_LENGTH_MS / 1000 * 2;
- if (output_buf_len < out_size)
- return PJMEDIA_CODEC_EPCMTOOSHORT;
- input_.buf = &bitstream[1];
- /* AMR max frame size */
- input_.size = (amr_data->dec_setting.amr_nb? 31: 60);
- pjmedia_codec_amr_predecode(input, &amr_data->dec_setting, &input_);
- info = (pjmedia_codec_amr_bit_info*)&input_.bit_info;
- /* VA AMR decoder requires frame info in the first byte. */
- bitstream[0] = (info->frame_type << 3) | (info->good_quality << 2);
- TRACE_((THIS_FILE, "AMR decode(): mode=%d, ft=%d, size=%d",
- info->mode, info->frame_type, input_.size));
- /* Decode */
- if (amr_data->dec_setting.amr_nb) {
- #ifdef USE_AMRNB
- Decoder_Interface_Decode(amr_data->decoder, bitstream,
- (pj_int16_t*)output->buf, 0);
- #endif
- } else {
- #ifdef USE_AMRWB
- D_IF_decode(amr_data->decoder, bitstream,
- (pj_int16_t*)output->buf, 0);
- #endif
- }
- output->size = out_size;
- output->type = PJMEDIA_FRAME_TYPE_AUDIO;
- output->timestamp = input->timestamp;
- #if USE_PJMEDIA_PLC
- if (amr_data->plc_enabled)
- pjmedia_plc_save(amr_data->plc, (pj_int16_t*)output->buf);
- #endif
- return PJ_SUCCESS;
- }
- /*
- * Recover lost frame.
- */
- #if USE_PJMEDIA_PLC
- /*
- * Recover lost frame.
- */
- static pj_status_t amr_codec_recover( pjmedia_codec *codec,
- unsigned output_buf_len,
- struct pjmedia_frame *output)
- {
- struct amr_data *amr_data = codec->codec_data;
- unsigned out_size = amr_data->clock_rate * FRAME_LENGTH_MS / 1000 * 2;
- TRACE_((THIS_FILE, "amr_codec_recover"));
- PJ_ASSERT_RETURN(amr_data->plc_enabled, PJ_EINVALIDOP);
- PJ_ASSERT_RETURN(output_buf_len >= out_size, PJMEDIA_CODEC_EPCMTOOSHORT);
- pjmedia_plc_generate(amr_data->plc, (pj_int16_t*)output->buf);
- output->size = out_size;
- output->type = PJMEDIA_FRAME_TYPE_AUDIO;
-
- return PJ_SUCCESS;
- }
- #endif
- #if defined(_MSC_VER) && PJMEDIA_AUTO_LINK_OPENCORE_AMR_LIBS
- # if PJMEDIA_OPENCORE_AMR_BUILT_WITH_GCC
- # ifdef USE_AMRNB
- # pragma comment( lib, "libopencore-amrnb.a")
- # endif
- # ifdef USE_AMRWB
- # pragma comment( lib, "libopencore-amrwb.a")
- # pragma comment( lib, "libvo-amrwbenc.a")
- # endif
- # else
- # error Unsupported OpenCORE AMR library, fix here
- # endif
- #endif
- #endif
|