From e538a94eda51b5847ae4996a9dc9abfd15297df6 Mon Sep 17 00:00:00 2001 From: Marko Viitanen Date: Fri, 30 Sep 2016 14:59:47 +0300 Subject: [PATCH] Enable TMVP with B-frames --- src/cfg.c | 4 ---- src/encoder_state-bitstream.c | 35 ++++++++++++++++++----------- src/inter.c | 42 +++++++++++++++++++++++++---------- 3 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/cfg.c b/src/cfg.c index c279c0b9..f6b9b665 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -955,10 +955,6 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value) cfg->lossless = (bool)atobool(value); else if OPT("tmvp") { cfg->tmvp_enable = atobool(value); - if (cfg->gop_len && cfg->tmvp_enable) { - fprintf(stderr, "Cannot enable TMVP because GOP is used.\n"); - cfg->tmvp_enable = false; - } if (cfg->tiles_width_count > 1 || cfg->tiles_height_count > 1) { fprintf(stderr, "Cannot enable TMVP because tiles are used.\n"); cfg->tmvp_enable = false; diff --git a/src/encoder_state-bitstream.c b/src/encoder_state-bitstream.c index fcfae6f7..56aace1f 100644 --- a/src/encoder_state-bitstream.c +++ b/src/encoder_state-bitstream.c @@ -460,7 +460,7 @@ static void encoder_state_write_bitstream_pic_parameter_set(bitstream_t* stream, WRITE_U(stream, 1, 1, "cu_qp_delta_enabled_flag"); WRITE_UE(stream, 0, "diff_cu_qp_delta_depth"); } else { - WRITE_U(stream, 0, 1, "cu_qp_delta_enabled_flag"); + WRITE_U(stream, 0, 1, "cu_qp_delta_enabled_flag"); } //TODO: add QP offsets @@ -704,10 +704,10 @@ static void kvz_encoder_state_write_bitstream_slice_header_independent( int last_poc = 0; int poc_shift = 0; - WRITE_U(stream, state->frame->poc&0x1f, 5, "pic_order_cnt_lsb"); - WRITE_U(stream, 0, 1, "short_term_ref_pic_set_sps_flag"); - WRITE_UE(stream, ref_negative, "num_negative_pics"); - WRITE_UE(stream, ref_positive, "num_positive_pics"); + WRITE_U(stream, state->frame->poc&0x1f, 5, "pic_order_cnt_lsb"); + WRITE_U(stream, 0, 1, "short_term_ref_pic_set_sps_flag"); + WRITE_UE(stream, ref_negative, "num_negative_pics"); + WRITE_UE(stream, ref_positive, "num_positive_pics"); for (j = 0; j < ref_negative; j++) { int8_t delta_poc = 0; @@ -777,18 +777,27 @@ static void kvz_encoder_state_write_bitstream_slice_header_independent( WRITE_U(stream, 1, 1, "slice_sao_chroma_flag"); } } - + if (state->frame->slicetype != KVZ_SLICE_I) { WRITE_U(stream, 1, 1, "num_ref_idx_active_override_flag"); WRITE_UE(stream, ref_negative != 0 ? ref_negative - 1: 0, "num_ref_idx_l0_active_minus1"); + if (state->frame->slicetype == KVZ_SLICE_B) { + WRITE_UE(stream, ref_positive != 0 ? ref_positive - 1 : 0, "num_ref_idx_l1_active_minus1"); + WRITE_U(stream, 0, 1, "mvd_l1_zero_flag"); + } + + // Temporal Motion Vector Prediction flags + if (state->encoder_control->cfg->tmvp_enable && ref_negative > 1) { if (state->frame->slicetype == KVZ_SLICE_B) { - WRITE_UE(stream, ref_positive != 0 ? ref_positive - 1 : 0, "num_ref_idx_l1_active_minus1"); - WRITE_U(stream, 0, 1, "mvd_l1_zero_flag"); + // Always use L0 for prediction + WRITE_U(stream, 1, 1, "collocated_from_l0_flag"); } - // ToDo: handle B-frames with TMVP - if (state->encoder_control->cfg.tmvp_enable && ref_negative > 1) { - WRITE_UE(stream, 0, "collocated_ref_idx"); + if (ref_negative > 1) { + // Use first reference from L0 + // ToDo: use better reference + WRITE_UE(stream, 0, "collocated_ref_idx"); + } } WRITE_UE(stream, 5-MRG_MAX_NUM_CANDS, "five_minus_max_num_merge_cand"); @@ -852,7 +861,7 @@ void kvz_encoder_state_write_bitstream_slice_header( if (state->is_leaf) { num_entry_points = 1; } else { - encoder_state_entry_points_explore(state, &num_entry_points, &max_length_seen); + encoder_state_entry_points_explore(state, &num_entry_points, &max_length_seen); } int num_offsets = num_entry_points - 1; @@ -1031,7 +1040,7 @@ static void encoder_state_write_bitstream_main(encoder_state_t * const state) } state->frame->total_bits_coded += newpos - curpos; - state->frame->cur_gop_bits_coded = state->previous_encoder_state->frame->cur_gop_bits_coded; + state->frame->cur_gop_bits_coded = state->previous_encoder_state->frame->cur_gop_bits_coded; state->frame->cur_gop_bits_coded += newpos - curpos; } diff --git a/src/inter.c b/src/inter.c index e943b0fe..c1d3bd22 100644 --- a/src/inter.c +++ b/src/inter.c @@ -679,7 +679,7 @@ static void kvz_inter_get_temporal_merge_candidates(const encoder_state_t * cons for (int temporal_cand = 0; temporal_cand < state->frame->ref->used_size; temporal_cand++) { int td = state->frame->poc - state->frame->ref->pocs[temporal_cand]; - td = td < 0 ? -td : td; + td = td < 0 ? UINT_MAX : td; if (td < poc_diff) { closest_ref = temporal_cand; poc_diff = td; @@ -1088,12 +1088,12 @@ static void get_mv_cand_from_spatial(const encoder_state_t * const state, if (state->encoder_control->cfg.tmvp_enable) { /* Predictor block locations - _________ + __________ |CurrentPU| | |C0|__ | | |C3| | |_________|_ - |H| + |H| */ // Find temporal reference, closest POC @@ -1102,7 +1102,7 @@ static void get_mv_cand_from_spatial(const encoder_state_t * const state, for (int temporal_cand = 0; temporal_cand < state->frame->ref->used_size; temporal_cand++) { int td = state->frame->poc - state->frame->ref->pocs[temporal_cand]; - td = td < 0 ? -td : td; + td = td < 0 ? UINT_MAX : td; if (td < poc_diff) { poc_diff = td; } @@ -1113,6 +1113,10 @@ static void get_mv_cand_from_spatial(const encoder_state_t * const state, if (selected_CU) { int td = selected_CU->inter.mv_ref[reflist] + 1; int tb = cur_cu->inter.mv_ref[reflist] + 1; + // If the selected CU does not have the correct list (L0/L1) vector, use the other + if (!selected_CU->inter.mv_dir & (1 << reflist)) { + td = selected_CU->inter.mv_ref[reflist2nd] + 1; + } int scale = CALCULATE_SCALE(NULL, tb, td); mv_cand[candidates][0] = ((scale * selected_CU->inter.mv[0][0] + 127 + (scale * selected_CU->inter.mv[0][0] < 0)) >> 8); @@ -1330,18 +1334,32 @@ uint8_t kvz_inter_get_merge_cand(const encoder_state_t * const state, const cu_info_t *selected_CU = (h != NULL) ? h : (c3 != NULL) ? c3 : NULL; if (selected_CU) { + uint32_t poc_diff = UINT_MAX; + for (int temporal_cand = 0; temporal_cand < state->frame->ref->used_size; temporal_cand++) { + int td = state->frame->poc - state->frame->ref->pocs[temporal_cand]; + td = td < 0 ? UINT_MAX : td; + if (td < poc_diff) { + poc_diff = td; + } + } + int td = selected_CU->inter.mv_ref[0] + 1; - int tb = 1; + int tb = poc_diff; int scale = CALCULATE_SCALE(NULL, tb, td); - mv_cand[candidates].mv[0][0] = ((scale * selected_CU->inter.mv[0][0] + 127 + (scale * selected_CU->inter.mv[0][0] < 0)) >> 8); - mv_cand[candidates].mv[0][1] = ((scale * selected_CU->inter.mv[0][1] + 127 + (scale * selected_CU->inter.mv[0][1] < 0)) >> 8); - /* - ToDo: temporal prediction in B-pictures - mv_cand[candidates].mv[1][0] = selected_CU->inter.mv[1][0]; - mv_cand[candidates].mv[1][1] = selected_CU->inter.mv[1][1]; - */ + // L0 list + if (selected_CU->inter.mv_dir & 0x1) { + mv_cand[candidates].mv[0][0] = ((scale * selected_CU->inter.mv[0][0] + 127 + (scale * selected_CU->inter.mv[0][0] < 0)) >> 8); + mv_cand[candidates].mv[0][1] = ((scale * selected_CU->inter.mv[0][1] + 127 + (scale * selected_CU->inter.mv[0][1] < 0)) >> 8); + } + + // L1 list + if (selected_CU->inter.mv_dir & 0x2) { + mv_cand[candidates].mv[1][0] = ((scale * selected_CU->inter.mv[1][0] + 127 + (scale * selected_CU->inter.mv[1][0] < 0)) >> 8); + mv_cand[candidates].mv[1][1] = ((scale * selected_CU->inter.mv[1][1] + 127 + (scale * selected_CU->inter.mv[1][1] < 0)) >> 8); + } + mv_cand[candidates].dir = selected_CU->inter.mv_dir; mv_cand[candidates].ref[0] = 0; candidates++;