123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254 |
- /*
- * Copyright (C) 2015-2016 Teluu Inc. (http://www.teluu.com)
- * Copyright (C) 2012-2015 Zaark Technology AB
- *
- * 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
- */
- /* This file is the implementation of Opus codec wrapper and was contributed by
- * Zaark Technology AB
- */
- #include <pjmedia-codec/opus.h>
- #include <pjmedia/errno.h>
- #include <pjmedia/endpoint.h>
- #include <pj/log.h>
- #include <pj/math.h>
- #if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC!=0)
- #include <opus/opus.h>
- #define THIS_FILE "opus.c"
- /* Default packet loss concealment setting. */
- #define OPUS_DEFAULT_PLC 1
- /* Default Voice Activity Detector setting. */
- #define OPUS_DEFAULT_VAD 0
- /* Maximum size of an encoded packet.
- * If the the actual size is bigger, the encode/parse will fail.
- */
- #define MAX_ENCODED_PACKET_SIZE 1280
- /* Default frame time (msec) */
- #define PTIME 20
- #define PTIME_DENUM 1
- /* Tracing */
- #if 0
- # define TRACE_(expr) PJ_LOG(4,expr)
- #else
- # define TRACE_(expr)
- #endif
- /* Prototypes for Opus factory */
- static pj_status_t factory_test_alloc( pjmedia_codec_factory *factory,
- const pjmedia_codec_info *ci );
- static pj_status_t factory_default_attr( pjmedia_codec_factory *factory,
- const pjmedia_codec_info *ci,
- pjmedia_codec_param *attr );
- static pj_status_t factory_enum_codecs( pjmedia_codec_factory *factory,
- unsigned *count,
- pjmedia_codec_info codecs[]);
- static pj_status_t factory_alloc_codec( pjmedia_codec_factory *factory,
- const pjmedia_codec_info *ci,
- pjmedia_codec **p_codec);
- static pj_status_t factory_dealloc_codec( pjmedia_codec_factory *factory,
- pjmedia_codec *codec );
- /* Prototypes for Opus implementation. */
- static pj_status_t codec_init( pjmedia_codec *codec,
- pj_pool_t *pool );
- static pj_status_t codec_open( pjmedia_codec *codec,
- pjmedia_codec_param *attr );
- static pj_status_t codec_close( pjmedia_codec *codec );
- static pj_status_t codec_modify( pjmedia_codec *codec,
- const pjmedia_codec_param *attr );
- static pj_status_t 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 codec_encode( pjmedia_codec *codec,
- const struct pjmedia_frame *input,
- unsigned output_buf_len,
- struct pjmedia_frame *output);
- static pj_status_t codec_decode( pjmedia_codec *codec,
- const struct pjmedia_frame *input,
- unsigned output_buf_len,
- struct pjmedia_frame *output);
- static pj_status_t codec_recover( pjmedia_codec *codec,
- unsigned output_buf_len,
- struct pjmedia_frame *output);
- /* Definition for Opus operations. */
- static pjmedia_codec_op opus_op =
- {
- &codec_init,
- &codec_open,
- &codec_close,
- &codec_modify,
- &codec_parse,
- &codec_encode,
- &codec_decode,
- &codec_recover
- };
- /* Definition for Opus factory operations. */
- static pjmedia_codec_factory_op opus_factory_op =
- {
- &factory_test_alloc,
- &factory_default_attr,
- &factory_enum_codecs,
- &factory_alloc_codec,
- &factory_dealloc_codec,
- &pjmedia_codec_opus_deinit
- };
- /* Opus factory */
- struct opus_codec_factory
- {
- pjmedia_codec_factory base;
- pjmedia_endpt *endpt;
- pj_pool_t *pool;
- };
- /* Opus codec private data. */
- struct opus_data
- {
- pj_pool_t *pool;
- pj_mutex_t *mutex;
- OpusEncoder *enc;
- OpusDecoder *dec;
- OpusRepacketizer *enc_packer;
- OpusRepacketizer *dec_packer;
- pjmedia_codec_opus_config cfg;
- unsigned enc_ptime;
- unsigned enc_ptime_denum;
- unsigned dec_ptime;
- unsigned dec_ptime_denum;
- pjmedia_frame dec_frame[2];
- int dec_frame_index;
- };
- /* Codec factory instance */
- static struct opus_codec_factory opus_codec_factory;
- /* Opus default configuration */
- static pjmedia_codec_opus_config opus_cfg =
- {
- PJMEDIA_CODEC_OPUS_DEFAULT_SAMPLE_RATE, /* Sample rate */
- 1, /* Channel count */
- PTIME, /* Frame ptime */
- PTIME_DENUM, /* Frame ptime denum */
- PJMEDIA_CODEC_OPUS_DEFAULT_BIT_RATE, /* Bit rate */
- 5, /* Expected packet loss */
- PJMEDIA_CODEC_OPUS_DEFAULT_COMPLEXITY, /* Complexity */
- PJMEDIA_CODEC_OPUS_DEFAULT_CBR, /* Constant bit rate */
- };
- static int get_opus_bw_constant (unsigned sample_rate)
- {
- if (sample_rate <= 8000)
- return OPUS_BANDWIDTH_NARROWBAND;
- else if (sample_rate <= 12000)
- return OPUS_BANDWIDTH_MEDIUMBAND;
- else if (sample_rate <= 16000)
- return OPUS_BANDWIDTH_WIDEBAND;
- else if (sample_rate <= 24000)
- return OPUS_BANDWIDTH_SUPERWIDEBAND;
- else
- return OPUS_BANDWIDTH_FULLBAND;
- }
- /*
- * Initialize and register Opus codec factory to pjmedia endpoint.
- */
- PJ_DEF(pj_status_t) pjmedia_codec_opus_init( pjmedia_endpt *endpt )
- {
- pj_status_t status;
- pjmedia_codec_mgr *codec_mgr;
- PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
- if (opus_codec_factory.pool != NULL)
- return PJ_SUCCESS;
- /* Create the Opus codec factory */
- opus_codec_factory.base.op = &opus_factory_op;
- opus_codec_factory.base.factory_data = &opus_codec_factory;
- opus_codec_factory.endpt = endpt;
- opus_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "opus-factory",
- 1024, 1024);
- if (!opus_codec_factory.pool) {
- PJ_LOG(2, (THIS_FILE, "Unable to create memory pool for Opus codec"));
- return PJ_ENOMEM;
- }
- /* Get the codec manager */
- codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
- if (!codec_mgr) {
- PJ_LOG(2, (THIS_FILE, "Unable to get the codec manager"));
- status = PJ_EINVALIDOP;
- goto on_codec_factory_error;
- }
- /* Register the codec factory */
- status = pjmedia_codec_mgr_register_factory (codec_mgr,
- &opus_codec_factory.base);
- if (status != PJ_SUCCESS) {
- PJ_LOG(2, (THIS_FILE, "Unable to register the codec factory"));
- goto on_codec_factory_error;
- }
- return PJ_SUCCESS;
- on_codec_factory_error:
- pj_pool_release(opus_codec_factory.pool);
- opus_codec_factory.pool = NULL;
- return status;
- }
- /*
- * Unregister Opus codec factory from pjmedia endpoint and
- * deinitialize the codec.
- */
- PJ_DEF(pj_status_t) pjmedia_codec_opus_deinit( void )
- {
- pj_status_t status;
- pjmedia_codec_mgr *codec_mgr;
- if (opus_codec_factory.pool == NULL)
- return PJ_SUCCESS;
- /* Get the codec manager */
- codec_mgr = pjmedia_endpt_get_codec_mgr(opus_codec_factory.endpt);
- if (!codec_mgr) {
- PJ_LOG(2, (THIS_FILE, "Unable to get the codec manager"));
- pj_pool_release(opus_codec_factory.pool);
- opus_codec_factory.pool = NULL;
- return PJ_EINVALIDOP;
- }
- /* Unregister the codec factory */
- status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
- &opus_codec_factory.base);
- if (status != PJ_SUCCESS)
- PJ_LOG(2, (THIS_FILE, "Unable to unregister the codec factory"));
- /* Release the memory pool */
- pj_pool_release(opus_codec_factory.pool);
- opus_codec_factory.pool = NULL;
- return status;
- }
- /**
- * Get the opus configuration for a specific sample rate.
- */
- PJ_DEF(pj_status_t)
- pjmedia_codec_opus_get_config( pjmedia_codec_opus_config *cfg )
- {
- PJ_ASSERT_RETURN(cfg, PJ_EINVAL);
- pj_memcpy(cfg, &opus_cfg, sizeof(pjmedia_codec_opus_config));
- return PJ_SUCCESS;
- }
- static pj_str_t STR_MAX_PLAYBACK = {"maxplaybackrate", 15};
- static pj_str_t STR_MAX_CAPTURE = {"sprop-maxcapturerate", 20};
- static pj_str_t STR_STEREO = {"stereo", 6};
- static pj_str_t STR_SPROP_STEREO = {"sprop-stereo", 12};
- static pj_str_t STR_MAX_BIT_RATE = {"maxaveragebitrate", 17};
- static pj_str_t STR_INBAND_FEC = {"useinbandfec", 12};
- static pj_str_t STR_DTX = {"usedtx", 6};
- static pj_str_t STR_CBR = {"cbr", 3};
- static int find_fmtp(pjmedia_codec_fmtp *fmtp, pj_str_t *name, pj_bool_t add)
- {
- int i;
- for (i = 0; i < fmtp->cnt; i++) {
- if (pj_stricmp(&fmtp->param[i].name, name) == 0)
- return i;
- }
-
- if (add && (i < PJMEDIA_CODEC_MAX_FMTP_CNT)) {
- fmtp->param[i].name = *name;
- fmtp->cnt++;
- return i;
- } else
- return -1;
- }
- static void remove_fmtp(pjmedia_codec_fmtp *fmtp, pj_str_t *name)
- {
- int i, j;
- for (i = 0; i < fmtp->cnt; i++) {
- if (pj_stricmp(&fmtp->param[i].name, name) == 0) {
- fmtp->cnt--;
- for (j = i; j < fmtp->cnt; j++) {
- fmtp->param[i].name = fmtp->param[i+1].name;
- fmtp->param[i].val = fmtp->param[i+1].val;
- }
- }
- }
- }
- static pj_status_t generate_fmtp(pjmedia_codec_param *attr)
- {
- int idx;
- static char bitrate_str[12];
- static char clockrate_str[12];
-
- if (attr->info.clock_rate != 48000) {
- pj_ansi_snprintf(clockrate_str, sizeof(clockrate_str), "%u",
- attr->info.clock_rate);
- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_MAX_PLAYBACK, PJ_TRUE);
- if (idx >= 0)
- attr->setting.dec_fmtp.param[idx].val = pj_str(clockrate_str);
- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_MAX_CAPTURE, PJ_TRUE);
- if (idx >= 0)
- attr->setting.dec_fmtp.param[idx].val = pj_str(clockrate_str);
- } else {
- remove_fmtp(&attr->setting.dec_fmtp, &STR_MAX_PLAYBACK);
- remove_fmtp(&attr->setting.dec_fmtp, &STR_MAX_CAPTURE);
- }
- /* Check if we need to set parameter 'maxaveragebitrate' */
- if (opus_cfg.bit_rate > 0) {
- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_MAX_BIT_RATE, PJ_TRUE);
- if (idx >= 0) {
- pj_ansi_snprintf(bitrate_str, sizeof(bitrate_str), "%u",
- attr->info.avg_bps);
- attr->setting.dec_fmtp.param[idx].val = pj_str(bitrate_str);
- }
- } else {
- remove_fmtp(&attr->setting.dec_fmtp, &STR_MAX_BIT_RATE);
- }
- if (attr->info.channel_cnt > 1) {
- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_STEREO, PJ_TRUE);
- if (idx >= 0)
- attr->setting.dec_fmtp.param[idx].val = pj_str("1");
- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_SPROP_STEREO, PJ_TRUE);
- if (idx >= 0)
- attr->setting.dec_fmtp.param[idx].val = pj_str("1");
- } else {
- remove_fmtp(&attr->setting.dec_fmtp, &STR_STEREO);
- remove_fmtp(&attr->setting.dec_fmtp, &STR_SPROP_STEREO);
- }
- if (opus_cfg.cbr) {
- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_CBR, PJ_TRUE);
- if (idx >= 0)
- attr->setting.dec_fmtp.param[idx].val = pj_str("1");
- } else {
- remove_fmtp(&attr->setting.dec_fmtp, &STR_CBR);
- }
- if (attr->setting.plc) {
- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_INBAND_FEC, PJ_TRUE);
- if (idx >= 0)
- attr->setting.dec_fmtp.param[idx].val = pj_str("1");
- } else {
- remove_fmtp(&attr->setting.dec_fmtp, &STR_INBAND_FEC);
- }
- if (attr->setting.vad) {
- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_DTX, PJ_TRUE);
- if (idx >= 0)
- attr->setting.dec_fmtp.param[idx].val = pj_str("1");
- } else {
- remove_fmtp(&attr->setting.dec_fmtp, &STR_DTX);
- }
-
- return PJ_SUCCESS;
- }
- /**
- * Set the opus configuration and default param.
- */
- PJ_DEF(pj_status_t)
- pjmedia_codec_opus_set_default_param(const pjmedia_codec_opus_config *cfg,
- pjmedia_codec_param *param )
- {
- const pj_str_t opus_str = {"opus", 4};
- const pjmedia_codec_info *info[1];
- pjmedia_codec_mgr *codec_mgr;
- unsigned count = 1;
- pj_status_t status;
- TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__));
- PJ_ASSERT_RETURN(cfg && param, PJ_EINVAL);
- codec_mgr = pjmedia_endpt_get_codec_mgr(opus_codec_factory.endpt);
- status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, &opus_str,
- &count, info, NULL);
- if (status != PJ_SUCCESS)
- return status;
- /* Set sample rate */
- if (cfg->sample_rate != 8000 && cfg->sample_rate != 12000 &&
- cfg->sample_rate != 16000 && cfg->sample_rate != 24000 &&
- cfg->sample_rate != 48000)
- {
- return PJ_EINVAL;
- }
- param->info.clock_rate = opus_cfg.sample_rate = cfg->sample_rate;
- param->info.max_bps = opus_cfg.sample_rate * 2;
- opus_cfg.frm_ptime = cfg->frm_ptime;
- opus_cfg.frm_ptime_denum = cfg->frm_ptime_denum;
- param->info.frm_ptime = (pj_uint16_t)cfg->frm_ptime;
- param->info.frm_ptime_denum = (pj_uint8_t)cfg->frm_ptime_denum;
- /* Set channel count */
- if (cfg->channel_cnt != 1 && cfg->channel_cnt != 2)
- return PJ_EINVAL;
- param->info.channel_cnt = opus_cfg.channel_cnt = cfg->channel_cnt;
- /* Set bit_rate */
- if ((cfg->bit_rate != PJMEDIA_CODEC_OPUS_DEFAULT_BIT_RATE) &&
- (cfg->bit_rate < 6000 || cfg->bit_rate > 510000))
- {
- return PJ_EINVAL;
- }
- opus_cfg.bit_rate = cfg->bit_rate;
- param->info.avg_bps = opus_cfg.bit_rate;
- /* Set expected packet loss */
- if (cfg->packet_loss >= 100)
- return PJ_EINVAL;
- opus_cfg.packet_loss = cfg->packet_loss;
- /* Set complexity */
- if (cfg->complexity > 10)
- return PJ_EINVAL;
- opus_cfg.complexity = cfg->complexity;
- opus_cfg.cbr = cfg->cbr;
-
- generate_fmtp(param);
- status = pjmedia_codec_mgr_set_default_param(codec_mgr, info[0], param);
- return status;
- }
- /*
- * Check if factory can allocate the specified codec.
- */
- static pj_status_t factory_test_alloc( pjmedia_codec_factory *factory,
- const pjmedia_codec_info *ci )
- {
- const pj_str_t opus_tag = {"OPUS", 4};
- PJ_UNUSED_ARG(factory);
- PJ_ASSERT_RETURN(factory==&opus_codec_factory.base, PJ_EINVAL);
- /* Type MUST be audio. */
- if (ci->type != PJMEDIA_TYPE_AUDIO)
- return PJMEDIA_CODEC_EUNSUP;
- /* Check encoding name. */
- if (pj_stricmp(&ci->encoding_name, &opus_tag) != 0)
- return PJMEDIA_CODEC_EUNSUP;
- /* Check clock rate */
- if (ci->clock_rate != 8000 && ci->clock_rate != 12000 &&
- ci->clock_rate != 16000 && ci->clock_rate != 24000 &&
- ci->clock_rate != 48000)
- {
- return PJMEDIA_CODEC_EUNSUP;
- }
- return PJ_SUCCESS;
- }
- /*
- * Generate default attribute.
- */
- static pj_status_t factory_default_attr( pjmedia_codec_factory *factory,
- const pjmedia_codec_info *ci,
- pjmedia_codec_param *attr )
- {
- PJ_UNUSED_ARG(factory);
- TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__));
- pj_bzero(attr, sizeof(pjmedia_codec_param));
- attr->info.pt = (pj_uint8_t)ci->pt;
- attr->info.clock_rate = opus_cfg.sample_rate;
- attr->info.channel_cnt = opus_cfg.channel_cnt;
- attr->info.avg_bps = opus_cfg.bit_rate;
- attr->info.max_bps = opus_cfg.sample_rate * 2;
- attr->info.frm_ptime = (pj_uint16_t)opus_cfg.frm_ptime;
- attr->info.frm_ptime_denum = (pj_uint8_t)opus_cfg.frm_ptime_denum;
- attr->setting.frm_per_pkt = 1;
- attr->info.pcm_bits_per_sample = 16;
- attr->setting.vad = OPUS_DEFAULT_VAD;
- attr->setting.plc = OPUS_DEFAULT_PLC;
- attr->setting.packet_loss = opus_cfg.packet_loss;
- attr->setting.complexity = opus_cfg.complexity;
- attr->setting.cbr = opus_cfg.cbr;
- /* Set max RX frame size to 1275 (max Opus frame size) to anticipate
- * possible ptime change on the fly.
- */
- attr->info.max_rx_frame_size = 1275;
- generate_fmtp(attr);
- return PJ_SUCCESS;
- }
- /*
- * Enum codecs supported by this factory.
- */
- static pj_status_t factory_enum_codecs( pjmedia_codec_factory *factory,
- unsigned *count,
- pjmedia_codec_info codecs[] )
- {
- PJ_UNUSED_ARG(factory);
- PJ_ASSERT_RETURN(codecs, PJ_EINVAL);
- if (*count > 0) {
- pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
- codecs[0].type = PJMEDIA_TYPE_AUDIO;
- codecs[0].pt = PJMEDIA_RTP_PT_OPUS;
- /*
- * RFC 7587, Section 7:
- * The media subtype ("opus") goes in SDP "a=rtpmap" as the encoding
- * name. The RTP clock rate in "a=rtpmap" MUST be 48000 and the
- * number of channels MUST be 2.
- */
- codecs[0].encoding_name = pj_str("opus");
- codecs[0].clock_rate = 48000;
- codecs[0].channel_cnt = 2;
- *count = 1;
- }
- return PJ_SUCCESS;
- }
- /*
- * Allocate a new Opus codec instance.
- */
- static pj_status_t factory_alloc_codec( pjmedia_codec_factory *factory,
- const pjmedia_codec_info *ci,
- pjmedia_codec **p_codec )
- {
- pjmedia_codec *codec;
- pj_pool_t *pool;
- pj_status_t status;
- struct opus_data *opus_data;
- struct opus_codec_factory *f = (struct opus_codec_factory*) factory;
- PJ_UNUSED_ARG(ci);
- TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__));
- pool = pjmedia_endpt_create_pool(f->endpt, "opus", 4000, 4000);
- if (!pool) return PJ_ENOMEM;
-
- opus_data = PJ_POOL_ZALLOC_T(pool, struct opus_data);
- codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
- status = pj_mutex_create_simple (pool, "opus_mutex", &opus_data->mutex);
- if (status != PJ_SUCCESS) {
- pj_pool_release(pool);
- return status;
- }
- pj_memcpy(&opus_data->cfg, &opus_cfg, sizeof(pjmedia_codec_opus_config));
- opus_data->pool = pool;
- codec->op = &opus_op;
- codec->factory = factory;
- codec->codec_data = opus_data;
- *p_codec = codec;
- return PJ_SUCCESS;
- }
- /*
- * Free codec.
- */
- static pj_status_t factory_dealloc_codec( pjmedia_codec_factory *factory,
- pjmedia_codec *codec )
- {
- struct opus_data *opus_data;
- PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
- PJ_ASSERT_RETURN(factory == &opus_codec_factory.base, PJ_EINVAL);
- opus_data = (struct opus_data *)codec->codec_data;
- if (opus_data) {
- pj_mutex_destroy(opus_data->mutex);
- opus_data->mutex = NULL;
- pj_pool_release(opus_data->pool);
- }
- return PJ_SUCCESS;
- }
- /*
- * Init codec.
- */
- static pj_status_t 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 codec_open( pjmedia_codec *codec,
- pjmedia_codec_param *attr )
- {
- struct opus_data *opus_data = (struct opus_data *)codec->codec_data;
- int idx, err;
- pj_bool_t auto_bit_rate = PJ_TRUE;
- pj_bool_t enc_use_plc = PJ_FALSE;
- PJ_ASSERT_RETURN(codec && attr && opus_data, PJ_EINVAL);
- pj_mutex_lock (opus_data->mutex);
- TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__));
- opus_data->cfg.sample_rate = attr->info.clock_rate;
- opus_data->cfg.channel_cnt = attr->info.channel_cnt;
- opus_data->enc_ptime = opus_data->dec_ptime = attr->info.frm_ptime;
- opus_data->enc_ptime_denum = attr->info.frm_ptime_denum?
- attr->info.frm_ptime_denum: 1;
- opus_data->dec_ptime_denum = opus_data->enc_ptime_denum;
- /* Allocate memory used by the codec */
- if (!opus_data->enc) {
- /* Allocate memory for max 2 channels */
- opus_data->enc = pj_pool_zalloc(opus_data->pool,
- opus_encoder_get_size(2));
- }
- if (!opus_data->dec) {
- /* Allocate memory for max 2 channels */
- opus_data->dec = pj_pool_zalloc(opus_data->pool,
- opus_decoder_get_size(2));
- }
- if (!opus_data->enc_packer) {
- opus_data->enc_packer = pj_pool_zalloc(opus_data->pool,
- opus_repacketizer_get_size());
- }
- if (!opus_data->dec_packer) {
- opus_data->dec_packer = pj_pool_zalloc(opus_data->pool,
- opus_repacketizer_get_size());
- }
- if (!opus_data->enc || !opus_data->dec ||
- !opus_data->enc_packer || !opus_data->dec_packer)
- {
- PJ_LOG(2, (THIS_FILE, "Unable to allocate memory for the codec"));
- pj_mutex_unlock (opus_data->mutex);
- return PJ_ENOMEM;
- }
- /* Check max average bit rate */
- idx = find_fmtp(&attr->setting.enc_fmtp, &STR_MAX_BIT_RATE, PJ_FALSE);
- if (idx >= 0) {
- unsigned rate;
- auto_bit_rate = PJ_FALSE;
- rate = (unsigned)pj_strtoul(&attr->setting.enc_fmtp.param[idx].val);
- if (rate < attr->info.avg_bps)
- attr->info.avg_bps = rate;
- }
- /* Check plc */
- idx = find_fmtp(&attr->setting.enc_fmtp, &STR_INBAND_FEC, PJ_FALSE);
- if (idx >= 0) {
- unsigned plc;
- plc = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val);
- if (plc > 0)
- enc_use_plc = PJ_TRUE;
- /* Do not modify local PLC setting as it's used for decoding. */
- // attr->setting.plc = plc > 0? PJ_TRUE: PJ_FALSE
- }
- /* Check vad */
- idx = find_fmtp(&attr->setting.enc_fmtp, &STR_DTX, PJ_FALSE);
- if (idx >= 0) {
- unsigned vad;
- vad = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val);
- attr->setting.vad = vad > 0? PJ_TRUE: PJ_FALSE;
- }
- /* Check cbr */
- idx = find_fmtp(&attr->setting.enc_fmtp, &STR_CBR, PJ_FALSE);
- if (idx >= 0) {
- unsigned cbr;
- cbr = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val);
- opus_data->cfg.cbr = cbr > 0? PJ_TRUE: PJ_FALSE;
- }
-
- /* Check max average bit rate */
- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_MAX_BIT_RATE, PJ_FALSE);
- if (idx >= 0) {
- unsigned rate;
- rate = (unsigned) pj_strtoul(&attr->setting.dec_fmtp.param[idx].val);
- if (rate < attr->info.avg_bps)
- attr->info.avg_bps = rate;
- }
- TRACE_((THIS_FILE, "%s:%d: sample_rate: %u",
- __FUNCTION__, __LINE__, opus_data->cfg.sample_rate));
- /* Initialize encoder */
- err = opus_encoder_init(opus_data->enc,
- opus_data->cfg.sample_rate,
- attr->info.channel_cnt,
- OPUS_APPLICATION_VOIP);
- if (err != OPUS_OK) {
- PJ_LOG(2, (THIS_FILE, "Unable to create encoder: %s %d",
- opus_strerror(err), err));
- pj_mutex_unlock (opus_data->mutex);
- return PJMEDIA_CODEC_EFAILED;
- }
-
- /* Set signal type */
- opus_encoder_ctl(opus_data->enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
- /* Set bitrate */
- opus_encoder_ctl(opus_data->enc, OPUS_SET_BITRATE(auto_bit_rate?
- OPUS_AUTO:
- (int)attr->info.avg_bps));
- /* Set VAD */
- opus_encoder_ctl(opus_data->enc, OPUS_SET_DTX(attr->setting.vad ? 1 : 0));
- /* Set PLC */
- opus_encoder_ctl(opus_data->enc,
- OPUS_SET_INBAND_FEC(enc_use_plc));
- /* Set bandwidth */
- opus_encoder_ctl(opus_data->enc,
- OPUS_SET_MAX_BANDWIDTH(get_opus_bw_constant(
- opus_data->cfg.sample_rate)));
- /* Set expected packet loss */
- opus_encoder_ctl(opus_data->enc,
- OPUS_SET_PACKET_LOSS_PERC(opus_data->cfg.packet_loss));
- /* Set complexity */
- opus_encoder_ctl(opus_data->enc,
- OPUS_SET_COMPLEXITY(opus_data->cfg.complexity));
- /* Set constant bit rate */
- opus_encoder_ctl(opus_data->enc,
- OPUS_SET_VBR(opus_data->cfg.cbr ? 0 : 1));
- PJ_LOG(4, (THIS_FILE, "Initialize Opus encoder, sample rate: %d, ch: %d, "
- "avg bitrate: %d%s, vad: %d, plc: %d, pkt loss: %d, "
- "complexity: %d, constant bit rate: %d, "
- "ptime: %d/%d",
- opus_data->cfg.sample_rate,
- opus_data->cfg.channel_cnt,
- (auto_bit_rate? 0: attr->info.avg_bps),
- (auto_bit_rate? "(auto)": ""),
- attr->setting.vad?1:0,
- attr->setting.plc?1:0,
- opus_data->cfg.packet_loss,
- opus_data->cfg.complexity,
- opus_data->cfg.cbr?1:0,
- opus_data->enc_ptime,
- opus_data->enc_ptime_denum));
- /* Initialize decoder */
- err = opus_decoder_init (opus_data->dec,
- opus_data->cfg.sample_rate,
- attr->info.channel_cnt);
- if (err != OPUS_OK) {
- PJ_LOG(2, (THIS_FILE, "Unable to initialize decoder: %s %d",
- opus_strerror(err), err));
- pj_mutex_unlock (opus_data->mutex);
- return PJMEDIA_CODEC_EFAILED;
- }
- /* Initialize temporary decode frames used for FEC */
- opus_data->dec_frame[0].type = PJMEDIA_FRAME_TYPE_NONE;
- opus_data->dec_frame[0].buf = pj_pool_zalloc(opus_data->pool,
- (opus_data->cfg.sample_rate / 1000)
- * 60 * attr->info.channel_cnt * 2 /* bytes per sample */);
- opus_data->dec_frame[1].type = PJMEDIA_FRAME_TYPE_NONE;
- opus_data->dec_frame[1].buf = pj_pool_zalloc(opus_data->pool,
- (opus_data->cfg.sample_rate / 1000)
- * 60 * attr->info.channel_cnt * 2 /* bytes per sample */);
- opus_data->dec_frame_index = -1;
- /* Initialize the repacketizers */
- opus_repacketizer_init(opus_data->enc_packer);
- opus_repacketizer_init(opus_data->dec_packer);
- pj_mutex_unlock (opus_data->mutex);
- return PJ_SUCCESS;
- }
- /*
- * Close codec.
- */
- static pj_status_t codec_close( pjmedia_codec *codec )
- {
- PJ_UNUSED_ARG(codec);
- return PJ_SUCCESS;
- }
- /*
- * Modify codec settings.
- */
- static pj_status_t codec_modify( pjmedia_codec *codec,
- const pjmedia_codec_param *attr )
- {
- struct opus_data *opus_data = (struct opus_data *)codec->codec_data;
- pj_mutex_lock (opus_data->mutex);
- TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__));
- /* Set encoder ptime */
- opus_data->enc_ptime = attr->info.frm_ptime;
- opus_data->enc_ptime_denum = attr->info.frm_ptime_denum?
- attr->info.frm_ptime_denum: 1;
- /* Set bitrate */
- opus_data->cfg.bit_rate = attr->info.avg_bps;
- opus_encoder_ctl(opus_data->enc, OPUS_SET_BITRATE(attr->info.avg_bps?
- (int)attr->info.avg_bps:
- OPUS_AUTO));
- /* Set VAD */
- opus_encoder_ctl(opus_data->enc, OPUS_SET_DTX(attr->setting.vad ? 1 : 0));
- /* Set PLC */
- opus_encoder_ctl(opus_data->enc,
- OPUS_SET_INBAND_FEC(attr->setting.plc ? 1 : 0));
- /* Set bandwidth */
- opus_encoder_ctl(opus_data->enc,
- OPUS_SET_MAX_BANDWIDTH(get_opus_bw_constant(
- attr->info.clock_rate)));
- /* Set expected packet loss */
- opus_encoder_ctl(opus_data->enc,
- OPUS_SET_PACKET_LOSS_PERC(attr->setting.packet_loss));
- /* Set complexity */
- opus_encoder_ctl(opus_data->enc,
- OPUS_SET_COMPLEXITY(attr->setting.complexity));
- /* Set constant bit rate */
- opus_encoder_ctl(opus_data->enc,
- OPUS_SET_VBR(attr->setting.cbr ? 0 : 1));
- PJ_LOG(4, (THIS_FILE, "Modifying Opus encoder, sample rate: %d, ch: %d, "
- "avg bitrate: %d%s, vad: %d, plc: %d, pkt loss: %d, "
- "complexity: %d, constant bit rate: %d, "
- "ptime: %d/%d ms",
- attr->info.clock_rate,
- attr->info.channel_cnt,
- (attr->info.avg_bps? attr->info.avg_bps: 0),
- (attr->info.avg_bps? "": "(auto)"),
- attr->setting.vad?1:0,
- attr->setting.plc?1:0,
- attr->setting.packet_loss,
- attr->setting.complexity,
- attr->setting.cbr?1:0,
- opus_data->enc_ptime,
- opus_data->enc_ptime_denum));
- pj_mutex_unlock (opus_data->mutex);
- return PJ_SUCCESS;
- }
- /*
- * Get frames in the packet.
- */
- static pj_status_t codec_parse( pjmedia_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- const pj_timestamp *ts,
- unsigned *frame_cnt,
- pjmedia_frame frames[] )
- {
- struct opus_data *opus_data = (struct opus_data *)codec->codec_data;
- unsigned char tmp_buf[MAX_ENCODED_PACKET_SIZE];
- int i, num_frames;
- int size, out_pos;
- unsigned samples_per_frame = 0;
- #if (USE_INCOMING_WORSE_SETTINGS)
- int bw;
- #endif
- pj_mutex_lock (opus_data->mutex);
- if (pkt_size > sizeof(tmp_buf)) {
- PJ_LOG(5, (THIS_FILE, "Encoded size bigger than buffer"));
- pj_mutex_unlock (opus_data->mutex);
- return PJMEDIA_CODEC_EFRMTOOSHORT;
- }
- pj_memcpy(tmp_buf, pkt, pkt_size);
- opus_repacketizer_init(opus_data->dec_packer);
- opus_repacketizer_cat(opus_data->dec_packer, tmp_buf, pkt_size);
- num_frames = opus_repacketizer_get_nb_frames(opus_data->dec_packer);
- if (num_frames == 0) {
- PJ_LOG(2, (THIS_FILE, "No frames retrieved (num_frames = 0)"));
- pj_mutex_unlock(opus_data->mutex);
- return PJMEDIA_CODEC_EFAILED;
- }
- out_pos = 0;
- for (i = 0; i < num_frames; ++i) {
- size = opus_repacketizer_out_range(opus_data->dec_packer, i, i+1,
- ((unsigned char*)pkt) + out_pos,
- sizeof(tmp_buf));
- if (size < 0) {
- PJ_LOG(5, (THIS_FILE, "Parse failed! (pkt_size=%lu, err=%d)",
- (unsigned long)pkt_size, size));
- pj_mutex_unlock (opus_data->mutex);
- return PJMEDIA_CODEC_EFAILED;
- }
- frames[i].type = PJMEDIA_FRAME_TYPE_AUDIO;
- frames[i].buf = ((char*)pkt) + out_pos;
- frames[i].size = size;
- frames[i].bit_info = 0;
- if (i == 0) {
- int nsamples;
- unsigned ptime;
- unsigned ptime_denum = 1;
- nsamples = opus_packet_get_nb_samples(frames[i].buf,
- frames[i].size,
- opus_data->cfg.sample_rate);
- if (nsamples <= 0) {
- PJ_LOG(5, (THIS_FILE, "Parse failed to get samples number! "
- "(err=%d)", nsamples));
- pj_mutex_unlock (opus_data->mutex);
- return PJMEDIA_CODEC_EFAILED;
- }
- if ((nsamples * 1000) % opus_data->cfg.sample_rate != 0) {
- /* The only non-integer ptime that Opus supports is 2.5 ms */
- ptime_denum = 2;
- }
- ptime = nsamples * ptime_denum * 1000 / opus_data->cfg.sample_rate;
- if (ptime * opus_data->dec_ptime_denum !=
- opus_data->dec_ptime * ptime_denum)
- {
- PJ_LOG(4, (THIS_FILE, "Opus ptime change detected: %d/%d ms "
- "--> %d/%d ms",
- opus_data->dec_ptime,
- opus_data->dec_ptime_denum,
- ptime, ptime_denum));
- opus_data->dec_ptime = ptime;
- opus_data->dec_ptime_denum = ptime_denum;
- opus_data->dec_frame_index = -1;
- /* Signal to the stream about ptime change. */
- frames[i].bit_info = 0x10000 | nsamples;
- }
- samples_per_frame = nsamples;
- }
- frames[i].timestamp.u64 = ts->u64 + i * samples_per_frame;
- out_pos += size;
- }
- *frame_cnt = num_frames;
- pj_mutex_unlock (opus_data->mutex);
- return PJ_SUCCESS;
- }
- /*
- * Encode frame.
- */
- static pj_status_t codec_encode( pjmedia_codec *codec,
- const struct pjmedia_frame *input,
- unsigned output_buf_len,
- struct pjmedia_frame *output )
- {
- struct opus_data *opus_data = (struct opus_data *)codec->codec_data;
- opus_int32 size = 0;
- unsigned in_pos = 0;
- unsigned out_pos = 0;
- unsigned frame_size;
- unsigned samples_per_frame;
- unsigned char tmp_buf[MAX_ENCODED_PACKET_SIZE];
- unsigned tmp_bytes_left = sizeof(tmp_buf);
- pj_mutex_lock (opus_data->mutex);
- samples_per_frame = (opus_data->cfg.sample_rate *
- opus_data->enc_ptime /
- opus_data->enc_ptime_denum) / 1000;
- frame_size = samples_per_frame * opus_data->cfg.channel_cnt *
- sizeof(opus_int16);
- opus_repacketizer_init(opus_data->enc_packer);
- while (input->size - in_pos >= frame_size) {
- size = opus_encode(opus_data->enc,
- (const opus_int16*)(((char*)input->buf) + in_pos),
- samples_per_frame,
- tmp_buf + out_pos,
- (tmp_bytes_left < frame_size ?
- tmp_bytes_left : frame_size));
- if (size < 0) {
- PJ_LOG(4, (THIS_FILE, "Encode failed! (%d)", size));
- pj_mutex_unlock (opus_data->mutex);
- return PJMEDIA_CODEC_EFAILED;
- } else if (size > 0) {
- /* Only add packets containing more than the TOC */
- opus_repacketizer_cat(opus_data->enc_packer,
- tmp_buf + out_pos,
- size);
- out_pos += size;
- tmp_bytes_left -= size;
- }
- in_pos += frame_size;
- }
- if (!opus_repacketizer_get_nb_frames(opus_data->enc_packer)) {
- /* Empty packet */
- output->size = 0;
- output->type = PJMEDIA_FRAME_TYPE_NONE;
- output->timestamp = input->timestamp;
- }
- if (size) {
- size = opus_repacketizer_out(opus_data->enc_packer,
- output->buf,
- output_buf_len);
- if (size < 0) {
- PJ_LOG(4, (THIS_FILE, "Encode failed! (%d), out_size: %u",
- size, output_buf_len));
- pj_mutex_unlock (opus_data->mutex);
- return PJMEDIA_CODEC_EFAILED;
- }
- }
- output->size = (unsigned)size;
- output->type = PJMEDIA_FRAME_TYPE_AUDIO;
- output->timestamp = input->timestamp;
- pj_mutex_unlock (opus_data->mutex);
- return PJ_SUCCESS;
- }
- /*
- * Decode frame.
- */
- static pj_status_t codec_decode( pjmedia_codec *codec,
- const struct pjmedia_frame *input,
- unsigned output_buf_len,
- struct pjmedia_frame *output )
- {
- struct opus_data *opus_data = (struct opus_data *)codec->codec_data;
- int decoded_samples;
- pjmedia_frame *inframe;
- int fec = 0;
- int frm_size;
- PJ_UNUSED_ARG(output_buf_len);
- pj_mutex_lock (opus_data->mutex);
- if (opus_data->dec_frame_index == -1) {
- /* First packet, buffer it. */
- opus_data->dec_frame[0].type = input->type;
- opus_data->dec_frame[0].size = input->size;
- opus_data->dec_frame[0].timestamp = input->timestamp;
- pj_memcpy(opus_data->dec_frame[0].buf, input->buf, input->size);
- opus_data->dec_frame_index = 0;
- pj_mutex_unlock (opus_data->mutex);
- /* Return zero decoded bytes */
- output->size = 0;
- output->type = PJMEDIA_FRAME_TYPE_NONE;
- output->timestamp = input->timestamp;
- return PJ_SUCCESS;
- }
- inframe = &opus_data->dec_frame[opus_data->dec_frame_index];
- if (inframe->type != PJMEDIA_FRAME_TYPE_AUDIO) {
- /* Update current frame index */
- opus_data->dec_frame_index++;
- if (opus_data->dec_frame_index > 1)
- opus_data->dec_frame_index = 0;
- /* Copy original input buffer to current indexed frame */
- inframe = &opus_data->dec_frame[opus_data->dec_frame_index];
- inframe->type = input->type;
- inframe->size = input->size;
- inframe->timestamp = input->timestamp;
- pj_memcpy(inframe->buf, input->buf, input->size);
- fec = 1;
- }
- /* From Opus doc: In the case of PLC (data==NULL) or FEC(decode_fec=1),
- * then frame_size needs to be exactly the duration of audio that
- * is missing.
- */
- frm_size = output->size / (sizeof(opus_int16) *
- opus_data->cfg.channel_cnt);
- if (inframe->type != PJMEDIA_FRAME_TYPE_AUDIO || fec) {
- frm_size = PJ_MIN((unsigned)frm_size,
- opus_data->cfg.sample_rate *
- opus_data->dec_ptime /
- opus_data->dec_ptime_denum / 1000);
- }
- decoded_samples = opus_decode( opus_data->dec,
- inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ?
- inframe->buf : NULL,
- inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ?
- inframe->size : 0,
- (opus_int16*)output->buf,
- frm_size,
- fec);
- output->timestamp = inframe->timestamp;
-
- if (inframe->type == PJMEDIA_FRAME_TYPE_AUDIO) {
- /* Mark current indexed frame as invalid */
- inframe->type = PJMEDIA_FRAME_TYPE_NONE;
- /* Update current frame index */
- opus_data->dec_frame_index++;
- if (opus_data->dec_frame_index > 1)
- opus_data->dec_frame_index = 0;
- /* Copy original input buffer to current indexed frame */
- inframe = &opus_data->dec_frame[opus_data->dec_frame_index];
- inframe->type = input->type;
- inframe->size = input->size;
- inframe->timestamp = input->timestamp;
- pj_memcpy(inframe->buf, input->buf, input->size);
- }
- if (decoded_samples < 0) {
- PJ_LOG(4, (THIS_FILE, "Decode failed!"));
- pj_mutex_unlock (opus_data->mutex);
- return PJMEDIA_CODEC_EFAILED;
- }
- output->size = decoded_samples * sizeof(opus_int16) *
- opus_data->cfg.channel_cnt;
- output->type = PJMEDIA_FRAME_TYPE_AUDIO;
- pj_mutex_unlock (opus_data->mutex);
- return PJ_SUCCESS;
- }
- /*
- * Recover lost frame.
- */
- static pj_status_t codec_recover( pjmedia_codec *codec,
- unsigned output_buf_len,
- struct pjmedia_frame *output )
- {
- struct opus_data *opus_data = (struct opus_data *)codec->codec_data;
- int decoded_samples;
- pjmedia_frame *inframe;
- int frm_size;
- PJ_UNUSED_ARG(output_buf_len);
- pj_mutex_lock (opus_data->mutex);
- if (opus_data->dec_frame_index == -1) {
- /* Recover the first packet? Don't think so, fill it with zeroes. */
- unsigned samples_per_frame;
- samples_per_frame = opus_data->cfg.sample_rate * opus_data->dec_ptime/
- opus_data->dec_ptime_denum / 1000;
- output->type = PJMEDIA_FRAME_TYPE_AUDIO;
- output->size = samples_per_frame << 1;
- pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame);
- pj_mutex_unlock (opus_data->mutex);
- return PJ_SUCCESS;
- }
- inframe = &opus_data->dec_frame[opus_data->dec_frame_index];
- frm_size = output->size / (sizeof(opus_int16) *
- opus_data->cfg.channel_cnt);
- if (inframe->type != PJMEDIA_FRAME_TYPE_AUDIO) {
- frm_size = PJ_MIN((unsigned)frm_size, opus_data->cfg.sample_rate *
- opus_data->dec_ptime / opus_data->dec_ptime_denum /
- 1000);
- }
- decoded_samples = opus_decode(opus_data->dec,
- inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ?
- inframe->buf : NULL,
- inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ?
- inframe->size : 0,
- (opus_int16*)output->buf,
- frm_size,
- 0);
- /* Mark current indexed frame as invalid */
- inframe->type = PJMEDIA_FRAME_TYPE_NONE;
-
- /* Update current frame index */
- opus_data->dec_frame_index++;
- if (opus_data->dec_frame_index > 1)
- opus_data->dec_frame_index = 0;
- /* Mark current indexed frame as invalid */
- inframe = &opus_data->dec_frame[opus_data->dec_frame_index];
- inframe->type = PJMEDIA_FRAME_TYPE_NONE;
- if (decoded_samples < 0) {
- PJ_LOG(4, (THIS_FILE, "Recover failed!"));
- pj_mutex_unlock (opus_data->mutex);
- return PJMEDIA_CODEC_EFAILED;
- }
- output->size = decoded_samples * sizeof(opus_int16) *
- opus_data->cfg.channel_cnt;
- output->type = PJMEDIA_FRAME_TYPE_AUDIO;
- output->timestamp = inframe->timestamp;
- pj_mutex_unlock (opus_data->mutex);
- return PJ_SUCCESS;
- }
- #if defined(_MSC_VER)
- # if 1 /* Change to 0 if Opus lib name is "opus.lib" */
- # pragma comment(lib, "libopus.a")
- # else
- # pragma comment(lib, "opus.lib")
- # endif
- #endif
- #endif /* PJMEDIA_HAS_OPUS_CODEC */
|