]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-11189: add vpx settings
authorSeven Du <dujinfang@x-y-t.cn>
Wed, 13 Jun 2018 05:52:54 +0000 (13:52 +0800)
committerMuteesa Fred <muteesafred@hotmail.com>
Tue, 24 Jul 2018 07:21:57 +0000 (07:21 +0000)
load time:
first load config default settings
merge any settings in XML

run time:
merge any dynamic settings

src/switch_vpx.c

index 07cb64e40d0dc183ea349a24236a4243d2f98350..3cb3f861a7a90ca95992c46eb880d37b9bb25a8c 100644 (file)
@@ -276,7 +276,7 @@ struct vpx_context {
        unsigned int flags;
        switch_codec_settings_t codec_settings;
        unsigned int bandwidth;
-       vpx_codec_enc_cfg_t     config;
+       vpx_codec_enc_cfg_t config;
        switch_time_t last_key_frame;
 
        vpx_codec_ctx_t encoder;
@@ -314,6 +314,21 @@ struct vpx_context {
 };
 typedef struct vpx_context vpx_context_t;
 
+struct vpx_globals {
+       int debug;
+       uint32_t max_bitrate;
+       char vp8_profile[20];
+       char vp9_profile[20];
+       char vp10_profile[20];
+       vpx_codec_enc_cfg_t vp8_enc_cfg;
+       vpx_codec_dec_cfg_t vp8_dec_cfg;
+       vpx_codec_enc_cfg_t vp9_enc_cfg;
+       vpx_codec_dec_cfg_t vp9_dec_cfg;
+       vpx_codec_enc_cfg_t vp10_enc_cfg;
+       vpx_codec_dec_cfg_t vp10_dec_cfg;
+};
+
+struct vpx_globals vpx_globals = { 0 };
 
 static switch_status_t init_decoder(switch_codec_t *codec)
 {
@@ -329,14 +344,11 @@ static switch_status_t init_decoder(switch_codec_t *codec)
                //      context->decoder_init = 0;
                //}
 
-               //cfg.threads = 1;//(switch_core_cpu_count() > 1) ? 2 : 1;
-
-               cfg.threads = switch_core_cpu_count() / 2;
-               if (cfg.threads > 4) cfg.threads = 4;
-               if (cfg.threads < 1) cfg.threads = 1;
-
-               if (!context->is_vp9) { // vp8 only
+               if (context->is_vp9) {
+                       cfg.threads = vpx_globals.vp9_dec_cfg.threads;
+               } else {
                        // dec_flags = VPX_CODEC_USE_POSTPROC;
+                       cfg.threads = vpx_globals.vp8_dec_cfg.threads;
                }
 
                if (vpx_codec_dec_init(&context->decoder, context->decoder_interface, &cfg, dec_flags) != VPX_CODEC_OK) {
@@ -377,7 +389,14 @@ static switch_status_t init_encoder(switch_codec_t *codec)
        vpx_codec_enc_cfg_t *config = &context->config;
        int token_parts = 1;
        int cpus = switch_core_cpu_count();
-       int sane, threads = 1;
+       vpx_codec_enc_cfg_t *vp8_enc_cfg = &vpx_globals.vp8_enc_cfg;
+       vpx_codec_enc_cfg_t *vp9_enc_cfg = &vpx_globals.vp9_enc_cfg;
+
+       if (context->is_vp9) {
+               *config = *vp9_enc_cfg;
+       } else {
+               *config = *vp8_enc_cfg;
+       }
 
        if (!context->codec_settings.video.width) {
                context->codec_settings.video.width = 1280;
@@ -395,14 +414,11 @@ static switch_status_t init_encoder(switch_codec_t *codec)
                context->bandwidth = context->codec_settings.video.bandwidth;
        } else {
                context->bandwidth = switch_calc_bitrate(context->codec_settings.video.width, context->codec_settings.video.height, 1, 15);
-
        }
 
-       sane = switch_calc_bitrate(1920, 1080, 5, 60);
-
-       if (context->bandwidth > sane) {
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_WARNING, "BITRATE TRUNCATED FROM %d TO %d\n", context->bandwidth, sane);
-               context->bandwidth = sane;
+       if (context->bandwidth > vpx_globals.max_bitrate) {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_WARNING, "BITRATE TRUNCATED FROM %d TO %d\n", context->bandwidth, vpx_globals.max_bitrate);
+               context->bandwidth = vpx_globals.max_bitrate;
        }
 
        context->pkt = NULL;
@@ -413,29 +429,16 @@ static switch_status_t init_encoder(switch_codec_t *codec)
 
        context->start_time = switch_micro_time_now();
 
-       config->g_timebase.num = 1;
-       config->g_timebase.den = 90000;
-       config->g_pass = VPX_RC_ONE_PASS;
        config->g_w = context->codec_settings.video.width;
        config->g_h = context->codec_settings.video.height;
        config->rc_target_bitrate = context->bandwidth;
-       config->g_lag_in_frames = 0;
-       config->kf_max_dist = 360;//2000;
-       threads = cpus / 2;
-       if (threads > 4) threads = 4;
-       if (threads < 1) threads = 1;
-       config->g_threads = threads;
 
        if (context->is_vp9) {
-               //config->rc_dropframe_thresh = 2;
                token_parts = (cpus > 1) ? 3 : 0;
 
                if (context->lossless) {
                        config->rc_min_quantizer = 0;
                        config->rc_max_quantizer = 0;
-               } else {
-                       config->rc_min_quantizer = 0;
-                       config->rc_max_quantizer = 63;
                }
 
                config->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_NOLAYERING;
@@ -444,63 +447,7 @@ static switch_status_t init_encoder(switch_codec_t *codec)
                config->ts_periodicity = 1;
                config->ts_layer_id[0] = 0;
        } else {
-
-               // settings
-               config->g_profile = 2;
-               config->g_error_resilient = VPX_ERROR_RESILIENT_PARTITIONS;
                token_parts = (cpus > 1) ? 3 : 0;
-
-               // rate control settings
-               config->rc_dropframe_thresh = 0;
-               config->rc_end_usage = VPX_CBR;
-               //config->g_pass = VPX_RC_ONE_PASS;
-               config->kf_mode = VPX_KF_AUTO;
-               config->kf_max_dist = 360;//1000;
-
-               //config->kf_mode = VPX_KF_DISABLED;
-               config->rc_resize_allowed = 1;
-               //config->rc_min_quantizer = 0;
-               //config->rc_max_quantizer = 63;
-               config->rc_min_quantizer = 0;
-               config->rc_max_quantizer = 63;
-               //Rate control adaptation undershoot control.
-               //      This value, expressed as a percentage of the target bitrate,
-               //      controls the maximum allowed adaptation speed of the codec.
-               //      This factor controls the maximum amount of bits that can be
-               //      subtracted from the target bitrate in order to compensate for
-               //      prior overshoot.
-               //      Valid values in the range 0-1000.
-               config->rc_undershoot_pct = 100;
-               //Rate control adaptation overshoot control.
-               //      This value, expressed as a percentage of the target bitrate,
-               //      controls the maximum allowed adaptation speed of the codec.
-               //      This factor controls the maximum amount of bits that can be
-               //      added to the target bitrate in order to compensate for prior
-               //      undershoot.
-               //      Valid values in the range 0-1000.
-               config->rc_overshoot_pct = 15;
-               //Decoder Buffer Size.
-               //      This value indicates the amount of data that may be buffered
-               //      by the decoding application. Note that this value is expressed
-               //      in units of time (milliseconds). For example, a value of 5000
-               //      indicates that the client will buffer (at least) 5000ms worth
-               //      of encoded data. Use the target bitrate (rc_target_bitrate) to
-               //      convert to bits/bytes, if necessary.
-               config->rc_buf_sz = 5000;
-               //Decoder Buffer Initial Size.
-               //      This value indicates the amount of data that will be buffered
-               //      by the decoding application prior to beginning playback.
-               //      This value is expressed in units of time (milliseconds).
-               //      Use the target bitrate (rc_target_bitrate) to convert to
-               //      bits/bytes, if necessary.
-               config->rc_buf_initial_sz = 1000;
-               //Decoder Buffer Optimal Size.
-               //      This value indicates the amount of data that the encoder should
-               //      try to maintain in the decoder's buffer. This value is expressed
-               //      in units of time (milliseconds).
-               //      Use the target bitrate (rc_target_bitrate) to convert to
-               //      bits/bytes, if necessary.
-               config->rc_buf_optimal_sz = 1000;
        }
 
        if (context->encoder_init) {
@@ -510,45 +457,45 @@ static switch_status_t init_encoder(switch_codec_t *codec)
                }
        } else if (context->flags & SWITCH_CODEC_FLAG_ENCODE) {
 
-               // #define SHOW(field) fprintf(stderr, "    %-28s = %d\n", #field, config->field);
-
-#ifdef SHOW
-               fprintf(stderr, "Codec: %s\n", vpx_codec_iface_name(context->encoder_interface));
-
-               SHOW(g_usage);
-               SHOW(g_threads);
-               SHOW(g_profile);
-               SHOW(g_w);
-               SHOW(g_h);
-               SHOW(g_bit_depth);
-               SHOW(g_input_bit_depth);
-               SHOW(g_timebase.num);
-               SHOW(g_timebase.den);
-               SHOW(g_error_resilient);
-               SHOW(g_pass);
-               SHOW(g_lag_in_frames);
-               SHOW(rc_dropframe_thresh);
-               SHOW(rc_resize_allowed);
-               SHOW(rc_scaled_width);
-               SHOW(rc_scaled_height);
-               SHOW(rc_resize_up_thresh);
-               SHOW(rc_resize_down_thresh);
-               SHOW(rc_end_usage);
-               SHOW(rc_target_bitrate);
-               SHOW(rc_min_quantizer);
-               SHOW(rc_max_quantizer);
-               SHOW(rc_undershoot_pct);
-               SHOW(rc_overshoot_pct);
-               SHOW(rc_buf_sz);
-               SHOW(rc_buf_initial_sz);
-               SHOW(rc_buf_optimal_sz);
-               SHOW(rc_2pass_vbr_bias_pct);
-               SHOW(rc_2pass_vbr_minsection_pct);
-               SHOW(rc_2pass_vbr_maxsection_pct);
-               SHOW(kf_mode);
-               SHOW(kf_min_dist);
-               SHOW(kf_max_dist);
-#endif
+               if (vpx_globals.debug) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec: %s\n", vpx_codec_iface_name(context->encoder_interface));
+
+#define SHOW(field) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "    %-28s = %d\n", #field, config->field);
+
+                       SHOW(g_usage);
+                       SHOW(g_threads);
+                       SHOW(g_profile);
+                       SHOW(g_w);
+                       SHOW(g_h);
+                       SHOW(g_bit_depth);
+                       SHOW(g_input_bit_depth);
+                       SHOW(g_timebase.num);
+                       SHOW(g_timebase.den);
+                       SHOW(g_error_resilient);
+                       SHOW(g_pass);
+                       SHOW(g_lag_in_frames);
+                       SHOW(rc_dropframe_thresh);
+                       SHOW(rc_resize_allowed);
+                       SHOW(rc_scaled_width);
+                       SHOW(rc_scaled_height);
+                       SHOW(rc_resize_up_thresh);
+                       SHOW(rc_resize_down_thresh);
+                       SHOW(rc_end_usage);
+                       SHOW(rc_target_bitrate);
+                       SHOW(rc_min_quantizer);
+                       SHOW(rc_max_quantizer);
+                       SHOW(rc_undershoot_pct);
+                       SHOW(rc_overshoot_pct);
+                       SHOW(rc_buf_sz);
+                       SHOW(rc_buf_initial_sz);
+                       SHOW(rc_buf_optimal_sz);
+                       SHOW(rc_2pass_vbr_bias_pct);
+                       SHOW(rc_2pass_vbr_minsection_pct);
+                       SHOW(rc_2pass_vbr_maxsection_pct);
+                       SHOW(kf_mode);
+                       SHOW(kf_min_dist);
+                       SHOW(kf_max_dist);
+               }
 
                if (vpx_codec_enc_init(&context->encoder, context->encoder_interface, config, 0 & VPX_CODEC_USE_OUTPUT_PARTITION) != VPX_CODEC_OK) {
                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec %s init error: [%d:%s]\n", vpx_codec_iface_name(context->encoder_interface), context->encoder.err, context->encoder.err_detail);
@@ -625,11 +572,6 @@ static switch_status_t switch_vpx_init(switch_codec_t *codec, switch_codec_flag_
                codec->fmtp_out = switch_core_strdup(codec->memory_pool, codec->fmtp_in);
        }
 
-       if (vpx_codec_enc_config_default(context->encoder_interface, &context->config, 0) != VPX_CODEC_OK) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Encoder config Error\n");
-               return SWITCH_STATUS_FALSE;
-       }
-
        context->codec_settings.video.width = 320;
        context->codec_settings.video.height = 240;
 
@@ -826,7 +768,7 @@ static switch_status_t switch_vpx_encode(switch_codec_t *codec, switch_frame_t *
                height = frame->img->h;
        }
 
-       if (context->config.g_w != width || context->config.g_h != height) {
+       if (context->codec_settings.video.width != width || context->codec_settings.video.height != height) {
                context->codec_settings.video.width = width;
                context->codec_settings.video.height = height;
                reset_codec_encoder(codec);
@@ -834,7 +776,6 @@ static switch_status_t switch_vpx_encode(switch_codec_t *codec, switch_frame_t *
                context->need_key_frame = 3;
        }
 
-
        if (!context->encoder_init) {
                if (init_encoder(codec) != SWITCH_STATUS_SUCCESS) {
                        return SWITCH_STATUS_FALSE;
@@ -1386,12 +1327,290 @@ static switch_status_t switch_vpx_destroy(switch_codec_t *codec)
        return SWITCH_STATUS_SUCCESS;
 }
 
+#define UINTVAL(v) (v > 0 ? v : 0);
+
+static void load_config()
+{
+       switch_xml_t cfg = NULL, xml = NULL;
+
+       vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &vpx_globals.vp8_enc_cfg, 0);
+       vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &vpx_globals.vp9_enc_cfg, 0);
+
+       switch_set_string(vpx_globals.vp8_profile, "vp8");
+       switch_set_string(vpx_globals.vp9_profile, "vp9");
+       switch_set_string(vpx_globals.vp10_profile, "vp10");
+
+       vpx_globals.max_bitrate = 0;
+
+       xml = switch_xml_open_cfg("vpx.conf", &cfg, NULL);
+
+       if (xml) {
+               switch_xml_t settings = switch_xml_child(cfg, "settings");
+               switch_xml_t profiles = switch_xml_child(cfg, "profiles");
+
+               if (settings) {
+                       switch_xml_t param;
+
+                       for (param = switch_xml_child(settings, "param"); param; param = param->next) {
+                               const char *name = switch_xml_attr(param, "name");
+                               const char *value = switch_xml_attr(param, "value");
+
+                               if (zstr(name) || zstr(value)) continue;
+
+                               if (!strcmp(name, "max-bitrate")) {
+                                       vpx_globals.max_bitrate = switch_parse_bandwidth_string(value);
+                               } else if (!strcmp(name, "dec-threads")) {
+                                       vpx_globals.vp8_dec_cfg.threads = switch_parse_cpu_string(value);
+                                       vpx_globals.vp9_dec_cfg.threads = switch_parse_cpu_string(value);
+                                       vpx_globals.vp10_dec_cfg.threads = switch_parse_cpu_string(value);
+                               } else if (!strcmp(name, "enc-threads")) {
+                                       vpx_globals.vp8_enc_cfg.g_threads = switch_parse_cpu_string(value);
+                                       vpx_globals.vp9_enc_cfg.g_threads = switch_parse_cpu_string(value);
+                                       vpx_globals.vp10_enc_cfg.g_threads = switch_parse_cpu_string(value);
+                               } else if (!strcmp(name, "vp8-profile")) {
+                                       switch_set_string(vpx_globals.vp8_profile, value);
+                               } else if (!strcmp(name, "vp9-profile")) {
+                                       switch_set_string(vpx_globals.vp9_profile, value);
+                               } else if (!strcmp(name, "vp10-profile")) {
+                                       switch_set_string(vpx_globals.vp10_profile, value);
+                               }
+                       }
+               }
+
+               if (profiles) {
+                       switch_xml_t profile = switch_xml_child(profiles, "profile");
+
+                       for (; profile; profile = profile->next) {
+                               switch_xml_t param = NULL;
+                               const char *profile_name = switch_xml_attr(profile, "name");
+                               vpx_codec_dec_cfg_t *dec_cfg = NULL;
+                               vpx_codec_enc_cfg_t *enc_cfg = NULL;
+
+                               if (zstr(profile_name)) continue;
+
+                               if (!strcmp(profile_name, vpx_globals.vp8_profile)) {
+                                       dec_cfg = &vpx_globals.vp8_dec_cfg;
+                                       enc_cfg = &vpx_globals.vp8_enc_cfg;
+                               } else if (!strcmp(profile_name, vpx_globals.vp9_profile)) {
+                                       dec_cfg = &vpx_globals.vp9_dec_cfg;
+                                       enc_cfg = &vpx_globals.vp9_enc_cfg;
+                               } else if (!strcmp(profile_name, vpx_globals.vp10_profile)) {
+                                       dec_cfg = &vpx_globals.vp10_dec_cfg;
+                                       enc_cfg = &vpx_globals.vp10_enc_cfg;
+                               }
+
+                               for (param = switch_xml_child(profile, "param"); param; param = param->next) {
+                                       const char *name = switch_xml_attr(param, "name");
+                                       const char *value = switch_xml_attr(param, "value");
+                                       int val;
+
+                                       if (!enc_cfg || !dec_cfg) break;
+                                       if (zstr(name) || zstr(value)) continue;
+
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: %s = %s\n", profile_name, name, value);
+
+                                       val = atoi(value);
+
+                                       if (!strcmp(name, "dec-threads")) {
+                                               dec_cfg->threads = switch_parse_cpu_string(value);
+                                       } else if (!strcmp(name, "enc-threads")) {
+                                               enc_cfg->g_threads = switch_parse_cpu_string(value);
+                                       } else if (!strcmp(name, "g-profile")) {
+                                               enc_cfg->g_profile = UINTVAL(val);
+                                       } else if (!strcmp(name, "g-timebase")) {
+                                               int num = 0;
+                                               int den = 0;
+                                               char *slash = strchr(value, '/');
+
+                                               num = UINTVAL(val);
+
+                                               if (slash) {
+                                                       slash++;
+                                                       den = atoi(slash);
+
+                                                       if (den < 0) den = 0;
+                                               }
+
+                                               if (num && den) {
+                                                       enc_cfg->g_timebase.num = num;
+                                                       enc_cfg->g_timebase.den = den;
+                                               }
+                                       } else if (!strcmp(name, "g-error-resilient")) {
+                                               char *s = strdup(value);
+                                               vpx_codec_er_flags_t res = 0;
+
+                                               if (s) {
+                                                       int argc;
+                                                       char *argv[10];
+                                                       int i;
+
+                                                       argc = switch_separate_string(s, '|', argv, (sizeof(argv) / sizeof(argv[0])));
+
+                                                       for (i = 0; i < argc; i++) {
+                                                               if (!strcasecmp(argv[i], "DEFAULT")) {
+                                                                       res |= VPX_ERROR_RESILIENT_DEFAULT;
+                                                               } else if (!strcasecmp(argv[i], "PARTITIONS")) {
+                                                                       res |= VPX_ERROR_RESILIENT_PARTITIONS;
+                                                               }
+                                                       }
+
+                                                       free(s);
+                                                       enc_cfg->g_error_resilient = res;
+                                               }
+                                       } else if (!strcmp(name, "g-pass")) {
+                                               int pass = 0;
+
+                                               if (!strcasecmp(value, "ONE_PASS")) {
+                                                       pass = VPX_RC_ONE_PASS;
+                                               } else if (!strcasecmp(value, "FIRST_PASS")) {
+                                                       pass = VPX_RC_FIRST_PASS;
+                                               } else if (!strcasecmp(value, "LAST_PASS")) {
+                                                       pass = VPX_RC_FIRST_PASS;
+                                               }
+
+                                               enc_cfg->g_pass = pass;
+                                       } else if (!strcmp(name, "g-lag-in-frames")) {
+                                               enc_cfg->g_lag_in_frames = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc_dropframe_thresh")) {
+                                               enc_cfg->rc_dropframe_thresh = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-resize-allowed")) {
+                                               enc_cfg->rc_resize_allowed = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-scaled-width")) {
+                                               enc_cfg->rc_scaled_width = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-scaled-height")) {
+                                               enc_cfg->rc_scaled_height = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-resize-up-thresh")) {
+                                               enc_cfg->rc_resize_up_thresh = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-resize-down-thresh")) {
+                                               enc_cfg->rc_resize_down_thresh = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-end-usage")) {
+                                               int mode = 0;
+
+                                               if (!strcasecmp(value, "VBR")) {
+                                                       mode = VPX_VBR;
+                                               } else if (!strcasecmp(value, "CBR")) {
+                                                       mode = VPX_CBR;
+                                               } else if (!strcasecmp(value, "CQ")) {
+                                                       mode = VPX_CQ;
+                                               } else if (!strcasecmp(value, "Q")) {
+                                                       mode = VPX_Q;
+                                               }
+                                               enc_cfg->rc_end_usage = mode;
+                                       } else if (!strcmp(name, "rc-target-bitrate")) {
+                                               int br = switch_parse_bandwidth_string(value);
+
+                                               if (br > 0) {
+                                                       enc_cfg->rc_target_bitrate = br;
+                                               }
+                                       } else if (!strcmp(name, "rc-min-quantizer")) {
+                                               enc_cfg->rc_min_quantizer = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-max-quantizer")) {
+                                               enc_cfg->rc_max_quantizer = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-undershoot-pct")) {
+                                               enc_cfg->rc_undershoot_pct = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-overshoot-pct")) {
+                                               enc_cfg->rc_overshoot_pct = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-buf-sz")) {
+                                               enc_cfg->rc_buf_sz = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-buf-initial-sz")) {
+                                               enc_cfg->rc_buf_initial_sz = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-buf-optimal-sz")) {
+                                               enc_cfg->rc_buf_optimal_sz = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-2pass-vbr-bias-pct")) {
+                                               enc_cfg->rc_2pass_vbr_bias_pct = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-2pass-vbr-minsection-pct")) {
+                                               enc_cfg->rc_2pass_vbr_minsection_pct = UINTVAL(val);
+                                       } else if (!strcmp(name, "rc-2pass-vbr-maxsection-pct")) {
+                                               enc_cfg->rc_2pass_vbr_maxsection_pct = UINTVAL(val);
+                                       } else if (!strcmp(name, "kf-mode")) {
+                                               int mode = 0;
+
+                                               if (!strcasecmp(value, "AUTO")) {
+                                                       mode = VPX_KF_AUTO;
+                                               } else if (!strcasecmp(value, "DISABLED")) {
+                                                       mode = VPX_KF_DISABLED;
+                                               }
+
+                                               enc_cfg->kf_mode = mode;
+                                       } else if (!strcmp(name, "kf-min-dist")) {
+                                               enc_cfg->kf_min_dist = UINTVAL(val);
+                                       } else if (!strcmp(name, "kf-max-dist")) {
+                                               enc_cfg->kf_max_dist = UINTVAL(val);
+                                       } else if (!strcmp(name, "ss-number-layers")) {
+                                               enc_cfg->ss_number_layers = UINTVAL(val);
+                                       } else if (!strcmp(name, "ts-number-layers")) {
+                                               enc_cfg->ts_number_layers = UINTVAL(val);
+                                       } else if (!strcmp(name, "ts-periodicity")) {
+                                               enc_cfg->ts_periodicity = UINTVAL(val);
+                                       } else if (!strcmp(name, "temporal-layering-mode")) {
+                                               enc_cfg->temporal_layering_mode = UINTVAL(val);
+                                       }
+                               } // for param
+                       } // for profile
+               } // profiles
+
+               switch_xml_free(xml);
+       } // xml
+
+       if (vpx_globals.max_bitrate <= 0) {
+               vpx_globals.max_bitrate = switch_calc_bitrate(1920, 1080, 5, 60);
+       }
+
+       if (!vpx_globals.vp8_enc_cfg.g_threads) vpx_globals.vp8_enc_cfg.g_threads = 1;
+       if (!vpx_globals.vp8_dec_cfg.threads) vpx_globals.vp8_dec_cfg.threads = switch_parse_cpu_string("cpu/2/4");
+       if (!vpx_globals.vp9_enc_cfg.g_threads) vpx_globals.vp9_enc_cfg.g_threads = vpx_globals.vp8_enc_cfg.g_threads;
+       if (!vpx_globals.vp9_dec_cfg.threads) vpx_globals.vp9_dec_cfg.threads = vpx_globals.vp8_dec_cfg.threads;
+       if (!vpx_globals.vp10_enc_cfg.g_threads) vpx_globals.vp10_enc_cfg.g_threads = vpx_globals.vp8_enc_cfg.g_threads;
+       if (!vpx_globals.vp10_dec_cfg.threads) vpx_globals.vp10_dec_cfg.threads = vpx_globals.vp8_dec_cfg.threads;
+}
+
+#define VPX_API_SYNTAX "<reload|debug <on|off>>"
+SWITCH_STANDARD_API(vpx_api_function)
+{
+       if (session) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       if (zstr(cmd)) {
+               goto usage;
+       }
+
+       if (!strcasecmp(cmd, "reload")) {
+               const char *err;
+
+               switch_xml_reload(&err);
+               stream->write_function(stream, "Reload XML [%s]\n", err);
+
+               load_config();
+               stream->write_function(stream, "+OK\n");
+       } else if (!strcasecmp(cmd, "debug")) {
+               stream->write_function(stream, "+OK debug %s\n", vpx_globals.debug ? "on" : "off");
+       } else if (!strcasecmp(cmd, "debug on")) {
+               vpx_globals.debug = 1;
+               stream->write_function(stream, "+OK debug on\n");
+       } else if (!strcasecmp(cmd, "debug off")) {
+               vpx_globals.debug = 0;
+               stream->write_function(stream, "+OK debug off\n");
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+
+  usage:
+       stream->write_function(stream, "USAGE: %s\n", VPX_API_SYNTAX);
+       return SWITCH_STATUS_SUCCESS;
+}
+
 SWITCH_MODULE_LOAD_FUNCTION(mod_vpx_load)
 {
        switch_codec_interface_t *codec_interface;
+       switch_api_interface_t *vpx_api_interface;
+
+       memset(&vpx_globals, 0, sizeof(struct vpx_globals));
+       load_config();
 
        /* 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, "VP8 Video");
        switch_core_codec_add_video_implementation(pool, codec_interface, 99, "VP8", NULL,
                                                                                           switch_vpx_init, switch_vpx_encode, switch_vpx_decode, switch_vpx_control, switch_vpx_destroy);
@@ -1399,6 +1618,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_vpx_load)
        switch_core_codec_add_video_implementation(pool, codec_interface, 99, "VP9", NULL,
                                                                                           switch_vpx_init, switch_vpx_encode, switch_vpx_decode, switch_vpx_control, switch_vpx_destroy);
 
+       SWITCH_ADD_API(vpx_api_interface, "vpx",
+                                  "VPX API", vpx_api_function, VPX_API_SYNTAX);
+
        /* indicate that the module should continue to be loaded */
        return SWITCH_STATUS_SUCCESS;
 }