diff --git a/codecs/codec_opus_open_source.c b/codecs/codec_opus_open_source.c index abc7318..deecde9 100644 --- a/codecs/codec_opus_open_source.c +++ b/codecs/codec_opus_open_source.c @@ -60,7 +60,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: $") #include "asterisk/opus.h" /* for CODEC_OPUS_DEFAULT_* */ -#define BUFFER_SAMPLES 5760 +#define BUFFER_SAMPLES 11520 #define MAX_CHANNELS 2 #define OPUS_SAMPLES 960 @@ -90,6 +90,7 @@ struct opus_coder_pvt { int multiplier; int id; int16_t buf[BUFFER_SAMPLES]; + int16_t buf_stereo[BUFFER_SAMPLES * 2]; int framesize; int inited; int channels; @@ -106,7 +107,7 @@ struct opus_attr { unsigned int fec; unsigned int dtx; unsigned int spropmaxcapturerate; /* FIXME: not utilised, yet */ - unsigned int spropstereo; /* FIXME: currently, we are just mono */ + unsigned int spropstereo; }; /* Helper methods */ @@ -116,7 +117,7 @@ static int opus_encoder_construct(struct ast_trans_pvt *pvt, int sampling_rate) struct opus_attr *attr = pvt->explicit_dst ? ast_format_get_attribute_data(pvt->explicit_dst) : NULL; const opus_int32 bitrate = attr ? attr->maxbitrate : CODEC_OPUS_DEFAULT_BITRATE; const int maxplayrate = attr ? attr->maxplayrate : CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE; - const int channels = attr ? attr->stereo + 1 : CODEC_OPUS_DEFAULT_STEREO + 1; + const int channels = attr ? attr->spropstereo + 1 : CODEC_OPUS_DEFAULT_STEREO + 1; const opus_int32 vbr = attr ? !(attr->cbr) : !CODEC_OPUS_DEFAULT_CBR; const opus_int32 fec = attr ? attr->fec : CODEC_OPUS_DEFAULT_FEC; const opus_int32 dtx = attr ? attr->dtx : CODEC_OPUS_DEFAULT_DTX; @@ -150,11 +151,12 @@ static int opus_encoder_construct(struct ast_trans_pvt *pvt, int sampling_rate) opvt->sampling_rate = sampling_rate; opvt->multiplier = 48000 / sampling_rate; opvt->framesize = sampling_rate / 50; + opvt->channels = channels; opvt->id = ast_atomic_fetchadd_int(&usage.encoder_id, 1) + 1; ast_atomic_fetchadd_int(&usage.encoders, +1); - ast_debug(3, "Created encoder #%d (%d -> opus)\n", opvt->id, sampling_rate); + ast_debug(3, "Created encoder #%d (%d -> opus) (channels %d)\n", opvt->id, sampling_rate, opvt->channels); return 0; } @@ -162,12 +164,16 @@ static int opus_encoder_construct(struct ast_trans_pvt *pvt, int sampling_rate) static int opus_decoder_construct(struct ast_trans_pvt *pvt, struct ast_frame *f) { struct opus_coder_pvt *opvt = pvt->pvt; - /* struct opus_attr *attr = ast_format_get_attribute_data(f->subclass.format); */ + struct opus_attr *attr = ast_format_get_attribute_data(f->subclass.format); int error = 0; opvt->sampling_rate = pvt->t->dst_codec.sample_rate; opvt->multiplier = 48000 / opvt->sampling_rate; - opvt->channels = /* attr ? attr->spropstereo + 1 :*/ 1; /* FIXME */; + + opvt->channels = CODEC_OPUS_DEFAULT_STEREO + 1; + if (attr != NULL) { + opvt->channels = attr->stereo + 1; + } opvt->opus = opus_decoder_create(opvt->sampling_rate, opvt->channels, &error); @@ -180,7 +186,7 @@ static int opus_decoder_construct(struct ast_trans_pvt *pvt, struct ast_frame *f ast_atomic_fetchadd_int(&usage.decoders, +1); - ast_debug(3, "Created decoder #%d (opus -> %d)\n", opvt->id, opvt->sampling_rate); + ast_debug(3, "Created decoder #%d (opus -> %d) (channels %d)\n", opvt->id, opvt->sampling_rate, opvt->channels); return 0; } @@ -205,6 +211,16 @@ static int lintoopus_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) { struct opus_coder_pvt *opvt = pvt->pvt; + /* One time upgrade to stereo. */ + struct opus_attr *attr = ast_format_get_attribute_data(pvt->f.subclass.format); + if (attr != NULL) { + if (attr->spropstereo + 1 != opvt->channels) { + ast_log(LOG_ERROR, "Changing attr %d opvt channels %d \n", attr->stereo, opvt->channels); + opus_encoder_destroy(opvt->opus); + opus_encoder_construct(pvt, pvt->t->src_codec.sample_rate); + } + } + /* XXX We should look at how old the rest of our stream is, and if it is too old, then we should overwrite it entirely, otherwise we can get artifacts of earlier talk that do not belong */ @@ -220,17 +236,30 @@ static struct ast_frame *lintoopus_frameout(struct ast_trans_pvt *pvt) struct ast_frame *result = NULL; struct ast_frame *last = NULL; int samples = 0; /* output samples */ + int interleaved = pvt->interleaved_stereo ? 2 : 1; + int status; + int i; + int k; + + while (pvt->samples >= opvt->framesize * interleaved) { + + if (opvt->channels == 2 && pvt->interleaved_stereo == 0) { + /* No stereo samples, but stereo output (put the same audio on both channels). */ + opus_int16 stereobuf[opvt->framesize * 2]; + k = 0; + for (i = 0; i < opvt->framesize * 2; i += 2) { + stereobuf[i] = opvt->buf[samples + k]; + stereobuf[i + 1] = opvt->buf[samples + k]; + k++; + } + status = opus_encode(opvt->opus, stereobuf, opvt->framesize, pvt->outbuf.uc, BUFFER_SAMPLES); + } else { + /* Stereo source (interleaved format) and stereo output or everything mono. */ + status = opus_encode(opvt->opus, opvt->buf + samples, opvt->framesize, pvt->outbuf.uc, BUFFER_SAMPLES); + } - while (pvt->samples >= opvt->framesize) { - /* status is either error or output bytes */ - const int status = opus_encode(opvt->opus, - opvt->buf + samples, - opvt->framesize, - pvt->outbuf.uc, - BUFFER_SAMPLES); - - samples += opvt->framesize; - pvt->samples -= opvt->framesize; + samples += opvt->framesize * interleaved; + pvt->samples -= opvt->framesize * interleaved; if (status < 0) { ast_log(LOG_ERROR, "Error encoding the Opus frame: %s\n", opus_strerror(status)); @@ -267,6 +296,8 @@ static int opustolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) opus_int32 len; unsigned char *src; int status; + int k; + int i; if (!opvt->inited && f->datalen == 0) { return 0; /* we cannot start without data */ @@ -291,6 +322,28 @@ static int opustolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) } decode_fec = opvt->decode_fec_incoming; + /* + * In case of stereo, we do not use FEC or PLC (yet). + */ + if (opvt->channels == 2) { + /* + * If we have incoming stereo signals, we will only copy one channel to lin. + * Asterisk can't handle stereo signals internally at the moment. + */ + status = opus_decode(opvt->opus, f->data.ptr, f->datalen, opvt->buf_stereo, BUFFER_SAMPLES, decode_fec); + if (status < 0) { + ast_log(LOG_ERROR, "Error decoding the Opus stereo frame: %s\n", opus_strerror(status)); + } + k = 0; + for (i = 0; i < status * 2; i += 2) { + pvt->outbuf.i16[k] = opvt->buf_stereo[i]; + k++; + } + pvt->samples += status; + pvt->datalen += status * opvt->channels * sizeof(int16_t); + return 0; + } + /* * The Opus Codec, actually its library allows * - Forward-Error Correction (FEC), and diff --git a/res/res_format_attr_opus.c b/res/res_format_attr_opus.c deleted file mode 100644 index b5b42ab..0000000 --- a/res/res_format_attr_opus.c +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2013, Digium, Inc. - * - * Lorenzo Miniero - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! - * \file - * \brief Opus format attribute interface - * - * \author Lorenzo Miniero - */ - -/*** MODULEINFO - core - ***/ - -#include "asterisk.h" - -#if defined(ASTERISK_REGISTER_FILE) -ASTERISK_REGISTER_FILE() -#elif defined(ASTERISK_FILE_VERSION) -ASTERISK_FILE_VERSION(__FILE__, "$Revision: $") -#endif - -#include "asterisk/module.h" -#include "asterisk/format.h" -#include "asterisk/logger.h" /* for ast_log, LOG_WARNING */ -#include "asterisk/opus.h" /* for CODEC_OPUS_DEFAULT_* */ -#include "asterisk/strings.h" /* for ast_str_append */ -#include "asterisk/utils.h" /* for MIN, ast_malloc, ast_free */ - -/*! - * \brief Opus attribute structure. - * - * \note http://tools.ietf.org/html/rfc7587#section-6 - */ -struct opus_attr { - unsigned int maxbitrate; - unsigned int maxplayrate; - unsigned int unused; /* was minptime, kept for binary compatibility */ - unsigned int stereo; - unsigned int cbr; - unsigned int fec; - unsigned int dtx; - unsigned int spropmaxcapturerate; - unsigned int spropstereo; -}; - -static struct opus_attr default_opus_attr = { - .maxplayrate = CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE, - .spropmaxcapturerate = CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE, - .maxbitrate = CODEC_OPUS_DEFAULT_BITRATE, - .stereo = CODEC_OPUS_DEFAULT_STEREO, - .spropstereo = CODEC_OPUS_DEFAULT_STEREO, - .cbr = CODEC_OPUS_DEFAULT_CBR, - .fec = CODEC_OPUS_DEFAULT_FEC, - .dtx = CODEC_OPUS_DEFAULT_DTX, -}; - -static void opus_destroy(struct ast_format *format) -{ - struct opus_attr *attr = ast_format_get_attribute_data(format); - - ast_free(attr); -} - -static int opus_clone(const struct ast_format *src, struct ast_format *dst) -{ - struct opus_attr *original = ast_format_get_attribute_data(src); - struct opus_attr *attr = ast_malloc(sizeof(*attr)); - - if (!attr) { - return -1; - } - - if (original) { - *attr = *original; - } else { - *attr = default_opus_attr; - } - - ast_format_set_attribute_data(dst, attr); - - return 0; -} - -static struct ast_format *opus_parse_sdp_fmtp(const struct ast_format *format, const char *attributes) -{ - struct ast_format *cloned; - struct opus_attr *attr; - const char *kvp; - unsigned int val; - - cloned = ast_format_clone(format); - if (!cloned) { - return NULL; - } - attr = ast_format_get_attribute_data(cloned); - - if ((kvp = strstr(attributes, "maxplaybackrate")) && sscanf(kvp, "maxplaybackrate=%30u", &val) == 1) { - attr->maxplayrate = val; - } else { - attr->maxplayrate = 48000; - } - - if ((kvp = strstr(attributes, "sprop-maxcapturerate")) && sscanf(kvp, "sprop-maxcapturerate=%30u", &val) == 1) { - attr->spropmaxcapturerate = val; - } else { - attr->spropmaxcapturerate = 48000; - } - - if ((kvp = strstr(attributes, "maxaveragebitrate")) && sscanf(kvp, "maxaveragebitrate=%30u", &val) == 1) { - attr->maxbitrate = val; - } else { - attr->maxbitrate = 510000; - } - - if (!strncmp(attributes, "stereo=1", 8)) { - attr->stereo = 1; - } else if (strstr(attributes, " stereo=1")) { - attr->stereo = 1; - } else if (strstr(attributes, ";stereo=1")) { - attr->stereo = 1; - } else { - attr->stereo = 0; - } - - if (strstr(attributes, "sprop-stereo=1")) { - attr->spropstereo = 1; - } else { - attr->spropstereo = 0; - } - - if (strstr(attributes, "cbr=1")) { - attr->cbr = 1; - } else { - attr->cbr = 0; - } - - if (strstr(attributes, "useinbandfec=1")) { - attr->fec = 1; - } else { - attr->fec = 0; - } - - if (strstr(attributes, "usedtx=1")) { - attr->dtx = 1; - } else { - attr->dtx = 0; - } - - return cloned; -} - -static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str) -{ - struct opus_attr *attr = ast_format_get_attribute_data(format); - int added = 0; - - if (!attr) { - /* - * (Only) cached formats do not have attribute data assigned because - * they were created before this attribute module was registered. - * Therefore, we assume the default attribute values here. - */ - attr = &default_opus_attr; - } - - if (48000 != attr->maxplayrate) { - if (added) { - ast_str_append(str, 0, ";"); - } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { - added = 1; - } - ast_str_append(str, 0, "maxplaybackrate=%u", attr->maxplayrate); - } - - if (48000 != attr->spropmaxcapturerate) { - if (added) { - ast_str_append(str, 0, ";"); - } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { - added = 1; - } - ast_str_append(str, 0, "sprop-maxcapturerate=%u", attr->spropmaxcapturerate); - } - - if (510000 != attr->maxbitrate) { - if (added) { - ast_str_append(str, 0, ";"); - } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { - added = 1; - } - ast_str_append(str, 0, "maxaveragebitrate=%u", attr->maxbitrate); - } - - if (0 != attr->stereo) { - if (added) { - ast_str_append(str, 0, ";"); - } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { - added = 1; - } - ast_str_append(str, 0, "stereo=%u", attr->stereo); - } - - if (0 != attr->spropstereo) { - if (added) { - ast_str_append(str, 0, ";"); - } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { - added = 1; - } - ast_str_append(str, 0, "sprop-stereo=%u", attr->spropstereo); - } - - if (0 != attr->cbr) { - if (added) { - ast_str_append(str, 0, ";"); - } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { - added = 1; - } - ast_str_append(str, 0, "cbr=%u", attr->cbr); - } - - if (0 != attr->fec) { - if (added) { - ast_str_append(str, 0, ";"); - } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { - added = 1; - } - ast_str_append(str, 0, "useinbandfec=%u", attr->fec); - } - - if (0 != attr->dtx) { - if (added) { - ast_str_append(str, 0, ";"); - } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { - added = 1; - } - ast_str_append(str, 0, "usedtx=%u", attr->dtx); - } - - if (added) { - ast_str_append(str, 0, "\r\n"); - } -} - -static struct ast_format *opus_getjoint(const struct ast_format *format1, const struct ast_format *format2) -{ - struct opus_attr *attr1 = ast_format_get_attribute_data(format1); - struct opus_attr *attr2 = ast_format_get_attribute_data(format2); - struct ast_format *jointformat; - struct opus_attr *attr_res; - - if (!attr1) { - attr1 = &default_opus_attr; - } - - if (!attr2) { - attr2 = &default_opus_attr; - } - - jointformat = ast_format_clone(format1); - if (!jointformat) { - return NULL; - } - attr_res = ast_format_get_attribute_data(jointformat); - - attr_res->dtx = attr1->dtx || attr2->dtx ? 1 : 0; - - /* Only do FEC if both sides want it. If a peer specifically requests not - * to receive with FEC, it may be a waste of bandwidth. */ - attr_res->fec = attr1->fec && attr2->fec ? 1 : 0; - - attr_res->cbr = attr1->cbr || attr2->cbr ? 1 : 0; - attr_res->spropstereo = attr1->spropstereo || attr2->spropstereo ? 1 : 0; - - /* Only do stereo if both sides want it. If a peer specifically requests not - * to receive stereo signals, it may be a waste of bandwidth. */ - attr_res->stereo = attr1->stereo && attr2->stereo ? 1 : 0; - - attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate); - attr_res->spropmaxcapturerate = MIN(attr1->spropmaxcapturerate, attr2->spropmaxcapturerate); - attr_res->maxplayrate = MIN(attr1->maxplayrate, attr2->maxplayrate); - - return jointformat; -} - -static struct ast_format *opus_set(const struct ast_format *format, const char *name, const char *value) -{ - struct ast_format *cloned; - struct opus_attr *attr; - unsigned int val; - - if (sscanf(value, "%30u", &val) != 1) { - ast_log(LOG_WARNING, "Unknown value '%s' for attribute type '%s'\n", - value, name); - return NULL; - } - - cloned = ast_format_clone(format); - if (!cloned) { - return NULL; - } - attr = ast_format_get_attribute_data(cloned); - - if (!strcasecmp(name, "max_bitrate")) { - attr->maxbitrate = val; - } else if (!strcasecmp(name, "max_playrate")) { - attr->maxplayrate = val; - } else if (!strcasecmp(name, "minptime")) { - attr->unused = val; - } else if (!strcasecmp(name, "stereo")) { - attr->stereo = val; - } else if (!strcasecmp(name, "cbr")) { - attr->cbr = val; - } else if (!strcasecmp(name, "fec")) { - attr->fec = val; - } else if (!strcasecmp(name, "dtx")) { - attr->dtx = val; - } else if (!strcasecmp(name, "sprop_capture_rate")) { - attr->spropmaxcapturerate = val; - } else if (!strcasecmp(name, "sprop_stereo")) { - attr->spropstereo = val; - } else { - ast_log(LOG_WARNING, "unknown attribute type %s\n", name); - } - - return cloned; -} - -static struct ast_format_interface opus_interface = { - .format_destroy = opus_destroy, - .format_clone = opus_clone, - .format_get_joint = opus_getjoint, - .format_attribute_set = opus_set, - .format_parse_sdp_fmtp = opus_parse_sdp_fmtp, - .format_generate_sdp_fmtp = opus_generate_sdp_fmtp, -}; - -static int load_module(void) -{ - if (ast_format_interface_register("opus", &opus_interface)) { - return AST_MODULE_LOAD_DECLINE; - } - - return AST_MODULE_LOAD_SUCCESS; -} - -static int unload_module(void) -{ - return 0; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Opus Format Attribute Module", - .support_level = AST_MODULE_SUPPORT_CORE, - .load = load_module, - .unload = unload_module, - .load_pri = AST_MODPRI_CHANNEL_DEPEND, -);