ref: 89da176473fa9ed3a9b670dba2ea92259dc34da3
parent: c731d6a4f19eea861ceb2ff31399420b2452eb74
parent: a8d44b99154767bb20b64589e1c90361a5910c40
author: Minghai Shang <[email protected]>
date: Mon Sep 8 09:10:21 EDT 2014
Merge "[spatial svc]Add layer bitrates options and clean up parsing options from string"
--- a/vpx/src/svc_encodeframe.c
+++ b/vpx/src/svc_encodeframe.c
@@ -15,6 +15,7 @@
#include <assert.h>
#include <math.h>
+#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -47,9 +48,34 @@
#define OPTION_BUFFER_SIZE 256
#define COMPONENTS 4 // psnr & sse statistics maintained for total, y, u, v
-static const char *DEFAULT_QUANTIZER_VALUES = "60,53,39,33,27";
-static const char *DEFAULT_SCALE_FACTORS = "4/16,5/16,7/16,11/16,16/16";
+static const int DEFAULT_QUANTIZER_VALUES[VPX_SS_MAX_LAYERS] = {
+ 60, 53, 39, 33, 27
+};
+static const int DEFAULT_SCALE_FACTORS_NUM[VPX_SS_MAX_LAYERS] = {
+ 4, 5, 7, 11, 16
+};
+
+static const int DEFAULT_SCALE_FACTORS_DEN[VPX_SS_MAX_LAYERS] = {
+ 16, 16, 16, 16, 16
+};
+
+typedef enum {
+ QUANTIZER = 0,
+ BITRATE,
+ SCALE_FACTOR,
+ AUTO_ALT_REF,
+ ALL_OPTION_TYPES
+} LAYER_OPTION_TYPE;
+
+static const int option_max_values[ALL_OPTION_TYPES] = {
+ 63, INT_MAX, INT_MAX, 1
+};
+
+static const int option_min_values[ALL_OPTION_TYPES] = {
+ 0, 0, 1, 0
+};
+
// One encoded frame
typedef struct FrameData {
void *buf; // compressed data buffer
@@ -68,6 +94,7 @@
int scaling_factor_den[VPX_SS_MAX_LAYERS];
int quantizer[VPX_SS_MAX_LAYERS];
int enable_auto_alt_ref[VPX_SS_MAX_LAYERS];
+ int bitrates[VPX_SS_MAX_LAYERS];
// accumulated statistics
double psnr_sum[VPX_SS_MAX_LAYERS][COMPONENTS]; // total/Y/U/V
@@ -197,164 +224,68 @@
return retval;
}
-static vpx_codec_err_t parse_quantizer_values(SvcContext *svc_ctx,
- const char *quantizer_values) {
- char *input_string;
- char *token;
- const char *delim = ",";
- char *save_ptr;
- int found = 0;
- int i, q;
- vpx_codec_err_t res = VPX_CODEC_OK;
- SvcInternal *const si = get_svc_internal(svc_ctx);
+static vpx_codec_err_t extract_option(LAYER_OPTION_TYPE type,
+ char *input,
+ int *value0,
+ int *value1) {
+ if (type == SCALE_FACTOR) {
+ *value0 = strtol(input, &input, 10);
+ if (*input++ != '/')
+ return VPX_CODEC_INVALID_PARAM;
+ *value1 = strtol(input, &input, 10);
- if (quantizer_values == NULL || strlen(quantizer_values) == 0) {
- input_string = strdup(DEFAULT_QUANTIZER_VALUES);
+ if (*value0 < option_min_values[SCALE_FACTOR] ||
+ *value1 < option_min_values[SCALE_FACTOR] ||
+ *value0 > option_max_values[SCALE_FACTOR] ||
+ *value1 > option_max_values[SCALE_FACTOR])
+ return VPX_CODEC_INVALID_PARAM;
} else {
- input_string = strdup(quantizer_values);
+ *value0 = atoi(input);
+ if (*value0 < option_min_values[type] ||
+ *value0 > option_max_values[type])
+ return VPX_CODEC_INVALID_PARAM;
}
-
- token = strtok_r(input_string, delim, &save_ptr);
- for (i = 0; i < svc_ctx->spatial_layers; ++i) {
- if (token != NULL) {
- q = atoi(token);
- if (q <= 0 || q > 100) {
- svc_log(svc_ctx, SVC_LOG_ERROR,
- "svc-quantizer-values: invalid value %s\n", token);
- res = VPX_CODEC_INVALID_PARAM;
- break;
- }
- token = strtok_r(NULL, delim, &save_ptr);
- found = i + 1;
- } else {
- q = 0;
- }
- si->quantizer[i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers] = q;
- }
- if (res == VPX_CODEC_OK && found != svc_ctx->spatial_layers) {
- svc_log(svc_ctx, SVC_LOG_ERROR,
- "svc: quantizers: %d values required, but only %d specified\n",
- svc_ctx->spatial_layers, found);
- res = VPX_CODEC_INVALID_PARAM;
- }
- free(input_string);
- return res;
+ return VPX_CODEC_OK;
}
-static vpx_codec_err_t parse_auto_alt_ref(SvcContext *svc_ctx,
- const char *alt_ref_options) {
+static vpx_codec_err_t parse_layer_options_from_string(SvcContext *svc_ctx,
+ LAYER_OPTION_TYPE type,
+ const char *input,
+ int *option0,
+ int *option1) {
+ int i;
+ vpx_codec_err_t res = VPX_CODEC_OK;
char *input_string;
char *token;
const char *delim = ",";
char *save_ptr;
- int found = 0, enabled = 0;
- int i, value;
- vpx_codec_err_t res = VPX_CODEC_OK;
- SvcInternal *const si = get_svc_internal(svc_ctx);
- if (alt_ref_options == NULL || strlen(alt_ref_options) == 0) {
+ if (input == NULL || option0 == NULL ||
+ (option1 == NULL && type == SCALE_FACTOR))
return VPX_CODEC_INVALID_PARAM;
- } else {
- input_string = strdup(alt_ref_options);
- }
+ input_string = strdup(input);
token = strtok_r(input_string, delim, &save_ptr);
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
if (token != NULL) {
- value = atoi(token);
- if (value < 0 || value > 1) {
- svc_log(svc_ctx, SVC_LOG_ERROR,
- "enable auto alt ref values: invalid value %s\n", token);
- res = VPX_CODEC_INVALID_PARAM;
+ res = extract_option(type, token, option0 + i, option1 + i);
+ if (res != VPX_CODEC_OK)
break;
- }
token = strtok_r(NULL, delim, &save_ptr);
- found = i + 1;
} else {
- value = 0;
+ break;
}
- si->enable_auto_alt_ref[i] = value;
- if (value > 0)
- ++enabled;
}
- if (res == VPX_CODEC_OK && found != svc_ctx->spatial_layers) {
+ if (res == VPX_CODEC_OK && i != svc_ctx->spatial_layers) {
svc_log(svc_ctx, SVC_LOG_ERROR,
- "svc: quantizers: %d values required, but only %d specified\n",
- svc_ctx->spatial_layers, found);
+ "svc: layer params type: %d %d values required, "
+ "but only %d specified\n", type, svc_ctx->spatial_layers, i);
res = VPX_CODEC_INVALID_PARAM;
}
- if (enabled > REF_FRAMES - svc_ctx->spatial_layers) {
- svc_log(svc_ctx, SVC_LOG_ERROR,
- "svc: auto alt ref: Maxinum %d(REF_FRAMES - layers) layers could"
- "enabled auto alt reference frame, but % layers are enabled\n",
- REF_FRAMES - svc_ctx->spatial_layers, enabled);
- res = VPX_CODEC_INVALID_PARAM;
- }
free(input_string);
return res;
}
-static void log_invalid_scale_factor(SvcContext *svc_ctx, const char *value) {
- svc_log(svc_ctx, SVC_LOG_ERROR, "svc scale-factors: invalid value %s\n",
- value);
-}
-
-static vpx_codec_err_t parse_scale_factors(SvcContext *svc_ctx,
- const char *scale_factors) {
- char *input_string;
- char *token;
- const char *delim = ",";
- char *save_ptr;
- int found = 0;
- int i;
- int64_t num, den;
- vpx_codec_err_t res = VPX_CODEC_OK;
- SvcInternal *const si = get_svc_internal(svc_ctx);
-
- if (scale_factors == NULL || strlen(scale_factors) == 0) {
- input_string = strdup(DEFAULT_SCALE_FACTORS);
- } else {
- input_string = strdup(scale_factors);
- }
- token = strtok_r(input_string, delim, &save_ptr);
- for (i = 0; i < svc_ctx->spatial_layers; ++i) {
- num = den = 0;
- if (token != NULL) {
- num = strtol(token, &token, 10);
- if (num <= 0) {
- log_invalid_scale_factor(svc_ctx, token);
- res = VPX_CODEC_INVALID_PARAM;
- break;
- }
- if (*token++ != '/') {
- log_invalid_scale_factor(svc_ctx, token);
- res = VPX_CODEC_INVALID_PARAM;
- break;
- }
- den = strtol(token, &token, 10);
- if (den <= 0) {
- log_invalid_scale_factor(svc_ctx, token);
- res = VPX_CODEC_INVALID_PARAM;
- break;
- }
- token = strtok_r(NULL, delim, &save_ptr);
- found = i + 1;
- }
- si->scaling_factor_num[i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers] =
- (int)num;
- si->scaling_factor_den[i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers] =
- (int)den;
- }
- if (res == VPX_CODEC_OK && found != svc_ctx->spatial_layers) {
- svc_log(svc_ctx, SVC_LOG_ERROR,
- "svc: scale-factors: %d values required, but only %d specified\n",
- svc_ctx->spatial_layers, found);
- res = VPX_CODEC_INVALID_PARAM;
- }
- free(input_string);
- return res;
-}
-
/**
* Parse SVC encoding options
* Format: encoding-mode=<svc_mode>,layers=<layer_count>
@@ -369,6 +300,7 @@
char *input_ptr;
SvcInternal *const si = get_svc_internal(svc_ctx);
vpx_codec_err_t res = VPX_CODEC_OK;
+ int i, alt_ref_enabled = 0;
if (options == NULL) return VPX_CODEC_OK;
input_string = strdup(options);
@@ -389,14 +321,22 @@
} else if (strcmp("temporal-layers", option_name) == 0) {
svc_ctx->temporal_layers = atoi(option_value);
} else if (strcmp("scale-factors", option_name) == 0) {
- res = parse_scale_factors(svc_ctx, option_value);
+ res = parse_layer_options_from_string(svc_ctx, SCALE_FACTOR, option_value,
+ si->scaling_factor_num,
+ si->scaling_factor_den);
if (res != VPX_CODEC_OK) break;
} else if (strcmp("quantizers", option_name) == 0) {
- res = parse_quantizer_values(svc_ctx, option_value);
+ res = parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value,
+ si->quantizer, NULL);
if (res != VPX_CODEC_OK) break;
} else if (strcmp("auto-alt-refs", option_name) == 0) {
- res = parse_auto_alt_ref(svc_ctx, option_value);
+ res = parse_layer_options_from_string(svc_ctx, AUTO_ALT_REF, option_value,
+ si->enable_auto_alt_ref, NULL);
if (res != VPX_CODEC_OK) break;
+ } else if (strcmp("bitrates", option_name) == 0) {
+ res = parse_layer_options_from_string(svc_ctx, BITRATE, option_value,
+ si->bitrates, NULL);
+ if (res != VPX_CODEC_OK) break;
} else if (strcmp("multi-frame-contexts", option_name) == 0) {
si->use_multiple_frame_contexts = atoi(option_value);
} else {
@@ -413,6 +353,16 @@
svc_ctx->spatial_layers * svc_ctx->temporal_layers > 4))
res = VPX_CODEC_INVALID_PARAM;
+ for (i = 0; i < svc_ctx->spatial_layers; ++i)
+ alt_ref_enabled += si->enable_auto_alt_ref[i];
+ if (alt_ref_enabled > REF_FRAMES - svc_ctx->spatial_layers) {
+ svc_log(svc_ctx, SVC_LOG_ERROR,
+ "svc: auto alt ref: Maxinum %d(REF_FRAMES - layers) layers could"
+ "enabled auto alt reference frame, but % layers are enabled\n",
+ REF_FRAMES - svc_ctx->spatial_layers, alt_ref_enabled);
+ res = VPX_CODEC_INVALID_PARAM;
+ }
+
return res;
}
@@ -448,6 +398,39 @@
return VPX_CODEC_OK;
}
+void assign_layer_bitrates(const SvcInternal *const si,
+ vpx_codec_enc_cfg_t *const enc_cfg) {
+ int i;
+
+ if (si->bitrates[0] != 0) {
+ enc_cfg->rc_target_bitrate = 0;
+ for (i = 0; i < si->layers; ++i) {
+ enc_cfg->ss_target_bitrate[i] = (unsigned int)si->bitrates[i];
+ enc_cfg->rc_target_bitrate += si->bitrates[i];
+ }
+ } else {
+ float total = 0;
+ float alloc_ratio[VPX_SS_MAX_LAYERS] = {0};
+
+ for (i = 0; i < si->layers; ++i) {
+ if (si->scaling_factor_den[i] > 0) {
+ alloc_ratio[i] = (float)(si->scaling_factor_num[i] * 1.0 /
+ si->scaling_factor_den[i]);
+
+ alloc_ratio[i] *= alloc_ratio[i];
+ total += alloc_ratio[i];
+ }
+ }
+
+ for (i = 0; i < si->layers; ++i) {
+ if (total > 0) {
+ enc_cfg->ss_target_bitrate[i] = (unsigned int)
+ (enc_cfg->rc_target_bitrate * alloc_ratio[i] / total);
+ }
+ }
+ }
+}
+
vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
vpx_codec_iface_t *iface,
vpx_codec_enc_cfg_t *enc_cfg) {
@@ -481,12 +464,28 @@
return VPX_CODEC_INVALID_PARAM;
}
- res = parse_quantizer_values(svc_ctx, si->quantizers);
- if (res != VPX_CODEC_OK) return res;
+ for (i = 0; i < VPX_SS_MAX_LAYERS; ++i) {
+ si->quantizer[i] = DEFAULT_QUANTIZER_VALUES[i];
+ si->scaling_factor_num[i] = DEFAULT_SCALE_FACTORS_NUM[i];
+ si->scaling_factor_den[i] = DEFAULT_SCALE_FACTORS_DEN[i];
+ }
- res = parse_scale_factors(svc_ctx, si->scale_factors);
- if (res != VPX_CODEC_OK) return res;
+ if (strlen(si->quantizers) > 0) {
+ res = parse_layer_options_from_string(svc_ctx, QUANTIZER, si->quantizers,
+ si->quantizer, NULL);
+ if (res != VPX_CODEC_OK)
+ return res;
+ }
+ if (strlen(si->scale_factors) > 0) {
+ res = parse_layer_options_from_string(svc_ctx, SCALE_FACTOR,
+ si->scale_factors,
+ si->scaling_factor_num,
+ si->scaling_factor_den);
+ if (res != VPX_CODEC_OK)
+ return res;
+ }
+
// Parse aggregate command line options. Options must start with
// "layers=xx" then followed by other options
res = parse_options(svc_ctx, si->options);
@@ -504,34 +503,8 @@
si->layers = svc_ctx->spatial_layers;
- // Assign target bitrate for each layer. We calculate the ratio
- // from the resolution for now.
- // TODO(Minghai): Optimize the mechanism of allocating bits after
- // implementing svc two pass rate control.
- if (si->layers > 1) {
- float total = 0;
- float alloc_ratio[VPX_SS_MAX_LAYERS] = {0};
+ assign_layer_bitrates(si, enc_cfg);
- assert(si->layers <= VPX_SS_MAX_LAYERS);
- for (i = 0; i < si->layers; ++i) {
- int pos = i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers;
- if (pos < VPX_SS_MAX_LAYERS && si->scaling_factor_den[pos] > 0) {
- alloc_ratio[i] = (float)(si->scaling_factor_num[pos] * 1.0 /
- si->scaling_factor_den[pos]);
-
- alloc_ratio[i] *= alloc_ratio[i];
- total += alloc_ratio[i];
- }
- }
-
- for (i = 0; i < si->layers; ++i) {
- if (total > 0) {
- enc_cfg->ss_target_bitrate[i] = (unsigned int)
- (enc_cfg->rc_target_bitrate * alloc_ratio[i] / total);
- }
- }
- }
-
#if CONFIG_SPATIAL_SVC
for (i = 0; i < si->layers; ++i)
enc_cfg->ss_enable_auto_alt_ref[i] = si->enable_auto_alt_ref[i];
@@ -585,7 +558,7 @@
int layer,
unsigned int *width,
unsigned int *height) {
- int w, h, index, num, den;
+ int w, h, num, den;
const SvcInternal *const si = get_const_svc_internal(svc_ctx);
if (svc_ctx == NULL || si == NULL || width == NULL || height == NULL) {
@@ -593,9 +566,8 @@
}
if (layer < 0 || layer >= si->layers) return VPX_CODEC_INVALID_PARAM;
- index = layer + VPX_SS_MAX_LAYERS - si->layers;
- num = si->scaling_factor_num[index];
- den = si->scaling_factor_den[index];
+ num = si->scaling_factor_num[layer];
+ den = si->scaling_factor_den[layer];
if (num == 0 || den == 0) return VPX_CODEC_INVALID_PARAM;
w = si->width * num / den;
@@ -613,7 +585,7 @@
static void set_svc_parameters(SvcContext *svc_ctx,
vpx_codec_ctx_t *codec_ctx) {
- int layer, layer_index;
+ int layer;
vpx_svc_parameters_t svc_params;
SvcInternal *const si = get_svc_internal(svc_ctx);
@@ -627,11 +599,10 @@
&svc_params.height)) {
svc_log(svc_ctx, SVC_LOG_ERROR, "vpx_svc_get_layer_resolution failed\n");
}
- layer_index = layer + VPX_SS_MAX_LAYERS - si->layers;
if (codec_ctx->config.enc->g_pass == VPX_RC_ONE_PASS) {
- svc_params.min_quantizer = si->quantizer[layer_index];
- svc_params.max_quantizer = si->quantizer[layer_index];
+ svc_params.min_quantizer = si->quantizer[layer];
+ svc_params.max_quantizer = si->quantizer[layer];
} else {
svc_params.min_quantizer = codec_ctx->config.enc->rc_min_quantizer;
svc_params.max_quantizer = codec_ctx->config.enc->rc_max_quantizer;