Remove number of references from --gop=lp syntax

The number of references should be part of the presets, so gop should
be defined separately.
This commit is contained in:
Ari Koivula 2016-09-27 22:12:02 +03:00
parent cbfa824d1a
commit 16790c9f15
6 changed files with 126 additions and 91 deletions

193
src/cfg.c
View file

@ -118,6 +118,9 @@ int kvz_config_init(kvz_config *cfg)
cfg->input_format = KVZ_FORMAT_P420; cfg->input_format = KVZ_FORMAT_P420;
cfg->input_bitdepth = 8; cfg->input_bitdepth = 8;
cfg->gop_lp_definition.d = 0;
cfg->gop_lp_definition.t = 0;
return 1; return 1;
} }
@ -683,105 +686,32 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value)
struct { struct {
unsigned g; // length unsigned g; // length
unsigned d; // depth unsigned d; // depth
unsigned r; // references
unsigned t; // temporal unsigned t; // temporal
} gop = { 0, 0, 0, 0 }; } gop = { 0, 0, 0 };
if (sscanf(value, "lp-g%ud%ur%ut%u", &gop.g, &gop.d, &gop.r, &gop.t) != 4) { // Parse --gop=lp-g#d#t#
fprintf(stderr, "Error in GOP syntax. Example: lp-g8d4r2t2\n"); if (sscanf(value, "lp-g%ud%ut%u", &gop.g, &gop.d, &gop.t) != 3) {
fprintf(stderr, "Error in GOP syntax. Example: lp-g8d4t2\n");
return 0; return 0;
} }
if (gop.g < 1 || gop.g > 32) { if (gop.g < 1 || gop.g > 32) {
fprintf(stderr, "gop.g must be between 1 and 32.\n"); fprintf(stderr, "gop.g must be between 1 and 32.\n");
return 0;
} }
if (gop.d < 1 || gop.d > 8) { if (gop.d < 1 || gop.d > 8) {
fprintf(stderr, "gop.d must be between 1 and 8.\n"); fprintf(stderr, "gop.d must be between 1 and 8.\n");
} return 0;
if (gop.r < 1 || gop.r > 15) {
fprintf(stderr, "gop.d must be between 1 and 15.\n");
} }
if (gop.t < 1 || gop.t > 15) { if (gop.t < 1 || gop.t > 15) {
fprintf(stderr, "gop.t must be between 1 and 32.\n"); fprintf(stderr, "gop.t must be between 1 and 15.\n");
return 0;
} }
// Initialize modulos for testing depth.
// The picture belong to the lowest depth in which (poc % modulo) == 0.
unsigned depth_modulos[8] = { 0 };
for (int d = 0; d < gop.d; ++d) {
depth_modulos[gop.d - 1 - d] = 1 << d;
}
depth_modulos[0] = gop.g;
cfg->gop_lowdelay = 1; cfg->gop_lowdelay = true;
cfg->gop_len = gop.g; cfg->gop_len = gop.g;
for (int g = 1; g <= gop.g; ++g) { cfg->gop_lp_definition.d = gop.d;
kvz_gop_config *gop_pic = &cfg->gop[g - 1]; cfg->gop_lp_definition.t = gop.t;
// Find gop depth for picture.
int gop_layer = 0;
while (gop_layer < gop.d && (g % depth_modulos[gop_layer])) {
++gop_layer;
}
gop_pic->poc_offset = g;
gop_pic->layer = gop_layer + 1;
gop_pic->qp_offset = gop_layer + 1;
gop_pic->ref_pos_count = 0;
gop_pic->ref_neg_count = gop.r;
gop_pic->is_ref = 0;
// Set first ref to point to previous frame, and the rest to previous
// key-frames.
// If gop.t > 1, have (poc % gop.t) == 0 point gop.t frames away,
// instead of the previous frame. Set the frames in between to
// point to the nearest frame with a lower gop-depth.
if (gop.t > 1) {
if (gop_pic->poc_offset % gop.t == 0) {
gop_pic->ref_neg[0] = gop.t;
} else {
int r = gop_pic->poc_offset - 1;
while (r > 0) {
if (cfg->gop[r].layer < gop_pic->layer) break;
--r;
}
// Var r is now 0 or index of the pic with layer < depth.
if (cfg->gop[r].layer < gop_pic->layer) {
gop_pic->ref_neg[0] = gop_pic->poc_offset - cfg->gop[r].poc_offset;
cfg->gop[r].is_ref = 1;
} else {
// No ref was found, just refer to the previous key-frame.
gop_pic->ref_neg[0] = gop_pic->poc_offset % gop.g;
}
}
} else {
gop_pic->ref_neg[0] = 1;
if (gop_pic->poc_offset >= 2) {
cfg->gop[gop_pic->poc_offset - 2].is_ref = 1;
}
}
int keyframe = gop_pic->poc_offset;
for (int i = 1; i < gop_pic->ref_neg_count; ++i) {
while (keyframe == gop_pic->ref_neg[i - 1]) {
keyframe += gop.g;
}
gop_pic->ref_neg[i] = keyframe;
}
gop_pic->qp_factor = 0.4624; // from HM
}
for (int g = 0; g < gop.g; ++g) {
kvz_gop_config *gop_pic = &cfg->gop[g];
if (!gop_pic->is_ref) {
gop_pic->qp_factor = 0.68 * 1.31; // derived from HM
}
}
// Key-frame is always a reference.
cfg->gop[gop.g - 1].is_ref = 1;
cfg->gop[gop.g - 1].qp_factor = 0.578; // from HM
} else if (atoi(value) == 8) { } else if (atoi(value) == 8) {
cfg->gop_lowdelay = 0; cfg->gop_lowdelay = 0;
// GOP // GOP
@ -821,10 +751,6 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value)
fprintf(stderr, "Input error: unsupported gop length, must be 0 or 8\n"); fprintf(stderr, "Input error: unsupported gop length, must be 0 or 8\n");
return 0; return 0;
} }
if (cfg->gop_len && cfg->tmvp_enable) {
cfg->tmvp_enable = false;
fprintf(stderr, "Disabling TMVP because GOP is used.\n");
}
} }
else if OPT("bipred") else if OPT("bipred")
cfg->bipred = atobool(value); cfg->bipred = atobool(value);
@ -985,6 +911,97 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value)
return 1; return 1;
} }
void kvz_config_process_lp_gop(kvz_config *cfg)
{
struct {
unsigned g;
unsigned d;
unsigned t;
} gop;
gop.g = cfg->gop_len;
gop.d = cfg->gop_lp_definition.d;
gop.t = cfg->gop_lp_definition.t;
// Initialize modulos for testing depth.
// The picture belong to the lowest depth in which (poc % modulo) == 0.
unsigned depth_modulos[8] = { 0 };
for (int d = 0; d < gop.d; ++d) {
depth_modulos[gop.d - 1 - d] = 1 << d;
}
depth_modulos[0] = gop.g;
cfg->gop_lowdelay = 1;
cfg->gop_len = gop.g;
for (int g = 1; g <= gop.g; ++g) {
kvz_gop_config *gop_pic = &cfg->gop[g - 1];
// Find gop depth for picture.
int gop_layer = 0;
while (gop_layer < gop.d && (g % depth_modulos[gop_layer])) {
++gop_layer;
}
gop_pic->poc_offset = g;
gop_pic->layer = gop_layer + 1;
gop_pic->qp_offset = gop_layer + 1;
gop_pic->ref_pos_count = 0;
gop_pic->ref_neg_count = cfg->ref_frames;
gop_pic->is_ref = 0;
// Set first ref to point to previous frame, and the rest to previous
// key-frames.
// If gop.t > 1, have (poc % gop.t) == 0 point gop.t frames away,
// instead of the previous frame. Set the frames in between to
// point to the nearest frame with a lower gop-depth.
if (gop.t > 1) {
if (gop_pic->poc_offset % gop.t == 0) {
gop_pic->ref_neg[0] = gop.t;
} else {
int r = gop_pic->poc_offset - 1;
while (r > 0) {
if (cfg->gop[r].layer < gop_pic->layer) break;
--r;
}
// Var r is now 0 or index of the pic with layer < depth.
if (cfg->gop[r].layer < gop_pic->layer) {
gop_pic->ref_neg[0] = gop_pic->poc_offset - cfg->gop[r].poc_offset;
cfg->gop[r].is_ref = 1;
} else {
// No ref was found, just refer to the previous key-frame.
gop_pic->ref_neg[0] = gop_pic->poc_offset % gop.g;
}
}
} else {
gop_pic->ref_neg[0] = 1;
if (gop_pic->poc_offset >= 2) {
cfg->gop[gop_pic->poc_offset - 2].is_ref = 1;
}
}
int keyframe = gop_pic->poc_offset;
for (int i = 1; i < gop_pic->ref_neg_count; ++i) {
while (keyframe == gop_pic->ref_neg[i - 1]) {
keyframe += gop.g;
}
gop_pic->ref_neg[i] = keyframe;
}
gop_pic->qp_factor = 0.4624; // from HM
}
for (int g = 0; g < gop.g; ++g) {
kvz_gop_config *gop_pic = &cfg->gop[g];
if (!gop_pic->is_ref) {
gop_pic->qp_factor = 0.68 * 1.31; // derived from HM
}
}
// Key-frame is always a reference.
cfg->gop[gop.g - 1].is_ref = 1;
cfg->gop[gop.g - 1].qp_factor = 0.578; // from HM
}
/** /**
* \brief Check that configuration is sensible. * \brief Check that configuration is sensible.
* *

View file

@ -36,6 +36,7 @@ kvz_config *kvz_config_alloc(void);
int kvz_config_init(kvz_config *cfg); int kvz_config_init(kvz_config *cfg);
int kvz_config_destroy(kvz_config *cfg); int kvz_config_destroy(kvz_config *cfg);
int kvz_config_parse(kvz_config *cfg, const char *name, const char *value); int kvz_config_parse(kvz_config *cfg, const char *name, const char *value);
void kvz_config_process_lp_gop(kvz_config *cfg);
int kvz_config_validate(const kvz_config *cfg); int kvz_config_validate(const kvz_config *cfg);
#endif #endif

View file

@ -80,7 +80,7 @@ static int select_owf_auto(const kvz_config *const cfg)
* \param cfg encoder configuration * \param cfg encoder configuration
* \return initialized encoder control or NULL on failure * \return initialized encoder control or NULL on failure
*/ */
encoder_control_t* kvz_encoder_control_init(const kvz_config *const cfg) { encoder_control_t* kvz_encoder_control_init(kvz_config *const cfg) {
encoder_control_t *encoder = NULL; encoder_control_t *encoder = NULL;
if (!cfg) { if (!cfg) {
@ -88,6 +88,16 @@ encoder_control_t* kvz_encoder_control_init(const kvz_config *const cfg) {
goto init_failed; goto init_failed;
} }
if (cfg->gop_len > 0) {
if (cfg->tmvp_enable) {
cfg->tmvp_enable = false;
fprintf(stderr, "Disabling TMVP because GOP is used.\n");
}
if (cfg->gop_lowdelay) {
kvz_config_process_lp_gop(cfg);
}
}
// Make sure that the parameters make sense. // Make sure that the parameters make sense.
if (!kvz_config_validate(cfg)) { if (!kvz_config_validate(cfg)) {
goto init_failed; goto init_failed;

View file

@ -155,7 +155,7 @@ typedef struct encoder_control_t
} encoder_control_t; } encoder_control_t;
encoder_control_t* kvz_encoder_control_init(const kvz_config *cfg); encoder_control_t* kvz_encoder_control_init(kvz_config *cfg);
void kvz_encoder_control_free(encoder_control_t *encoder); void kvz_encoder_control_free(encoder_control_t *encoder);
void kvz_encoder_control_input_init(encoder_control_t *encoder, int32_t width, int32_t height); void kvz_encoder_control_input_init(encoder_control_t *encoder, int32_t width, int32_t height);

View file

@ -75,7 +75,9 @@ static kvz_encoder * kvazaar_open(const kvz_config *cfg)
goto kvazaar_open_failure; goto kvazaar_open_failure;
} }
encoder->control = kvz_encoder_control_init(cfg); // FIXME: const qualifier disgarded. I don't want to change kvazaar_open
// but I really need to change cfg.
encoder->control = kvz_encoder_control_init((kvz_config*)cfg);
if (!encoder->control) { if (!encoder->control) {
goto kvazaar_open_failure; goto kvazaar_open_failure;
} }

View file

@ -312,6 +312,11 @@ typedef struct kvz_config
enum kvz_input_format input_format; /*!< \brief Use Temporal Motion Vector Predictors. */ enum kvz_input_format input_format; /*!< \brief Use Temporal Motion Vector Predictors. */
int32_t input_bitdepth; /*!< \brief Use Temporal Motion Vector Predictors. */ int32_t input_bitdepth; /*!< \brief Use Temporal Motion Vector Predictors. */
struct {
unsigned d; // depth
unsigned t; // temporal
} gop_lp_definition;
} kvz_config; } kvz_config;
/** /**