From cf1329df63a671ef7b2af0d435b87f6a0fb2f922 Mon Sep 17 00:00:00 2001 From: Brian West Date: Tue, 18 Feb 2014 22:21:46 -0600 Subject: [PATCH] FS-6209 --resolve --- conf/vanilla/autoload_configs/opus.conf.xml | 6 + src/mod/codecs/mod_opus/mod_opus.c | 246 ++++++++++++-------- 2 files changed, 151 insertions(+), 101 deletions(-) create mode 100644 conf/vanilla/autoload_configs/opus.conf.xml diff --git a/conf/vanilla/autoload_configs/opus.conf.xml b/conf/vanilla/autoload_configs/opus.conf.xml new file mode 100644 index 0000000000..bde782a8a6 --- /dev/null +++ b/conf/vanilla/autoload_configs/opus.conf.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/mod/codecs/mod_opus/mod_opus.c b/src/mod/codecs/mod_opus/mod_opus.c index 8be3631ac2..0dadd18134 100644 --- a/src/mod/codecs/mod_opus/mod_opus.c +++ b/src/mod/codecs/mod_opus/mod_opus.c @@ -1,4 +1,4 @@ -/* +/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2014, Anthony Minessale II * @@ -22,8 +22,9 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): - * + * * Brian K. West + * Noel Morgan * * mod_opus.c -- The OPUS ultra-low delay audio codec (http://www.opus-codec.org/) * @@ -72,26 +73,33 @@ struct opus_context { int frame_size; }; +struct { + int use_vbr; + int complexity; + switch_mutex_t *mutex; +} opus_prefs; + + static switch_status_t switch_opus_fmtp_parse(const char *fmtp, switch_codec_fmtp_t *codec_fmtp) { if (codec_fmtp) { opus_codec_settings_t local_settings = { 0 }; opus_codec_settings_t *codec_settings = &local_settings; - + if (codec_fmtp->private_info) { codec_settings = codec_fmtp->private_info; if (zstr(fmtp)) { memcpy(codec_settings, &default_codec_settings, sizeof(*codec_settings)); } } - + if (fmtp) { int x, argc; char *argv[10]; char *fmtp_dup = strdup(fmtp); - + switch_assert(fmtp_dup); - + argc = switch_separate_string(fmtp_dup, ';', argv, (sizeof(argv) / sizeof(argv[0]))); for (x = 0; x < argc; x++) { char *data = argv[x]; @@ -101,27 +109,27 @@ static switch_status_t switch_opus_fmtp_parse(const char *fmtp, switch_codec_fmt data++; } - + if ((arg = strchr(data, '='))) { *arg++ = '\0'; - + if (codec_settings) { if (!strcasecmp(data, "useinbandfec")) { codec_settings->useinbandfec = switch_true(arg); } - + if (!strcasecmp(data, "usedtx")) { codec_settings->usedtx = switch_true(arg); } - + if (!strcasecmp(data, "sprop-maxcapturerate")) { codec_settings->sprop_maxcapturerate = atoi(arg); } - + if (!strcasecmp(data, "maxptime")) { codec_settings->maxptime = atoi(arg); } - + if (!strcasecmp(data, "minptime")) { codec_settings->minptime = atoi(arg); } @@ -130,54 +138,54 @@ static switch_status_t switch_opus_fmtp_parse(const char *fmtp, switch_codec_fmt codec_settings->ptime = atoi(arg); codec_fmtp->microseconds_per_packet = codec_settings->ptime * 1000; } - + if (!strcasecmp(data, "samplerate")) { codec_settings->samplerate = atoi(arg); codec_fmtp->actual_samples_per_second = codec_settings->samplerate; } - + if (!strcasecmp(data, "maxaveragebitrate")) { codec_settings->maxaveragebitrate = atoi(arg); switch(codec_fmtp->actual_samples_per_second) { case 8000: - { - if(codec_settings->maxaveragebitrate < 6000 || codec_settings->maxaveragebitrate > 20000) { - codec_settings->maxaveragebitrate = 20000; - } - break; - } + { + if(codec_settings->maxaveragebitrate < 6000 || codec_settings->maxaveragebitrate > 20000) { + codec_settings->maxaveragebitrate = 20000; + } + break; + } case 12000: - { - if(codec_settings->maxaveragebitrate < 7000 || codec_settings->maxaveragebitrate > 25000) { - codec_settings->maxaveragebitrate = 25000; - } - break; - } + { + if(codec_settings->maxaveragebitrate < 7000 || codec_settings->maxaveragebitrate > 25000) { + codec_settings->maxaveragebitrate = 25000; + } + break; + } case 16000: - { - if(codec_settings->maxaveragebitrate < 8000 || codec_settings->maxaveragebitrate > 30000) { - codec_settings->maxaveragebitrate = 30000; - } - break; - } + { + if(codec_settings->maxaveragebitrate < 8000 || codec_settings->maxaveragebitrate > 30000) { + codec_settings->maxaveragebitrate = 30000; + } + break; + } case 24000: - { - if(codec_settings->maxaveragebitrate < 12000 || codec_settings->maxaveragebitrate > 40000) { - codec_settings->maxaveragebitrate = 40000; - } - break; - } - + { + if(codec_settings->maxaveragebitrate < 12000 || codec_settings->maxaveragebitrate > 40000) { + codec_settings->maxaveragebitrate = 40000; + } + break; + } + default: /* this should never happen but 20000 is common among all rates */ codec_settings->maxaveragebitrate = 20000; break; } - - + + codec_fmtp->bits_per_second = codec_settings->maxaveragebitrate; } - + } } } @@ -192,42 +200,42 @@ static switch_status_t switch_opus_fmtp_parse(const char *fmtp, switch_codec_fmt static char *gen_fmtp(opus_codec_settings_t *settings, switch_memory_pool_t *pool) { char buf[256] = ""; - + if (settings->useinbandfec) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "useinbandfec=1;"); } - + if (settings->usedtx) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "usedtx=1;"); } - + if (settings->maxaveragebitrate) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "maxaveragebitrate=%d;", settings->maxaveragebitrate); - + } - + if (settings->ptime) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "ptime=%d;", settings->ptime); } - + if (settings->minptime) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "minptime=%d;", settings->minptime); } - + if (settings->maxptime) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "maxptime=%d;", settings->maxptime); } - + if (settings->samplerate) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "samplerate=%d;", settings->samplerate); } - + if (end_of(buf) == ';') { end_of(buf) = '\0'; } return switch_core_strdup(pool, buf); - + } static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) @@ -237,36 +245,36 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag int decoding = (flags & SWITCH_CODEC_FLAG_DECODE); switch_codec_fmtp_t codec_fmtp; opus_codec_settings_t opus_codec_settings = { 0 }; - + if (!(encoding || decoding) || (!(context = switch_core_alloc(codec->memory_pool, sizeof(*context))))) { return SWITCH_STATUS_FALSE; } - + context->frame_size = codec->implementation->samples_per_packet; - + memset(&codec_fmtp, '\0', sizeof(struct switch_codec_fmtp)); codec_fmtp.private_info = &opus_codec_settings; switch_opus_fmtp_parse(codec->fmtp_in, &codec_fmtp); - + codec->fmtp_out = gen_fmtp(&opus_codec_settings, codec->memory_pool); - + if (encoding) { /* come up with a way to specify these */ int bitrate_bps = OPUS_AUTO; - int use_vbr = 1; - int complexity = 10; + int use_vbr = opus_prefs.use_vbr; + int complexity = opus_prefs.complexity; int err; int samplerate = opus_codec_settings.samplerate ? opus_codec_settings.samplerate : codec->implementation->actual_samples_per_second; - + context->encoder_object = opus_encoder_create(samplerate, codec->implementation->number_of_channels, OPUS_APPLICATION_VOIP, &err); - - if (err != OPUS_OK) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot create encoder: %s\n", opus_strerror(err)); - return SWITCH_STATUS_GENERR; - } - + + if (err != OPUS_OK) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot create encoder: %s\n", opus_strerror(err)); + return SWITCH_STATUS_GENERR; + } + opus_encoder_ctl(context->encoder_object, OPUS_SET_BITRATE(bitrate_bps)); if (codec->implementation->actual_samples_per_second == 8000) { @@ -275,14 +283,14 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag } else { opus_encoder_ctl(context->encoder_object, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); } - + opus_encoder_ctl(context->encoder_object, OPUS_SET_VBR(use_vbr)); opus_encoder_ctl(context->encoder_object, OPUS_SET_COMPLEXITY(complexity)); - + if (opus_codec_settings.useinbandfec) { opus_encoder_ctl(context->encoder_object, OPUS_SET_INBAND_FEC(opus_codec_settings.useinbandfec)); } - + if (opus_codec_settings.usedtx) { opus_encoder_ctl(context->encoder_object, OPUS_SET_DTX(opus_codec_settings.usedtx)); } @@ -290,33 +298,33 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag if (decoding) { int err; - - context->decoder_object = opus_decoder_create(codec->implementation->actual_samples_per_second, + + context->decoder_object = opus_decoder_create(codec->implementation->actual_samples_per_second, codec->implementation->number_of_channels, &err); - + if (err != OPUS_OK) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot create decoder: %s\n", opus_strerror(err)); - + if (context->encoder_object) { opus_encoder_destroy(context->encoder_object); context->encoder_object = NULL; } - + return SWITCH_STATUS_GENERR; } - + } - + codec->private_info = context; - + return SWITCH_STATUS_SUCCESS; } static switch_status_t switch_opus_destroy(switch_codec_t *codec) { struct opus_context *context = codec->private_info; - + if (context) { if (context->decoder_object) { opus_decoder_destroy(context->decoder_object); @@ -327,7 +335,7 @@ static switch_status_t switch_opus_destroy(switch_codec_t *codec) context->encoder_object = NULL; } } - + codec->private_info = NULL; return SWITCH_STATUS_SUCCESS; } @@ -342,20 +350,20 @@ static switch_status_t switch_opus_encode(switch_codec_t *codec, struct opus_context *context = codec->private_info; int bytes = 0; int len = (int) *encoded_data_len; - + if (!context) { return SWITCH_STATUS_FALSE; } - + if (len > 1275) len = 1275; - + bytes = opus_encode(context->encoder_object, (void *) decoded_data, decoded_data_len / 2, (unsigned char *) encoded_data, len); - + if (bytes > 0) { *encoded_data_len = (uint32_t) bytes; return SWITCH_STATUS_SUCCESS; } - + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Encoder Error!\n"); return SWITCH_STATUS_GENERR; } @@ -369,22 +377,53 @@ static switch_status_t switch_opus_decode(switch_codec_t *codec, { struct opus_context *context = codec->private_info; int samples = 0; - + if (!context) { return SWITCH_STATUS_FALSE; } samples = opus_decode(context->decoder_object, (*flag & SFF_PLC) ? NULL : encoded_data, encoded_data_len, decoded_data, *decoded_data_len, 0); - + if (samples < 0) { return SWITCH_STATUS_GENERR; } - + *decoded_data_len = samples * 2; return SWITCH_STATUS_SUCCESS; } +static switch_status_t opus_load_config(switch_bool_t reload) +{ + char *cf = "opus.conf"; + switch_xml_t cfg, xml = NULL, param, settings; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Opening of %s failed\n", cf); + status = SWITCH_STATUS_TERM; + } + + if ((settings = switch_xml_child(cfg, "settings"))) { + for (param = switch_xml_child(settings, "param"); param; param = param->next) { + char *key = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(key, "use_vbr") && !zstr(val)) { + opus_prefs.use_vbr = atoi(val); + } else if (!strcasecmp(key, "complexity")) { + opus_prefs.complexity = atoi(val); + } + } + } + + if (xml) { + switch_xml_free(xml); + } + + return status; +} + SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load) { switch_codec_interface_t *codec_interface; @@ -396,25 +435,30 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load) int bits = 0; char *dft_fmtp = NULL; opus_codec_settings_t settings = { 0 }; - + switch_status_t status = SWITCH_FALSE; + + if ((status = opus_load_config(SWITCH_FALSE)) != SWITCH_STATUS_SUCCESS) { + return status; + } + /* connect my internal structure to the blank pointer passed to me */ *module_interface = switch_loadable_module_create_module_interface(pool, modname); - + SWITCH_ADD_CODEC(codec_interface, "OPUS (STANDARD)"); - + codec_interface->parse_fmtp = switch_opus_fmtp_parse; - + settings = default_codec_settings; - - + + for (x = 0; x < 3; x++) { - + settings.ptime = mss / 1000; settings.maxptime = settings.ptime; settings.minptime = settings.ptime; settings.samplerate = rate; dft_fmtp = gen_fmtp(&settings, pool); - + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ 116, /* the IANA code number */ "opus",/* the IANA code name */ @@ -436,23 +480,23 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load) bytes *= 2; samples *= 2; mss *= 2; - + } - + samples = 80; bytes = 160; mss = 10000; rate = 8000; for (x = 0; x < 3; x++) { - + settings.ptime = mss / 1000; settings.maxptime = settings.ptime; settings.minptime = settings.ptime; settings.samplerate = rate; dft_fmtp = gen_fmtp(&settings, pool); - + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ 116, /* the IANA code number */ "opus",/* the IANA code name */ @@ -470,13 +514,13 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load) switch_opus_encode, /* function to encode raw data into encoded data */ switch_opus_decode, /* function to decode encoded data into raw data */ switch_opus_destroy); /* deinitalize a codec handle using this implementation */ - + bytes += 160; samples += 80; mss += 10000; } - + /* indicate that the module should continue to be loaded */ return SWITCH_STATUS_SUCCESS; }