]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add 8k support for opus
authorAnthony Minessale <anthm@freeswitch.org>
Thu, 7 Nov 2013 22:44:21 +0000 (03:44 +0500)
committerAnthony Minessale <anthm@freeswitch.org>
Thu, 7 Nov 2013 22:44:21 +0000 (03:44 +0500)
src/mod/codecs/mod_opus/mod_opus.c

index b1681ea5fa0ebfb9fa2d55f9b01faa9b79cfcdd5..f3d5585fdf09f0924d5322886613bb2ed16c35c6 100644 (file)
 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);
 
@@ -74,12 +268,24 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag
        }
 
                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) {
@@ -187,19 +393,33 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load)
        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 */
@@ -219,6 +439,44 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load)
 
        }
        
+
+       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;
 }