mirror of
https://github.com/ultravideo/uvg266.git
synced 2024-11-27 19:24:06 +00:00
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:
parent
cbfa824d1a
commit
16790c9f15
193
src/cfg.c
193
src/cfg.c
|
@ -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.
|
cfg->gop_lowdelay = true;
|
||||||
// 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;
|
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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue