From 7fe031a9f53c8ae34123cbbbc2bc7eae38e37a54 Mon Sep 17 00:00:00 2001 From: Seven Du Date: Wed, 13 Jun 2018 13:52:54 +0800 Subject: [PATCH] FS-11189: add vpx settings load time: first load config default settings merge any settings in XML run time: merge any dynamic settings --- src/switch_vpx.c | 478 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 350 insertions(+), 128 deletions(-) diff --git a/src/switch_vpx.c b/src/switch_vpx.c index 07cb64e40d..3cb3f861a7 100644 --- a/src/switch_vpx.c +++ b/src/switch_vpx.c @@ -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); + if (vpx_globals.debug) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec: %s\n", vpx_codec_iface_name(context->encoder_interface)); -#ifdef SHOW - fprintf(stderr, "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); -#endif + 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 ">" +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; }