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;
};
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)
{
// 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) {
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;
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;
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;
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) {
}
} 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);
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;
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);
context->need_key_frame = 3;
}
-
if (!context->encoder_init) {
if (init_encoder(codec) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_FALSE;
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);
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;
}