[spatial svc]Add layer bitrates options and clean up parsing options from string
Change-Id: I6e8d3f08c8a8ce3555a498feba471ba9fec411c6
This commit is contained in:
Родитель
7ba600dc89
Коммит
a8d44b9915
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.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 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 {
|
||||
|
@ -68,6 +94,7 @@ typedef struct SvcInternal {
|
|||
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,158 +224,62 @@ static int svc_log(SvcContext *svc_ctx, SVC_LOG_LEVEL level,
|
|||
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) {
|
||||
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;
|
||||
} else {
|
||||
input_string = strdup(alt_ref_options);
|
||||
}
|
||||
|
||||
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;
|
||||
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 *token;
|
||||
const char *delim = ",";
|
||||
char *save_ptr;
|
||||
int found = 0;
|
||||
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;
|
||||
int64_t num, den;
|
||||
vpx_codec_err_t res = VPX_CODEC_OK;
|
||||
SvcInternal *const si = get_svc_internal(svc_ctx);
|
||||
char *input_string;
|
||||
char *token;
|
||||
const char *delim = ",";
|
||||
char *save_ptr;
|
||||
|
||||
if (scale_factors == NULL || strlen(scale_factors) == 0) {
|
||||
input_string = strdup(DEFAULT_SCALE_FACTORS);
|
||||
} else {
|
||||
input_string = strdup(scale_factors);
|
||||
}
|
||||
if (input == NULL || option0 == NULL ||
|
||||
(option1 == NULL && type == SCALE_FACTOR))
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
|
||||
input_string = strdup(input);
|
||||
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;
|
||||
res = extract_option(type, token, option0 + i, option1 + i);
|
||||
if (res != VPX_CODEC_OK)
|
||||
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;
|
||||
} 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: scale-factors: %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;
|
||||
}
|
||||
free(input_string);
|
||||
|
@ -369,6 +300,7 @@ static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
|
|||
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,13 +321,21 @@ static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
|
|||
} 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);
|
||||
|
@ -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))
|
||||
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 @@ vpx_codec_err_t vpx_svc_set_scale_factors(SvcContext *svc_ctx,
|
|||
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,11 +464,27 @@ vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
|
|||
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
|
||||
|
@ -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;
|
||||
|
||||
// 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};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
assign_layer_bitrates(si, enc_cfg);
|
||||
|
||||
#if CONFIG_SPATIAL_SVC
|
||||
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,
|
||||
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 @@ vpx_codec_err_t vpx_svc_get_layer_resolution(const SvcContext *svc_ctx,
|
|||
}
|
||||
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 @@ vpx_codec_err_t vpx_svc_get_layer_resolution(const SvcContext *svc_ctx,
|
|||
|
||||
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 @@ static void set_svc_parameters(SvcContext *svc_ctx,
|
|||
&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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче