Call setup_firstpass_data based on max frame size
Call setup_firstpass_data() with unit_rows and unit_cols calculated from
the maximum frame size. If we call setup_firstpass_data() with unit_rows
and unit_cols calculated from the first frame's size and later encode a
larger frame, the firstpass data (specifically firstpass_data->mb_stats)
will be too small for the larger frame.
The code modified by this CL was originally added by Cheng Chen in
https://aomedia-review.googlesource.com/c/aom/+/125181.
Use ROUND_POWER_OF_TWO() when calculating mb_cols and mb_rows.
Also add tests for good-quality mode to
forced_max_frame_width_height_test.cc. These tests are disabled because
there are still bugs.
Bug: aomedia:3326
Change-Id: I3213dd992956e353bda70c37a3ae8ce8ebd37006
diff --git a/av1/common/alloccommon.c b/av1/common/alloccommon.c
index 5cf6c0f..e373dc1 100644
--- a/av1/common/alloccommon.c
+++ b/av1/common/alloccommon.c
@@ -28,8 +28,8 @@
const int mi_cols = aligned_width >> MI_SIZE_LOG2;
const int mi_rows = aligned_height >> MI_SIZE_LOG2;
- const int mb_cols = (mi_cols + 2) >> 2;
- const int mb_rows = (mi_rows + 2) >> 2;
+ const int mb_cols = ROUND_POWER_OF_TWO(mi_cols, 2);
+ const int mb_rows = ROUND_POWER_OF_TWO(mi_rows, 2);
return mb_rows * mb_cols;
}
diff --git a/av1/decoder/decoder.c b/av1/decoder/decoder.c
index 2553ffb..f1ffaa4 100644
--- a/av1/decoder/decoder.c
+++ b/av1/decoder/decoder.c
@@ -58,8 +58,8 @@
mi_params->mi_rows = aligned_height >> MI_SIZE_LOG2;
mi_params->mi_stride = calc_mi_size(mi_params->mi_cols);
- mi_params->mb_cols = (mi_params->mi_cols + 2) >> 2;
- mi_params->mb_rows = (mi_params->mi_rows + 2) >> 2;
+ mi_params->mb_cols = ROUND_POWER_OF_TWO(mi_params->mi_cols, 2);
+ mi_params->mb_rows = ROUND_POWER_OF_TWO(mi_params->mi_rows, 2);
mi_params->MBs = mi_params->mb_rows * mi_params->mb_cols;
mi_params->mi_alloc_bsize = BLOCK_4X4;
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 2ec7770..ca99531 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -3763,8 +3763,10 @@
// the frame token allocation.
static INLINE unsigned int allocated_tokens(const TileInfo *tile,
int sb_size_log2, int num_planes) {
- int tile_mb_rows = (tile->mi_row_end - tile->mi_row_start + 2) >> 2;
- int tile_mb_cols = (tile->mi_col_end - tile->mi_col_start + 2) >> 2;
+ int tile_mb_rows =
+ ROUND_POWER_OF_TWO(tile->mi_row_end - tile->mi_row_start, 2);
+ int tile_mb_cols =
+ ROUND_POWER_OF_TWO(tile->mi_col_end - tile->mi_col_start, 2);
return get_token_alloc(tile_mb_rows, tile_mb_cols, sb_size_log2, num_planes);
}
diff --git a/av1/encoder/encoder_utils.h b/av1/encoder/encoder_utils.h
index 427cd83..dd91bb1 100644
--- a/av1/encoder/encoder_utils.h
+++ b/av1/encoder/encoder_utils.h
@@ -67,8 +67,8 @@
mi_params->mi_rows = size_in_mi(height);
mi_params->mi_stride = calc_mi_size(mi_params->mi_cols);
- mi_params->mb_cols = (mi_params->mi_cols + 2) >> 2;
- mi_params->mb_rows = (mi_params->mi_rows + 2) >> 2;
+ mi_params->mb_cols = ROUND_POWER_OF_TWO(mi_params->mi_cols, 2);
+ mi_params->mb_rows = ROUND_POWER_OF_TWO(mi_params->mi_rows, 2);
mi_params->MBs = mi_params->mb_rows * mi_params->mb_cols;
const int mi_alloc_size_1d = mi_size_wide[mi_params->mi_alloc_bsize];
diff --git a/av1/encoder/firstpass.c b/av1/encoder/firstpass.c
index 7e137d0..8434208 100644
--- a/av1/encoder/firstpass.c
+++ b/av1/encoder/firstpass.c
@@ -36,6 +36,7 @@
#include "av1/encoder/encodemb.h"
#include "av1/encoder/encodemv.h"
#include "av1/encoder/encoder.h"
+#include "av1/encoder/encoder_utils.h"
#include "av1/encoder/encode_strategy.h"
#include "av1/encoder/ethread.h"
#include "av1/encoder/extend.h"
@@ -1219,8 +1220,18 @@
AV1_COMMON *const cm = &cpi->common;
CurrentFrame *const current_frame = &cm->current_frame;
const CommonModeInfoParams *const mi_params = &cm->mi_params;
- const int unit_rows = get_unit_rows(BLOCK_16X16, mi_params->mb_rows);
- const int unit_cols = get_unit_cols(BLOCK_16X16, mi_params->mb_cols);
+ int max_mb_rows = mi_params->mb_rows;
+ int max_mb_cols = mi_params->mb_cols;
+ if (cpi->oxcf.frm_dim_cfg.forced_max_frame_width) {
+ int max_mi_cols = size_in_mi(cpi->oxcf.frm_dim_cfg.forced_max_frame_width);
+ max_mb_cols = ROUND_POWER_OF_TWO(max_mi_cols, 2);
+ }
+ if (cpi->oxcf.frm_dim_cfg.forced_max_frame_height) {
+ int max_mi_rows = size_in_mi(cpi->oxcf.frm_dim_cfg.forced_max_frame_height);
+ max_mb_rows = ROUND_POWER_OF_TWO(max_mi_rows, 2);
+ }
+ const int unit_rows = get_unit_rows(BLOCK_16X16, max_mb_rows);
+ const int unit_cols = get_unit_cols(BLOCK_16X16, max_mb_cols);
setup_firstpass_data(cm, &cpi->firstpass_data, unit_rows, unit_cols);
FRAME_STATS *mb_stats = cpi->firstpass_data.mb_stats;
FRAME_STATS stats = accumulate_frame_stats(mb_stats, unit_rows, unit_cols);
@@ -1254,10 +1265,21 @@
const BLOCK_SIZE fp_block_size =
get_fp_block_size(cpi->is_screen_content_type);
+ int max_mb_rows = mi_params->mb_rows;
+ int max_mb_cols = mi_params->mb_cols;
+ if (cpi->oxcf.frm_dim_cfg.forced_max_frame_width) {
+ int max_mi_cols = size_in_mi(cpi->oxcf.frm_dim_cfg.forced_max_frame_width);
+ max_mb_cols = ROUND_POWER_OF_TWO(max_mi_cols, 2);
+ }
+ if (cpi->oxcf.frm_dim_cfg.forced_max_frame_height) {
+ int max_mi_rows = size_in_mi(cpi->oxcf.frm_dim_cfg.forced_max_frame_height);
+ max_mb_rows = ROUND_POWER_OF_TWO(max_mi_rows, 2);
+ }
+
// Number of rows in the unit size.
- // Note mi_params->mb_rows and mi_params->mb_cols are in the unit of 16x16.
- const int unit_rows = get_unit_rows(fp_block_size, mi_params->mb_rows);
- const int unit_cols = get_unit_cols(fp_block_size, mi_params->mb_cols);
+ // Note max_mb_rows and max_mb_cols are in the unit of 16x16.
+ const int unit_rows = get_unit_rows(fp_block_size, max_mb_rows);
+ const int unit_cols = get_unit_cols(fp_block_size, max_mb_cols);
// Set fp_block_size, for the convenience of multi-thread usage.
cpi->fp_block_size = fp_block_size;
diff --git a/test/forced_max_frame_width_height_test.cc b/test/forced_max_frame_width_height_test.cc
index abd22a3..9c10b01 100644
--- a/test/forced_max_frame_width_height_test.cc
+++ b/test/forced_max_frame_width_height_test.cc
@@ -23,7 +23,18 @@
namespace {
-TEST(EncodeForcedMaxFrameWidthHeight, RealtimeTunePSNR) {
+// cfg.g_lag_in_frames must be set to 0 or 1 to allow the frame size to change,
+// as required by the following check in encoder_set_config() in
+// av1/av1_cx_iface.c:
+//
+// if (cfg->g_w != ctx->cfg.g_w || cfg->g_h != ctx->cfg.g_h) {
+// if (cfg->g_lag_in_frames > 1 || cfg->g_pass != AOM_RC_ONE_PASS)
+// ERROR("Cannot change width or height after initialization");
+// ...
+// }
+
+void RunTest(unsigned int usage, unsigned int lag_in_frames,
+ const char *tune_metric) {
// A buffer of gray samples. Large enough for 128x128 and 256x256, YUV 4:2:0.
constexpr size_t kImageDataSize = 256 * 256 + 2 * 128 * 128;
std::unique_ptr<unsigned char[]> img_data(new unsigned char[kImageDataSize]);
@@ -32,15 +43,15 @@
aom_codec_iface_t *iface = aom_codec_av1_cx();
aom_codec_enc_cfg_t cfg;
- EXPECT_EQ(AOM_CODEC_OK,
- aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME));
+ EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_default(iface, &cfg, usage));
cfg.g_w = 128;
cfg.g_h = 128;
cfg.g_forced_max_frame_width = 256;
cfg.g_forced_max_frame_height = 256;
+ cfg.g_lag_in_frames = lag_in_frames;
aom_codec_ctx_t enc;
EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, iface, &cfg, 0));
- EXPECT_EQ(AOM_CODEC_OK, aom_codec_set_option(&enc, "tune", "psnr"));
+ EXPECT_EQ(AOM_CODEC_OK, aom_codec_set_option(&enc, "tune", tune_metric));
aom_image_t img;
EXPECT_EQ(&img,
@@ -59,40 +70,40 @@
EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
}
-TEST(EncodeForcedMaxFrameWidthHeight, RealtimeTuneSSIM) {
- // A buffer of gray samples. Large enough for 128x128 and 256x256, YUV 4:2:0.
- constexpr size_t kImageDataSize = 256 * 256 + 2 * 128 * 128;
- std::unique_ptr<unsigned char[]> img_data(new unsigned char[kImageDataSize]);
- ASSERT_NE(img_data, nullptr);
- memset(img_data.get(), 128, kImageDataSize);
+#if !CONFIG_REALTIME_ONLY
- aom_codec_iface_t *iface = aom_codec_av1_cx();
- aom_codec_enc_cfg_t cfg;
- EXPECT_EQ(AOM_CODEC_OK,
- aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME));
- cfg.g_w = 128;
- cfg.g_h = 128;
- cfg.g_forced_max_frame_width = 256;
- cfg.g_forced_max_frame_height = 256;
- aom_codec_ctx_t enc;
- EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, iface, &cfg, 0));
- EXPECT_EQ(AOM_CODEC_OK, aom_codec_set_option(&enc, "tune", "ssim"));
+TEST(EncodeForcedMaxFrameWidthHeight, DISABLED_GoodQualityLag0TunePSNR) {
+ RunTest(AOM_USAGE_GOOD_QUALITY, /*lag_in_frames=*/0, "psnr");
+}
- aom_image_t img;
- EXPECT_EQ(&img,
- aom_img_wrap(&img, AOM_IMG_FMT_I420, 128, 128, 1, img_data.get()));
- EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
+TEST(EncodeForcedMaxFrameWidthHeight, DISABLED_GoodQualityLag0TuneSSIM) {
+ RunTest(AOM_USAGE_GOOD_QUALITY, /*lag_in_frames=*/0, "ssim");
+}
- cfg.g_w = 256;
- cfg.g_h = 256;
- EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_set(&enc, &cfg));
+TEST(EncodeForcedMaxFrameWidthHeight, DISABLED_GoodQualityLag1TunePSNR) {
+ RunTest(AOM_USAGE_GOOD_QUALITY, /*lag_in_frames=*/1, "psnr");
+}
- EXPECT_EQ(&img,
- aom_img_wrap(&img, AOM_IMG_FMT_I420, 256, 256, 1, img_data.get()));
- EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
+TEST(EncodeForcedMaxFrameWidthHeight, DISABLED_GoodQualityLag1TuneSSIM) {
+ RunTest(AOM_USAGE_GOOD_QUALITY, /*lag_in_frames=*/1, "ssim");
+}
- EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, nullptr, 0, 0, 0));
- EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
+#endif // !CONFIG_REALTIME_ONLY
+
+TEST(EncodeForcedMaxFrameWidthHeight, RealtimeLag0TunePSNR) {
+ RunTest(AOM_USAGE_REALTIME, /*lag_in_frames=*/0, "psnr");
+}
+
+TEST(EncodeForcedMaxFrameWidthHeight, RealtimeLag0TuneSSIM) {
+ RunTest(AOM_USAGE_REALTIME, /*lag_in_frames=*/0, "ssim");
+}
+
+TEST(EncodeForcedMaxFrameWidthHeight, RealtimeLag1TunePSNR) {
+ RunTest(AOM_USAGE_REALTIME, /*lag_in_frames=*/1, "psnr");
+}
+
+TEST(EncodeForcedMaxFrameWidthHeight, RealtimeLag1TuneSSIM) {
+ RunTest(AOM_USAGE_REALTIME, /*lag_in_frames=*/1, "ssim");
}
} // namespace