SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load);
SWITCH_MODULE_DEFINITION(mod_opus, mod_opus_load, NULL, NULL);
+/*! \brief Various codec settings */
+struct opus_codec_settings {
+ int useinbandfec;
+ int usedtx;
+ int maxaveragebitrate;
+ int stereo;
+ int cbr;
+ int sprop_maxcapturerate;
+ int sprop_stereo;
+ int maxptime;
+ int minptime;
+ int ptime;
+ int samplerate;
+};
+typedef struct opus_codec_settings opus_codec_settings_t;
+
+static opus_codec_settings_t default_codec_settings = {
+ /*.useinbandfec */ 1,
+ /*.usedtx */ 1,
+ /*.maxaveragebitrate */ 30000,
+ /*.stereo*/ 0,
+ /*.cbr*/ 0,
+ /*.sprop_maxcapturerate*/ 0,
+ /*.sprop_stereo*/ 0,
+ /*.maxptime*/ 0,
+ /*.minptime*/ 0,
+ /*.ptime*/ 0,
+ /*.samplerate*/ 0
+};
+
struct opus_context {
OpusEncoder *encoder_object;
OpusDecoder *decoder_object;
int frame_size;
};
+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];
+ char *arg;
+ switch_assert(data);
+ while (*data == ' ') {
+ 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);
+ }
+
+ if (!strcasecmp(data, "ptime")) {
+ 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;
+ }
+ case 12000:
+ {
+ 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;
+ }
+ case 24000:
+ {
+ 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;
+ }
+
+ }
+ }
+ }
+ free(fmtp_dup);
+ }
+ //codec_fmtp->bits_per_second = bit_rate;
+ return SWITCH_STATUS_SUCCESS;
+ }
+ return SWITCH_STATUS_FALSE;
+}
+
+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)
{
struct opus_context *context = NULL;
int encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);
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 = codec->implementation->bits_per_second;
+ int bitrate_bps = OPUS_AUTO;
int use_vbr = 1;
int complexity = 10;
- int use_inbandfec = 1;
- int use_dtx = 1;
- int bandwidth = OPUS_BANDWIDTH_FULLBAND;
int err;
+ int samplerate = opus_codec_settings.samplerate ? opus_codec_settings.samplerate : codec->implementation->actual_samples_per_second;
- context->encoder_object = opus_encoder_create(codec->implementation->actual_samples_per_second,
+ context->encoder_object = opus_encoder_create(samplerate,
codec->implementation->number_of_channels,
OPUS_APPLICATION_VOIP, &err);
}
opus_encoder_ctl(context->encoder_object, OPUS_SET_BITRATE(bitrate_bps));
- opus_encoder_ctl(context->encoder_object, OPUS_SET_BANDWIDTH(bandwidth));
+
+ if (codec->implementation->actual_samples_per_second == 8000) {
+ opus_encoder_ctl(context->encoder_object, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
+ opus_encoder_ctl(context->encoder_object, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
+ } 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));
- opus_encoder_ctl(context->encoder_object, OPUS_SET_INBAND_FEC(use_inbandfec));
- opus_encoder_ctl(context->encoder_object, OPUS_SET_DTX(use_dtx));
+ 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));
+ }
}
if (decoding) {
int mss = 10000;
int x = 0;
int rate = 48000;
- int bits = 32000;
+ int bits = 0;
+ char *dft_fmtp = NULL;
+ opus_codec_settings_t settings = { 0 };
/* 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 */
- NULL, /* default fmtp to send (can be overridden by the init function) */
- rate, /* samples transferred per second */
+ dft_fmtp, /* default fmtp to send (can be overridden by the init function) */
+ 48000, /* samples transferred per second */
rate, /* actual samples transferred per second */
bits, /* bits transferred per second */
mss, /* number of microseconds per frame */
}
+
+ 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 */
+ dft_fmtp, /* default fmtp to send (can be overridden by the init function) */
+ 48000, /* samples transferred per second */
+ rate, /* actual samples transferred per second */
+ bits, /* bits transferred per second */
+ mss, /* number of microseconds per frame */
+ samples, /* number of samples per frame */
+ bytes, /* number of bytes per frame decompressed */
+ 0, /* number of bytes per frame compressed */
+ 1,/* number of channels represented */
+ 1, /* number of frames per network packet */
+ switch_opus_init, /* function to initialize a codec handle using this implementation */
+ 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;
}