Merge "Merge tag 'v3.3.0' into main branch" into main
diff --git a/av1/common/av1_loopfilter.c b/av1/common/av1_loopfilter.c
index 25f5f95..a4ca051 100644
--- a/av1/common/av1_loopfilter.c
+++ b/av1/common/av1_loopfilter.c
@@ -188,8 +188,8 @@
static TX_SIZE get_transform_size(const MACROBLOCKD *const xd,
const MB_MODE_INFO *const mbmi,
- const EDGE_DIR edge_dir, const int mi_row,
- const int mi_col, const int plane,
+ const int mi_row, const int mi_col,
+ const int plane,
const struct macroblockd_plane *plane_ptr) {
assert(mbmi != NULL);
if (xd && xd->lossless[mbmi->segment_id]) return TX_4X4;
@@ -210,12 +210,6 @@
tx_size = mb_tx_size;
}
- // since in case of chrominance or non-square transform need to convert
- // transform size into transform size in particular direction.
- // for vertical edge, filter direction is horizontal, for horizontal
- // edge, filter direction is vertical.
- tx_size = (VERT_EDGE == edge_dir) ? txsize_horz_map[tx_size]
- : txsize_vert_map[tx_size];
return tx_size;
}
@@ -254,7 +248,7 @@
if (mbmi == NULL) return TX_INVALID;
const TX_SIZE ts =
- get_transform_size(xd, mi[0], edge_dir, mi_row, mi_col, plane, plane_ptr);
+ get_transform_size(xd, mi[0], mi_row, mi_col, plane, plane_ptr);
{
const uint32_t coord = (VERT_EDGE == edge_dir) ? (x) : (y);
@@ -278,8 +272,8 @@
(VERT_EDGE == edge_dir) ? (mi_row) : (mi_row - (1 << scale_vert));
const int pv_col =
(VERT_EDGE == edge_dir) ? (mi_col - (1 << scale_horz)) : (mi_col);
- const TX_SIZE pv_ts = get_transform_size(
- xd, mi_prev, edge_dir, pv_row, pv_col, plane, plane_ptr);
+ const TX_SIZE pv_ts =
+ get_transform_size(xd, mi_prev, pv_row, pv_col, plane, plane_ptr);
const uint32_t pv_lvl =
av1_get_filter_level(cm, &cm->lf_info, edge_dir, plane, mi_prev);
@@ -297,20 +291,19 @@
// deblock the edge if the edge belongs to a PU's edge only.
if ((curr_level || pv_lvl) &&
(!pv_skip_txfm || !curr_skipped || pu_edge)) {
- const TX_SIZE min_ts = AOMMIN(ts, pv_ts);
- if (TX_4X4 >= min_ts) {
- params->filter_length = 4;
- } else if (TX_8X8 == min_ts) {
- if (plane != 0)
- params->filter_length = 6;
- else
- params->filter_length = 8;
+ const int dim = (VERT_EDGE == edge_dir)
+ ? AOMMIN(tx_size_wide_unit_log2[ts],
+ tx_size_wide_unit_log2[pv_ts])
+ : AOMMIN(tx_size_high_unit_log2[ts],
+ tx_size_high_unit_log2[pv_ts]);
+ if (plane) {
+ params->filter_length = (dim == 0) ? 4 : 6;
} else {
- params->filter_length = 14;
- // No wide filtering for chroma plane
- if (plane != 0) {
- params->filter_length = 6;
- }
+ static const int tx_dim_to_filter_length[TX_SIZES] = { 4, 8, 14,
+ 14, 14 };
+ assert(dim < TX_SIZES);
+ assert(dim >= 0);
+ params->filter_length = tx_dim_to_filter_length[dim];
}
// update the level if the current block is skipped,
@@ -332,7 +325,7 @@
// Similar to set_lpf_parameters, but does so one row/col at a time to reduce
// calls to \ref get_transform_size and \ref av1_get_filter_level
-static AOM_INLINE void set_lpf_parameters_for_line(
+static AOM_FORCE_INLINE void set_lpf_parameters_for_line(
AV1_DEBLOCKING_PARAMETERS *const params_buf, TX_SIZE *tx_buf,
const AV1_COMMON *const cm, const MACROBLOCKD *const xd,
const EDGE_DIR edge_dir, uint32_t x, uint32_t y, const int plane,
@@ -341,7 +334,6 @@
TX_SIZE *tx_size = tx_buf;
TX_SIZE prev_tx_size = TX_INVALID;
- int prev_level = -1;
const int is_vert = edge_dir == VERT_EDGE;
const uint32_t scale_horz = plane_ptr->subsampling_x;
@@ -360,25 +352,18 @@
const MB_MODE_INFO *prev_mbmi = *(mi - mode_step);
const int pv_row = is_vert ? (mi_row) : (mi_row - (1 << scale_vert));
const int pv_col = is_vert ? (mi_col - (1 << scale_horz)) : (mi_col);
- prev_tx_size = get_transform_size(xd, prev_mbmi, edge_dir, pv_row, pv_col,
- plane, plane_ptr);
+ prev_tx_size =
+ get_transform_size(xd, prev_mbmi, pv_row, pv_col, plane, plane_ptr);
}
}
uint32_t *counter_ptr = is_vert ? &x : &y;
while (*counter_ptr < range) {
+ assert(x < (uint32_t)plane_ptr->dst.width &&
+ y < (uint32_t)plane_ptr->dst.height);
// reset to initial values
params->filter_length = 0;
- // no deblocking is required
- const uint32_t width = plane_ptr->dst.width;
- const uint32_t height = plane_ptr->dst.height;
- if ((width <= x) || (height <= y)) {
- // just return the smallest transform unit size
- *tx_size = TX_4X4;
- return;
- }
-
// for sub8x8 block, chroma prediction mode is obtained from the
// bottom/right mi structure of the co-located 8x8 luma block. so for chroma
// plane, mi_row and mi_col should map to the bottom/right mi structure,
@@ -388,97 +373,56 @@
MB_MODE_INFO **mi =
cm->mi_params.mi_grid_base + mi_row * cm->mi_params.mi_stride + mi_col;
const MB_MODE_INFO *mbmi = mi[0];
- // If current mbmi is not correctly setup, return an invalid value to stop
- // filtering. One example is that if this tile is not coded, then its mbmi
- // it not set up.
- if (mbmi == NULL) {
- *tx_size = TX_INVALID;
+ assert(mbmi);
- *counter_ptr += MIN_TX_SIZE;
- params += 1;
- tx_size += 1;
-
- prev_tx_size = TX_INVALID;
- continue;
- }
-
- const TX_SIZE ts = get_transform_size(xd, mi[0], edge_dir, mi_row, mi_col,
- plane, plane_ptr);
+ const TX_SIZE ts =
+ get_transform_size(xd, mi[0], mi_row, mi_col, plane, plane_ptr);
*tx_size = ts;
const int advance_units =
is_vert ? tx_size_wide_unit[ts] : tx_size_high_unit[ts];
const uint32_t coord = is_vert ? (x) : (y);
+#ifndef NDEBUG
const uint32_t transform_masks =
is_vert ? tx_size_wide[ts] - 1 : tx_size_high[ts] - 1;
const int32_t tu_edge = (coord & transform_masks) ? (0) : (1);
-
- if (!tu_edge) {
- *counter_ptr += MIN_TX_SIZE * advance_units;
- params += advance_units;
- tx_size += advance_units;
-
- prev_tx_size = ts;
- continue;
- }
+ assert(tu_edge);
+#endif // NDEBUG
// prepare outer edge parameters. deblock the edge if it's an edge of a TU
- const uint32_t curr_level =
- av1_get_filter_level(cm, &cm->lf_info, edge_dir, plane, mbmi);
- const int curr_skipped = mbmi->skip_txfm && is_inter_block(mbmi);
- uint32_t level = curr_level;
if (coord) {
const MB_MODE_INFO *const mi_prev = *(mi - mode_step);
- if (mi_prev == NULL) {
- *counter_ptr += MIN_TX_SIZE * advance_units;
- params += advance_units;
- tx_size += advance_units;
-
- prev_tx_size = ts;
- prev_level = level;
- continue;
+ const TX_SIZE pv_ts = prev_tx_size;
+ assert(mi_prev);
+ uint32_t level =
+ av1_get_filter_level(cm, &cm->lf_info, edge_dir, plane, mbmi);
+ if (!level) {
+ level =
+ av1_get_filter_level(cm, &cm->lf_info, edge_dir, plane, mi_prev);
}
- const TX_SIZE pv_ts = prev_tx_size;
- const uint32_t pv_lvl = prev_level;
-
- const int pv_skip_txfm = mi_prev->skip_txfm && is_inter_block(mi_prev);
- const BLOCK_SIZE bsize = get_plane_block_size(
- mbmi->bsize, plane_ptr->subsampling_x, plane_ptr->subsampling_y);
- assert(bsize < BLOCK_SIZES_ALL);
- const int prediction_masks =
- is_vert ? block_size_wide[bsize] - 1 : block_size_high[bsize] - 1;
- const int32_t pu_edge = !(coord & prediction_masks);
- // if the current and the previous blocks are skipped,
- // deblock the edge if the edge belongs to a PU's edge only.
- if ((curr_level || pv_lvl) &&
- (!pv_skip_txfm || !curr_skipped || pu_edge)) {
- const TX_SIZE min_ts = AOMMIN(ts, pv_ts);
- if (TX_4X4 >= min_ts) {
- params->filter_length = 4;
- } else if (TX_8X8 == min_ts) {
- if (plane != 0)
- params->filter_length = 6;
- else
- params->filter_length = 8;
+ const int curr_skipped = mbmi->skip_txfm && is_inter_block(mbmi);
+ const int32_t pu_edge = mi_prev != mbmi;
+ if (level && (!curr_skipped || pu_edge)) {
+ const int dim = is_vert ? AOMMIN(tx_size_wide_unit_log2[ts],
+ tx_size_wide_unit_log2[pv_ts])
+ : AOMMIN(tx_size_high_unit_log2[ts],
+ tx_size_high_unit_log2[pv_ts]);
+ if (plane) {
+ params->filter_length = (dim == 0) ? 4 : 6;
} else {
- params->filter_length = 14;
- // No wide filtering for chroma plane
- if (plane != 0) {
- params->filter_length = 6;
- }
+ static const int tx_dim_to_filter_length[TX_SIZES] = { 4, 8, 14, 14,
+ 14 };
+ assert(dim < TX_SIZES);
+ assert(dim >= 0);
+ params->filter_length = tx_dim_to_filter_length[dim];
}
- // update the level if the current block is skipped,
- // but the previous one is not
- level = (curr_level) ? (curr_level) : (pv_lvl);
+ // prepare common parameters
+ const loop_filter_thresh *const limits = cm->lf_info.lfthr + level;
+ params->lfthr = limits;
}
}
- // prepare common parameters
- if (params->filter_length) {
- const loop_filter_thresh *const limits = cm->lf_info.lfthr + level;
- params->lfthr = limits;
- }
// Advance
*counter_ptr += MIN_TX_SIZE * advance_units;
@@ -486,103 +430,39 @@
tx_size += advance_units;
prev_tx_size = ts;
- prev_level = level;
}
}
-static AOM_INLINE TX_TYPE
-get_uv_tx_size(const MACROBLOCKD *const xd, const MB_MODE_INFO *const mbmi,
- const int plane, const struct macroblockd_plane *plane_ptr) {
- assert(plane > AOM_PLANE_Y);
- if (xd && xd->lossless[mbmi->segment_id]) return TX_4X4;
-
- TX_SIZE tx_size =
- (plane == AOM_PLANE_Y)
- ? mbmi->tx_size
- : av1_get_max_uv_txsize(mbmi->bsize, plane_ptr->subsampling_x,
- plane_ptr->subsampling_y);
- return tx_size;
-}
-
-static AOM_INLINE int get_min_tx_height(
- const AV1_COMMON *const cm, const MACROBLOCKD *const xd, const int plane,
- const MACROBLOCKD_PLANE *const plane_ptr, const uint32_t mi_row,
- const uint32_t mi_col, const int y) {
+static AOM_INLINE int get_min_tx_height(const TX_SIZE *tx_buf,
+ const int x_range) {
int min_dim = INT_MAX;
- const uint32_t scale_horz = plane_ptr->subsampling_x;
- const uint32_t scale_vert = plane_ptr->subsampling_y;
- const int plane_mi_cols =
- ROUND_POWER_OF_TWO(cm->mi_params.mi_cols, scale_horz);
- const int x_range = AOMMIN((int)(plane_mi_cols - (mi_col >> scale_horz)),
- (MAX_MIB_SIZE >> scale_horz));
-
- for (int x = 0; x < x_range; x++) {
- const uint32_t curr_x = ((mi_col * MI_SIZE) >> scale_horz) + x * MI_SIZE;
- const uint32_t curr_y = ((mi_row * MI_SIZE) >> scale_vert) + y * MI_SIZE;
- const int curr_mi_row =
- scale_vert | ((curr_y << scale_vert) >> MI_SIZE_LOG2);
- const int curr_mi_col =
- scale_horz | ((curr_x << scale_horz) >> MI_SIZE_LOG2);
-
- assert(curr_mi_col < cm->mi_params.mi_cols);
- assert(curr_mi_row < cm->mi_params.mi_rows);
- MB_MODE_INFO **mi = cm->mi_params.mi_grid_base +
- curr_mi_row * cm->mi_params.mi_stride + curr_mi_col;
- const MB_MODE_INFO *mbmi = mi[0];
- if (mbmi == NULL) {
- continue;
- }
-
- // get_uv_tx_size is used instead of get_transform_size because
- // get_transform_size upscales the tx_size to the nearest square size.
- const TX_SIZE ts = get_uv_tx_size(xd, mi[0], plane, plane_ptr);
+ for (int x = 0; x < x_range;) {
+ const TX_SIZE ts = *tx_buf;
if (ts == TX_INVALID) {
+ x++;
continue;
}
+ tx_buf += tx_size_wide_unit[ts];
+ x += tx_size_wide_unit[ts];
min_dim = AOMMIN(min_dim, tx_size_high[ts]);
}
return min_dim;
}
-static AOM_INLINE int get_min_tx_width(const AV1_COMMON *const cm,
- const MACROBLOCKD *const xd,
- const int plane,
- const MACROBLOCKD_PLANE *const plane_ptr,
- const uint32_t mi_row,
- const uint32_t mi_col, const int x) {
+static AOM_INLINE int get_min_tx_width(const TX_SIZE *tx_buf,
+ const int y_range) {
int min_dim = INT_MAX;
- const uint32_t scale_horz = plane_ptr->subsampling_x;
- const uint32_t scale_vert = plane_ptr->subsampling_y;
- const int plane_mi_rows =
- ROUND_POWER_OF_TWO(cm->mi_params.mi_rows, scale_vert);
- const int y_range = AOMMIN((int)(plane_mi_rows - (mi_row >> scale_vert)),
- (MAX_MIB_SIZE >> scale_vert));
-
- for (int y = 0; y < y_range; y++) {
- const uint32_t curr_x = ((mi_col * MI_SIZE) >> scale_horz) + x * MI_SIZE;
- const uint32_t curr_y = ((mi_row * MI_SIZE) >> scale_vert) + y * MI_SIZE;
- const int curr_mi_row =
- scale_vert | ((curr_y << scale_vert) >> MI_SIZE_LOG2);
- const int curr_mi_col =
- scale_horz | ((curr_x << scale_horz) >> MI_SIZE_LOG2);
- assert(curr_mi_col < cm->mi_params.mi_cols);
- assert(curr_mi_row < cm->mi_params.mi_rows);
- MB_MODE_INFO **mi = cm->mi_params.mi_grid_base +
- curr_mi_row * cm->mi_params.mi_stride + curr_mi_col;
- const MB_MODE_INFO *mbmi = mi[0];
- if (mbmi == NULL) {
- continue;
- }
-
- // get_uv_tx_size is used instead of get_transform_size because
- // get_transform_size upscales the tx_size to the nearest square size.
- const TX_SIZE ts = get_uv_tx_size(xd, mi[0], plane, plane_ptr);
+ for (int y = 0; y < y_range;) {
+ const TX_SIZE ts = *tx_buf;
if (ts == TX_INVALID) {
+ y++;
continue;
}
+ tx_buf += tx_size_high_unit[ts];
+ y += tx_size_high_unit[ts];
min_dim = AOMMIN(min_dim, tx_size_wide[ts]);
}
@@ -830,8 +710,7 @@
// current and below rows must contain the same tx block. This is because
// dim 4 can only happen every unit of 2**0, and 8 every unit of 2**1,
// etc.
- min_height =
- get_min_tx_height(cm, xd, plane, plane_ptr, mi_row, mi_col, y);
+ min_height = get_min_tx_height(tx_buf, x_range);
}
uint8_t *p = dst_ptr + y * MI_SIZE * dst_stride;
for (int x = 0; x < x_range;) {
@@ -1099,7 +978,7 @@
// current and left cols must contain the same tx block. This is because
// dim 4 can only happen every unit of 2**0, and 8 every unit of 2**1,
// etc.
- min_width = get_min_tx_width(cm, xd, plane, plane_ptr, mi_row, mi_col, x);
+ min_width = get_min_tx_width(tx_buf, y_range);
}
uint8_t *p = dst_ptr + x * MI_SIZE;
for (int y = 0; y < y_range;) {
diff --git a/av1/common/common_data.h b/av1/common/common_data.h
index 38e1471..6ab7af4 100644
--- a/av1/common/common_data.h
+++ b/av1/common/common_data.h
@@ -257,11 +257,21 @@
2, 3, 4, 5, 6, 2, 3, 3, 4, 4, 5, 5, 6, 2, 4, 3, 5, 4, 6,
};
+// Transform block width in log2 unit
+static const int tx_size_wide_unit_log2[TX_SIZES_ALL] = {
+ 0, 1, 2, 3, 4, 0, 1, 1, 2, 2, 3, 3, 4, 0, 2, 1, 3, 2, 4,
+};
+
// Transform block height in log2
static const int tx_size_high_log2[TX_SIZES_ALL] = {
2, 3, 4, 5, 6, 3, 2, 4, 3, 5, 4, 6, 5, 4, 2, 5, 3, 6, 4,
};
+// Transform block height in log2 unit
+static const int tx_size_high_unit_log2[TX_SIZES_ALL] = {
+ 0, 1, 2, 3, 4, 1, 0, 2, 1, 3, 2, 4, 3, 2, 0, 3, 1, 4, 2,
+};
+
static const int tx_size_2d[TX_SIZES_ALL + 1] = {
16, 64, 256, 1024, 4096, 32, 32, 128, 128, 512,
512, 2048, 2048, 64, 64, 256, 256, 1024, 1024,
diff --git a/av1/encoder/encode_strategy.c b/av1/encoder/encode_strategy.c
index 80c89b5..a46bac1 100644
--- a/av1/encoder/encode_strategy.c
+++ b/av1/encoder/encode_strategy.c
@@ -1970,6 +1970,14 @@
}
#endif // CONFIG_REALTIME_ONLY
+ // This is used in rtc temporal filter case. Use true source in the PSNR
+ // calculation.
+ if (is_psnr_calc_enabled(cpi) && cpi->sf.rt_sf.use_rtc_tf &&
+ cpi->common.current_frame.frame_type != KEY_FRAME) {
+ assert(cpi->orig_source.buffer_alloc_sz > 0);
+ cpi->source = &cpi->orig_source;
+ }
+
// As the frame_update_type can get modified as part of
// av1_adjust_gf_refresh_qp_one_pass_rt
frame_update_type = get_frame_update_type(gf_group, cpi->gf_frame_index);
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index 990c201..7794a20 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -479,8 +479,7 @@
if (sf->rt_sf.source_metrics_sb_nonrd &&
cpi->svc.number_spatial_layers <= 1 &&
cm->current_frame.frame_type != KEY_FRAME) {
- int offset = cpi->source->y_stride * (mi_row << 2) + (mi_col << 2);
- av1_source_content_sb(cpi, x, offset);
+ av1_source_content_sb(cpi, x, mi_row, mi_col);
}
#if CONFIG_RT_ML_PARTITIONING
if (sf->part_sf.partition_search_type == ML_BASED_PARTITION) {
diff --git a/av1/encoder/encodeframe_utils.c b/av1/encoder/encodeframe_utils.c
index 734ddf8..4271338 100644
--- a/av1/encoder/encodeframe_utils.c
+++ b/av1/encoder/encodeframe_utils.c
@@ -1282,7 +1282,8 @@
// Grade the temporal variation of the source by comparing the current sb and
// its collocated block in the last frame.
-void av1_source_content_sb(AV1_COMP *cpi, MACROBLOCK *x, int offset) {
+void av1_source_content_sb(AV1_COMP *cpi, MACROBLOCK *x, int mi_row,
+ int mi_col) {
unsigned int tmp_sse;
unsigned int tmp_variance;
const BLOCK_SIZE bsize = cpi->common.seq_params->sb_size;
@@ -1290,6 +1291,7 @@
int src_ystride = cpi->source->y_stride;
uint8_t *last_src_y = cpi->last_source->y_buffer;
int last_src_ystride = cpi->last_source->y_stride;
+ const int offset = cpi->source->y_stride * (mi_row << 2) + (mi_col << 2);
uint64_t avg_source_sse_threshold = 100000; // ~5*5*(64*64)
uint64_t avg_source_sse_threshold_high = 1000000; // ~15*15*(64*64)
uint64_t sum_sq_thresh = 10000; // sum = sqrt(thresh / 64*64)) ~1.5
@@ -1317,6 +1319,47 @@
if ((tmp_sse - tmp_variance) < (sum_sq_thresh >> 1))
x->content_state_sb.low_sumdiff = 1;
}
+
+ if (cpi->last_source->y_width != cpi->source->y_width ||
+ cpi->last_source->y_height != cpi->source->y_height)
+ return;
+ if (!cpi->sf.rt_sf.use_rtc_tf) return;
+
+ // In-place temporal filter. If psnr calculation is enabled, we store the
+ // source for that.
+ AV1_COMMON *const cm = &cpi->common;
+ // Calculate n*mean^2
+ const unsigned int nmean2 = tmp_sse - tmp_variance;
+ const int ac_q_step = av1_ac_quant_QTX(cm->quant_params.base_qindex, 0,
+ cm->seq_params->bit_depth);
+ const unsigned int threshold = 3 * ac_q_step * ac_q_step / 2;
+
+ // TODO(yunqing): use a weighted sum instead of averaging in filtering.
+ if (tmp_variance <= threshold && nmean2 <= 15) {
+ const int shift_x[2] = { 0, cpi->source->subsampling_x };
+ const int shift_y[2] = { 0, cpi->source->subsampling_y };
+ const uint8_t h = block_size_high[bsize];
+ const uint8_t w = block_size_wide[bsize];
+
+ for (int plane = 0; plane < av1_num_planes(cm); ++plane) {
+ uint8_t *src = cpi->source->buffers[plane];
+ const int src_stride = cpi->source->strides[plane != 0];
+ uint8_t *last_src = cpi->last_source->buffers[plane];
+ const int last_src_stride = cpi->last_source->strides[plane != 0];
+ src += src_stride * (mi_row << (2 - shift_y[plane != 0])) +
+ (mi_col << (2 - shift_x[plane != 0]));
+ last_src += last_src_stride * (mi_row << (2 - shift_y[plane != 0])) +
+ (mi_col << (2 - shift_x[plane != 0]));
+
+ for (int i = 0; i < (h >> shift_y[plane != 0]); ++i) {
+ for (int j = 0; j < (w >> shift_x[plane != 0]); ++j) {
+ src[j] = (last_src[j] + src[j]) >> 1;
+ }
+ src += src_stride;
+ last_src += last_src_stride;
+ }
+ }
+ }
}
// Memset the mbmis at the current superblock to 0
diff --git a/av1/encoder/encodeframe_utils.h b/av1/encoder/encodeframe_utils.h
index 3dbbaa2..dd3f9aa 100644
--- a/av1/encoder/encodeframe_utils.h
+++ b/av1/encoder/encodeframe_utils.h
@@ -391,7 +391,8 @@
void av1_avg_cdf_symbols(FRAME_CONTEXT *ctx_left, FRAME_CONTEXT *ctx_tr,
int wt_left, int wt_tr);
-void av1_source_content_sb(AV1_COMP *cpi, MACROBLOCK *x, int offset);
+void av1_source_content_sb(AV1_COMP *cpi, MACROBLOCK *x, int mi_row,
+ int mi_col);
void av1_reset_mbmi(CommonModeInfoParams *const mi_params, BLOCK_SIZE sb_size,
int mi_row, int mi_col);
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index ddec870..d143523 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -2465,6 +2465,29 @@
segfeatures_copy(&cm->cur_frame->seg, &cm->seg);
cm->cur_frame->seg.enabled = cm->seg.enabled;
+ // This is for rtc temporal filtering case.
+ if (is_psnr_calc_enabled(cpi) && cpi->sf.rt_sf.use_rtc_tf &&
+ cm->current_frame.frame_type != KEY_FRAME) {
+ const SequenceHeader *seq_params = cm->seq_params;
+
+ if (cpi->orig_source.buffer_alloc_sz == 0 ||
+ cpi->last_source->y_width != cpi->source->y_width ||
+ cpi->last_source->y_height != cpi->source->y_height) {
+ // Allocate a source buffer to store the true source for psnr calculation.
+ if (aom_alloc_frame_buffer(
+ &cpi->orig_source, cpi->oxcf.frm_dim_cfg.width,
+ cpi->oxcf.frm_dim_cfg.height, seq_params->subsampling_x,
+ seq_params->subsampling_y, seq_params->use_highbitdepth,
+ cpi->oxcf.border_in_pixels, cm->features.byte_alignment))
+ aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR,
+ "Failed to allocate scaled buffer");
+ }
+
+ aom_yv12_copy_y(cpi->source, &cpi->orig_source);
+ aom_yv12_copy_u(cpi->source, &cpi->orig_source);
+ aom_yv12_copy_v(cpi->source, &cpi->orig_source);
+ }
+
#if CONFIG_COLLECT_COMPONENT_TIMING
start_timing(cpi, av1_encode_frame_time);
#endif
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index aeb277a..e175262 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -2764,6 +2764,12 @@
YV12_BUFFER_CONFIG *unfiltered_source;
/*!
+ * Frame buffer holding the orig source frame for PSNR calculation in rtc tf
+ * case.
+ */
+ YV12_BUFFER_CONFIG orig_source;
+
+ /*!
* Skip tpl setup when tpl data from gop length decision can be reused.
*/
int skip_tpl_setup_stats;
diff --git a/av1/encoder/encoder_alloc.h b/av1/encoder/encoder_alloc.h
index 5f95332..ff96d9d 100644
--- a/av1/encoder/encoder_alloc.h
+++ b/av1/encoder/encoder_alloc.h
@@ -278,6 +278,7 @@
aom_free_frame_buffer(&cpi->trial_frame_rst);
aom_free_frame_buffer(&cpi->scaled_source);
aom_free_frame_buffer(&cpi->scaled_last_source);
+ aom_free_frame_buffer(&cpi->orig_source);
free_token_info(token_info);
diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c
index f9df1de..b97483a 100644
--- a/av1/encoder/ratectrl.c
+++ b/av1/encoder/ratectrl.c
@@ -2627,7 +2627,8 @@
// Refresh this slot, which will become LAST on next frame.
svc->refresh[last_idx_refresh] = 1;
// Update GOLDEN on period for fixed slot case.
- if (gld_fixed_slot && gf_update) {
+ if (gld_fixed_slot && gf_update &&
+ cm->current_frame.frame_type != KEY_FRAME) {
ext_refresh_frame_flags->golden_frame = 1;
svc->refresh[gld_idx] = 1;
}
diff --git a/av1/encoder/speed_features.c b/av1/encoder/speed_features.c
index c6b056f..8b9ebaf 100644
--- a/av1/encoder/speed_features.c
+++ b/av1/encoder/speed_features.c
@@ -1259,6 +1259,9 @@
sf->rt_sf.gf_length_lvl = 1;
sf->rt_sf.skip_cdef_sb = 1;
}
+
+ // TODO(yunqing): extend this sf to other speeds and/or other resolutions.
+ if (speed >= 10) sf->rt_sf.use_rtc_tf = 1;
}
if (!is_480p_or_larger) {
if (speed == 7) {
@@ -1916,6 +1919,7 @@
rt_sf->prune_intra_mode_based_on_mv_range = 0;
rt_sf->var_part_split_threshold_shift = 7;
rt_sf->gf_refresh_based_on_qp = 0;
+ rt_sf->use_rtc_tf = 0;
}
void av1_set_speed_features_framesize_dependent(AV1_COMP *cpi, int speed) {
diff --git a/av1/encoder/speed_features.h b/av1/encoder/speed_features.h
index 17ebb23..5309f40 100644
--- a/av1/encoder/speed_features.h
+++ b/av1/encoder/speed_features.h
@@ -1425,6 +1425,9 @@
// Enable GF refresh based on Q value.
int gf_refresh_based_on_qp;
+
+ // Temporal filtering
+ int use_rtc_tf;
} REAL_TIME_SPEED_FEATURES;
/*!\endcond */
diff --git a/av1/encoder/tpl_model.c b/av1/encoder/tpl_model.c
index 74a8eac..d7e1fe1 100644
--- a/av1/encoder/tpl_model.c
+++ b/av1/encoder/tpl_model.c
@@ -2017,7 +2017,7 @@
int qindex = leaf_qindex;
for (qindex = leaf_qindex; qindex > 0; --qindex) {
const double qstep = av1_dc_quant_QTX(qindex, 0, bit_depth);
- if (qstep + 0.1 <= target_qstep) break;
+ if (qstep <= target_qstep) break;
}
return qindex;
}
diff --git a/test/tpl_model_test.cc b/test/tpl_model_test.cc
index d25b9f1..97fa050 100644
--- a/test/tpl_model_test.cc
+++ b/test/tpl_model_test.cc
@@ -170,6 +170,25 @@
EXPECT_EQ(overlap_area, 0);
}
+TEST(TplModelTest, GetQIndexFromQstepRatio) {
+ const aom_bit_depth_t bit_depth = AOM_BITS_8;
+ // When qstep_ratio is 1, the output q_index should be equal to leaf_qindex.
+ double qstep_ratio = 1.0;
+ for (int leaf_qindex = 1; leaf_qindex <= 255; ++leaf_qindex) {
+ const int q_index =
+ av1_get_q_index_from_qstep_ratio(leaf_qindex, qstep_ratio, bit_depth);
+ EXPECT_EQ(q_index, leaf_qindex);
+ }
+
+ // When qstep_ratio is very low, the output q_index should be 1.
+ qstep_ratio = 0.0001;
+ for (int leaf_qindex = 1; leaf_qindex <= 255; ++leaf_qindex) {
+ const int q_index =
+ av1_get_q_index_from_qstep_ratio(leaf_qindex, qstep_ratio, bit_depth);
+ EXPECT_EQ(q_index, 0);
+ }
+} // namespace
+
TEST(TplModelTest, TxfmStatsInitTest) {
TplTxfmStats tpl_txfm_stats;
av1_init_tpl_txfm_stats(&tpl_txfm_stats);