Fix mode_ref_delta_update signaling when delta is disabled When mode_ref_delta_enabled is false (0), the encoder was unconditionally writing the mode_ref_delta_update flag to the bitstream. However, standard AV1 decoders (following Section 5.9.11 of the specification) only parse the update flag and delta lists if loop_filter_delta_enabled is true. This patch fixes the issue by nesting the delta update signaling inside the mode_ref_delta_enabled check in encode_loopfilter(), matching the parsing logic of the decoder. To validate this, a codec control API (AV1E_SET_MODE_REF_DELTA_ENABLED) is added to the encoder and 'LFControlModeRefDeltaEndToEndTestTest' unit test is added in loopfilter_control_test.cc using the exisiting framework. Change-Id: I3fa91f6c6d7eb818f3ea164fb950690aefa37891
diff --git a/aom/aomcx.h b/aom/aomcx.h index 0e33a39..b1a4bed 100644 --- a/aom/aomcx.h +++ b/aom/aomcx.h
@@ -1647,6 +1647,15 @@ */ AOME_SET_VALIDATE_HBD_INPUT = 175, + /*!\brief Codec control function to toggle loopfilter mode_ref_delta_enabled. + * + * - 0 = disable + * - 1 = enable (default) + * + * \note This is only used in loopfilter control unit test. + */ + AV1E_SET_MODE_REF_DELTA_ENABLED = 176, + // Any new encoder control IDs should be added above. // Maximum allowed encoder control ID is 229. // No encoder control ID should be added below. @@ -2433,6 +2442,9 @@ AOM_CTRL_USE_TYPE(AV1E_GET_GOP_INFO, aom_gop_info_t *) #define AOM_CTRL_AV1E_GET_GOP_INFO +AOM_CTRL_USE_TYPE(AV1E_SET_MODE_REF_DELTA_ENABLED, int) +#define AOM_CTRL_AV1E_SET_MODE_REF_DELTA_ENABLED + /*!\endcond */ /*! @} - end defgroup aom_encoder */ #ifdef __cplusplus
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c index 9e921bd..ba59e9e 100644 --- a/av1/av1_cx_iface.c +++ b/av1/av1_cx_iface.c
@@ -227,6 +227,7 @@ // Indicates if the application of post-processing filters should be skipped // on reconstructed frame. unsigned int skip_postproc_filtering; + int mode_ref_delta_enabled; // the name of the second pass output file when passes > 2 const char *two_pass_output; const char *second_pass_log; @@ -397,6 +398,7 @@ -1, // fwd_kf_dist LOOPFILTER_ALL, // loopfilter_control 0, // skip_postproc_filtering + 1, // mode_ref_delta_enabled NULL, // two_pass_output NULL, // second_pass_log 0, // auto_intra_tools_off @@ -555,6 +557,7 @@ -1, // fwd_kf_dist LOOPFILTER_ALL, // loopfilter_control 0, // skip_postproc_filtering + 1, // mode_ref_delta_enabled NULL, // two_pass_output NULL, // second_pass_log 0, // auto_intra_tools_off @@ -946,6 +949,7 @@ RANGE_CHECK_HI(extra_cfg, deltaq_strength, 1000); RANGE_CHECK_HI(extra_cfg, loopfilter_control, 3); RANGE_CHECK_BOOL(extra_cfg, skip_postproc_filtering); + RANGE_CHECK_BOOL(extra_cfg, mode_ref_delta_enabled); RANGE_CHECK_HI(extra_cfg, enable_cdef, 3); RANGE_CHECK_BOOL(extra_cfg, auto_intra_tools_off); RANGE_CHECK_BOOL(extra_cfg, strict_level_conformance); @@ -1366,6 +1370,7 @@ resize_cfg->resize_mode ? 0 : extra_cfg->enable_tpl_model; algo_cfg->loopfilter_control = extra_cfg->loopfilter_control; algo_cfg->skip_postproc_filtering = extra_cfg->skip_postproc_filtering; + algo_cfg->mode_ref_delta_enabled = extra_cfg->mode_ref_delta_enabled; algo_cfg->screen_detection_mode = extra_cfg->screen_detection_mode; // Set two-pass stats configuration. @@ -2838,6 +2843,14 @@ return update_extra_cfg(ctx, &extra_cfg); } +static aom_codec_err_t ctrl_set_mode_ref_delta_enabled( + aom_codec_alg_priv_t *ctx, va_list args) { + struct av1_extracfg extra_cfg = ctx->extra_cfg; + extra_cfg.mode_ref_delta_enabled = + CAST(AV1E_SET_MODE_REF_DELTA_ENABLED, args); + return update_extra_cfg(ctx, &extra_cfg); +} + static aom_codec_err_t ctrl_set_rtc_external_rc(aom_codec_alg_priv_t *ctx, va_list args) { ctx->ppi->cpi->rc.rtc_external_ratectrl = @@ -5086,6 +5099,7 @@ { AV1E_SET_ENABLE_TX_SIZE_SEARCH, ctrl_set_enable_tx_size_search }, { AV1E_SET_LOOPFILTER_CONTROL, ctrl_set_loopfilter_control }, { AV1E_SET_SKIP_POSTPROC_FILTERING, ctrl_set_skip_postproc_filtering }, + { AV1E_SET_MODE_REF_DELTA_ENABLED, ctrl_set_mode_ref_delta_enabled }, { AV1E_SET_AUTO_INTRA_TOOLS_OFF, ctrl_set_auto_intra_tools_off }, { AV1E_SET_RTC_EXTERNAL_RC, ctrl_set_rtc_external_rc }, { AV1E_SET_QUANTIZER_ONE_PASS, ctrl_set_quantizer_one_pass },
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c index 558ac36..53ca4ad 100644 --- a/av1/encoder/bitstream.c +++ b/av1/encoder/bitstream.c
@@ -2047,35 +2047,39 @@ aom_wb_write_bit(wb, lf->mode_ref_delta_enabled); - // Write out loop filter deltas applied at the MB level based on mode or - // ref frame (if they are enabled), only if there is information to write. - int meaningful = is_mode_ref_delta_meaningful(cm); - aom_wb_write_bit(wb, meaningful); - if (!meaningful) { - return; - } + if (lf->mode_ref_delta_enabled) { + // Check if the loop filter deltas have changed from the previous frame. + // If mode_ref_delta_update_decision is 1, signal the update and write out + // the actual delta values. Otherwise, signal 0 and skip writing delta + // values. + int mode_ref_delta_update = is_mode_ref_delta_meaningful(cm); + aom_wb_write_bit(wb, mode_ref_delta_update); + if (!mode_ref_delta_update) { + return; + } - const RefCntBuffer *buf = get_primary_ref_frame_buf(cm); - int8_t last_ref_deltas[REF_FRAMES]; - int8_t last_mode_deltas[MAX_MODE_LF_DELTAS]; - if (buf == NULL) { - av1_set_default_ref_deltas(last_ref_deltas); - av1_set_default_mode_deltas(last_mode_deltas); - } else { - memcpy(last_ref_deltas, buf->ref_deltas, REF_FRAMES); - memcpy(last_mode_deltas, buf->mode_deltas, MAX_MODE_LF_DELTAS); - } - for (int i = 0; i < REF_FRAMES; i++) { - const int delta = lf->ref_deltas[i]; - const int changed = delta != last_ref_deltas[i]; - aom_wb_write_bit(wb, changed); - if (changed) aom_wb_write_inv_signed_literal(wb, delta, 6); - } - for (int i = 0; i < MAX_MODE_LF_DELTAS; i++) { - const int delta = lf->mode_deltas[i]; - const int changed = delta != last_mode_deltas[i]; - aom_wb_write_bit(wb, changed); - if (changed) aom_wb_write_inv_signed_literal(wb, delta, 6); + const RefCntBuffer *buf = get_primary_ref_frame_buf(cm); + int8_t last_ref_deltas[REF_FRAMES]; + int8_t last_mode_deltas[MAX_MODE_LF_DELTAS]; + if (buf == NULL) { + av1_set_default_ref_deltas(last_ref_deltas); + av1_set_default_mode_deltas(last_mode_deltas); + } else { + memcpy(last_ref_deltas, buf->ref_deltas, REF_FRAMES); + memcpy(last_mode_deltas, buf->mode_deltas, MAX_MODE_LF_DELTAS); + } + for (int i = 0; i < REF_FRAMES; i++) { + const int delta = lf->ref_deltas[i]; + const int changed = delta != last_ref_deltas[i]; + aom_wb_write_bit(wb, changed); + if (changed) aom_wb_write_inv_signed_literal(wb, delta, 6); + } + for (int i = 0; i < MAX_MODE_LF_DELTAS; i++) { + const int delta = lf->mode_deltas[i]; + const int changed = delta != last_mode_deltas[i]; + aom_wb_write_bit(wb, changed); + if (changed) aom_wb_write_inv_signed_literal(wb, delta, 6); + } } }
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c index 8170c1d..1ccff9e 100644 --- a/av1/encoder/encodeframe.c +++ b/av1/encoder/encodeframe.c
@@ -2384,6 +2384,7 @@ memcpy(cm->lf.ref_deltas, cm->prev_frame->ref_deltas, REF_FRAMES); memcpy(cm->lf.mode_deltas, cm->prev_frame->mode_deltas, MAX_MODE_LF_DELTAS); } + cm->lf.mode_ref_delta_enabled = oxcf->algo_cfg.mode_ref_delta_enabled; memcpy(cm->cur_frame->ref_deltas, cm->lf.ref_deltas, REF_FRAMES); memcpy(cm->cur_frame->mode_deltas, cm->lf.mode_deltas, MAX_MODE_LF_DELTAS);
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h index ab858ea..601a111 100644 --- a/av1/encoder/encoder.h +++ b/av1/encoder/encoder.h
@@ -887,6 +887,12 @@ bool skip_postproc_filtering; /*! + * Indicates if mode and reference frame delta should be enabled during + * loopfiltering. + */ + int mode_ref_delta_enabled; + + /*! * Controls screen content detection mode */ aom_screen_detection_mode screen_detection_mode;
diff --git a/test/loopfilter_control_test.cc b/test/loopfilter_control_test.cc index f498559..7a19a7f 100644 --- a/test/loopfilter_control_test.cc +++ b/test/loopfilter_control_test.cc
@@ -67,17 +67,18 @@ { "niklas_1280_720_30.y4m", 8, AOM_IMG_FMT_I420, AOM_BITS_8, 0 }, }; -// Params: test video, lf_control, aq mode, threads, tile columns. +// Params: test video, lf_control, aq mode, threads, tile columns, +// mode_ref_delta_enabled. class LFControlEndToEndTest - : public ::libaom_test::CodecTestWith5Params<TestVideoParam, int, - unsigned int, int, int>, + : public ::libaom_test::CodecTestWith6Params<TestVideoParam, int, + unsigned int, int, int, int>, public ::libaom_test::EncoderTest { protected: LFControlEndToEndTest() : EncoderTest(GET_PARAM(0)), test_video_param_(GET_PARAM(1)), lf_control_(GET_PARAM(2)), psnr_(0.0), nframes_(0), aq_mode_(GET_PARAM(3)), threads_(GET_PARAM(4)), - tile_columns_(GET_PARAM(5)) {} + tile_columns_(GET_PARAM(5)), mode_ref_delta_enabled_(GET_PARAM(6)) {} ~LFControlEndToEndTest() override = default; @@ -123,6 +124,8 @@ encoder->Control(AV1E_SET_MV_COST_UPD_FREQ, 2); encoder->Control(AV1E_SET_DV_COST_UPD_FREQ, 2); encoder->Control(AV1E_SET_LOOPFILTER_CONTROL, lf_control_); + encoder->Control(AV1E_SET_MODE_REF_DELTA_ENABLED, + mode_ref_delta_enabled_); } } @@ -164,14 +167,19 @@ unsigned int aq_mode_; int threads_; int tile_columns_; + int mode_ref_delta_enabled_; }; class LFControlEndToEndTestThreaded : public LFControlEndToEndTest {}; +class LFControlModeRefDeltaEndToEndTestTest : public LFControlEndToEndTest {}; + TEST_P(LFControlEndToEndTest, EndtoEndPSNRTest) { DoTest(); } TEST_P(LFControlEndToEndTestThreaded, EndtoEndPSNRTest) { DoTest(); } +TEST_P(LFControlModeRefDeltaEndToEndTestTest, EndtoEndPSNRTest) { DoTest(); } + TEST(LFControlGetterTest, NullptrInput) { int *lf_level = nullptr; aom_codec_ctx_t encoder; @@ -188,11 +196,20 @@ ::testing::ValuesIn(kTestVectors), ::testing::Range(0, 4), ::testing::Values<unsigned int>(0, 3), - ::testing::Values(1), ::testing::Values(1)); + ::testing::Values(1), ::testing::Values(1), + ::testing::Values(1)); AV1_INSTANTIATE_TEST_SUITE(LFControlEndToEndTestThreaded, ::testing::ValuesIn(kTestVectors), ::testing::Range(0, 4), ::testing::Values<unsigned int>(0, 3), - ::testing::Range(2, 5), ::testing::Range(2, 5)); + ::testing::Range(2, 5), ::testing::Range(2, 5), + ::testing::Values(1)); + +AV1_INSTANTIATE_TEST_SUITE(LFControlModeRefDeltaEndToEndTestTest, + ::testing::Values(kTestVectors[0]), + ::testing::Range(0, 4), + ::testing::Values<unsigned int>(0, 3), + ::testing::Values(1), ::testing::Values(1), + ::testing::Values(0)); } // namespace