diff --git a/core/plug-in/opus/opus.c b/core/plug-in/opus/opus.c index 57a92bc4..664adb8b 100644 --- a/core/plug-in/opus/opus.c +++ b/core/plug-in/opus/opus.c @@ -47,6 +47,7 @@ */ #include +#include #define _OPUS_APPLICATION_ OPUS_APPLICATION_VOIP /* Allowed values: @@ -88,6 +89,10 @@ static long opus_create(const char* format_parameters, const char** format_param amci_codec_fmt_info_t** format_description); static void opus_destroy(long h_inst); +static int opus_negotiate_fmt(int is_offer, const char* params_in, char* params_out, unsigned int params_out_len); + +static int opus_load(const char* ModConfigPath); + #if SYSTEM_SAMPLECLOCK_RATE >= 48000 #define _OPUS_RATE 48000 #elif SYSTEM_SAMPLECLOCK_RATE >= 24000 @@ -103,13 +108,14 @@ static void opus_destroy(long h_inst); static amci_codec_fmt_info_t opus_fmt_description[] = { {AMCI_FMT_FRAME_LENGTH, 20}, {AMCI_FMT_FRAME_SIZE, 20 * _OPUS_RATE / 1000}, {0,0}}; -BEGIN_EXPORTS( "opus" , AMCI_NO_MODULEINIT, AMCI_NO_MODULEDESTROY ) +BEGIN_EXPORTS( "opus" , opus_load, AMCI_NO_MODULEDESTROY ) BEGIN_CODECS - CODEC( CODEC_OPUS, pcm16_2_opus, opus_2_pcm16, opus_plc, + CODEC_WITH_FMT( CODEC_OPUS, pcm16_2_opus, opus_2_pcm16, opus_plc, opus_create, opus_destroy, - NULL, NULL ) + NULL, NULL , + opus_negotiate_fmt) END_CODECS BEGIN_PAYLOADS @@ -126,13 +132,154 @@ typedef struct { OpusDecoder* opus_dec; } opus_state_t; +/* e.g. "maxplaybackrate=8000; stereo=0; useinbandfec=1" */ +char default_format_parameters[80]; + +int opus_load(const char* ModConfigPath) { + default_format_parameters[0]='\0'; + char conf_file[256]; + if (NULL != ModConfigPath) { + sprintf(conf_file, "%sopus.conf",ModConfigPath); + FILE* fp = fopen(conf_file, "rt"); + if (fp) { + char line[80]; + while(fgets(line, 80, fp) != NULL) { + if (!line[0] ||line[0]=='#') + continue; + strcpy(default_format_parameters, line); + break; + } + DBG("initialized default format parameters as '%s'\n", default_format_parameters); + fclose(fp); + } + } + DBG("OPUS: initialized\n"); + return 0; +} + +int opus_negotiate_fmt(int is_offer, const char* params_in, char* params_out, unsigned int params_out_len) { + // todo: properly negotiating features + strncpy(params_out, default_format_parameters, params_out_len); + return 0; +} + +/* + Search for a parameter assignement in input string. + If it's not found *param_value is null, otherwise *param_value points to the + right hand term. + In both cases a pointer suitable for a new search is returned +*/ +static char* read_param(char* input, const char *param, char** param_value) +{ + int param_size; + + /* Eat spaces and semi-colons */ + while (*input && (*input==' ' || *input==';' || *input=='"')) + input; + + *param_value = NULL; + param_size = strlen(param); + if (strncmp(input, param, param_size)) + return input; + if (*(input+param_size) != '=') + return input; + input=param_size+1; + + /* Found and discarded a matching parameter */ + *param_value = input; + while (*input && *input!=' ' && *input!=';' && *input!='"') + input; + if (*input=='"') + { + *param_value = *param_value+1; /* remove " */ + /* string will end after next: " */ + while (*input && *input!='"' && *input!='\r' && *input!='\n') + input; + if (*input=='"') + input--; /* remove " */ + } + if (*input) + *input = 0; + + return input; +} + +#define BLEN 63 + +void decode_format_parameters(const char* format_parameters, unsigned int* maxbandwidth, int* useinbandfec, int* stereo) { + if (format_parameters && strlen(format_parameters)<=BLEN){ + + char buffer2[BLEN+1]; + char *buffer = buffer2; + + strcpy(buffer, format_parameters); + + while (*buffer) { + char *param_value; + + /* maxplaybackrate */ + buffer=read_param(buffer, "maxplaybackrate", ¶m_value); + if (param_value) { + *maxbandwidth = atoi(param_value); + if (!*maxbandwidth) { + *maxbandwidth = _OPUS_RATE; + DBG("wrong maxbandwidth value '%s'\n", param_value); + } + continue; + } + + /* stereo */ + buffer=read_param(buffer, "stereo", ¶m_value); + if (param_value) { + if (*param_value == '1') + *stereo = 1; + else + *stereo = 0; + + continue; + } + + /* useinbandfec */ + buffer=read_param(buffer, "useinbandfec", ¶m_value); + if (param_value) { + if (*param_value == '1') + *useinbandfec = 1; + else + *useinbandfec = 0; + + continue; + } + + /* Unknown parameter */ + if (*buffer) { + param_value = buffer; + while (*buffer && *buffer!=';') + buffer; + + if (*buffer) + *buffer = 0; + + DBG("OPUS: SDP parameter fmtp: %s ignored in creating encoder.\n", param_value); + } + } + } +} + long opus_create(const char* format_parameters, const char** format_parameters_out, amci_codec_fmt_info_t** format_description) { opus_state_t* codec_inst; int error; - + + unsigned int maxbandwidth = _OPUS_RATE; + int useinbandfec = _OPUS_INBAND_FEC_; + int stereo = 0; + if (format_parameters) { + DBG("\n\n\n"); DBG("OPUS params: >>%s<<.\n", format_parameters); + DBG("\n\n\n"); + + decode_format_parameters(format_parameters, &maxbandwidth, &useinbandfec, &stereo); } codec_inst = (opus_state_t*)malloc(sizeof(opus_state_t)); @@ -140,17 +287,34 @@ long opus_create(const char* format_parameters, const char** format_parameters_o if (!codec_inst) return -1; + DBG("OPUS: creating encoder with maxbandwidth=%u, stereo=%s, useinbandfec=%s\n", + maxbandwidth, stereo?"true":"false", useinbandfec?"true":"false"); + codec_inst->opus_enc = opus_encoder_create(_OPUS_RATE,1,_OPUS_APPLICATION_,&error); if (error) { DBG("OPUS: error %d while creating encoder state.\n", error); return -1; } - opus_encoder_ctl(codec_inst->opus_enc, OPUS_SET_FORCE_CHANNELS(1)); - opus_encoder_ctl(codec_inst->opus_enc, OPUS_SET_MAX_BANDWIDTH(_OPUS_MAX_BANDWIDTH_)); + opus_encoder_ctl(codec_inst->opus_enc, OPUS_SET_FORCE_CHANNELS(stereo ? 2:1)); + + unsigned int opus_set_bw = _OPUS_RATE; + if (maxbandwidth <= 8000) { + opus_set_bw = OPUS_BANDWIDTH_NARROWBAND; + } else if (maxbandwidth <= 12000) { + opus_set_bw = OPUS_BANDWIDTH_MEDIUMBAND; + } else if (maxbandwidth <= 16000) { + opus_set_bw = OPUS_BANDWIDTH_WIDEBAND; + } else if (maxbandwidth <= 24000) { + opus_set_bw = OPUS_BANDWIDTH_SUPERWIDEBAND; + } else { + opus_set_bw = OPUS_BANDWIDTH_FULLBAND; + } + opus_encoder_ctl(codec_inst->opus_enc, OPUS_SET_MAX_BANDWIDTH(opus_set_bw)); + opus_encoder_ctl(codec_inst->opus_enc, OPUS_SET_PACKET_LOSS_PERC(_OPUS_PKT_LOSS_PCT_)); opus_encoder_ctl(codec_inst->opus_enc, OPUS_SET_COMPLEXITY(_OPUS_COMPLEXITY_)); - opus_encoder_ctl(codec_inst->opus_enc, OPUS_SET_INBAND_FEC(_OPUS_INBAND_FEC_)); + opus_encoder_ctl(codec_inst->opus_enc, OPUS_SET_INBAND_FEC(useinbandfec ? 1:0)); opus_encoder_ctl(codec_inst->opus_enc, OPUS_SET_DTX(_OPUS_DTX_)); codec_inst->opus_dec = opus_decoder_create(_OPUS_RATE,1,&error);