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);