diff --git a/media/libaom/moz.yaml b/media/libaom/moz.yaml --- a/media/libaom/moz.yaml +++ b/media/libaom/moz.yaml @@ -18,15 +18,15 @@ # Usually different from repository url url: https://aomedia.googlesource.com/aom/ # Human-readable identifier for this version/release # Generally "version NNN", "tag SSS", "bookmark SSS" - release: 5f6ce718d903dca3e49c5c10db0859a394c9be84 (Fri Feb 28 09:42:44 2025 -0800). + release: c5e1e39e9defe95965d74ad75ac9e19aad1ee9f7 (Wed Mar 19 12:44:41 2025 -0700). # Revision to pull in # Must be a long or short commit SHA (long preferred) - revision: 5f6ce718d903dca3e49c5c10db0859a394c9be84 + revision: c5e1e39e9defe95965d74ad75ac9e19aad1ee9f7 # The package's license, where possible using the mnemonic from # https://spdx.org/licenses/ # Multiple licenses can be specified (as a YAML list) # A "LICENSE" file must exist containing the full license text diff --git a/third_party/aom/aom/aomcx.h b/third_party/aom/aom/aomcx.h --- a/third_party/aom/aom/aomcx.h +++ b/third_party/aom/aom/aomcx.h @@ -206,15 +206,15 @@ * * Changes in this value influences the complexity of algorithms used in * encoding process, values greater than 0 will increase encoder speed at * the expense of quality. * - * Valid range: 0..11. 0 runs the slowest, and 11 runs the fastest; + * Valid range: 0..12. 0 runs the slowest, and 12 runs the fastest; * quality improves as speed decreases (since more compression * possibilities are explored). * - * NOTE: 10 and 11 are only allowed in AOM_USAGE_REALTIME. In + * NOTE: 10 - 12 are only allowed in AOM_USAGE_REALTIME. In * AOM_USAGE_GOOD_QUALITY and AOM_USAGE_ALL_INTRA, 9 is the highest allowed * value. However, AOM_USAGE_GOOD_QUALITY treats 7..9 the same as 6. Also, * AOM_USAGE_REALTIME treats 0..4 the same as 5. */ AOME_SET_CPUUSED = 13, @@ -1672,13 +1672,14 @@ /*!\brief Model tuning parameters * * Changes the encoder to tune for certain types of input material. * * \note - * AOM_TUNE_IQ is restricted to all intra mode (AOM_USAGE_ALL_INTRA). Setting - * the tuning option to AOM_TUNE_IQ causes the following options to be set - * (expressed as command-line options): + * AOM_TUNE_IQ and AOM_TUNE_SSIMULACRA2 are restricted to all intra mode + * (AOM_USAGE_ALL_INTRA). Setting the tuning option to either AOM_TUNE_IQ or + * AOM_TUNE_SSIMULACRA2 causes the following options to be set (expressed as + * command-line options): * * --enable-qm=1 * * --qm-min=2 * * --qm-max=10 * * --sharpness=7 * * --dist-metric=qm-psnr @@ -1701,10 +1702,16 @@ /* Image quality (or intra quality). Increases image quality and consistency, * guided by the SSIMULACRA 2 metric and subjective quality checks. Shares * the rdmult code with AOM_TUNE_SSIM. */ AOM_TUNE_IQ = 10, +/*!\brief Allows detection of the presence of AOM_TUNE_SSIMULACRA2 at compile + * time. */ +#define AOM_HAVE_TUNE_SSIMULACRA2 1 + /* Tune that optimizes for maximum SSIMULACRA 2 scores. Shares the rdmult code + with AOM_TUNE_SSIM. */ + AOM_TUNE_SSIMULACRA2 = 11, } aom_tune_metric; /*!\brief Distortion metric to use for RD optimization. * * Changes the encoder to use a different distortion metric for RD search. Note diff --git a/third_party/aom/aom_dsp/x86/synonyms.h b/third_party/aom/aom_dsp/x86/synonyms.h --- a/third_party/aom/aom_dsp/x86/synonyms.h +++ b/third_party/aom/aom_dsp/x86/synonyms.h @@ -44,28 +44,17 @@ static inline __m128i xx_loadu_128(const void *a) { return _mm_loadu_si128((const __m128i *)a); } -// _mm_loadu_si64 has been introduced in GCC 9, reimplement the function -// manually on older compilers. -#if !defined(__clang__) && __GNUC_MAJOR__ < 9 -static inline __m128i xx_loadu_2x64(const void *hi, const void *lo) { - __m64 hi_, lo_; - memcpy(&hi_, hi, sizeof(hi_)); - memcpy(&lo_, lo, sizeof(lo_)); - return _mm_set_epi64(hi_, lo_); -} -#else // Load 64 bits from each of hi and low, and pack into an SSE register // Since directly loading as `int64_t`s and using _mm_set_epi64 may violate // the strict aliasing rule, this takes a different approach static inline __m128i xx_loadu_2x64(const void *hi, const void *lo) { return _mm_unpacklo_epi64(_mm_loadl_epi64((const __m128i *)lo), _mm_loadl_epi64((const __m128i *)hi)); } -#endif static inline void xx_storel_32(void *const a, const __m128i v) { const int val = _mm_cvtsi128_si32(v); memcpy(a, &val, sizeof(val)); } diff --git a/third_party/aom/aom_dsp/x86/synonyms_avx2.h b/third_party/aom/aom_dsp/x86/synonyms_avx2.h --- a/third_party/aom/aom_dsp/x86/synonyms_avx2.h +++ b/third_party/aom/aom_dsp/x86/synonyms_avx2.h @@ -74,30 +74,15 @@ // _mm256_set_m128d() with all supported compilers. This version is used to // match the behavior with yy_set_m128i(). return yy_set_m128i(_mm_castpd_si128(v23), _mm_castpd_si128(v01)); } -#define GCC_VERSION (__GNUC__ * 10000 \ - + __GNUC_MINOR__ * 100 \ - + __GNUC_PATCHLEVEL__) - -// _mm256_loadu2_m128i has been introduced in GCC 10.1 -#if !defined(__clang__) && GCC_VERSION < 101000 -static inline __m256i yy_loadu2_128(const void *hi, const void *lo) { - __m128i mhi = _mm_loadu_si128((const __m128i *)(hi)); - __m128i mlo = _mm_loadu_si128((const __m128i *)(lo)); - return _mm256_set_m128i(mhi, mlo); -} -#else static inline __m256i yy_loadu2_128(const void *hi, const void *lo) { __m128i mhi = _mm_loadu_si128((const __m128i *)(hi)); __m128i mlo = _mm_loadu_si128((const __m128i *)(lo)); return yy_set_m128i(mhi, mlo); } -#endif - -#undef GCC_VERSION static inline void yy_storeu2_128(void *hi, void *lo, const __m256i a) { _mm_storeu_si128((__m128i *)hi, _mm256_extracti128_si256(a, 1)); _mm_storeu_si128((__m128i *)lo, _mm256_castsi256_si128(a)); } diff --git a/third_party/aom/av1/arg_defs.c b/third_party/aom/av1/arg_defs.c --- a/third_party/aom/av1/arg_defs.c +++ b/third_party/aom/av1/arg_defs.c @@ -47,10 +47,11 @@ { "vmaf", AOM_TUNE_VMAF_MAX_GAIN }, { "vmaf_neg", AOM_TUNE_VMAF_NEG_MAX_GAIN }, { "butteraugli", AOM_TUNE_BUTTERAUGLI }, { "vmaf_saliency_map", AOM_TUNE_VMAF_SALIENCY_MAP }, { "iq", AOM_TUNE_IQ }, + { "ssimulacra2", AOM_TUNE_SSIMULACRA2 }, { NULL, 0 } }; static const struct arg_enum_list dist_metric_enum[] = { { "psnr", AOM_DIST_METRIC_PSNR }, @@ -303,11 +304,11 @@ ARG_DEF(NULL, "cq-level", 1, "Constant/Constrained Quality level"), .max_intra_rate_pct = ARG_DEF(NULL, "max-intra-rate", 1, "Max I-frame bitrate (pct)"), #if CONFIG_AV1_ENCODER .cpu_used_av1 = ARG_DEF(NULL, "cpu-used", 1, - "Speed setting (0..6 in good mode, 5..11 in realtime " + "Speed setting (0..6 in good mode, 5..12 in realtime " "mode, 0..9 in all intra mode)"), .rowmtarg = ARG_DEF(NULL, "row-mt", 1, "Enable row based multi-threading (0: off, 1: on (default))"), .fpmtarg = ARG_DEF( diff --git a/third_party/aom/av1/av1_cx_iface.c b/third_party/aom/av1/av1_cx_iface.c --- a/third_party/aom/av1/av1_cx_iface.c +++ b/third_party/aom/av1/av1_cx_iface.c @@ -737,11 +737,11 @@ RANGE_CHECK_HI(extra_cfg, sb_multipass_unit_test, 1); RANGE_CHECK_HI(extra_cfg, ext_tile_debug, 1); RANGE_CHECK_HI(extra_cfg, enable_auto_alt_ref, 1); RANGE_CHECK_HI(extra_cfg, enable_auto_bwd_ref, 2); RANGE_CHECK(extra_cfg, cpu_used, 0, - (cfg->g_usage == AOM_USAGE_REALTIME) ? 11 : 9); + (cfg->g_usage == AOM_USAGE_REALTIME) ? 12 : 9); RANGE_CHECK_HI(extra_cfg, noise_sensitivity, 6); RANGE_CHECK(extra_cfg, superblock_size, AOM_SUPERBLOCK_SIZE_64X64, AOM_SUPERBLOCK_SIZE_DYNAMIC); RANGE_CHECK_HI(cfg, large_scale_tile, 1); RANGE_CHECK_HI(extra_cfg, single_tile_decoding, 1); @@ -854,11 +854,11 @@ "This error may be related to the wrong configuration options: try to " "set -DCONFIG_TUNE_VMAF=1 at the time CMake is run."); } #endif - RANGE_CHECK(extra_cfg, tuning, AOM_TUNE_PSNR, AOM_TUNE_IQ); + RANGE_CHECK(extra_cfg, tuning, AOM_TUNE_PSNR, AOM_TUNE_SSIMULACRA2); RANGE_CHECK(extra_cfg, dist_metric, AOM_DIST_METRIC_PSNR, AOM_DIST_METRIC_QM_PSNR); RANGE_CHECK(extra_cfg, timing_info_type, AOM_TIMING_UNSPECIFIED, @@ -1571,15 +1571,18 @@ if (cfg->g_lag_in_frames > 1 || cfg->g_pass != AOM_RC_ONE_PASS) ERROR("Cannot change width or height after initialization"); // Note: function encoder_set_config() is allowed to be called multiple // times. However, when the original frame width or height is less than two // times of the new frame width or height, a forced key frame should be - // used. To make sure the correct detection of a forced key frame, we need + // used (for the case of single spatial layer, since otherwise a previous + // encoded frame at a lower layer may be the desired reference). To make + // sure the correct detection of a forced key frame, we need // to update the frame width and height only when the actual encoding is // performed. cpi->last_coded_width and cpi->last_coded_height are used to // track the actual coded frame size. - if (ctx->ppi->cpi->last_coded_width && ctx->ppi->cpi->last_coded_height && + if (ctx->ppi->cpi->svc.number_spatial_layers == 1 && + ctx->ppi->cpi->last_coded_width && ctx->ppi->cpi->last_coded_height && (!valid_ref_frame_size(ctx->ppi->cpi->last_coded_width, ctx->ppi->cpi->last_coded_height, cfg->g_w, cfg->g_h) || ((int)cfg->g_w > ctx->ppi->cpi->last_coded_width) || ((int)cfg->g_h > ctx->ppi->cpi->last_coded_height))) { @@ -1817,20 +1820,21 @@ return update_extra_cfg(ctx, &extra_cfg); } static aom_codec_err_t handle_tuning(aom_codec_alg_priv_t *ctx, struct av1_extracfg *extra_cfg) { - if (extra_cfg->tuning == AOM_TUNE_IQ) { + if (extra_cfg->tuning == AOM_TUNE_IQ || + extra_cfg->tuning == AOM_TUNE_SSIMULACRA2) { if (ctx->cfg.g_usage != AOM_USAGE_ALL_INTRA) return AOM_CODEC_INCAPABLE; // Enable QMs as they've been found to be beneficial for images, when used // with alternative QM formulas: // - aom_get_qmlevel_allintra() - // - aom_get_qmlevel_luma_iq() - // - aom_get_qmlevel_444_chroma_iq() + // - aom_get_qmlevel_luma_ssimulacra2() + // - aom_get_qmlevel_444_chroma() extra_cfg->enable_qm = 1; - extra_cfg->qm_min = QM_FIRST_IQ; - extra_cfg->qm_max = QM_LAST_IQ; + extra_cfg->qm_min = QM_FIRST_IQ_SSIMULACRA2; + extra_cfg->qm_max = QM_LAST_IQ_SSIMULACRA2; // We can turn on loop filter sharpness, as frames do not have to serve as // references to others. extra_cfg->sharpness = 7; // Using the QM-PSNR metric was found to be beneficial for images (over the // default PSNR metric), as it correlates better with subjective image diff --git a/third_party/aom/av1/common/av1_common_int.h b/third_party/aom/av1/common/av1_common_int.h --- a/third_party/aom/av1/common/av1_common_int.h +++ b/third_party/aom/av1/common/av1_common_int.h @@ -167,10 +167,12 @@ // 0 = ZERO_MV, MV int8_t mode_deltas[MAX_MODE_LF_DELTAS]; FRAME_CONTEXT frame_context; + + int filter_level[2]; } RefCntBuffer; typedef struct BufferPool { // Protect BufferPool from being accessed by several FrameWorkers at // the same time during frame parallel decode. @@ -618,10 +620,15 @@ * Base qindex of the frame in the range 0 to 255. */ int base_qindex; /*! + * Sharpness adjustment in the quantization process. + */ + int sharpness; + + /*! * Delta of qindex (from base_qindex) for Y plane DC coefficient. * Note: y_ac_delta_q is implicitly 0. */ int y_dc_delta_q; diff --git a/third_party/aom/av1/common/av1_loopfilter.h b/third_party/aom/av1/common/av1_loopfilter.h --- a/third_party/aom/av1/common/av1_loopfilter.h +++ b/third_party/aom/av1/common/av1_loopfilter.h @@ -44,10 +44,14 @@ struct loopfilter { int filter_level[2]; int filter_level_u; int filter_level_v; + int backup_filter_level[2]; + int backup_filter_level_u; + int backup_filter_level_v; + int sharpness_level; uint8_t mode_ref_delta_enabled; uint8_t mode_ref_delta_update; diff --git a/third_party/aom/av1/common/mvref_common.c b/third_party/aom/av1/common/mvref_common.c --- a/third_party/aom/av1/common/mvref_common.c +++ b/third_party/aom/av1/common/mvref_common.c @@ -842,10 +842,12 @@ void av1_setup_frame_buf_refs(AV1_COMMON *cm) { cm->cur_frame->order_hint = cm->current_frame.order_hint; cm->cur_frame->display_order_hint = cm->current_frame.display_order_hint; cm->cur_frame->pyramid_level = cm->current_frame.pyramid_level; + cm->cur_frame->filter_level[0] = -1; + cm->cur_frame->filter_level[1] = -1; MV_REFERENCE_FRAME ref_frame; for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) { const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref_frame); if (buf != NULL) { cm->cur_frame->ref_order_hints[ref_frame - LAST_FRAME] = buf->order_hint; diff --git a/third_party/aom/av1/common/quant_common.h b/third_party/aom/av1/common/quant_common.h --- a/third_party/aom/av1/common/quant_common.h +++ b/third_party/aom/av1/common/quant_common.h @@ -36,12 +36,12 @@ #define DEFAULT_QM_V 12 #define DEFAULT_QM_FIRST 5 #define DEFAULT_QM_LAST 9 #define DEFAULT_QM_FIRST_ALLINTRA 4 #define DEFAULT_QM_LAST_ALLINTRA 10 -#define QM_FIRST_IQ 2 -#define QM_LAST_IQ 10 +#define QM_FIRST_IQ_SSIMULACRA2 2 +#define QM_LAST_IQ_SSIMULACRA2 10 #define LOSSLESS_Q_STEP 4 // this should equal to dc/ac_qlookup_QTX[0] struct AV1Common; struct CommonQuantParams; struct macroblockd; @@ -94,11 +94,11 @@ } return clamp(qm_level, first, last); } -// Luma QM levels tuned for image quality (IQ) +// Luma QM levels tuned for SSIMULACRA 2 // This formula was empirically derived by encoding Daala's subset1 validation // testset for each QP/QM tuple, and building a convex hull that maximizes // SSIMULACRA 2 scores, and a final subjective visual quality pass as a quick // validation. This is a decreasing function in qindex. // There are a total of 16 luma QM levels, and the higher the level, the @@ -106,11 +106,12 @@ // QM level 15 is a completely-flat matrix and level 0 is the steepest. // This formula only uses levels 2 through 10, unless qm-min and qm-max are // both set below or above this range. // For more information on quantization matrices, please refer to // https://arxiv.org/pdf/2008.06091, section F. -static inline int aom_get_qmlevel_luma_iq(int qindex, int first, int last) { +static inline int aom_get_qmlevel_luma_ssimulacra2(int qindex, int first, + int last) { int qm_level = 0; if (qindex <= 40) { qm_level = 10; } else if (qindex <= 60) { @@ -132,11 +133,11 @@ } return clamp(qm_level, first, last); } -// Chroma QM levels for 4:4:4 subsampling tuned for image quality (IQ) +// Chroma QM levels for 4:4:4 subsampling used for SSIMULACRA 2 and IQ tunings // This formula was empirically derived by encoding Daala's subset1 validation // testset for each QP/QM tuple, and building a convex hull that maximizes // SSIMULACRA 2 scores, and a final subjective visual quality pass as a quick // validation. This is a decreasing function in qindex. // Like with luma QMs, there are a total of 16 chroma QM levels, and the higher @@ -144,12 +145,11 @@ // QM level 15 is a completely-flat matrix and level 0 is the steepest. // This formula only uses levels 2 through 10, unless qm-min and qm-max are // both set below or above this range. // For more information on quantization matrices, please refer to // https://arxiv.org/pdf/2008.06091, section F. -static inline int aom_get_qmlevel_444_chroma_iq(int qindex, int first, - int last) { +static inline int aom_get_qmlevel_444_chroma(int qindex, int first, int last) { int chroma_qm_level = 0; if (qindex <= 12) { chroma_qm_level = 10; } else if (qindex <= 24) { diff --git a/third_party/aom/av1/encoder/aq_cyclicrefresh.c b/third_party/aom/av1/encoder/aq_cyclicrefresh.c --- a/third_party/aom/av1/encoder/aq_cyclicrefresh.c +++ b/third_party/aom/av1/encoder/aq_cyclicrefresh.c @@ -563,19 +563,19 @@ const GF_GROUP *const gf_group = &cpi->ppi->gf_group; const int boost_index = AOMMIN(15, (cpi->ppi->p_rc.gfu_boost / 100)); const int layer_depth = AOMMIN(gf_group->layer_depth[cpi->gf_frame_index], 6); const FRAME_TYPE frame_type = cm->current_frame.frame_type; - // Set resolution_change flag: for svc only set it when the - // number of spatial layers has not changed. - const int resolution_change = - cm->prev_frame && - (cm->width != cm->prev_frame->width || - cm->height != cm->prev_frame->height) && - cpi->svc.prev_number_spatial_layers == cpi->svc.number_spatial_layers; + // Set resolution_change flag: for single spatial layers only. + const int resolution_change = !cpi->rc.rtc_external_ratectrl && + cm->prev_frame && + cpi->svc.number_spatial_layers == 1 && + (cm->width != cm->prev_frame->width || + cm->height != cm->prev_frame->height); - if (resolution_change) cyclic_refresh_reset_resize(cpi); + if (resolution_change && cpi->svc.temporal_layer_id == 0) + cyclic_refresh_reset_resize(cpi); if (!cr->apply_cyclic_refresh) { // Don't disable and set seg_map to 0 if active_maps is enabled, unless // whole frame is set as inactive (since we only apply cyclic_refresh to // active blocks). if (!cpi->active_map.enabled || cpi->rc.percent_blocks_inactive == 100) { diff --git a/third_party/aom/av1/encoder/av1_quantize.h b/third_party/aom/av1/encoder/av1_quantize.h --- a/third_party/aom/av1/encoder/av1_quantize.h +++ b/third_party/aom/av1/encoder/av1_quantize.h @@ -89,10 +89,11 @@ int y_dc_delta_q; int u_dc_delta_q; int u_ac_delta_q; int v_dc_delta_q; int v_ac_delta_q; + int sharpness; } DeltaQuantParams; typedef struct { // Quantization parameters for internal quantizer setup. QUANTS quants; @@ -113,15 +114,15 @@ int segment_id, const int do_update); void av1_build_quantizer(aom_bit_depth_t bit_depth, int y_dc_delta_q, int u_dc_delta_q, int u_ac_delta_q, int v_dc_delta_q, int v_ac_delta_q, QUANTS *const quants, - Dequants *const deq); + Dequants *const deq, int sharpness); void av1_init_quantizer(EncQuantDequantParams *const enc_quant_dequant_params, - const CommonQuantParams *quant_params, - aom_bit_depth_t bit_depth); + CommonQuantParams *quant_params, + aom_bit_depth_t bit_depth, int sharpness); void av1_set_quantizer(struct AV1Common *const cm, int min_qmlevel, int max_qmlevel, int q, int enable_chroma_deltaq, int enable_hdr_deltaq, bool is_allintra, aom_tune_metric tuning); diff --git a/third_party/aom/av1/encoder/av1_quantize.c b/third_party/aom/av1/encoder/av1_quantize.c --- a/third_party/aom/av1/encoder/av1_quantize.c +++ b/third_party/aom/av1/encoder/av1_quantize.c @@ -602,19 +602,26 @@ } void av1_build_quantizer(aom_bit_depth_t bit_depth, int y_dc_delta_q, int u_dc_delta_q, int u_ac_delta_q, int v_dc_delta_q, int v_ac_delta_q, QUANTS *const quants, - Dequants *const deq) { + Dequants *const deq, int sharpness) { int i, q, quant_QTX; + const int sharpness_adjustment = 16 * (7 - sharpness) / 7; for (q = 0; q < QINDEX_RANGE; q++) { const int qzbin_factor = get_qzbin_factor(q, bit_depth); - const int qrounding_factor = q == 0 ? 64 : 48; + int qrounding_factor = q == 0 ? 64 : 48; for (i = 0; i < 2; ++i) { - const int qrounding_factor_fp = 64; + int qrounding_factor_fp = 64; + + if (sharpness != 0 && q != 0) { + qrounding_factor = 64 - sharpness_adjustment; + qrounding_factor_fp = 64 - sharpness_adjustment; + } + // y quantizer with TX scale quant_QTX = i == 0 ? av1_dc_quant_QTX(q, y_dc_delta_q, bit_depth) : av1_ac_quant_QTX(q, 0, bit_depth); invert_quant(&quants->y_quant[q][i], &quants->y_quant_shift[q][i], quant_QTX); @@ -680,35 +687,38 @@ const CommonQuantParams *quant_params) { return (prev_deltaq_params->y_dc_delta_q != quant_params->y_dc_delta_q || prev_deltaq_params->u_dc_delta_q != quant_params->u_dc_delta_q || prev_deltaq_params->v_dc_delta_q != quant_params->v_dc_delta_q || prev_deltaq_params->u_ac_delta_q != quant_params->u_ac_delta_q || - prev_deltaq_params->v_ac_delta_q != quant_params->v_ac_delta_q); + prev_deltaq_params->v_ac_delta_q != quant_params->v_ac_delta_q || + prev_deltaq_params->sharpness != quant_params->sharpness); } void av1_init_quantizer(EncQuantDequantParams *const enc_quant_dequant_params, - const CommonQuantParams *quant_params, - aom_bit_depth_t bit_depth) { + CommonQuantParams *quant_params, + aom_bit_depth_t bit_depth, int sharpness) { DeltaQuantParams *const prev_deltaq_params = &enc_quant_dequant_params->prev_deltaq_params; + quant_params->sharpness = sharpness; // Re-initialize the quantizer only if any of the dc/ac deltaq parameters // change. if (!deltaq_params_have_changed(prev_deltaq_params, quant_params)) return; QUANTS *const quants = &enc_quant_dequant_params->quants; Dequants *const dequants = &enc_quant_dequant_params->dequants; av1_build_quantizer(bit_depth, quant_params->y_dc_delta_q, quant_params->u_dc_delta_q, quant_params->u_ac_delta_q, quant_params->v_dc_delta_q, quant_params->v_ac_delta_q, - quants, dequants); + quants, dequants, sharpness); // Record the state of deltaq parameters. prev_deltaq_params->y_dc_delta_q = quant_params->y_dc_delta_q; prev_deltaq_params->u_dc_delta_q = quant_params->u_dc_delta_q; prev_deltaq_params->v_dc_delta_q = quant_params->v_dc_delta_q; prev_deltaq_params->u_ac_delta_q = quant_params->u_ac_delta_q; prev_deltaq_params->v_ac_delta_q = quant_params->v_ac_delta_q; + prev_deltaq_params->sharpness = sharpness; } /*!\brief Update quantize parameters in MACROBLOCK * * \param[in] enc_quant_dequant_params This parameter cached the quantize and @@ -872,11 +882,12 @@ CommonQuantParams *quant_params = &cm->quant_params; quant_params->base_qindex = AOMMAX(cm->delta_q_info.delta_q_present_flag, q); quant_params->y_dc_delta_q = 0; if (enable_chroma_deltaq) { - if (is_allintra && tuning == AOM_TUNE_IQ) { + if (is_allintra && + (tuning == AOM_TUNE_IQ || tuning == AOM_TUNE_SSIMULACRA2)) { int chroma_dc_delta_q = 0; int chroma_ac_delta_q = 0; if (cm->seq_params->subsampling_x == 1 && cm->seq_params->subsampling_y == 1) { @@ -967,21 +978,25 @@ // tuning int (*get_luma_qmlevel)(int, int, int); int (*get_chroma_qmlevel)(int, int, int); if (is_allintra) { - if (tuning == AOM_TUNE_IQ) { - // Use luma QM formula specifically tailored for tune IQ - get_luma_qmlevel = aom_get_qmlevel_luma_iq; + if (tuning == AOM_TUNE_IQ || tuning == AOM_TUNE_SSIMULACRA2) { + if (tuning == AOM_TUNE_SSIMULACRA2) { + // Use luma QM formula specifically tailored for tune SSIMULACRA2 + get_luma_qmlevel = aom_get_qmlevel_luma_ssimulacra2; + } else { + get_luma_qmlevel = aom_get_qmlevel_allintra; + } if (cm->seq_params->subsampling_x == 0 && cm->seq_params->subsampling_y == 0) { // 4:4:4 subsampling mode has 4x the number of chroma coefficients // compared to 4:2:0 (2x on each dimension). This means the encoder // should use lower chroma QM levels that more closely match the scaling // of an equivalent 4:2:0 chroma QM. - get_chroma_qmlevel = aom_get_qmlevel_444_chroma_iq; + get_chroma_qmlevel = aom_get_qmlevel_444_chroma; } else { // For all other chroma subsampling modes, use the all intra QM formula get_chroma_qmlevel = aom_get_qmlevel_allintra; } } else { diff --git a/third_party/aom/av1/encoder/encodeframe.c b/third_party/aom/av1/encoder/encodeframe.c --- a/third_party/aom/av1/encoder/encodeframe.c +++ b/third_party/aom/av1/encoder/encodeframe.c @@ -2017,11 +2017,12 @@ av1_frame_init_quantizer(cpi); init_encode_frame_mb_context(cpi); set_default_interp_skip_flags(cm, &cpi->interp_search_flags); - if (cm->prev_frame && cm->prev_frame->seg.enabled) + if (cm->prev_frame && cm->prev_frame->seg.enabled && + cpi->svc.number_spatial_layers == 1) cm->last_frame_seg_map = cm->prev_frame->seg_map; else cm->last_frame_seg_map = NULL; if (features->allow_intrabc || features->coded_lossless) { av1_set_default_ref_deltas(cm->lf.ref_deltas); diff --git a/third_party/aom/av1/encoder/encodeframe_utils.c b/third_party/aom/av1/encoder/encodeframe_utils.c --- a/third_party/aom/av1/encoder/encodeframe_utils.c +++ b/third_party/aom/av1/encoder/encodeframe_utils.c @@ -44,11 +44,12 @@ // (maximum value a double data type can hold). If bsize_base is modified to // BLOCK_4X4 (minimum possible block size), geom_mean_of_scale can go up // to 4.8323^1024 and exceed DBL_MAX, resulting in data overflow. assert(bsize_base >= BLOCK_8X8); assert(cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIM || - cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ); + cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ || + cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2); for (row = mi_row / num_mi_w; row < num_rows && row < mi_row / num_mi_w + num_brows; ++row) { for (col = mi_col / num_mi_h; col < num_cols && col < mi_col / num_mi_h + num_bcols; ++col) { diff --git a/third_party/aom/av1/encoder/encoder.h b/third_party/aom/av1/encoder/encoder.h --- a/third_party/aom/av1/encoder/encoder.h +++ b/third_party/aom/av1/encoder/encoder.h @@ -4405,10 +4405,12 @@ CdefInfo *const cdef_info = &cm->cdef_info; RestorationInfo *const rst_info = cm->rst_info; lf->filter_level[0] = 0; lf->filter_level[1] = 0; + lf->backup_filter_level[0] = 0; + lf->backup_filter_level[1] = 0; cdef_info->cdef_bits = 0; cdef_info->cdef_strengths[0] = 0; cdef_info->nb_cdef_strengths = 1; cdef_info->cdef_uv_strengths[0] = 0; rst_info[0].frame_restoration_type = RESTORE_NONE; diff --git a/third_party/aom/av1/encoder/encoder.c b/third_party/aom/av1/encoder/encoder.c --- a/third_party/aom/av1/encoder/encoder.c +++ b/third_party/aom/av1/encoder/encoder.c @@ -515,14 +515,15 @@ ? DEFAULT_EXPLICIT_ORDER_HINT_BITS - 1 : -1; seq->max_frame_width = frm_dim_cfg->forced_max_frame_width ? frm_dim_cfg->forced_max_frame_width - : frm_dim_cfg->width; - seq->max_frame_height = frm_dim_cfg->forced_max_frame_height - ? frm_dim_cfg->forced_max_frame_height - : frm_dim_cfg->height; + : AOMMAX(seq->max_frame_width, frm_dim_cfg->width); + seq->max_frame_height = + frm_dim_cfg->forced_max_frame_height + ? frm_dim_cfg->forced_max_frame_height + : AOMMAX(seq->max_frame_height, frm_dim_cfg->height); seq->num_bits_width = (seq->max_frame_width > 1) ? get_msb(seq->max_frame_width - 1) + 1 : 1; seq->num_bits_height = (seq->max_frame_height > 1) ? get_msb(seq->max_frame_height - 1) + 1 : 1; assert(seq->num_bits_width <= 16); @@ -930,10 +931,13 @@ cm->render_height = frm_dim_cfg->render_height; } else { cm->render_width = frm_dim_cfg->width; cm->render_height = frm_dim_cfg->height; } + + int last_width = cm->width; + int last_height = cm->height; cm->width = frm_dim_cfg->width; cm->height = frm_dim_cfg->height; if (cm->width > cpi->data_alloc_width || cm->height > cpi->data_alloc_height || is_sb_size_changed) { @@ -948,10 +952,35 @@ cpi->data_alloc_height = cm->height; cpi->frame_size_related_setup_done = false; } av1_update_frame_size(cpi); + if (cm->width != last_width || cm->height != last_height) { + if (cpi->oxcf.q_cfg.aq_mode == CYCLIC_REFRESH_AQ) { + int mi_rows = cpi->common.mi_params.mi_rows; + int mi_cols = cpi->common.mi_params.mi_cols; + aom_free(cpi->cyclic_refresh->map); + CHECK_MEM_ERROR( + cm, cpi->cyclic_refresh->map, + aom_calloc(mi_rows * mi_cols, sizeof(*cpi->cyclic_refresh->map))); + if (cpi->svc.number_spatial_layers > 1) { + for (int sl = 0; sl < cpi->svc.number_spatial_layers; ++sl) { + const int layer = + LAYER_IDS_TO_IDX(sl, 0, cpi->svc.number_temporal_layers); + LAYER_CONTEXT *const lc = &cpi->svc.layer_context[layer]; + lc->sb_index = 0; + lc->actual_num_seg1_blocks = 0; + lc->actual_num_seg2_blocks = 0; + lc->counter_encode_maxq_scene_change = 0; + aom_free(lc->map); + CHECK_MEM_ERROR(cm, lc->map, + aom_calloc(mi_rows * mi_cols, sizeof(*lc->map))); + } + } + } + } + rc->is_src_frame_alt_ref = 0; if (!cpi->ppi->rtc_ref.set_ref_frame_config) cpi->ext_flags.refresh_frame.update_pending = 0; cpi->ext_flags.refresh_frame_context_pending = 0; @@ -1587,11 +1616,11 @@ prev_deltaq_params->v_dc_delta_q = INT_MAX; prev_deltaq_params->u_ac_delta_q = INT_MAX; prev_deltaq_params->v_ac_delta_q = INT_MAX; av1_init_quantizer(&cpi->enc_quant_dequant_params, &cm->quant_params, - cm->seq_params->bit_depth); + cm->seq_params->bit_depth, cpi->oxcf.algo_cfg.sharpness); av1_qm_init(&cm->quant_params, av1_num_planes(cm)); av1_loop_filter_init(cm); cm->superres_scale_denominator = SCALE_NUMERATOR; cm->superres_upscaled_width = oxcf->frm_dim_cfg.width; @@ -2634,11 +2663,11 @@ av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel, q, q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq, cpi->oxcf.mode == ALLINTRA, cpi->oxcf.tune_cfg.tuning); av1_set_speed_features_qindex_dependent(cpi, cpi->oxcf.speed); av1_init_quantizer(&cpi->enc_quant_dequant_params, &cm->quant_params, - cm->seq_params->bit_depth); + cm->seq_params->bit_depth, cpi->oxcf.algo_cfg.sharpness); av1_set_variance_partition_thresholds(cpi, q, 0); av1_setup_frame(cpi); // Check if this high_source_sad (scene/slide change) frame should be // encoded at high/max QP, and if so, set the q and adjust some rate @@ -2649,11 +2678,12 @@ av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel, q, q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq, cpi->oxcf.mode == ALLINTRA, cpi->oxcf.tune_cfg.tuning); av1_set_speed_features_qindex_dependent(cpi, cpi->oxcf.speed); av1_init_quantizer(&cpi->enc_quant_dequant_params, &cm->quant_params, - cm->seq_params->bit_depth); + cm->seq_params->bit_depth, + cpi->oxcf.algo_cfg.sharpness); av1_set_variance_partition_thresholds(cpi, q, 0); if (frame_is_intra_only(cm) || cm->features.error_resilient_mode || cm->features.primary_ref_frame == PRIMARY_REF_NONE) av1_setup_frame(cpi); } @@ -2954,11 +2984,11 @@ av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel, q, q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq, oxcf->mode == ALLINTRA, oxcf->tune_cfg.tuning); av1_set_speed_features_qindex_dependent(cpi, oxcf->speed); av1_init_quantizer(&cpi->enc_quant_dequant_params, &cm->quant_params, - cm->seq_params->bit_depth); + cm->seq_params->bit_depth, cpi->oxcf.algo_cfg.sharpness); av1_set_variance_partition_thresholds(cpi, q, 0); // printf("Frame %d/%d: q = %d, frame_type = %d superres_denom = %d\n", // cm->current_frame.frame_number, cm->show_frame, q, @@ -3772,11 +3802,12 @@ return AOM_CODEC_OK; } } if (oxcf->tune_cfg.tuning == AOM_TUNE_SSIM || - oxcf->tune_cfg.tuning == AOM_TUNE_IQ) { + oxcf->tune_cfg.tuning == AOM_TUNE_IQ || + oxcf->tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) { av1_set_mb_ssim_rdmult_scaling(cpi); } #if CONFIG_SALIENCY_MAP else if (oxcf->tune_cfg.tuning == AOM_TUNE_VMAF_SALIENCY_MAP && !(cpi->source->flags & YV12_FLAG_HIGHBITDEPTH)) { @@ -4540,14 +4571,14 @@ if (cpi->do_frame_data_update) { // Store current frame loopfilter levels in ppi, if update flag is set. if (!cpi->common.show_existing_frame) { AV1_COMMON *const cm = &cpi->common; struct loopfilter *const lf = &cm->lf; - cpi->ppi->filter_level[0] = lf->filter_level[0]; - cpi->ppi->filter_level[1] = lf->filter_level[1]; - cpi->ppi->filter_level_u = lf->filter_level_u; - cpi->ppi->filter_level_v = lf->filter_level_v; + cpi->ppi->filter_level[0] = lf->backup_filter_level[0]; + cpi->ppi->filter_level[1] = lf->backup_filter_level[1]; + cpi->ppi->filter_level_u = lf->backup_filter_level_u; + cpi->ppi->filter_level_v = lf->backup_filter_level_v; } } // Store frame level mv_stats from cpi to ppi. cpi->ppi->mv_stats = cpi->mv_stats; } diff --git a/third_party/aom/av1/encoder/encoder_utils.c b/third_party/aom/av1/encoder/encoder_utils.c --- a/third_party/aom/av1/encoder/encoder_utils.c +++ b/third_party/aom/av1/encoder/encoder_utils.c @@ -1132,12 +1132,13 @@ av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel, q_for_screen_content_quick_run, q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq, oxcf->mode == ALLINTRA, oxcf->tune_cfg.tuning); av1_set_speed_features_qindex_dependent(cpi, oxcf->speed); + av1_init_quantizer(&cpi->enc_quant_dequant_params, &cm->quant_params, - cm->seq_params->bit_depth); + cm->seq_params->bit_depth, oxcf->algo_cfg.sharpness); av1_set_variance_partition_thresholds(cpi, q_for_screen_content_quick_run, 0); // transform / motion compensation build reconstruction frame av1_encode_frame(cpi); diff --git a/third_party/aom/av1/encoder/firstpass.h b/third_party/aom/av1/encoder/firstpass.h --- a/third_party/aom/av1/encoder/firstpass.h +++ b/third_party/aom/av1/encoder/firstpass.h @@ -389,10 +389,12 @@ // refreshed by the current frame. int skip_frame_refresh[MAX_STATIC_GF_GROUP_LENGTH][REF_FRAMES]; // Stores the display order hint of the frame to be excluded during reference // assignment. int skip_frame_as_ref[MAX_STATIC_GF_GROUP_LENGTH]; + // Indicates whether a switch frame is due. + bool is_sframe_due; /*!\endcond */ } GF_GROUP; /*!\cond */ typedef struct { diff --git a/third_party/aom/av1/encoder/gop_structure.c b/third_party/aom/av1/encoder/gop_structure.c --- a/third_party/aom/av1/encoder/gop_structure.c +++ b/third_party/aom/av1/encoder/gop_structure.c @@ -616,17 +616,35 @@ // ALTREF. const int use_altref = gf_group->max_layer_depth_allowed > 0; int is_fwd_kf = rc->frames_to_fwd_kf == gf_interval; + const int sframe_dist = cpi->oxcf.kf_cfg.sframe_dist; + const int sframe_mode = cpi->oxcf.kf_cfg.sframe_mode; + const int sframe_enabled = (sframe_mode > 0) && (sframe_dist > 0); + + if (sframe_enabled) { + switch (sframe_mode) { + case 1: gf_group->is_sframe_due = use_altref; break; + case 2: + gf_group->is_sframe_due |= + (frame_index && !(frame_index % sframe_dist)); + break; + } + } + if (use_altref) { gf_group->update_type[frame_index] = ARF_UPDATE; gf_group->arf_src_offset[frame_index] = gf_interval - cur_frame_index; gf_group->cur_frame_idx[frame_index] = cur_frame_index; gf_group->layer_depth[frame_index] = 1; gf_group->arf_boost[frame_index] = cpi->ppi->p_rc.gfu_boost; - gf_group->frame_type[frame_index] = is_fwd_kf ? KEY_FRAME : INTER_FRAME; + gf_group->frame_type[frame_index] = is_fwd_kf ? KEY_FRAME + : gf_group->is_sframe_due ? S_FRAME + : INTER_FRAME; + gf_group->is_sframe_due = + sframe_enabled && !(gf_group->frame_type[frame_index] == S_FRAME); gf_group->refbuf_state[frame_index] = REFBUF_UPDATE; gf_group->max_layer_depth = 1; gf_group->arf_index = frame_index; gf_group->display_idx[frame_index] = cur_disp_index + gf_group->arf_src_offset[frame_index]; diff --git a/third_party/aom/av1/encoder/nonrd_opt.h b/third_party/aom/av1/encoder/nonrd_opt.h --- a/third_party/aom/av1/encoder/nonrd_opt.h +++ b/third_party/aom/av1/encoder/nonrd_opt.h @@ -522,16 +522,18 @@ return -1; } static inline bool prune_palette_testing_inter(AV1_COMP *cpi, unsigned int source_variance) { - return (cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN && - cpi->oxcf.speed >= 11 && cpi->rc.high_source_sad && - cpi->sf.rt_sf.rc_compute_spatial_var_sc && - cpi->rc.frame_spatial_variance < 1200 && - cpi->rc.perc_spatial_flat_blocks < 5 && - cpi->rc.percent_blocks_with_motion > 98 && source_variance < 4000); + return ( + cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN && + cpi->oxcf.speed >= 11 && cpi->rc.high_source_sad && + ((cpi->sf.rt_sf.prune_palette_search_nonrd > 2) || + (cpi->sf.rt_sf.rc_compute_spatial_var_sc && + cpi->rc.frame_spatial_variance < 1200 && + cpi->rc.perc_spatial_flat_blocks < 5 && + cpi->rc.percent_blocks_with_motion > 98 && source_variance < 4000))); } static inline void free_pred_buffer(PRED_BUFFER *p) { if (p != NULL) p->in_use = 0; } diff --git a/third_party/aom/av1/encoder/nonrd_pickmode.c b/third_party/aom/av1/encoder/nonrd_pickmode.c --- a/third_party/aom/av1/encoder/nonrd_pickmode.c +++ b/third_party/aom/av1/encoder/nonrd_pickmode.c @@ -320,22 +320,21 @@ if (bsize < BLOCK_16X16) return -1; int me_search_size_col = block_size_wide[bsize] >> 1; int me_search_size_row = block_size_high[bsize] >> 1; + MV ref_mv = av1_get_ref_mv(x, 0).as_mv; tmp_sad = av1_int_pro_motion_estimation( - cpi, x, bsize, mi_row, mi_col, - &x->mbmi_ext.ref_mv_stack[ref_frame][0].this_mv.as_mv, &y_sad_zero, - me_search_size_col, me_search_size_row); + cpi, x, bsize, mi_row, mi_col, &ref_mv, &y_sad_zero, me_search_size_col, + me_search_size_row); if (tmp_sad > x->pred_mv_sad[LAST_FRAME]) return -1; this_ref_frm_newmv->as_int = mi->mv[0].as_int; int_mv best_mv = mi->mv[0]; best_mv.as_mv.row >>= 3; best_mv.as_mv.col >>= 3; - MV ref_mv = av1_get_ref_mv(x, 0).as_mv; this_ref_frm_newmv->as_mv.row >>= 3; this_ref_frm_newmv->as_mv.col >>= 3; SUBPEL_MOTION_SEARCH_PARAMS ms_params; av1_make_default_subpel_ms_params(&ms_params, cpi, x, bsize, &ref_mv, NULL); @@ -2430,10 +2429,15 @@ *this_mode = ref_mode_set[idx].pred_mode; *ref_frame = ref_mode_set[idx].ref_frame; *ref_frame2 = NONE_FRAME; } + if (cpi->sf.rt_sf.skip_newmv_mode_sad_screen && cpi->rc.high_source_sad && + x->content_state_sb.source_sad_nonrd >= kMedSad && bsize <= BLOCK_16X16 && + !x->sb_me_block && (*ref_frame != LAST_FRAME || *this_mode == NEWMV)) + return true; + if (segfeature_active(&cm->seg, segment_id, SEG_LVL_SKIP) && (*this_mode != GLOBALMV || *ref_frame != LAST_FRAME)) return true; // Skip the mode if use reference frame mask flag is not set. @@ -3145,10 +3149,17 @@ BLOCK_SIZE bsize, unsigned int source_variance, int force_zeromv_skip, int skip_idtx_palette, int force_palette_test, unsigned int best_intra_sad_norm) { + const unsigned int sad_thresh = + cpi->sf.rt_sf.prune_palette_search_nonrd > 2 + ? (cpi->oxcf.frm_dim_cfg.width * cpi->oxcf.frm_dim_cfg.height <= + 1280 * 720) + ? 6 + : 12 + : 10; if (!cpi->oxcf.tool_cfg.enable_palette) return false; if (!av1_allow_palette(cpi->common.features.allow_screen_content_tools, bsize)) { return false; } @@ -3159,11 +3170,11 @@ bsize > BLOCK_16X16)) { return false; } if (prune_palette_testing_inter(cpi, source_variance) && - best_intra_sad_norm < 10) + best_intra_sad_norm < sad_thresh) return false; if ((is_mode_intra || force_palette_test) && source_variance > 0 && !force_zeromv_skip && (cpi->rc.high_source_sad || source_variance > 300)) { @@ -3535,11 +3546,12 @@ cpi, is_mode_intra(best_pickmode->best_mode), bsize, x->source_variance, x->force_zeromv_skip_for_blk, skip_idtx_palette, force_palette_test, best_intra_sad_norm); if (try_palette && prune_palette_testing_inter(cpi, x->source_variance)) - x->color_palette_thresh = 32; + x->color_palette_thresh = + cpi->sf.rt_sf.prune_palette_search_nonrd > 2 ? 20 : 32; // Perform screen content mode evaluation for non-rd handle_screen_content_mode_nonrd( cpi, x, &search_state, this_mode_pred, ctx, tmp_buffer, &orig_dst, skip_idtx_palette, try_palette, bsize, reuse_inter_pred, mi_col, mi_row); diff --git a/third_party/aom/av1/encoder/partition_search.c b/third_party/aom/av1/encoder/partition_search.c --- a/third_party/aom/av1/encoder/partition_search.c +++ b/third_party/aom/av1/encoder/partition_search.c @@ -614,11 +614,12 @@ x->rdmult = av1_get_cb_rdmult(cpi, x, bsize, mi_row, mi_col); } #endif // !CONFIG_REALTIME_ONLY if (cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIM || - cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ) { + cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ || + cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) { av1_set_ssim_rdmult(cpi, &x->errorperbit, bsize, mi_row, mi_col, &x->rdmult); } #if CONFIG_SALIENCY_MAP else if (cpi->oxcf.tune_cfg.tuning == AOM_TUNE_VMAF_SALIENCY_MAP) { diff --git a/third_party/aom/av1/encoder/pickcdef.c b/third_party/aom/av1/encoder/pickcdef.c --- a/third_party/aom/av1/encoder/pickcdef.c +++ b/third_party/aom/av1/encoder/pickcdef.c @@ -228,11 +228,11 @@ BLOCK_SIZE bsize) { *src_stride = block_size_wide[bsize]; *width = block_size_wide[bsize]; *height = block_size_high[bsize]; *width_log2 = MI_SIZE_LOG2 + mi_size_wide_log2[bsize]; - *height_log2 = MI_SIZE_LOG2 + mi_size_wide_log2[bsize]; + *height_log2 = MI_SIZE_LOG2 + mi_size_high_log2[bsize]; } #if CONFIG_AV1_HIGHBITDEPTH /* Compute MSE only on the blocks we filtered. */ static uint64_t compute_cdef_dist_highbd(void *dst, int dstride, uint16_t *src, cdef_list *dlist, int cdef_count, diff --git a/third_party/aom/av1/encoder/picklpf.c b/third_party/aom/av1/encoder/picklpf.c --- a/third_party/aom/av1/encoder/picklpf.c +++ b/third_party/aom/av1/encoder/picklpf.c @@ -352,7 +352,57 @@ last_frame_filter_level, 1, 0); lf->filter_level_v = search_filter_level(sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, last_frame_filter_level, 2, 0); } + + lf->backup_filter_level[0] = lf->filter_level[0]; + lf->backup_filter_level[1] = lf->filter_level[1]; + lf->backup_filter_level_u = lf->filter_level_u; + lf->backup_filter_level_v = lf->filter_level_v; + + if (cpi->sf.lpf_sf.adaptive_luma_loop_filter_skip >= 1) { + int32_t min_ref_filter_level[2] = { MAX_LOOP_FILTER, MAX_LOOP_FILTER }; + // Find the minimum luma filter levels across all reference frames. + for (int ref = LAST_FRAME; ref <= ALTREF_FRAME; ++ref) { + const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref); + if (buf == NULL) continue; + + if (buf->filter_level[0] != -1) + min_ref_filter_level[0] = + AOMMIN(min_ref_filter_level[0], buf->filter_level[0]); + if (buf->filter_level[1] != -1) + min_ref_filter_level[1] = + AOMMIN(min_ref_filter_level[1], buf->filter_level[1]); + } + + // Reset luma filter levels to zero based on minimum filter levels of + // reference frames and current frame's pyramid level. + unsigned int pyramid_level = cm->current_frame.pyramid_level; + if (pyramid_level > 1) { + int filter_threshold; + if (pyramid_level >= 5) + filter_threshold = 32; + else if (pyramid_level >= 4) + filter_threshold = 16; + else + filter_threshold = 8; + + const bool reset_filter_level_y = + lf->filter_level[0] < filter_threshold && + lf->filter_level[1] < filter_threshold && + lf->filter_level_u < filter_threshold && + lf->filter_level_v < filter_threshold && + min_ref_filter_level[0] == 0 && min_ref_filter_level[1] == 0; + if (reset_filter_level_y) { + lf->filter_level[0] = 0; + lf->filter_level[1] = 0; + } + } + + // Store the current frame's filter levels to be referenced + // while determining the minimum filter level from reference frames. + cm->cur_frame->filter_level[0] = lf->filter_level[0]; + cm->cur_frame->filter_level[1] = lf->filter_level[1]; + } } } diff --git a/third_party/aom/av1/encoder/ratectrl.c b/third_party/aom/av1/encoder/ratectrl.c --- a/third_party/aom/av1/encoder/ratectrl.c +++ b/third_party/aom/av1/encoder/ratectrl.c @@ -2492,10 +2492,13 @@ if (cpi->rc.high_source_sad) cpi->rc.avg_source_sad = 0; if (cpi->ppi->use_svc && cpi->svc.number_spatial_layers > 1) { cpi->svc.last_layer_dropped[cpi->svc.spatial_layer_id] = true; cpi->svc.drop_spatial_layer[cpi->svc.spatial_layer_id] = true; } + if (cpi->svc.spatial_layer_id == cpi->svc.number_spatial_layers - 1) { + cpi->svc.prev_number_spatial_layers = cpi->svc.number_spatial_layers; + } } int av1_find_qindex(double desired_q, aom_bit_depth_t bit_depth, int best_qindex, int worst_qindex) { assert(best_qindex <= worst_qindex); diff --git a/third_party/aom/av1/encoder/rd.c b/third_party/aom/av1/encoder/rd.c --- a/third_party/aom/av1/encoder/rd.c +++ b/third_party/aom/av1/encoder/rd.c @@ -398,11 +398,11 @@ } else { double def_rd_q_mult = def_inter_rd_multiplier(q); rdmult = (int64_t)((double)rdmult * def_rd_q_mult); } - if (tuning == AOM_TUNE_IQ) { + if (tuning == AOM_TUNE_IQ || tuning == AOM_TUNE_SSIMULACRA2) { // Further multiply rdmult (by up to 200/128 = 1.5625) to improve image // quality. The most noticeable effect is a mild bias towards choosing // larger transform sizes (e.g. one 16x16 transform instead of 4 8x8 // transforms). // For very high qindexes, start progressively reducing the weight towards diff --git a/third_party/aom/av1/encoder/speed_features.h b/third_party/aom/av1/encoder/speed_features.h --- a/third_party/aom/av1/encoder/speed_features.h +++ b/third_party/aom/av1/encoder/speed_features.h @@ -1496,10 +1496,14 @@ // Skip some final iterations in the determination of the best loop filter // level. int use_coarse_filter_level_search; + // Reset luma filter levels to zero based on minimum filter levels of + // reference frames and current frame's pyramid level. + int adaptive_luma_loop_filter_skip; + // Control how the CDEF strength is determined. CDEF_PICK_METHOD cdef_pick_method; // Decoder side speed feature to add penalty for use of dual-sgr filters. // Takes values 0 - 10, 0 indicating no penalty and each additional level @@ -1633,11 +1637,11 @@ // fewer blocks will go through the palette search. // For nonrd encoding path, enable this feature reduces encoding time when // palette mode is used. Disabling it leads to better compression efficiency. // 0: off // 1: less aggressive pruning mode - // 2: more aggressive pruning mode + // 2, 3: more aggressive pruning mode int prune_palette_search_nonrd; // Compute variance/sse on source difference, prior to encoding superblock. int source_metrics_sb_nonrd; @@ -1927,10 +1931,13 @@ int skip_encoding_non_reference_slide_change; // Flag to indicate more aggressive QP downward adjustment for screen static // content, to make convergence to min_qp faster. int rc_faster_convergence_static; + + // Skip NEWMV mode evaluation based on sad for screen content. + int skip_newmv_mode_sad_screen; } REAL_TIME_SPEED_FEATURES; /*!\endcond */ /*! diff --git a/third_party/aom/av1/encoder/speed_features.c b/third_party/aom/av1/encoder/speed_features.c --- a/third_party/aom/av1/encoder/speed_features.c +++ b/third_party/aom/av1/encoder/speed_features.c @@ -915,10 +915,24 @@ sf->hl_sf.recode_tolerance = 55; } } +// Configures speed features for low complexity decoding. +static void set_speed_features_low_complexity_decode(const AV1_COMP *const cpi, + SPEED_FEATURES *const sf) { + const FRAME_UPDATE_TYPE update_type = + get_frame_update_type(&cpi->ppi->gf_group, cpi->gf_frame_index); + + if (cpi->oxcf.enable_low_complexity_decode >= 1) { + sf->lpf_sf.adaptive_luma_loop_filter_skip = + (update_type != OVERLAY_UPDATE && update_type != INTNL_OVERLAY_UPDATE) + ? 1 + : 0; + } +} + static void set_good_speed_features_framesize_independent( const AV1_COMP *const cpi, SPEED_FEATURES *const sf, int speed) { const AV1_COMMON *const cm = &cpi->common; const GF_GROUP *const gf_group = &cpi->ppi->gf_group; const int boosted = frame_is_boosted(cpi); @@ -1314,10 +1328,13 @@ sf->winner_mode_sf.dc_blk_pred_level = 3; sf->winner_mode_sf.multi_winner_mode_type = MULTI_WINNER_MODE_OFF; sf->fp_sf.skip_zeromv_motion_search = 1; } + + if (cpi->oxcf.enable_low_complexity_decode) + set_speed_features_low_complexity_decode(cpi, sf); } static void set_rt_speed_feature_framesize_dependent(const AV1_COMP *const cpi, SPEED_FEATURES *const sf, int speed) { @@ -1590,10 +1607,19 @@ sf->rt_sf.prune_palette_search_nonrd = 2; sf->rt_sf.increase_color_thresh_palette = 0; sf->rt_sf.prune_h_pred_using_best_mode_so_far = true; sf->rt_sf.enable_intra_mode_pruning_using_neighbors = true; } + if (speed >= 12) { + if (cpi->rc.high_source_sad && cpi->rc.frame_source_sad > 40000 && + cpi->rc.prev_avg_source_sad < 1000 && + cpi->oxcf.frm_dim_cfg.width * cpi->oxcf.frm_dim_cfg.height >= + 1280 * 720) { + sf->rt_sf.prune_palette_search_nonrd = 3; + sf->rt_sf.skip_newmv_mode_sad_screen = 1; + } + } sf->rt_sf.skip_encoding_non_reference_slide_change = cpi->oxcf.rc_cfg.drop_frames_water_mark > 0 ? 1 : 0; sf->rt_sf.skip_newmv_flat_blocks_screen = 1; sf->rt_sf.use_idtx_nonrd = 1; sf->rt_sf.higher_thresh_scene_detection = 0; @@ -2236,10 +2262,11 @@ lpf_sf->max_lr_unit_size = RESTORATION_UNITSIZE_MAX; lpf_sf->prune_wiener_based_on_src_var = 0; lpf_sf->prune_sgr_based_on_wiener = 0; lpf_sf->enable_sgr_ep_pruning = 0; lpf_sf->reduce_wiener_window_size = 0; + lpf_sf->adaptive_luma_loop_filter_skip = 0; lpf_sf->lpf_pick = LPF_PICK_FROM_FULL_IMAGE; lpf_sf->use_coarse_filter_level_search = 0; lpf_sf->cdef_pick_method = CDEF_FULL_SEARCH; // Set decoder side speed feature to use less dual sgr modes lpf_sf->dual_sgr_penalty_level = 0; @@ -2326,10 +2353,11 @@ rt_sf->force_only_last_ref = 0; rt_sf->higher_thresh_scene_detection = 1; rt_sf->skip_newmv_flat_blocks_screen = 0; rt_sf->skip_encoding_non_reference_slide_change = 0; rt_sf->rc_faster_convergence_static = 0; + rt_sf->skip_newmv_mode_sad_screen = 0; } static fractional_mv_step_fp *const fractional_mv_search[SUBPEL_SEARCH_METHODS] = { av1_find_best_sub_pixel_tree, // SUBPEL_TREE = 0 diff --git a/third_party/aom/av1/encoder/tpl_model.c b/third_party/aom/av1/encoder/tpl_model.c --- a/third_party/aom/av1/encoder/tpl_model.c +++ b/third_party/aom/av1/encoder/tpl_model.c @@ -1375,12 +1375,13 @@ const int base_qindex = cpi->use_ducky_encode ? gf_group->q_val[frame_idx] : pframe_qindex; // The TPL model is only meant to be run in inter mode, so ensure that we are // not running in all intra mode, which implies we are not tuning for image - // quality (IQ). + // quality (IQ) or SSIMULACRA2. assert(cpi->oxcf.tune_cfg.tuning != AOM_TUNE_IQ && + cpi->oxcf.tune_cfg.tuning != AOM_TUNE_SSIMULACRA2 && cpi->oxcf.mode != ALLINTRA); // Get rd multiplier set up. rdmult = av1_compute_rd_mult( base_qindex, cm->seq_params->bit_depth, cpi->ppi->gf_group.update_type[cpi->gf_frame_index], layer_depth, diff --git a/third_party/aom/av1/encoder/tune_butteraugli.c b/third_party/aom/av1/encoder/tune_butteraugli.c --- a/third_party/aom/av1/encoder/tune_butteraugli.c +++ b/third_party/aom/av1/encoder/tune_butteraugli.c @@ -301,11 +301,11 @@ av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel, q_index, q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq, oxcf->mode == ALLINTRA, oxcf->tune_cfg.tuning); av1_set_speed_features_qindex_dependent(cpi, oxcf->speed); av1_init_quantizer(&cpi->enc_quant_dequant_params, &cm->quant_params, - cm->seq_params->bit_depth); + cm->seq_params->bit_depth, cpi->oxcf.algo_cfg.sharpness); av1_set_variance_partition_thresholds(cpi, q_index, 0); av1_encode_frame(cpi); av1_setup_butteraugli_rdmult_and_restore_source(cpi, 0.3); diff --git a/third_party/aom/av1/encoder/txb_rdopt.c b/third_party/aom/av1/encoder/txb_rdopt.c --- a/third_party/aom/av1/encoder/txb_rdopt.c +++ b/third_party/aom/av1/encoder/txb_rdopt.c @@ -209,11 +209,11 @@ rate = rate_low; dist = dist_low; } } - if (sharpness == 0 && rd_new_eob < rd) { + if ((sharpness == 0 || new_eob >= 5) && rd_new_eob < rd) { for (int ni = 0; ni < *nz_num; ++ni) { int last_ci = nz_ci[ni]; levels[get_padded_idx(last_ci, bhl)] = 0; qcoeff[last_ci] = 0; dqcoeff[last_ci] = 0; @@ -334,20 +334,23 @@ &coeff_costs->coeff_costs[txs_ctx][plane_type]; const int eob_multi_size = txsize_log2_minus4[tx_size]; const LV_MAP_EOB_COST *txb_eob_costs = &coeff_costs->eob_costs[eob_multi_size][plane_type]; - // For the IQ tune, increase rshift from 2 to 4. + // For the IQ and SSIMULACRA 2 tunings, increase rshift from 2 to 4. // This biases trellis quantization towards keeping more coefficients, and - // together with the IQ rdmult adjustment in + // together with the IQ and SSIMULACRA2 rdmult adjustment in // av1_compute_rd_mult_based_on_qindex(), this helps preserve image // features (like repeating patterns and camera noise/film grain), which // improves SSIMULACRA 2 scores. - const int rshift = cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ ? 4 : 2; + const int rshift = (cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ || + cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) + ? 7 + : 5; const int64_t rdmult = ROUND_POWER_OF_TWO( - (int64_t)x->rdmult * + (int64_t)x->rdmult * (8 - sharpness) * (plane_rd_mult[is_inter][plane_type] << (2 * (xd->bd - 8))), rshift); uint8_t levels_buf[TX_PAD_2D]; uint8_t *const levels = set_levels(levels_buf, height); diff --git a/third_party/aom/build/cmake/cpu.cmake b/third_party/aom/build/cmake/cpu.cmake --- a/third_party/aom/build/cmake/cpu.cmake +++ b/third_party/aom/build/cmake/cpu.cmake @@ -10,11 +10,10 @@ # if("${AOM_TARGET_CPU}" STREQUAL "arm64") set(AOM_ARCH_ARM 1) set(AOM_ARCH_AARCH64 1) - set(RTCD_ARCH_ARM "yes") set(ARM64_FLAVORS "NEON;ARM_CRC32;NEON_DOTPROD;NEON_I8MM;SVE;SVE2") set(AOM_ARM_CRC32_DEFAULT_FLAG "-march=armv8-a+crc") set(AOM_NEON_DOTPROD_DEFAULT_FLAG "-march=armv8.2-a+dotprod") set(AOM_NEON_I8MM_DEFAULT_FLAG "-march=armv8.2-a+dotprod+i8mm") @@ -78,69 +77,59 @@ endif() foreach(flavor ${ARM64_FLAVORS}) if(ENABLE_${flavor}) set(HAVE_${flavor} 1) - set(RTCD_HAVE_${flavor} "yes") else() set(HAVE_${flavor} 0) string(TOLOWER ${flavor} flavor) set(AOM_RTCD_FLAGS ${AOM_RTCD_FLAGS} --disable-${flavor}) endif() endforeach() elseif("${AOM_TARGET_CPU}" MATCHES "^arm") set(AOM_ARCH_ARM 1) - set(RTCD_ARCH_ARM "yes") if(ENABLE_NEON) set(HAVE_NEON 1) - set(RTCD_HAVE_NEON "yes") else() set(HAVE_NEON 0) set(AOM_RTCD_FLAGS ${AOM_RTCD_FLAGS} --disable-neon) endif() elseif("${AOM_TARGET_CPU}" MATCHES "ppc") set(AOM_ARCH_PPC 1) - set(RTCD_ARCH_PPC "yes") if(ENABLE_VSX) set(HAVE_VSX 1) - set(RTCD_HAVE_VSX "yes") else() set(HAVE_VSX 0) set(AOM_RTCD_FLAGS ${AOM_RTCD_FLAGS} --disable-vsx) endif() elseif("${AOM_TARGET_CPU}" MATCHES "^x86") if("${AOM_TARGET_CPU}" STREQUAL "x86") set(AOM_ARCH_X86 1) - set(RTCD_ARCH_X86 "yes") elseif("${AOM_TARGET_CPU}" STREQUAL "x86_64") set(AOM_ARCH_X86_64 1) - set(RTCD_ARCH_X86_64 "yes") endif() set(X86_FLAVORS "MMX;SSE;SSE2;SSE3;SSSE3;SSE4_1;SSE4_2;AVX;AVX2") foreach(flavor ${X86_FLAVORS}) if(ENABLE_${flavor} AND NOT disable_remaining_flavors) set(HAVE_${flavor} 1) - set(RTCD_HAVE_${flavor} "yes") else() set(disable_remaining_flavors 1) set(HAVE_${flavor} 0) string(TOLOWER ${flavor} flavor) set(AOM_RTCD_FLAGS ${AOM_RTCD_FLAGS} --disable-${flavor}) endif() endforeach() elseif("${AOM_TARGET_CPU}" MATCHES "riscv") set(AOM_ARCH_RISCV64 1) - set(RTCD_ARCH_RISCV64 "yes") if(ENABLE_RVV) set(HAVE_RVV 1) - set(RTCD_HAVE_RVV "yes") else() set(HAVE_RVV 0) set(AOM_RTCD_FLAGS ${AOM_RTCD_FLAGS} --disable-rvv) endif() endif() diff --git a/third_party/aom/test/datarate_test.h b/third_party/aom/test/datarate_test.h --- a/third_party/aom/test/datarate_test.h +++ b/third_party/aom/test/datarate_test.h @@ -55,10 +55,11 @@ target_bitrate_update_[i] = 0; frame_number_dynamic_[i] = 0; bits_total_dynamic_[i] = 0; effective_datarate_dynamic_[i] = 0.0; } + avif_mode_ = 0; } void PreEncodeFrameHook(::libaom_test::VideoSource *video, ::libaom_test::Encoder *encoder) override { if (video->frame() == 0) { @@ -88,10 +89,25 @@ if (screen_mode_) { encoder->Control(AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN); encoder->Control(AV1E_SET_ENABLE_PALETTE, 1); encoder->Control(AV1E_SET_ENABLE_INTRABC, 0); } + if (avif_mode_) { + encoder->Control(AV1E_SET_COEFF_COST_UPD_FREQ, 0); + encoder->Control(AV1E_SET_MODE_COST_UPD_FREQ, 0); + encoder->Control(AV1E_SET_MV_COST_UPD_FREQ, 0); +#if !CONFIG_REALTIME_ONLY + encoder->Control(AV1E_SET_DELTAQ_MODE, 3); +#endif +#if CONFIG_QUANT_MATRIX + encoder->Control(AV1E_SET_ENABLE_QM, 1); +#endif + encoder->Control(AOME_SET_SHARPNESS, 1); + encoder->Control(AV1E_SET_ENABLE_CHROMA_DELTAQ, 1); + encoder->Control(AOME_SET_CQ_LEVEL, 0); + encoder->Control(AV1E_SET_AQ_MODE, (aq_mode_ > 0) ? 1 : 0); + } } if (speed_change_test_) { if (video->frame() == 0) { encoder->Control(AOME_SET_CPUUSED, 8); @@ -225,9 +241,10 @@ int frame_update_bitrate_; int target_bitrate_update_[3]; double effective_datarate_dynamic_[3]; int64_t bits_total_dynamic_[3]; int frame_number_dynamic_[3]; + int avif_mode_; }; } // namespace } // namespace datarate_test diff --git a/third_party/aom/test/datarate_test.cc b/third_party/aom/test/datarate_test.cc --- a/third_party/aom/test/datarate_test.cc +++ b/third_party/aom/test/datarate_test.cc @@ -19,13 +19,63 @@ #include "test/i420_video_source.h" #include "test/util.h" #include "test/y4m_video_source.h" #include "aom/aom_codec.h" +#if CONFIG_LIBYUV +#include "third_party/libyuv/include/libyuv/scale.h" +#endif + namespace datarate_test { namespace { +#if CONFIG_LIBYUV +class ResizingVideoSource : public ::libaom_test::DummyVideoSource { + public: + ResizingVideoSource(const int width, const int height, const int input_width, + const int input_height, const std::string file_name, + int limit) + : width_(width), height_(height), input_width_(input_width), + input_height_(input_height), limit_(limit) { + SetSize(width_, height_); + img_input_ = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, input_width_, + input_height_, 32); + raw_size_ = input_width_ * input_height_ * 3 / 2; + input_file_ = ::libaom_test::OpenTestDataFile(file_name); + } + + ~ResizingVideoSource() override { + aom_img_free(img_input_); + fclose(input_file_); + } + + protected: + void FillFrame() override { + // Read frame from input_file and scale up. + ASSERT_NE(input_file_, nullptr); + fread(img_input_->img_data, raw_size_, 1, input_file_); + libyuv::I420Scale( + img_input_->planes[AOM_PLANE_Y], img_input_->stride[AOM_PLANE_Y], + img_input_->planes[AOM_PLANE_U], img_input_->stride[AOM_PLANE_U], + img_input_->planes[AOM_PLANE_V], img_input_->stride[AOM_PLANE_V], + input_width_, input_height_, img_->planes[AOM_PLANE_Y], + img_->stride[AOM_PLANE_Y], img_->planes[AOM_PLANE_U], + img_->stride[AOM_PLANE_U], img_->planes[AOM_PLANE_V], + img_->stride[AOM_PLANE_V], width_, height_, libyuv::kFilterBox); + } + + const int width_; + const int height_; + const int input_width_; + const int input_height_; + const int limit_; + aom_image_t *img_input_; + size_t raw_size_; + FILE *input_file_; +}; +#endif // CONFIG_LIBYUV + // Params: test mode, speed, aq mode and index for bitrate array. class DatarateTestLarge : public ::libaom_test::CodecTestWith4Params, public DatarateTest { @@ -84,10 +134,31 @@ << " The datarate for the file is lower than target by too much!"; ASSERT_LE(effective_datarate_, cfg_.rc_target_bitrate * 1.19) << " The datarate for the file is greater than target by too much!"; } +#if CONFIG_LIBYUV + // Test for an encoding mode that triggers an assert in nonrd_pickmode + // (in av1_is_subpelmv_in_range), issue b:396169342. + // The assert is triggered on a 2456x2054 resolution with settings defined + // with the flag avif_mode_. This test upsamples a QVGA clip to the target + // resolution, using libyuv for the scaling. + virtual void BasicRateTargetingCBRAssertAvifModeTest() { + cfg_.rc_min_quantizer = 0; + cfg_.rc_max_quantizer = 63; + cfg_.rc_end_usage = AOM_CBR; + cfg_.g_lag_in_frames = 0; + ResizingVideoSource video(2456, 2054, 320, 240, + "pixel_capture_w320h240.yuv", 100); + const int bitrate_array[2] = { 1000, 2000 }; + cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; + ResetModel(); + avif_mode_ = 1; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + } +#endif // CONFIG_LIBYUV + virtual void BasicRateTargetingCBRSpikeTest() { cfg_.rc_buf_initial_sz = 500; cfg_.rc_buf_optimal_sz = 500; cfg_.rc_buf_sz = 1000; cfg_.rc_dropframe_thresh = 0; @@ -553,10 +624,17 @@ // Check basic rate targeting for CBR. TEST_P(DatarateTestRealtime, BasicRateTargetingCBR) { BasicRateTargetingCBRTest(); } +#if CONFIG_LIBYUV +// Check basic rate targeting for CBR, special case. +TEST_P(DatarateTestRealtime, BasicRateTargetingCBRAssertAvifMode) { + BasicRateTargetingCBRAssertAvifModeTest(); +} +#endif + // Check basic rate targeting for CBR. Use a longer clip, // and verify #encode size spikes above threshold. TEST_P(DatarateTestRealtime, BasicRateTargetingCBRSpike) { BasicRateTargetingCBRSpikeTest(); } diff --git a/third_party/aom/test/encode_api_test.cc b/third_party/aom/test/encode_api_test.cc --- a/third_party/aom/test/encode_api_test.cc +++ b/third_party/aom/test/encode_api_test.cc @@ -116,11 +116,11 @@ AOM_CODEC_OK); aom_codec_ctx_t enc; ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK); - ASSERT_EQ(aom_codec_control(&enc, AOME_SET_TUNING, AOM_TUNE_IQ), + ASSERT_EQ(aom_codec_control(&enc, AOME_SET_TUNING, AOM_TUNE_SSIMULACRA2), AOM_CODEC_INCAPABLE); ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); } @@ -1033,11 +1033,11 @@ AOM_CODEC_OK); aom_codec_ctx_t enc; ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK); - ASSERT_EQ(aom_codec_control(&enc, AOME_SET_TUNING, AOM_TUNE_IQ), + ASSERT_EQ(aom_codec_control(&enc, AOME_SET_TUNING, AOM_TUNE_SSIMULACRA2), AOM_CODEC_OK); aom_image_t *image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h); ASSERT_NE(image, nullptr); diff --git a/third_party/aom/test/quantize_func_test.cc b/third_party/aom/test/quantize_func_test.cc --- a/third_party/aom/test/quantize_func_test.cc +++ b/third_party/aom/test/quantize_func_test.cc @@ -119,11 +119,11 @@ aom_free(coeff_); coeff_ = nullptr; } void InitQuantizer() { - av1_build_quantizer(bd_, 0, 0, 0, 0, 0, &qtab_->quant, &qtab_->dequant); + av1_build_quantizer(bd_, 0, 0, 0, 0, 0, &qtab_->quant, &qtab_->dequant, 0); } virtual void RunQuantizeFunc( const CoeffType *coeff_ptr, intptr_t n_coeffs, const int16_t *zbin_ptr, const int16_t *round_ptr, const int16_t *quant_ptr, diff --git a/third_party/aom/test/svc_datarate_test.cc b/third_party/aom/test/svc_datarate_test.cc --- a/third_party/aom/test/svc_datarate_test.cc +++ b/third_party/aom/test/svc_datarate_test.cc @@ -33,10 +33,81 @@ aom_codec_pts_t pts; unsigned int w; unsigned int h; }; +void ScaleForFrameNumber(unsigned int frame, unsigned int initial_w, + unsigned int initial_h, unsigned int *w, + unsigned int *h, int resize_pattern) { + *w = initial_w; + *h = initial_h; + if (resize_pattern == 1) { + if (frame < 50) { + *w = initial_w / 4; + *h = initial_h / 4; + } else if (frame < 100) { + *w = initial_w / 2; + *h = initial_h / 2; + } else if (frame < 150) { + *w = initial_w; + *h = initial_h; + } else if (frame < 200) { + *w = initial_w / 4; + *h = initial_h / 4; + } else if (frame < 250) { + *w = initial_w / 2; + *h = initial_h / 2; + } + } else if (resize_pattern == 2) { + if (frame < 50) { + *w = initial_w / 2; + *h = initial_h / 2; + } else if (frame < 100) { + *w = initial_w / 4; + *h = initial_h / 4; + } else if (frame < 150) { + *w = initial_w; + *h = initial_h; + } else if (frame < 200) { + *w = initial_w / 2; + *h = initial_h / 2; + } else if (frame < 250) { + *w = initial_w / 4; + *h = initial_h / 4; + } + } +} + +class ResizingVideoSource : public ::libaom_test::DummyVideoSource { + public: + explicit ResizingVideoSource(int external_resize_pattern) { + external_resize_pattern_ = external_resize_pattern; + SetSize(1280, 720); + limit_ = 300; + } + ~ResizingVideoSource() override = default; + + protected: + void Next() override { + ++frame_; + unsigned int width = 0; + unsigned int height = 0; + libaom_test::ACMRandom rnd(libaom_test::ACMRandom::DeterministicSeed()); + ScaleForFrameNumber(frame_, 1280, 720, &width, &height, + external_resize_pattern_); + SetSize(width, height); + FillFrame(); + unsigned char *image = img_->planes[0]; + for (size_t i = 0; i < raw_sz_; ++i) { + image[i] = rnd.Rand8(); + } + } + + private: + int external_resize_pattern_; +}; + class DatarateTestSVC : public ::libaom_test::CodecTestWith4Params, public DatarateTest { public: @@ -97,10 +168,12 @@ user_define_frame_qp_ = 0; set_speed_per_layer_ = false; simulcast_mode_ = false; use_last_as_scaled_ = false; use_last_as_scaled_single_ref_ = false; + external_resize_dynamic_drop_layer_ = false; + external_resize_pattern_ = 0; } void PreEncodeFrameHook(::libaom_test::VideoSource *video, ::libaom_test::Encoder *encoder) override { int spatial_layer_id = 0; @@ -130,10 +203,17 @@ } if (screen_mode_) { encoder->Control(AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN); } encoder->Control(AV1E_SET_POSTENCODE_DROP_RTC, 1); + // We want to force external resize on the very first frame. + // Turn off frame-dropping. + if (external_resize_dynamic_drop_layer_) { + encoder->Control(AV1E_SET_POSTENCODE_DROP_RTC, 0); + DatarateTest::PreEncodeFrameHook(video, encoder); + video->Next(); + } } if (number_spatial_layers_ == 2) { spatial_layer_id = (layer_frame_cnt_ % 2 == 0) ? 0 : 1; } else if (number_spatial_layers_ == 3) { spatial_layer_id = (layer_frame_cnt_ % 3 == 0) ? 0 @@ -219,13 +299,107 @@ cfg_.rc_target_bitrate += target_layer_bitrate_[2]; encoder->Config(&cfg_); encoder->Control(AV1E_SET_SVC_PARAMS, &svc_params_); } } + if (external_resize_dynamic_drop_layer_) { + frame_flags_ = 0; + for (int i = 0; i < 9; ++i) { + svc_params_.min_quantizers[i] = 20; + svc_params_.max_quantizers[i] = 56; + } + if (layer_id_.spatial_layer_id == 0 && + (video->frame() == 1 || video->frame() == 150)) { + // Set the new top width/height for external resize. + top_sl_width_ = video->img()->d_w; + top_sl_height_ = video->img()->d_h; + for (int i = 0; i < 9; ++i) { + bitrate_layer_[i] = svc_params_.layer_target_bitrate[i]; + } + if (external_resize_pattern_ == 1) { + // Input size is 1/4. 2 top spatial layers are dropped. + // This will trigger skip encoding/dropping of two top spatial layers. + cfg_.rc_target_bitrate -= svc_params_.layer_target_bitrate[5] + + svc_params_.layer_target_bitrate[8]; + for (int i = 3; i < 9; ++i) { + svc_params_.layer_target_bitrate[i] = 0; + } + for (int sl = 0; sl < 3; sl++) { + svc_params_.scaling_factor_num[sl] = 1; + svc_params_.scaling_factor_den[sl] = 1; + } + } else if (external_resize_pattern_ == 2) { + // Input size is 1/2. Top spatial layer is dropped. + // This will trigger skip encoding/dropping of top spatial layer. + cfg_.rc_target_bitrate -= svc_params_.layer_target_bitrate[8]; + for (int i = 6; i < 9; ++i) { + svc_params_.layer_target_bitrate[i] = 0; + } + svc_params_.scaling_factor_num[0] = 1; + svc_params_.scaling_factor_den[0] = 2; + svc_params_.scaling_factor_num[1] = 1; + svc_params_.scaling_factor_den[1] = 1; + svc_params_.scaling_factor_num[2] = 1; + svc_params_.scaling_factor_den[2] = 1; + } + encoder->Config(&cfg_); + encoder->Control(AV1E_SET_SVC_PARAMS, &svc_params_); + } else if (layer_id_.spatial_layer_id == 0 && + (video->frame() == 50 || video->frame() == 200)) { + top_sl_width_ = video->img()->d_w; + top_sl_height_ = video->img()->d_h; + if (external_resize_pattern_ == 1) { + // Input size is 1/2. Change layer bitrates to set top layer to 0. + // This will trigger skip encoding/dropping of top spatial layer. + cfg_.rc_target_bitrate += bitrate_layer_[5]; + for (int i = 3; i < 6; ++i) { + svc_params_.layer_target_bitrate[i] = bitrate_layer_[i]; + } + svc_params_.scaling_factor_num[0] = 1; + svc_params_.scaling_factor_den[0] = 2; + svc_params_.scaling_factor_num[1] = 1; + svc_params_.scaling_factor_den[1] = 1; + svc_params_.scaling_factor_num[2] = 1; + svc_params_.scaling_factor_den[2] = 1; + } else if (external_resize_pattern_ == 2) { + // Input size is 1/4. Change layer bitrates to set two top layers to + // 0. This will trigger skip encoding/dropping of two top spatial + // layers. + cfg_.rc_target_bitrate -= bitrate_layer_[5]; + for (int i = 3; i < 6; ++i) { + svc_params_.layer_target_bitrate[i] = 0; + } + for (int sl = 0; sl < 3; sl++) { + svc_params_.scaling_factor_num[sl] = 1; + svc_params_.scaling_factor_den[sl] = 1; + } + } + encoder->Config(&cfg_); + encoder->Control(AV1E_SET_SVC_PARAMS, &svc_params_); + } else if (layer_id_.spatial_layer_id == 0 && + (video->frame() == 100 || video->frame() == 250)) { + top_sl_width_ = video->img()->d_w; + top_sl_height_ = video->img()->d_h; + // Input is original size. Change layer bitrates to nonzero for all + // layers. + cfg_.rc_target_bitrate = + bitrate_layer_[2] + bitrate_layer_[5] + bitrate_layer_[8]; + for (int i = 0; i < 9; ++i) { + svc_params_.layer_target_bitrate[i] = bitrate_layer_[i]; + } + svc_params_.scaling_factor_num[0] = 1; + svc_params_.scaling_factor_den[0] = 4; + svc_params_.scaling_factor_num[1] = 1; + svc_params_.scaling_factor_den[1] = 2; + svc_params_.scaling_factor_num[2] = 1; + svc_params_.scaling_factor_den[2] = 1; + encoder->Config(&cfg_); + encoder->Control(AV1E_SET_SVC_PARAMS, &svc_params_); + } + } layer_frame_cnt_++; DatarateTest::PreEncodeFrameHook(video, encoder); - if (user_define_frame_qp_) { frame_qp_ = rnd_.PseudoUniform(63); encoder->Control(AV1E_SET_QUANTIZER_ONE_PASS, frame_qp_); } } @@ -264,10 +438,17 @@ EXPECT_NE(pkt->data.frame.flags & AOM_FRAME_IS_KEY, AOM_FRAME_IS_KEY); } else if (layer_id_.spatial_layer_id == 0) { EXPECT_EQ(pkt->data.frame.flags & AOM_FRAME_IS_KEY, AOM_FRAME_IS_KEY); } } + if (external_resize_dynamic_drop_layer_) { + // No key frame is needed for these encoding patterns, except at the + // very first frame. + if (layer_frame_cnt_ > 1) { + EXPECT_NE(pkt->data.frame.flags & AOM_FRAME_IS_KEY, AOM_FRAME_IS_KEY); + } + } } void EndPassHook() override { duration_ = ((last_pts_ + 1) * timebase_); for (int i = 0; i < number_temporal_layers_ * number_spatial_layers_; i++) { @@ -2656,10 +2837,90 @@ EXPECT_EQ(300 - GetDecodedFrames(), drop_frames_); EXPECT_EQ((int)GetMismatchFrames(), 0); #endif } + virtual void BasicRateTargetingSVC3TL3SLExternalResizePattern1Test() { + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_dropframe_thresh = 0; + cfg_.rc_min_quantizer = 0; + cfg_.rc_max_quantizer = 63; + cfg_.rc_end_usage = AOM_CBR; + cfg_.g_lag_in_frames = 0; + cfg_.g_error_resilient = 0; + const int bitrate_array[2] = { 600, 1200 }; + cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; + cfg_.g_w = 1280; + cfg_.g_h = 720; + top_sl_width_ = 1280; + top_sl_height_ = 720; + ResizingVideoSource video(1); + ResetModel(); + external_resize_dynamic_drop_layer_ = true; + external_resize_pattern_ = 1; + number_temporal_layers_ = 3; + number_spatial_layers_ = 3; + // SL0 + const int bitrate_sl0 = 1 * cfg_.rc_target_bitrate / 8; + target_layer_bitrate_[0] = 50 * bitrate_sl0 / 100; + target_layer_bitrate_[1] = 70 * bitrate_sl0 / 100; + target_layer_bitrate_[2] = bitrate_sl0; + // SL1 + const int bitrate_sl1 = 3 * cfg_.rc_target_bitrate / 8; + target_layer_bitrate_[3] = 50 * bitrate_sl1 / 100; + target_layer_bitrate_[4] = 70 * bitrate_sl1 / 100; + target_layer_bitrate_[5] = bitrate_sl1; + // SL2 + const int bitrate_sl2 = 4 * cfg_.rc_target_bitrate / 8; + target_layer_bitrate_[6] = 50 * bitrate_sl2 / 100; + target_layer_bitrate_[7] = 70 * bitrate_sl2 / 100; + target_layer_bitrate_[8] = bitrate_sl2; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + } + + virtual void BasicRateTargetingSVC3TL3SLExternalResizePattern2Test() { + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_buf_optimal_sz = 500; + cfg_.rc_buf_sz = 1000; + cfg_.rc_dropframe_thresh = 0; + cfg_.rc_min_quantizer = 0; + cfg_.rc_max_quantizer = 63; + cfg_.rc_end_usage = AOM_CBR; + cfg_.g_lag_in_frames = 0; + cfg_.g_error_resilient = 0; + const int bitrate_array[2] = { 600, 1200 }; + cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)]; + cfg_.g_w = 1280; + cfg_.g_h = 720; + top_sl_width_ = 1280; + top_sl_height_ = 720; + ResizingVideoSource video(2); + ResetModel(); + external_resize_dynamic_drop_layer_ = true; + external_resize_pattern_ = 2; + number_temporal_layers_ = 3; + number_spatial_layers_ = 3; + // SL0 + const int bitrate_sl0 = 1 * cfg_.rc_target_bitrate / 8; + target_layer_bitrate_[0] = 50 * bitrate_sl0 / 100; + target_layer_bitrate_[1] = 70 * bitrate_sl0 / 100; + target_layer_bitrate_[2] = bitrate_sl0; + // SL1 + const int bitrate_sl1 = 3 * cfg_.rc_target_bitrate / 8; + target_layer_bitrate_[3] = 50 * bitrate_sl1 / 100; + target_layer_bitrate_[4] = 70 * bitrate_sl1 / 100; + target_layer_bitrate_[5] = bitrate_sl1; + // SL2 + const int bitrate_sl2 = 4 * cfg_.rc_target_bitrate / 8; + target_layer_bitrate_[6] = 50 * bitrate_sl2 / 100; + target_layer_bitrate_[7] = 70 * bitrate_sl2 / 100; + target_layer_bitrate_[8] = bitrate_sl2; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + } + int layer_frame_cnt_; int superframe_cnt_; int number_temporal_layers_; int number_spatial_layers_; // Allow for up to 3 temporal layers. @@ -2695,10 +2956,15 @@ int user_define_frame_qp_; int frame_qp_; int total_frame_; bool set_speed_per_layer_; libaom_test::ACMRandom rnd_; + bool external_resize_dynamic_drop_layer_; + int bitrate_layer_[9]; + int external_resize_pattern_; + int top_sl_width_; + int top_sl_height_; }; // Check basic rate targeting for CBR, for 3 temporal layers, 1 spatial. TEST_P(DatarateTestSVC, BasicRateTargetingSVC3TL1SL) { BasicRateTargetingSVC3TL1SLTest(); @@ -2987,10 +3253,28 @@ // and continue decoding successfully. TEST_P(DatarateTestSVC, BasicRateTargetingRPS1TL1SLDropFrames) { BasicRateTargetingRPS1TL1SLDropFramesTest(); } +// For 1 pass CBR SVC with 3 spatial and 3 temporal layers with external resize +// and denoiser enabled. The external resizer will resize down and back up, +// setting 0/nonzero bitrate on spatial enhancement layers to disable/enable +// layers. Resizing starts on first frame and the pattern is: +// 1/4 -> 1/2 -> 1 -> 1/4 -> 1/2. +TEST_P(DatarateTestSVC, BasicRateTargetingSVC3TL3SLExternalResizePattern1) { + BasicRateTargetingSVC3TL3SLExternalResizePattern1Test(); +} + +// For 1 pass CBR SVC with 3 spatial and 3 temporal layers with external resize +// and denoiser enabled. The external resizer will resize down and back up, +// setting 0/nonzero bitrate on spatial enhancement layers to disable/enable +// layers. Resizing starts on first frame and the pattern is: +// 1/2 -> 1/4 -> 1 -> 1/2 -> 1/4. +TEST_P(DatarateTestSVC, BasicRateTargetingSVC3TL3SLExternalResizePattern2) { + BasicRateTargetingSVC3TL3SLExternalResizePattern2Test(); +} + TEST(SvcParams, BitrateOverflow) { uint8_t buf[6] = { 0 }; aom_image_t img; aom_codec_ctx_t enc; aom_codec_enc_cfg_t cfg; diff --git a/third_party/aom/test/test.cmake b/third_party/aom/test/test.cmake --- a/third_party/aom/test/test.cmake +++ b/third_party/aom/test/test.cmake @@ -499,11 +499,17 @@ aom_gtest) list(APPEND AOM_APP_TARGETS test_intra_pred_speed) endif() endif() - target_link_libraries(test_libaom ${AOM_LIB_LINK_TYPE} aom aom_gtest) + if(CONFIG_LIBYUV) + # link test_libaom with yuv + target_link_libraries(test_libaom ${AOM_LIB_LINK_TYPE} aom aom_gtest yuv) + else() + # do not link test_libaom with yuv + target_link_libraries(test_libaom ${AOM_LIB_LINK_TYPE} aom aom_gtest) + endif() if(CONFIG_WEBM_IO) target_sources(test_libaom PRIVATE $) endif() if(HAVE_SSE2)