[spatial svc]Add layer bitrates options and clean up parsing options from string

Change-Id: I6e8d3f08c8a8ce3555a498feba471ba9fec411c6
This commit is contained in:
Minghai Shang 2014-09-04 16:35:28 -07:00
Родитель 7ba600dc89
Коммит a8d44b9915
1 изменённых файлов: 151 добавлений и 180 удалений

Просмотреть файл

@ -15,6 +15,7 @@
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
#include <limits.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -47,8 +48,33 @@ _CRTIMP char *__cdecl strtok_s(char *str, const char *delim, char **context);
#define OPTION_BUFFER_SIZE 256 #define OPTION_BUFFER_SIZE 256
#define COMPONENTS 4 // psnr & sse statistics maintained for total, y, u, v #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 int DEFAULT_QUANTIZER_VALUES[VPX_SS_MAX_LAYERS] = {
static const char *DEFAULT_SCALE_FACTORS = "4/16,5/16,7/16,11/16,16/16"; 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 // One encoded frame
typedef struct FrameData { typedef struct FrameData {
@ -68,6 +94,7 @@ typedef struct SvcInternal {
int scaling_factor_den[VPX_SS_MAX_LAYERS]; int scaling_factor_den[VPX_SS_MAX_LAYERS];
int quantizer[VPX_SS_MAX_LAYERS]; int quantizer[VPX_SS_MAX_LAYERS];
int enable_auto_alt_ref[VPX_SS_MAX_LAYERS]; int enable_auto_alt_ref[VPX_SS_MAX_LAYERS];
int bitrates[VPX_SS_MAX_LAYERS];
// accumulated statistics // accumulated statistics
double psnr_sum[VPX_SS_MAX_LAYERS][COMPONENTS]; // total/Y/U/V double psnr_sum[VPX_SS_MAX_LAYERS][COMPONENTS]; // total/Y/U/V
@ -197,158 +224,62 @@ static int svc_log(SvcContext *svc_ctx, SVC_LOG_LEVEL level,
return retval; return retval;
} }
static vpx_codec_err_t parse_quantizer_values(SvcContext *svc_ctx, static vpx_codec_err_t extract_option(LAYER_OPTION_TYPE type,
const char *quantizer_values) { char *input,
char *input_string; int *value0,
char *token; int *value1) {
const char *delim = ","; if (type == SCALE_FACTOR) {
char *save_ptr; *value0 = strtol(input, &input, 10);
int found = 0; if (*input++ != '/')
int i, q; return VPX_CODEC_INVALID_PARAM;
vpx_codec_err_t res = VPX_CODEC_OK; *value1 = strtol(input, &input, 10);
SvcInternal *const si = get_svc_internal(svc_ctx);
if (quantizer_values == NULL || strlen(quantizer_values) == 0) { if (*value0 < option_min_values[SCALE_FACTOR] ||
input_string = strdup(DEFAULT_QUANTIZER_VALUES); *value1 < option_min_values[SCALE_FACTOR] ||
} else { *value0 > option_max_values[SCALE_FACTOR] ||
input_string = strdup(quantizer_values); *value1 > option_max_values[SCALE_FACTOR])
}
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;
}
static vpx_codec_err_t parse_auto_alt_ref(SvcContext *svc_ctx,
const char *alt_ref_options) {
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) {
return VPX_CODEC_INVALID_PARAM; return VPX_CODEC_INVALID_PARAM;
} else { } else {
input_string = strdup(alt_ref_options); *value0 = atoi(input);
if (*value0 < option_min_values[type] ||
*value0 > option_max_values[type])
return VPX_CODEC_INVALID_PARAM;
}
return VPX_CODEC_OK;
} }
token = strtok_r(input_string, delim, &save_ptr); static vpx_codec_err_t parse_layer_options_from_string(SvcContext *svc_ctx,
for (i = 0; i < svc_ctx->spatial_layers; ++i) { LAYER_OPTION_TYPE type,
if (token != NULL) { const char *input,
value = atoi(token); int *option0,
if (value < 0 || value > 1) { int *option1) {
svc_log(svc_ctx, SVC_LOG_ERROR, int i;
"enable auto alt ref values: invalid value %s\n", token); vpx_codec_err_t res = VPX_CODEC_OK;
res = VPX_CODEC_INVALID_PARAM;
break;
}
token = strtok_r(NULL, delim, &save_ptr);
found = i + 1;
} else {
value = 0;
}
si->enable_auto_alt_ref[i] = value;
if (value > 0)
++enabled;
}
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;
}
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 *input_string;
char *token; char *token;
const char *delim = ","; const char *delim = ",";
char *save_ptr; 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) { if (input == NULL || option0 == NULL ||
input_string = strdup(DEFAULT_SCALE_FACTORS); (option1 == NULL && type == SCALE_FACTOR))
} else { return VPX_CODEC_INVALID_PARAM;
input_string = strdup(scale_factors);
} input_string = strdup(input);
token = strtok_r(input_string, delim, &save_ptr); token = strtok_r(input_string, delim, &save_ptr);
for (i = 0; i < svc_ctx->spatial_layers; ++i) { for (i = 0; i < svc_ctx->spatial_layers; ++i) {
num = den = 0;
if (token != NULL) { if (token != NULL) {
num = strtol(token, &token, 10); res = extract_option(type, token, option0 + i, option1 + i);
if (num <= 0) { if (res != VPX_CODEC_OK)
log_invalid_scale_factor(svc_ctx, token);
res = VPX_CODEC_INVALID_PARAM;
break; 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); token = strtok_r(NULL, delim, &save_ptr);
found = i + 1; } else {
break;
} }
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) { if (res == VPX_CODEC_OK && i != svc_ctx->spatial_layers) {
svc_log(svc_ctx, SVC_LOG_ERROR, svc_log(svc_ctx, SVC_LOG_ERROR,
"svc: scale-factors: %d values required, but only %d specified\n", "svc: layer params type: %d %d values required, "
svc_ctx->spatial_layers, found); "but only %d specified\n", type, svc_ctx->spatial_layers, i);
res = VPX_CODEC_INVALID_PARAM; res = VPX_CODEC_INVALID_PARAM;
} }
free(input_string); free(input_string);
@ -369,6 +300,7 @@ static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
char *input_ptr; char *input_ptr;
SvcInternal *const si = get_svc_internal(svc_ctx); SvcInternal *const si = get_svc_internal(svc_ctx);
vpx_codec_err_t res = VPX_CODEC_OK; vpx_codec_err_t res = VPX_CODEC_OK;
int i, alt_ref_enabled = 0;
if (options == NULL) return VPX_CODEC_OK; if (options == NULL) return VPX_CODEC_OK;
input_string = strdup(options); input_string = strdup(options);
@ -389,13 +321,21 @@ static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
} else if (strcmp("temporal-layers", option_name) == 0) { } else if (strcmp("temporal-layers", option_name) == 0) {
svc_ctx->temporal_layers = atoi(option_value); svc_ctx->temporal_layers = atoi(option_value);
} else if (strcmp("scale-factors", option_name) == 0) { } 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; if (res != VPX_CODEC_OK) break;
} else if (strcmp("quantizers", option_name) == 0) { } 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; if (res != VPX_CODEC_OK) break;
} else if (strcmp("auto-alt-refs", option_name) == 0) { } 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; if (res != VPX_CODEC_OK) break;
} else if (strcmp("multi-frame-contexts", option_name) == 0) { } else if (strcmp("multi-frame-contexts", option_name) == 0) {
si->use_multiple_frame_contexts = atoi(option_value); si->use_multiple_frame_contexts = atoi(option_value);
@ -413,6 +353,16 @@ static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
svc_ctx->spatial_layers * svc_ctx->temporal_layers > 4)) svc_ctx->spatial_layers * svc_ctx->temporal_layers > 4))
res = VPX_CODEC_INVALID_PARAM; 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; return res;
} }
@ -448,6 +398,39 @@ vpx_codec_err_t vpx_svc_set_scale_factors(SvcContext *svc_ctx,
return VPX_CODEC_OK; 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_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
vpx_codec_iface_t *iface, vpx_codec_iface_t *iface,
vpx_codec_enc_cfg_t *enc_cfg) { vpx_codec_enc_cfg_t *enc_cfg) {
@ -481,11 +464,27 @@ vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
return VPX_CODEC_INVALID_PARAM; return VPX_CODEC_INVALID_PARAM;
} }
res = parse_quantizer_values(svc_ctx, si->quantizers); for (i = 0; i < VPX_SS_MAX_LAYERS; ++i) {
if (res != VPX_CODEC_OK) return res; 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 (strlen(si->quantizers) > 0) {
if (res != VPX_CODEC_OK) return res; 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 // Parse aggregate command line options. Options must start with
// "layers=xx" then followed by other options // "layers=xx" then followed by other options
@ -504,33 +503,7 @@ vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
si->layers = svc_ctx->spatial_layers; si->layers = svc_ctx->spatial_layers;
// Assign target bitrate for each layer. We calculate the ratio assign_layer_bitrates(si, enc_cfg);
// 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};
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 #if CONFIG_SPATIAL_SVC
for (i = 0; i < si->layers; ++i) for (i = 0; i < si->layers; ++i)
@ -585,7 +558,7 @@ vpx_codec_err_t vpx_svc_get_layer_resolution(const SvcContext *svc_ctx,
int layer, int layer,
unsigned int *width, unsigned int *width,
unsigned int *height) { unsigned int *height) {
int w, h, index, num, den; int w, h, num, den;
const SvcInternal *const si = get_const_svc_internal(svc_ctx); const SvcInternal *const si = get_const_svc_internal(svc_ctx);
if (svc_ctx == NULL || si == NULL || width == NULL || height == NULL) { if (svc_ctx == NULL || si == NULL || width == NULL || height == NULL) {
@ -593,9 +566,8 @@ vpx_codec_err_t vpx_svc_get_layer_resolution(const SvcContext *svc_ctx,
} }
if (layer < 0 || layer >= si->layers) return VPX_CODEC_INVALID_PARAM; if (layer < 0 || layer >= si->layers) return VPX_CODEC_INVALID_PARAM;
index = layer + VPX_SS_MAX_LAYERS - si->layers; num = si->scaling_factor_num[layer];
num = si->scaling_factor_num[index]; den = si->scaling_factor_den[layer];
den = si->scaling_factor_den[index];
if (num == 0 || den == 0) return VPX_CODEC_INVALID_PARAM; if (num == 0 || den == 0) return VPX_CODEC_INVALID_PARAM;
w = si->width * num / den; w = si->width * num / den;
@ -613,7 +585,7 @@ vpx_codec_err_t vpx_svc_get_layer_resolution(const SvcContext *svc_ctx,
static void set_svc_parameters(SvcContext *svc_ctx, static void set_svc_parameters(SvcContext *svc_ctx,
vpx_codec_ctx_t *codec_ctx) { vpx_codec_ctx_t *codec_ctx) {
int layer, layer_index; int layer;
vpx_svc_parameters_t svc_params; vpx_svc_parameters_t svc_params;
SvcInternal *const si = get_svc_internal(svc_ctx); SvcInternal *const si = get_svc_internal(svc_ctx);
@ -627,11 +599,10 @@ static void set_svc_parameters(SvcContext *svc_ctx,
&svc_params.height)) { &svc_params.height)) {
svc_log(svc_ctx, SVC_LOG_ERROR, "vpx_svc_get_layer_resolution failed\n"); 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) { if (codec_ctx->config.enc->g_pass == VPX_RC_ONE_PASS) {
svc_params.min_quantizer = si->quantizer[layer_index]; svc_params.min_quantizer = si->quantizer[layer];
svc_params.max_quantizer = si->quantizer[layer_index]; svc_params.max_quantizer = si->quantizer[layer];
} else { } else {
svc_params.min_quantizer = codec_ctx->config.enc->rc_min_quantizer; svc_params.min_quantizer = codec_ctx->config.enc->rc_min_quantizer;
svc_params.max_quantizer = codec_ctx->config.enc->rc_max_quantizer; svc_params.max_quantizer = codec_ctx->config.enc->rc_max_quantizer;