Merge tag 'v3.15.1' into main branch

Release v3.15.1

2026-09-21 v3.15.1
  This release is ABI compatible with the last release. See
  https://aomedia.googlesource.com/aom/+log/v3.15.0..v3.15.1 for all the
  commits in this release.

  - Bug Fixes
    * b:558679217: Tune VMAF mode regression

Bug: 564595707
Change-Id: I5158b893f5112f7888844b51ccd0a6f10dfb5fa6
diff --git a/aom/aomcx.h b/aom/aomcx.h
index d1aa4ae..8b2d854 100644
--- a/aom/aomcx.h
+++ b/aom/aomcx.h
@@ -1777,8 +1777,18 @@
 typedef enum {
   AOM_TUNE_PSNR = 0,
   AOM_TUNE_SSIM = 1,
-  /* NOTE: enums 2 and 3 unused */
+  /* NOTE: enums 2, 3 unused */
+  /** Tune for Vmaf with preprocessing
+   *
+   * \deprecated This value is unsupported and will be removed in a future
+   * release.
+   */
   AOM_TUNE_VMAF_WITH_PREPROCESSING = 4,
+  /** Tune for Vmaf without preprocessing
+   *
+   * \deprecated This value is unsupported and will be removed in a future
+   * release.
+   */
   AOM_TUNE_VMAF_WITHOUT_PREPROCESSING = 5,
   AOM_TUNE_VMAF_MAX_GAIN = 6,
   AOM_TUNE_VMAF_NEG_MAX_GAIN = 7,
diff --git a/aom_dsp/aom_convolve.c b/aom_dsp/aom_convolve.c
index 99594de..65adfb8 100644
--- a/aom_dsp/aom_convolve.c
+++ b/aom_dsp/aom_convolve.c
@@ -148,7 +148,7 @@
 void aom_convolve_copy_c(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst,
                          ptrdiff_t dst_stride, int w, int h) {
   for (int r = h; r > 0; --r) {
-    memmove(dst, src, w);
+    memcpy(dst, src, w);
     src += src_stride;
     dst += dst_stride;
   }
@@ -245,7 +245,7 @@
                                 uint16_t *dst, ptrdiff_t dst_stride, int w,
                                 int h) {
   for (int y = 0; y < h; ++y) {
-    memmove(dst, src, w * sizeof(src[0]));
+    memcpy(dst, src, w * sizeof(src[0]));
     src += src_stride;
     dst += dst_stride;
   }
diff --git a/aom_dsp/aom_dsp_rtcd_defs.pl b/aom_dsp/aom_dsp_rtcd_defs.pl
index 5c274aa..a570954 100755
--- a/aom_dsp/aom_dsp_rtcd_defs.pl
+++ b/aom_dsp/aom_dsp_rtcd_defs.pl
@@ -1746,7 +1746,7 @@
     add_proto qw/double aom_compute_correlation/, "const unsigned char *frame1, int stride1, int x1, int y1, double mean1, double one_over_stddev1, const unsigned char *frame2, int stride2, int x2, int y2, double mean2, double one_over_stddev2";
     specialize qw/aom_compute_correlation sse4_1 avx2/;
 
-    add_proto qw/void aom_compute_flow_at_point/, "const uint8_t *src, const uint8_t *ref, int x, int y, int width, int height, int stride, double *u, double *v";
+    add_proto qw/void aom_compute_flow_at_point/, "const uint8_t *src, int src_stride, const uint8_t *ref, int ref_stride, int x, int y, int width, int height, double *u, double *v";
     specialize qw/aom_compute_flow_at_point sse4_1 avx2 neon sve/;
   }
 
diff --git a/aom_dsp/arm/aom_convolve_copy_neon.c b/aom_dsp/arm/aom_convolve_copy_neon.c
index a60f37f..2cfb9f6 100644
--- a/aom_dsp/arm/aom_convolve_copy_neon.c
+++ b/aom_dsp/arm/aom_convolve_copy_neon.c
@@ -59,11 +59,11 @@
                                    int h) {
   if (w < 4) {  // copy2
     do {
-      memmove(dst, src, 2 * sizeof(*src));
+      memcpy(dst, src, 2 * sizeof(*src));
       src += src_stride;
       dst += dst_stride;
 
-      memmove(dst, src, 2 * sizeof(*src));
+      memcpy(dst, src, 2 * sizeof(*src));
       src += src_stride;
       dst += dst_stride;
       h -= 2;
diff --git a/aom_dsp/flow_estimation/arm/disflow_neon.c b/aom_dsp/flow_estimation/arm/disflow_neon.c
index d6c0e0b..5d927cd 100644
--- a/aom_dsp/flow_estimation/arm/disflow_neon.c
+++ b/aom_dsp/flow_estimation/arm/disflow_neon.c
@@ -24,9 +24,10 @@
 // (x, y) in src and the other at (x + u, y + v) in ref.
 // This function returns the sum of squared pixel differences between
 // the two regions.
-static inline void compute_flow_error(const uint8_t *src, const uint8_t *ref,
-                                      int width, int height, int stride, int x,
-                                      int y, double u, double v, int16_t *dt) {
+static inline void compute_flow_error(const uint8_t *src, int src_stride,
+                                      const uint8_t *ref, int ref_stride,
+                                      int width, int height, int x, int y,
+                                      double u, double v, int16_t *dt) {
   // Split offset into integer and fractional parts, and compute cubic
   // interpolation kernels
   const int u_int = (int)floor(u);
@@ -56,11 +57,11 @@
   const int y0 = clamp(y + v_int, -9, height);
 
   // Horizontal convolution.
-  const uint8_t *ref_start = ref + (y0 - 1) * stride + (x0 - 1);
+  const uint8_t *ref_start = ref + (y0 - 1) * ref_stride + (x0 - 1);
   int16x4_t h_filter = vmovn_s32(vld1q_s32(h_kernel));
 
   for (int i = 0; i < DISFLOW_PATCH_SIZE + 3; ++i) {
-    uint8x16_t r = vld1q_u8(ref_start + i * stride);
+    uint8x16_t r = vld1q_u8(ref_start + i * ref_stride);
     uint16x8_t r0 = vmovl_u8(vget_low_u8(r));
     uint16x8_t r1 = vmovl_u8(vget_high_u8(r));
 
@@ -114,7 +115,7 @@
     sum_hi = vmlal_lane_s16(sum_hi, vget_high_s16(t2), v_filter, 2);
     sum_hi = vmlal_lane_s16(sum_hi, vget_high_s16(t3), v_filter, 3);
 
-    uint8x8_t s = vld1_u8(src + (i + y) * stride + x);
+    uint8x8_t s = vld1_u8(src + (i + y) * src_stride + x);
     int16x8_t s_s16 = vreinterpretq_s16_u16(vshll_n_u8(s, 3));
 
     // This time, we have to round off the 6 extra bits which were kept
@@ -230,9 +231,10 @@
   vst1_s32(b, add_pairwise_s32x4(b_red));
 }
 
-void aom_compute_flow_at_point_neon(const uint8_t *src, const uint8_t *ref,
-                                    int x, int y, int width, int height,
-                                    int stride, double *u, double *v) {
+void aom_compute_flow_at_point_neon(const uint8_t *src, int src_stride,
+                                    const uint8_t *ref, int ref_stride, int x,
+                                    int y, int width, int height, double *u,
+                                    double *v) {
   double M_inv[4];
   int b[2];
   int16_t dt[DISFLOW_PATCH_SIZE * DISFLOW_PATCH_SIZE];
@@ -240,14 +242,15 @@
   int16_t dy[DISFLOW_PATCH_SIZE * DISFLOW_PATCH_SIZE];
 
   // Compute gradients within this patch
-  const uint8_t *src_patch = &src[y * stride + x];
-  sobel_filter_x(src_patch, stride, dx, DISFLOW_PATCH_SIZE);
-  sobel_filter_y(src_patch, stride, dy, DISFLOW_PATCH_SIZE);
+  const uint8_t *src_patch = &src[y * src_stride + x];
+  sobel_filter_x(src_patch, src_stride, dx, DISFLOW_PATCH_SIZE);
+  sobel_filter_y(src_patch, src_stride, dy, DISFLOW_PATCH_SIZE);
 
   compute_flow_matrix(dx, DISFLOW_PATCH_SIZE, dy, DISFLOW_PATCH_SIZE, M_inv);
 
   for (int itr = 0; itr < DISFLOW_MAX_ITR; itr++) {
-    compute_flow_error(src, ref, width, height, stride, x, y, *u, *v, dt);
+    compute_flow_error(src, src_stride, ref, ref_stride, width, height, x, y,
+                       *u, *v, dt);
     compute_flow_vector(dx, DISFLOW_PATCH_SIZE, dy, DISFLOW_PATCH_SIZE, dt,
                         DISFLOW_PATCH_SIZE, b);
 
diff --git a/aom_dsp/flow_estimation/arm/disflow_sve.c b/aom_dsp/flow_estimation/arm/disflow_sve.c
index c2160a0..a8a912d 100644
--- a/aom_dsp/flow_estimation/arm/disflow_sve.c
+++ b/aom_dsp/flow_estimation/arm/disflow_sve.c
@@ -30,9 +30,10 @@
 // (x, y) in src and the other at (x + u, y + v) in ref.
 // This function returns the sum of squared pixel differences between
 // the two regions.
-static inline void compute_flow_error(const uint8_t *src, const uint8_t *ref,
-                                      int width, int height, int stride, int x,
-                                      int y, double u, double v, int16_t *dt) {
+static inline void compute_flow_error(const uint8_t *src, int src_stride,
+                                      const uint8_t *ref, int ref_stride,
+                                      int width, int height, int x, int y,
+                                      double u, double v, int16_t *dt) {
   // Split offset into integer and fractional parts, and compute cubic
   // interpolation kernels
   const int u_int = (int)floor(u);
@@ -62,16 +63,16 @@
   const int y0 = clamp(y + v_int, -9, height);
 
   // Horizontal convolution.
-  const uint8_t *ref_start = ref + (y0 - 1) * stride + (x0 - 1);
+  const uint8_t *ref_start = ref + (y0 - 1) * ref_stride + (x0 - 1);
   const int16x4_t h_kernel_s16 = vmovn_s32(vld1q_s32(h_kernel));
   const int16x8_t h_filter = vcombine_s16(h_kernel_s16, vdup_n_s16(0));
   const uint16x8_t idx = vld1q_u16(kDeinterleaveTbl);
 
   for (int i = 0; i < DISFLOW_PATCH_SIZE + 3; ++i) {
-    svuint16_t r0 = svld1ub_u16(svptrue_b16(), ref_start + i * stride + 0);
-    svuint16_t r1 = svld1ub_u16(svptrue_b16(), ref_start + i * stride + 1);
-    svuint16_t r2 = svld1ub_u16(svptrue_b16(), ref_start + i * stride + 2);
-    svuint16_t r3 = svld1ub_u16(svptrue_b16(), ref_start + i * stride + 3);
+    svuint16_t r0 = svld1ub_u16(svptrue_b16(), ref_start + i * ref_stride + 0);
+    svuint16_t r1 = svld1ub_u16(svptrue_b16(), ref_start + i * ref_stride + 1);
+    svuint16_t r2 = svld1ub_u16(svptrue_b16(), ref_start + i * ref_stride + 2);
+    svuint16_t r3 = svld1ub_u16(svptrue_b16(), ref_start + i * ref_stride + 3);
 
     int16x8_t s0 = vreinterpretq_s16_u16(svget_neonq_u16(r0));
     int16x8_t s1 = vreinterpretq_s16_u16(svget_neonq_u16(r1));
@@ -123,7 +124,7 @@
     sum_hi = vmlal_lane_s16(sum_hi, vget_high_s16(t2), v_filter, 2);
     sum_hi = vmlal_lane_s16(sum_hi, vget_high_s16(t3), v_filter, 3);
 
-    uint8x8_t s = vld1_u8(src + (i + y) * stride + x);
+    uint8x8_t s = vld1_u8(src + (i + y) * src_stride + x);
     int16x8_t s_s16 = vreinterpretq_s16_u16(vshll_n_u8(s, 3));
 
     // This time, we have to round off the 6 extra bits which were kept
@@ -232,9 +233,10 @@
   vst1_s32(b, vmovn_s64(b_s64[0]));
 }
 
-void aom_compute_flow_at_point_sve(const uint8_t *src, const uint8_t *ref,
-                                   int x, int y, int width, int height,
-                                   int stride, double *u, double *v) {
+void aom_compute_flow_at_point_sve(const uint8_t *src, int src_stride,
+                                   const uint8_t *ref, int ref_stride, int x,
+                                   int y, int width, int height, double *u,
+                                   double *v) {
   double M_inv[4];
   int b[2];
   int16_t dt[DISFLOW_PATCH_SIZE * DISFLOW_PATCH_SIZE];
@@ -242,14 +244,15 @@
   int16_t dy[DISFLOW_PATCH_SIZE * DISFLOW_PATCH_SIZE];
 
   // Compute gradients within this patch
-  const uint8_t *src_patch = &src[y * stride + x];
-  sobel_filter_x(src_patch, stride, dx, DISFLOW_PATCH_SIZE);
-  sobel_filter_y(src_patch, stride, dy, DISFLOW_PATCH_SIZE);
+  const uint8_t *src_patch = &src[y * src_stride + x];
+  sobel_filter_x(src_patch, src_stride, dx, DISFLOW_PATCH_SIZE);
+  sobel_filter_y(src_patch, src_stride, dy, DISFLOW_PATCH_SIZE);
 
   compute_flow_matrix(dx, DISFLOW_PATCH_SIZE, dy, DISFLOW_PATCH_SIZE, M_inv);
 
   for (int itr = 0; itr < DISFLOW_MAX_ITR; itr++) {
-    compute_flow_error(src, ref, width, height, stride, x, y, *u, *v, dt);
+    compute_flow_error(src, src_stride, ref, ref_stride, width, height, x, y,
+                       *u, *v, dt);
     compute_flow_vector(dx, DISFLOW_PATCH_SIZE, dy, DISFLOW_PATCH_SIZE, dt,
                         DISFLOW_PATCH_SIZE, b);
 
diff --git a/aom_dsp/flow_estimation/corner_match.c b/aom_dsp/flow_estimation/corner_match.c
index 07ea924..3679736 100644
--- a/aom_dsp/flow_estimation/corner_match.c
+++ b/aom_dsp/flow_estimation/corner_match.c
@@ -118,12 +118,11 @@
   double best_match_corr;
 } PointInfo;
 
-static int determine_correspondence(const unsigned char *src,
+static int determine_correspondence(const unsigned char *src, int src_stride,
                                     const int *src_corners, int num_src_corners,
-                                    const unsigned char *ref,
+                                    const unsigned char *ref, int ref_stride,
                                     const int *ref_corners, int num_ref_corners,
-                                    int width, int height, int src_stride,
-                                    int ref_stride,
+                                    int width, int height,
                                     Correspondence *correspondences) {
   PointInfo *src_point_info = NULL;
   PointInfo *ref_point_info = NULL;
@@ -233,8 +232,8 @@
       const int patch_tl_x = sx - DISFLOW_PATCH_CENTER;
       const int patch_tl_y = sy - DISFLOW_PATCH_CENTER;
 
-      aom_compute_flow_at_point(src, ref, patch_tl_x, patch_tl_y, width, height,
-                                src_stride, &u, &v);
+      aom_compute_flow_at_point(src, src_stride, ref, ref_stride, patch_tl_x,
+                                patch_tl_y, width, height, &u, &v);
 
       Correspondence *correspondence = &correspondences[num_correspondences];
       correspondence->x = (double)sx;
@@ -290,6 +289,10 @@
   assert(ref_pyramid->layers[0].height == src_height);
   const int ref_stride = ref_pyramid->layers[0].stride;
 
+  if (src_corners->num_corners == 0 || ref_corners->num_corners == 0) {
+    return false;
+  }
+
   // find correspondences between the two images
   correspondences = (Correspondence *)aom_malloc(src_corners->num_corners *
                                                  sizeof(*correspondences));
@@ -298,9 +301,9 @@
     return false;
   }
   num_correspondences = determine_correspondence(
-      src_buffer, src_corners->corners, src_corners->num_corners, ref_buffer,
-      ref_corners->corners, ref_corners->num_corners, src_width, src_height,
-      src_stride, ref_stride, correspondences);
+      src_buffer, src_stride, src_corners->corners, src_corners->num_corners,
+      ref_buffer, ref_stride, ref_corners->corners, ref_corners->num_corners,
+      src_width, src_height, correspondences);
 
   bool result = ransac(correspondences, num_correspondences, type,
                        motion_models, num_motion_models, mem_alloc_failed);
diff --git a/aom_dsp/flow_estimation/disflow.c b/aom_dsp/flow_estimation/disflow.c
index a1d7491..a275673 100644
--- a/aom_dsp/flow_estimation/disflow.c
+++ b/aom_dsp/flow_estimation/disflow.c
@@ -172,9 +172,10 @@
     const int patch_tl_x = x0 - DISFLOW_PATCH_CENTER;
     const int patch_tl_y = y0 - DISFLOW_PATCH_CENTER;
     aom_compute_flow_at_point(
-        src_pyr->layers[0].buffer, ref_pyr->layers[0].buffer, patch_tl_x,
+        src_pyr->layers[0].buffer, src_pyr->layers[0].stride,
+        ref_pyr->layers[0].buffer, ref_pyr->layers[0].stride, patch_tl_x,
         patch_tl_y, src_pyr->layers[0].width, src_pyr->layers[0].height,
-        src_pyr->layers[0].stride, &flow_u, &flow_v);
+        &flow_u, &flow_v);
 
     // Use original points (without offsets) when filling in correspondence
     // array
@@ -191,11 +192,11 @@
 // (x, y) in src and the other at (x + u, y + v) in ref.
 // This function returns the sum of squared pixel differences between
 // the two regions.
-static inline void compute_flow_vector(const uint8_t *src, const uint8_t *ref,
-                                       int width, int height, int stride, int x,
-                                       int y, double u, double v,
-                                       const int16_t *dx, const int16_t *dy,
-                                       int *b) {
+static inline void compute_flow_vector(const uint8_t *src, int src_stride,
+                                       const uint8_t *ref, int ref_stride,
+                                       int width, int height, int x, int y,
+                                       double u, double v, const int16_t *dx,
+                                       const int16_t *dy, int *b) {
   memset(b, 0, 2 * sizeof(*b));
 
   // Split offset into integer and fractional parts, and compute cubic
@@ -235,10 +236,10 @@
       const int x_w = x0 + j;
       int arr[4];
 
-      arr[0] = (int)ref[y_w * stride + (x_w - 1)];
-      arr[1] = (int)ref[y_w * stride + (x_w + 0)];
-      arr[2] = (int)ref[y_w * stride + (x_w + 1)];
-      arr[3] = (int)ref[y_w * stride + (x_w + 2)];
+      arr[0] = (int)ref[y_w * ref_stride + (x_w - 1)];
+      arr[1] = (int)ref[y_w * ref_stride + (x_w + 0)];
+      arr[2] = (int)ref[y_w * ref_stride + (x_w + 1)];
+      arr[3] = (int)ref[y_w * ref_stride + (x_w + 2)];
 
       // Apply kernel and round, keeping 6 extra bits of precision.
       //
@@ -270,7 +271,7 @@
       // of precision to match the scale of the dx and dy arrays.
       const int round_bits = DISFLOW_INTERP_BITS + 6 - DISFLOW_DERIV_SCALE_LOG2;
       const int warped = ROUND_POWER_OF_TWO(result, round_bits);
-      const int src_px = src[(x + j) + (y + i) * stride] << 3;
+      const int src_px = src[(x + j) + (y + i) * src_stride] << 3;
       const int dt = warped - src_px;
       b[0] += dx[i * DISFLOW_PATCH_SIZE + j] * dt;
       b[1] += dy[i * DISFLOW_PATCH_SIZE + j] * dt;
@@ -410,9 +411,10 @@
   M_inv[3] = M[0] * det_inv;
 }
 
-void aom_compute_flow_at_point_c(const uint8_t *src, const uint8_t *ref, int x,
-                                 int y, int width, int height, int stride,
-                                 double *u, double *v) {
+void aom_compute_flow_at_point_c(const uint8_t *src, int src_stride,
+                                 const uint8_t *ref, int ref_stride, int x,
+                                 int y, int width, int height, double *u,
+                                 double *v) {
   double M[4];
   double M_inv[4];
   int b[2];
@@ -420,16 +422,16 @@
   int16_t dy[DISFLOW_PATCH_SIZE * DISFLOW_PATCH_SIZE];
 
   // Compute gradients within this patch
-  const uint8_t *src_patch = &src[y * stride + x];
-  sobel_filter(src_patch, stride, dx, DISFLOW_PATCH_SIZE, 1);
-  sobel_filter(src_patch, stride, dy, DISFLOW_PATCH_SIZE, 0);
+  const uint8_t *src_patch = &src[y * src_stride + x];
+  sobel_filter(src_patch, src_stride, dx, DISFLOW_PATCH_SIZE, 1);
+  sobel_filter(src_patch, src_stride, dy, DISFLOW_PATCH_SIZE, 0);
 
   compute_flow_matrix(dx, DISFLOW_PATCH_SIZE, dy, DISFLOW_PATCH_SIZE, M);
   invert_2x2(M, M_inv);
 
   for (int itr = 0; itr < DISFLOW_MAX_ITR; itr++) {
-    compute_flow_vector(src, ref, width, height, stride, x, y, *u, *v, dx, dy,
-                        b);
+    compute_flow_vector(src, src_stride, ref, ref_stride, width, height, x, y,
+                        *u, *v, dx, dy, b);
 
     // Solve flow equations to find a better estimate for the flow vector
     // at this point
@@ -447,6 +449,9 @@
 
 static void fill_flow_field_borders(double *flow, int width, int height,
                                     int stride) {
+  if (width <= 2 * FLOW_BORDER_INNER || height <= 2 * FLOW_BORDER_INNER) return;
+  assert(width + 2 * FLOW_BORDER_OUTER <= stride);
+
   // Calculate the bounds of the rectangle which was filled in by
   // compute_flow_field() before calling this function.
   // These indices are inclusive on both ends.
@@ -478,7 +483,7 @@
   for (int i = -FLOW_BORDER_OUTER; i < top_index; i++) {
     double *row = flow + i * stride - FLOW_BORDER_OUTER;
     size_t length = width + 2 * FLOW_BORDER_OUTER;
-    memcpy(row, top_row, length * sizeof(*row));
+    memmove(row, top_row, length * sizeof(*row));
   }
 
   // Bottom area
@@ -486,7 +491,7 @@
   for (int i = bottom_index + 1; i < height + FLOW_BORDER_OUTER; i++) {
     double *row = flow + i * stride - FLOW_BORDER_OUTER;
     size_t length = width + 2 * FLOW_BORDER_OUTER;
-    memcpy(row, bottom_row, length * sizeof(*row));
+    memmove(row, bottom_row, length * sizeof(*row));
   }
 }
 
@@ -541,6 +546,10 @@
 // vector, even though these must be interpolated using different source points.
 static void upscale_flow_component(double *flow, int cur_width, int cur_height,
                                    int stride, double *tmpbuf) {
+  assert(cur_width > 0);
+  assert(cur_height > 0);
+  assert(2 * cur_width <= stride);
+
   const int half_len = FLOW_UPSCALE_TAPS / 2;
 
   // Check that the outer border is large enough to avoid needing to clamp
@@ -571,13 +580,13 @@
   const double *top_row = &tmpbuf[0];
   for (int i = -FLOW_BORDER_OUTER; i < 0; i++) {
     double *row = &tmpbuf[i * stride];
-    memcpy(row, top_row, 2 * cur_width * sizeof(*row));
+    memmove(row, top_row, 2 * cur_width * sizeof(*row));
   }
 
   const double *bottom_row = &tmpbuf[(cur_height - 1) * stride];
   for (int i = cur_height; i < cur_height + FLOW_BORDER_OUTER; i++) {
     double *row = &tmpbuf[i * stride];
-    memcpy(row, bottom_row, 2 * cur_width * sizeof(*row));
+    memmove(row, bottom_row, 2 * cur_width * sizeof(*row));
   }
 
   // Vertical upscale
@@ -643,10 +652,11 @@
     const PyramidLayer *cur_layer = &src_pyr->layers[level];
     const int cur_width = cur_layer->width;
     const int cur_height = cur_layer->height;
-    const int cur_stride = cur_layer->stride;
 
     const uint8_t *src_buffer = cur_layer->buffer;
+    const int src_stride = cur_layer->stride;
     const uint8_t *ref_buffer = ref_pyr->layers[level].buffer;
+    const int ref_stride = ref_pyr->layers[level].stride;
 
     const int cur_flow_width = cur_width >> DOWNSAMPLE_SHIFT;
     const int cur_flow_height = cur_height >> DOWNSAMPLE_SHIFT;
@@ -669,9 +679,9 @@
         assert(patch_tl_x >= 0);
         assert(patch_tl_y >= 0);
 
-        aom_compute_flow_at_point(src_buffer, ref_buffer, patch_tl_x,
-                                  patch_tl_y, cur_width, cur_height, cur_stride,
-                                  &flow_u[flow_field_idx],
+        aom_compute_flow_at_point(src_buffer, src_stride, ref_buffer,
+                                  ref_stride, patch_tl_x, patch_tl_y, cur_width,
+                                  cur_height, &flow_u[flow_field_idx],
                                   &flow_v[flow_field_idx]);
       }
     }
@@ -728,12 +738,17 @@
 }
 
 static FlowField *alloc_flow_field(int frame_width, int frame_height) {
+  const int flow_width = frame_width >> DOWNSAMPLE_SHIFT;
+  const int flow_height = frame_height >> DOWNSAMPLE_SHIFT;
+  assert(flow_width > 0);
+  assert(flow_height > 0);
+
   FlowField *flow = (FlowField *)aom_malloc(sizeof(FlowField));
   if (flow == NULL) return NULL;
 
   // Calculate the size of the bottom (largest) layer of the flow pyramid
-  flow->width = frame_width >> DOWNSAMPLE_SHIFT;
-  flow->height = frame_height >> DOWNSAMPLE_SHIFT;
+  flow->width = flow_width;
+  flow->height = flow_height;
   flow->stride = flow->width + 2 * FLOW_BORDER_OUTER;
 
   const size_t flow_size =
@@ -780,17 +795,30 @@
     *mem_alloc_failed = true;
     return false;
   }
+
+  if (src_layers != ref_layers) {
+    return false;
+  }
+
   if (!av1_compute_corner_list(src, bit_depth, downsample_level, src_corners)) {
     *mem_alloc_failed = true;
     return false;
   }
 
-  assert(src_layers == ref_layers);
-
   const int src_width = src_pyramid->layers[0].width;
   const int src_height = src_pyramid->layers[0].height;
-  assert(ref_pyramid->layers[0].width == src_width);
-  assert(ref_pyramid->layers[0].height == src_height);
+  if (ref_pyramid->layers[0].width != src_width ||
+      ref_pyramid->layers[0].height != src_height) {
+    return false;
+  }
+
+  if (src_width < (1 << DOWNSAMPLE_SHIFT) ||
+      src_height < (1 << DOWNSAMPLE_SHIFT)) {
+    return false;
+  }
+  if (src_corners->num_corners == 0) {
+    return false;
+  }
 
   FlowField *flow = alloc_flow_field(src_width, src_height);
   if (!flow) {
diff --git a/aom_dsp/flow_estimation/x86/disflow_avx2.c b/aom_dsp/flow_estimation/x86/disflow_avx2.c
index b93acdd..d1d1969 100644
--- a/aom_dsp/flow_estimation/x86/disflow_avx2.c
+++ b/aom_dsp/flow_estimation/x86/disflow_avx2.c
@@ -81,11 +81,11 @@
 //
 // TODO(rachelbarker): Test speed/quality impact of using bilinear interpolation
 // instad of bicubic interpolation
-static inline void compute_flow_vector(const uint8_t *src, const uint8_t *ref,
-                                       int width, int height, int stride, int x,
-                                       int y, double u, double v,
-                                       const int16_t *dx, const int16_t *dy,
-                                       int *b) {
+static inline void compute_flow_vector(const uint8_t *src, int src_stride,
+                                       const uint8_t *ref, int ref_stride,
+                                       int width, int height, int x, int y,
+                                       double u, double v, const int16_t *dx,
+                                       const int16_t *dy, int *b) {
   const __m256i zero = _mm256_setzero_si256();
 
   // Accumulate 8 32-bit partial sums for each element of b
@@ -137,7 +137,7 @@
 
   for (int i = -1; i < DISFLOW_PATCH_SIZE + 2; i += 2) {
     const int y_w = y0 + i;
-    const uint8_t *ref_row = &ref[y_w * stride + (x0 - 1)];
+    const uint8_t *ref_row = &ref[y_w * ref_stride + (x0 - 1)];
     int16_t *tmp_row = &tmp[i * DISFLOW_PATCH_SIZE];
 
     // Load this row of pixels.
@@ -145,7 +145,7 @@
     // for a total of 11 pixels. Here we load 16 pixels, but only use
     // the first 11.
     __m256i row =
-        yy_loadu2_128((__m128i *)(ref_row + stride), (__m128i *)ref_row);
+        yy_loadu2_128((__m128i *)(ref_row + ref_stride), (__m128i *)ref_row);
 
     // Expand pixels to int16s
     // We must use unpacks here, as we have one row in each 128-bit lane
@@ -234,8 +234,8 @@
         _mm256_srai_epi32(_mm256_add_epi32(sum1, round_const_v), round_bits);
 
     __m256i warped = _mm256_packs_epi32(sum0_rounded, sum1_rounded);
-    __m128i src_pixels_u8 = xx_loadu_2x64(&src[(y + i + 1) * stride + x],
-                                          &src[(y + i) * stride + x]);
+    __m128i src_pixels_u8 = xx_loadu_2x64(&src[(y + i + 1) * src_stride + x],
+                                          &src[(y + i) * src_stride + x]);
     __m256i src_pixels =
         _mm256_slli_epi16(_mm256_cvtepu8_epi16(src_pixels_u8), 3);
 
@@ -382,9 +382,10 @@
   M_inv[3] = M[0] * det_inv;
 }
 
-void aom_compute_flow_at_point_avx2(const uint8_t *src, const uint8_t *ref,
-                                    int x, int y, int width, int height,
-                                    int stride, double *u, double *v) {
+void aom_compute_flow_at_point_avx2(const uint8_t *src, int src_stride,
+                                    const uint8_t *ref, int ref_stride, int x,
+                                    int y, int width, int height, double *u,
+                                    double *v) {
   DECLARE_ALIGNED(32, double, M[4]);
   DECLARE_ALIGNED(32, double, M_inv[4]);
   DECLARE_ALIGNED(32, int16_t, dx[DISFLOW_PATCH_SIZE * DISFLOW_PATCH_SIZE]);
@@ -392,15 +393,15 @@
   int b[2];
 
   // Compute gradients within this patch
-  const uint8_t *src_patch = &src[y * stride + x];
-  sobel_filter(src_patch, stride, dx, dy);
+  const uint8_t *src_patch = &src[y * src_stride + x];
+  sobel_filter(src_patch, src_stride, dx, dy);
 
   compute_flow_matrix(dx, DISFLOW_PATCH_SIZE, dy, DISFLOW_PATCH_SIZE, M);
   invert_2x2(M, M_inv);
 
   for (int itr = 0; itr < DISFLOW_MAX_ITR; itr++) {
-    compute_flow_vector(src, ref, width, height, stride, x, y, *u, *v, dx, dy,
-                        b);
+    compute_flow_vector(src, src_stride, ref, ref_stride, width, height, x, y,
+                        *u, *v, dx, dy, b);
 
     // Solve flow equations to find a better estimate for the flow vector
     // at this point
diff --git a/aom_dsp/flow_estimation/x86/disflow_sse4.c b/aom_dsp/flow_estimation/x86/disflow_sse4.c
index ffbe485..2d01315 100644
--- a/aom_dsp/flow_estimation/x86/disflow_sse4.c
+++ b/aom_dsp/flow_estimation/x86/disflow_sse4.c
@@ -80,11 +80,11 @@
 //
 // TODO(rachelbarker): Test speed/quality impact of using bilinear interpolation
 // instad of bicubic interpolation
-static inline void compute_flow_vector(const uint8_t *src, const uint8_t *ref,
-                                       int width, int height, int stride, int x,
-                                       int y, double u, double v,
-                                       const int16_t *dx, const int16_t *dy,
-                                       int *b) {
+static inline void compute_flow_vector(const uint8_t *src, int src_stride,
+                                       const uint8_t *ref, int ref_stride,
+                                       int width, int height, int x, int y,
+                                       double u, double v, const int16_t *dx,
+                                       const int16_t *dy, int *b) {
   // This function is written to do 8x8 convolutions only
   assert(DISFLOW_PATCH_SIZE == 8);
 
@@ -134,7 +134,7 @@
 
   for (int i = -1; i < DISFLOW_PATCH_SIZE + 2; ++i) {
     const int y_w = y0 + i;
-    const uint8_t *ref_row = &ref[y_w * stride + (x0 - 1)];
+    const uint8_t *ref_row = &ref[y_w * ref_stride + (x0 - 1)];
     int16_t *tmp_row = &tmp[i * DISFLOW_PATCH_SIZE];
 
     // Load this row of pixels.
@@ -218,7 +218,7 @@
 
     __m128i warped = _mm_packs_epi32(sum0_rounded, sum1_rounded);
     __m128i src_pixels_u8 =
-        _mm_loadl_epi64((__m128i *)&src[(y + i) * stride + x]);
+        _mm_loadl_epi64((__m128i *)&src[(y + i) * src_stride + x]);
     __m128i src_pixels = _mm_slli_epi16(_mm_cvtepu8_epi16(src_pixels_u8), 3);
 
     // Calculate delta from the target patch
@@ -357,9 +357,10 @@
   M_inv[3] = M[0] * det_inv;
 }
 
-void aom_compute_flow_at_point_sse4_1(const uint8_t *src, const uint8_t *ref,
-                                      int x, int y, int width, int height,
-                                      int stride, double *u, double *v) {
+void aom_compute_flow_at_point_sse4_1(const uint8_t *src, int src_stride,
+                                      const uint8_t *ref, int ref_stride, int x,
+                                      int y, int width, int height, double *u,
+                                      double *v) {
   DECLARE_ALIGNED(16, double, M[4]);
   DECLARE_ALIGNED(16, double, M_inv[4]);
   DECLARE_ALIGNED(16, int16_t, dx[DISFLOW_PATCH_SIZE * DISFLOW_PATCH_SIZE]);
@@ -367,15 +368,15 @@
   int b[2];
 
   // Compute gradients within this patch
-  const uint8_t *src_patch = &src[y * stride + x];
-  sobel_filter(src_patch, stride, dx, dy);
+  const uint8_t *src_patch = &src[y * src_stride + x];
+  sobel_filter(src_patch, src_stride, dx, dy);
 
   compute_flow_matrix(dx, DISFLOW_PATCH_SIZE, dy, DISFLOW_PATCH_SIZE, M);
   invert_2x2(M, M_inv);
 
   for (int itr = 0; itr < DISFLOW_MAX_ITR; itr++) {
-    compute_flow_vector(src, ref, width, height, stride, x, y, *u, *v, dx, dy,
-                        b);
+    compute_flow_vector(src, src_stride, ref, ref_stride, width, height, x, y,
+                        *u, *v, dx, dy, b);
 
     // Solve flow equations to find a better estimate for the flow vector
     // at this point
diff --git a/aom_dsp/x86/aom_convolve_copy_avx2.c b/aom_dsp/x86/aom_convolve_copy_avx2.c
index efce154..86d396c 100644
--- a/aom_dsp/x86/aom_convolve_copy_avx2.c
+++ b/aom_dsp/x86/aom_convolve_copy_avx2.c
@@ -10,6 +10,7 @@
  */
 
 #include <immintrin.h>
+#include <string.h>
 
 #include "config/aom_dsp_rtcd.h"
 
@@ -36,20 +37,20 @@
 
   if (w == 2) {
     do {
-      memmove(dst, src, 2 * sizeof(*src));
+      memcpy(dst, src, 2 * sizeof(*src));
       src += src_stride;
       dst += dst_stride;
-      memmove(dst, src, 2 * sizeof(*src));
+      memcpy(dst, src, 2 * sizeof(*src));
       src += src_stride;
       dst += dst_stride;
       h -= 2;
     } while (h);
   } else if (w == 4) {
     do {
-      memmove(dst, src, 4 * sizeof(*src));
+      memcpy(dst, src, 4 * sizeof(*src));
       src += src_stride;
       dst += dst_stride;
-      memmove(dst, src, 4 * sizeof(*src));
+      memcpy(dst, src, 4 * sizeof(*src));
       src += src_stride;
       dst += dst_stride;
       h -= 2;
@@ -170,10 +171,10 @@
 
   if (w == 2) {
     do {
-      memmove(dst, src, 2 * sizeof(*src));
+      memcpy(dst, src, 2 * sizeof(*src));
       src += src_stride;
       dst += dst_stride;
-      memmove(dst, src, 2 * sizeof(*src));
+      memcpy(dst, src, 2 * sizeof(*src));
       src += src_stride;
       dst += dst_stride;
       h -= 2;
diff --git a/aom_dsp/x86/aom_convolve_copy_sse2.c b/aom_dsp/x86/aom_convolve_copy_sse2.c
index 223b404..871f53e 100644
--- a/aom_dsp/x86/aom_convolve_copy_sse2.c
+++ b/aom_dsp/x86/aom_convolve_copy_sse2.c
@@ -10,6 +10,7 @@
  */
 
 #include <immintrin.h>
+#include <string.h>
 
 #include "config/aom_dsp_rtcd.h"
 
@@ -44,20 +45,20 @@
 
   if (w == 2) {
     do {
-      memmove(dst, src, 2 * sizeof(*src));
+      memcpy(dst, src, 2 * sizeof(*src));
       src += src_stride;
       dst += dst_stride;
-      memmove(dst, src, 2 * sizeof(*src));
+      memcpy(dst, src, 2 * sizeof(*src));
       src += src_stride;
       dst += dst_stride;
       h -= 2;
     } while (h);
   } else if (w == 4) {
     do {
-      memmove(dst, src, 4 * sizeof(*src));
+      memcpy(dst, src, 4 * sizeof(*src));
       src += src_stride;
       dst += dst_stride;
-      memmove(dst, src, 4 * sizeof(*src));
+      memcpy(dst, src, 4 * sizeof(*src));
       src += src_stride;
       dst += dst_stride;
       h -= 2;
diff --git a/av1/arg_defs.c b/av1/arg_defs.c
index 9a3352c..d9627cd 100644
--- a/av1/arg_defs.c
+++ b/av1/arg_defs.c
@@ -42,8 +42,6 @@
 static const struct arg_enum_list tuning_enum[] = {
   { "psnr", AOM_TUNE_PSNR },
   { "ssim", AOM_TUNE_SSIM },
-  { "vmaf_with_preprocessing", AOM_TUNE_VMAF_WITH_PREPROCESSING },
-  { "vmaf_without_preprocessing", AOM_TUNE_VMAF_WITHOUT_PREPROCESSING },
   { "vmaf", AOM_TUNE_VMAF_MAX_GAIN },
   { "vmaf_neg", AOM_TUNE_VMAF_NEG_MAX_GAIN },
   { "butteraugli", AOM_TUNE_BUTTERAUGLI },
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index b43c96d..030da6e 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -887,8 +887,7 @@
 #endif
 
 #if !CONFIG_TUNE_VMAF
-  if (extra_cfg->tuning >= AOM_TUNE_VMAF_WITH_PREPROCESSING &&
-      extra_cfg->tuning <= AOM_TUNE_VMAF_NEG_MAX_GAIN) {
+  if (is_vmaf_tuning_mode(extra_cfg->tuning)) {
     ERROR(
         "This error may be related to the wrong configuration options: try to "
         "set -DCONFIG_TUNE_VMAF=1 at the time CMake is run.");
@@ -1673,21 +1672,32 @@
     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 (for the case of single spatial layer, since otherwise a previous
+    // times. In single-pass realtime mode without lookahead (g_lag_in_frames ==
+    // 0), and with the maximum frame size declared up front via
+    // g_forced_max_frame_width/height, reference frame scaling allows upscaling
+    // up to 16x and downscaling by up to 2x without forcing a keyframe. The
+    // forced maximum frame size is required because the internal buffers are
+    // then allocated for that maximum; without it they are only sized for the
+    // current frame size and a larger frame would overflow them. Outside of
+    // this mode, or if reference frame scaling constraints are violated, a
+    // keyframe is forced (for 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.
+    // 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.
+    const bool allow_ref_scaled_upscale =
+        cfg->g_forced_max_frame_width && cfg->g_forced_max_frame_height &&
+        ctx->oxcf.mode == REALTIME && cfg->g_pass == AOM_RC_ONE_PASS &&
+        cfg->g_lag_in_frames == 0;
     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))) {
+         (!allow_ref_scaled_upscale &&
+          (((int)cfg->g_w > ctx->ppi->cpi->last_coded_width) ||
+           ((int)cfg->g_h > ctx->ppi->cpi->last_coded_height))))) {
       force_key = 1;
     }
   }
@@ -3408,8 +3418,7 @@
   }
 
 #if CONFIG_TUNE_VMAF
-  if (ctx->extra_cfg.tuning >= AOM_TUNE_VMAF_WITH_PREPROCESSING &&
-      ctx->extra_cfg.tuning <= AOM_TUNE_VMAF_NEG_MAX_GAIN) {
+  if (is_vmaf_tuning_mode(ctx->extra_cfg.tuning)) {
     aom_init_vmaf_model(&ppi->cpi->vmaf_info.vmaf_model,
                         ppi->cpi->oxcf.tune_cfg.vmaf_model_path);
   }
@@ -4901,6 +4910,9 @@
   } else if (arg_match_helper(&arg, &g_av1_codec_arg_defs.force_max_q, argv,
                               err_string)) {
     extra_cfg.force_max_q = arg_parse_uint_helper(&arg, err_string);
+  } else if (arg_match_helper(&arg, &g_av1_codec_arg_defs.resize_mode, argv,
+                              err_string)) {
+    ctx->cfg.rc_resize_mode = arg_parse_uint_helper(&arg, err_string);
   } else {
     match = 0;
     snprintf(err_string, ARG_ERR_MSG_MAX_LEN, "Cannot find aom option %s",
diff --git a/av1/common/reconintra.c b/av1/common/reconintra.c
index 33016a2..4e94c00 100644
--- a/av1/common/reconintra.c
+++ b/av1/common/reconintra.c
@@ -1750,8 +1750,8 @@
   // separate function build_non_directional_intra_predictors() is introduced
   // for these modes to avoid redundant computations while generating pred data.
 
-  const int n_top_px = have_top ? AOMMIN(txwpx, xr + txwpx) : 0;
-  const int n_left_px = have_left ? AOMMIN(txhpx, yd + txhpx) : 0;
+  const int n_top_px = have_top ? clamp(xr + txwpx, 0, txwpx) : 0;
+  const int n_left_px = have_left ? clamp(yd + txhpx, 0, txhpx) : 0;
   if (!use_filter_intra && !is_dr_mode) {
 #if CONFIG_AV1_HIGHBITDEPTH
     if (is_hbd) {
@@ -1815,9 +1815,9 @@
   const int disable_edge_filter = !enable_intra_edge_filter;
   const int intra_edge_filter_type = get_intra_edge_filter_type(xd, plane);
   const int n_topright_px =
-      have_top_right > 0 ? AOMMIN(txwpx, xr) : have_top_right;
+      have_top_right > 0 ? clamp(xr, 0, txwpx) : have_top_right;
   const int n_bottomleft_px =
-      have_bottom_left > 0 ? AOMMIN(txhpx, yd) : have_bottom_left;
+      have_bottom_left > 0 ? clamp(yd, 0, txhpx) : have_bottom_left;
 #if CONFIG_AV1_HIGHBITDEPTH
   if (is_hbd) {
     highbd_build_directional_and_filter_intra_predictors(
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index b6ee3ac..fcb328d 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -5370,6 +5370,11 @@
 
   xd->error_info = cm->error;
   if (initialize_flag) setup_frame_info(pbi);
+  if (pbi->dec_tile_row < -1 || pbi->dec_tile_row >= tiles->rows ||
+      pbi->dec_tile_col < -1 || pbi->dec_tile_col >= tiles->cols) {
+    aom_internal_error(&pbi->error, AOM_CODEC_INVALID_PARAM,
+                       "Invalid tile row or column");
+  }
   const int num_planes = av1_num_planes(cm);
 
 #if CONFIG_COLLECT_COMPONENT_TIMING
diff --git a/av1/encoder/allintra_vis.c b/av1/encoder/allintra_vis.c
index ec8ec26..91b5147 100644
--- a/av1/encoder/allintra_vis.c
+++ b/av1/encoder/allintra_vis.c
@@ -59,34 +59,41 @@
 
 void av1_init_mb_wiener_var_buffer(AV1_COMP *cpi) {
   AV1_COMMON *cm = &cpi->common;
+  const int current_size = cpi->frame_info.mi_rows * cpi->frame_info.mi_cols;
 
   // This block size is also used to determine number of workers in
   // multi-threading. If it is changed, one needs to change it accordingly in
   // "compute_num_ai_workers()".
   cpi->weber_bsize = BLOCK_8X8;
 
-  if (cpi->oxcf.enable_rate_guide_deltaq) {
-    if (cpi->mb_weber_stats && cpi->prep_rate_estimates &&
-        cpi->ext_rate_distribution)
-      return;
-  } else {
-    if (cpi->mb_weber_stats) return;
+  if (cpi->mb_weber_stats && cpi->mb_weber_stats_alloc_size < current_size) {
+    aom_free(cpi->mb_weber_stats);
+    cpi->mb_weber_stats = NULL;
+    aom_free(cpi->prep_rate_estimates);
+    cpi->prep_rate_estimates = NULL;
+    aom_free(cpi->ext_rate_distribution);
+    cpi->ext_rate_distribution = NULL;
+    cpi->mb_weber_stats_alloc_size = 0;
   }
 
-  CHECK_MEM_ERROR(cm, cpi->mb_weber_stats,
-                  aom_calloc(cpi->frame_info.mi_rows * cpi->frame_info.mi_cols,
-                             sizeof(*cpi->mb_weber_stats)));
+  if (!cpi->mb_weber_stats) {
+    CHECK_MEM_ERROR(cm, cpi->mb_weber_stats,
+                    aom_calloc(current_size, sizeof(*cpi->mb_weber_stats)));
+    cpi->mb_weber_stats_alloc_size = current_size;
+  }
 
   if (cpi->oxcf.enable_rate_guide_deltaq) {
-    CHECK_MEM_ERROR(
-        cm, cpi->prep_rate_estimates,
-        aom_calloc(cpi->frame_info.mi_rows * cpi->frame_info.mi_cols,
-                   sizeof(*cpi->prep_rate_estimates)));
+    if (!cpi->prep_rate_estimates) {
+      CHECK_MEM_ERROR(cm, cpi->prep_rate_estimates,
+                      aom_calloc(cpi->mb_weber_stats_alloc_size,
+                                 sizeof(*cpi->prep_rate_estimates)));
+    }
 
-    CHECK_MEM_ERROR(
-        cm, cpi->ext_rate_distribution,
-        aom_calloc(cpi->frame_info.mi_rows * cpi->frame_info.mi_cols,
-                   sizeof(*cpi->ext_rate_distribution)));
+    if (!cpi->ext_rate_distribution) {
+      CHECK_MEM_ERROR(cm, cpi->ext_rate_distribution,
+                      aom_calloc(cpi->mb_weber_stats_alloc_size,
+                                 sizeof(*cpi->ext_rate_distribution)));
+    }
   }
 }
 
@@ -103,7 +110,8 @@
 
   for (int row = mi_row; row < mi_row + mi_high; row += mi_step) {
     for (int col = mi_col; col < mi_col + mi_wide; col += mi_step) {
-      if (row >= cm->mi_params.mi_rows || col >= cm->mi_params.mi_cols)
+      if (row < 0 || col < 0 || row >= cm->mi_params.mi_rows ||
+          col >= cm->mi_params.mi_cols)
         continue;
 
       satd += cpi->mb_weber_stats[(row / mi_step) * mb_stride + (col / mi_step)]
@@ -112,10 +120,10 @@
     }
   }
 
-  if (mb_count) satd = (int)(satd / mb_count);
+  if (mb_count) satd = satd / mb_count;
   satd = AOMMAX(1, satd);
 
-  return (int)satd;
+  return satd;
 }
 
 static int64_t get_sse(AV1_COMP *const cpi, BLOCK_SIZE bsize, int mi_row,
@@ -131,7 +139,8 @@
 
   for (int row = mi_row; row < mi_row + mi_high; row += mi_step) {
     for (int col = mi_col; col < mi_col + mi_wide; col += mi_step) {
-      if (row >= cm->mi_params.mi_rows || col >= cm->mi_params.mi_cols)
+      if (row < 0 || col < 0 || row >= cm->mi_params.mi_rows ||
+          col >= cm->mi_params.mi_cols)
         continue;
 
       distortion +=
@@ -141,10 +150,10 @@
     }
   }
 
-  if (mb_count) distortion = (int)(distortion / mb_count);
+  if (mb_count) distortion = distortion / mb_count;
   distortion = AOMMAX(1, distortion);
 
-  return (int)distortion;
+  return distortion;
 }
 
 static double get_max_scale(const AV1_COMP *const cpi, BLOCK_SIZE bsize,
@@ -158,7 +167,8 @@
 
   for (int row = mi_row; row < mi_row + mi_high; row += mi_step) {
     for (int col = mi_col; col < mi_col + mi_wide; col += mi_step) {
-      if (row >= cm->mi_params.mi_rows || col >= cm->mi_params.mi_cols)
+      if (row < 0 || col < 0 || row >= cm->mi_params.mi_rows ||
+          col >= cm->mi_params.mi_cols)
         continue;
       const WeberStats *weber_stats =
           &cpi->mb_weber_stats[(row / mi_step) * mb_stride + (col / mi_step)];
@@ -186,7 +196,8 @@
 
   for (int row = mi_row; row < mi_row + mi_high; row += mi_step) {
     for (int col = mi_col; col < mi_col + mi_wide; col += mi_step) {
-      if (row >= cm->mi_params.mi_rows || col >= cm->mi_params.mi_cols)
+      if (row < 0 || col < 0 || row >= cm->mi_params.mi_rows ||
+          col >= cm->mi_params.mi_cols)
         continue;
 
       const WeberStats *weber_stats =
@@ -206,6 +217,7 @@
     }
   }
 
+  if (mb_count == 0) return 1;
   sb_wiener_var =
       (int)(((base_num + base_reg) / (base_den + base_reg)) / mb_count);
   sb_wiener_var = AOMMAX(1, sb_wiener_var);
@@ -690,16 +702,20 @@
 
 static int get_rate_guided_quantizer(const AV1_COMP *const cpi,
                                      BLOCK_SIZE bsize, int mi_row, int mi_col) {
+  const AV1_COMMON *const cm = &cpi->common;
   // Calculation uses 8x8.
   const int mb_step = mi_size_wide[cpi->weber_bsize];
   // Accumulate to 16x16
   const int block_step = mi_size_wide[BLOCK_16X16];
   double sb_rate_hific = 0.0;
   double sb_rate_uniform = 0.0;
-  for (int row = mi_row; row < mi_row + mi_size_wide[bsize];
+  for (int row = mi_row; row < mi_row + mi_size_high[bsize];
        row += block_step) {
-    for (int col = mi_col; col < mi_col + mi_size_high[bsize];
+    for (int col = mi_col; col < mi_col + mi_size_wide[bsize];
          col += block_step) {
+      if (row < 0 || col < 0 || row >= cm->mi_params.mi_rows ||
+          col >= cm->mi_params.mi_cols)
+        continue;
       sb_rate_hific +=
           cpi->ext_rate_distribution[(row / mb_step) * cpi->frame_info.mi_cols +
                                      (col / mb_step)];
@@ -708,6 +724,10 @@
         for (int c = 0; c < block_step; c += mb_step) {
           const int this_row = row + r;
           const int this_col = col + c;
+          if (this_row < 0 || this_col < 0 ||
+              this_row >= cm->mi_params.mi_rows ||
+              this_col >= cm->mi_params.mi_cols)
+            continue;
           sb_rate_uniform +=
               cpi->prep_rate_estimates[(this_row / mb_step) *
                                            cpi->frame_info.mi_cols +
@@ -727,7 +747,6 @@
   double min_max_scale = AOMMAX(1.0, get_max_scale(cpi, bsize, mi_row, mi_col));
   scale = 1.0 / AOMMIN(1.0 / scale, min_max_scale);
 
-  const AV1_COMMON *const cm = &cpi->common;
   const int base_qindex = cm->quant_params.base_qindex;
   int offset =
       av1_get_deltaq_offset(cm->seq_params->bit_depth, base_qindex, scale);
@@ -774,12 +793,19 @@
 
 void av1_init_mb_ur_var_buffer(AV1_COMP *cpi) {
   AV1_COMMON *cm = &cpi->common;
+  const int current_size = cpi->frame_info.mb_rows * cpi->frame_info.mb_cols;
 
-  if (cpi->mb_delta_q) return;
+  if (cpi->mb_delta_q && cpi->mb_delta_q_alloc_size < current_size) {
+    aom_free(cpi->mb_delta_q);
+    cpi->mb_delta_q = NULL;
+    cpi->mb_delta_q_alloc_size = 0;
+  }
 
-  CHECK_MEM_ERROR(cm, cpi->mb_delta_q,
-                  aom_calloc(cpi->frame_info.mb_rows * cpi->frame_info.mb_cols,
-                             sizeof(*cpi->mb_delta_q)));
+  if (!cpi->mb_delta_q) {
+    CHECK_MEM_ERROR(cm, cpi->mb_delta_q,
+                    aom_calloc(current_size, sizeof(*cpi->mb_delta_q)));
+    cpi->mb_delta_q_alloc_size = current_size;
+  }
 }
 
 #if CONFIG_TFLITE
diff --git a/av1/encoder/allintra_vis.h b/av1/encoder/allintra_vis.h
index ee0b691..492defa 100644
--- a/av1/encoder/allintra_vis.h
+++ b/av1/encoder/allintra_vis.h
@@ -20,6 +20,10 @@
 #include "av1/encoder/block.h"
 #include "av1/encoder/encoder.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #define MB_WIENER_MT_UNIT_SIZE BLOCK_64X64
 
 void av1_init_mb_wiener_var_buffer(AV1_COMP *cpi);
@@ -48,4 +52,8 @@
 int av1_get_sbq_variance_boost(const AV1_COMP *const cpi, const MACROBLOCK *x);
 #endif
 
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
 #endif  // AOM_AV1_ENCODER_ALLINTRA_VIS_H_
diff --git a/av1/encoder/aq_complexity.c b/av1/encoder/aq_complexity.c
index 090463b..a497523 100644
--- a/av1/encoder/aq_complexity.c
+++ b/av1/encoder/aq_complexity.c
@@ -141,10 +141,10 @@
 
   // Rate depends on fraction of a SB64 in frame (xmis * ymis / bw * bh).
   // It is converted to bits << AV1_PROB_COST_SHIFT units.
-  const int64_t num = (int64_t)(cpi->rc.sb64_target_rate * xmis * ymis)
+  const int64_t num = ((int64_t)cpi->rc.sb64_target_rate * xmis * ymis)
                       << AV1_PROB_COST_SHIFT;
   const int denom = cm->seq_params->mib_size * cm->seq_params->mib_size;
-  const int target_rate = (int)(num / denom);
+  const int64_t target_rate = num / denom;
   double logvar;
   double low_var_thresh;
   const int aq_strength = get_aq_c_strength(cm->quant_params.base_qindex,
diff --git a/av1/encoder/arm/reconinter_enc_neon.c b/av1/encoder/arm/reconinter_enc_neon.c
index ac6501f..5c12301 100644
--- a/av1/encoder/arm/reconinter_enc_neon.c
+++ b/av1/encoder/arm/reconinter_enc_neon.c
@@ -88,7 +88,7 @@
     const int ref_vert_offset = ref_stride * ((SUBPEL_TAPS >> 1) - 1);
     const int im_vert_offset = im_stride * ((filter_params->taps >> 1) - 1);
 
-    assert(im_height <= (MAX_SB_SIZE * 2 + 16) + 16);
+    assert(im_height < (MAX_SB_SIZE + SUBPEL_TAPS));
     aom_convolve8_horiz(ref - ref_vert_offset, ref_stride, im_block,
                         MAX_SB_SIZE, filter_x, 16, NULL, -1, width, im_height);
     aom_convolve8_vert(im_block + im_vert_offset, MAX_SB_SIZE, comp_pred, width,
@@ -178,7 +178,7 @@
         av1_get_interp_filter_subpel_kernel(filter, subpel_y_q3 << 1);
     const int intermediate_height =
         (((height - 1) * 8 + subpel_y_q3) >> 3) + filter->taps;
-    assert(intermediate_height <= (MAX_SB_SIZE * 2 + 16) + 16);
+    assert(intermediate_height < (MAX_SB_SIZE + SUBPEL_TAPS));
     aom_highbd_convolve8_horiz_neon(
         CONVERT_TO_BYTEPTR(ref - ref_stride * ((filter->taps >> 1) - 1)),
         ref_stride, CONVERT_TO_BYTEPTR(temp), MAX_SB_SIZE, kernel_x, 16, NULL,
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 53ca4ad..6975195 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -2367,13 +2367,20 @@
                                     struct aom_write_bit_buffer *wb) {
   const int coded_width = cm->superres_upscaled_width - 1;
   const int coded_height = cm->superres_upscaled_height - 1;
+  const SequenceHeader *seq_params = cm->seq_params;
 
   if (frame_size_override) {
-    const SequenceHeader *seq_params = cm->seq_params;
     int num_bits_width = seq_params->num_bits_width;
     int num_bits_height = seq_params->num_bits_height;
     aom_wb_write_literal(wb, coded_width, num_bits_width);
     aom_wb_write_literal(wb, coded_height, num_bits_height);
+  } else {
+    if (cm->superres_upscaled_width != seq_params->max_frame_width ||
+        cm->superres_upscaled_height != seq_params->max_frame_height) {
+      aom_internal_error(cm->error, AOM_CODEC_UNSUP_BITSTREAM,
+                         "Frame dimensions are different from the maximum "
+                         "values when frame_size_override_flag is 0");
+    }
   }
 
   write_superres_scale(cm, wb);
@@ -2977,10 +2984,7 @@
 
   int frame_size_override_flag = 0;
 
-  if (seq_params->reduced_still_picture_hdr) {
-    assert(cm->superres_upscaled_width == seq_params->max_frame_width &&
-           cm->superres_upscaled_height == seq_params->max_frame_height);
-  } else {
+  if (!seq_params->reduced_still_picture_hdr) {
     if (seq_params->frame_id_numbers_present_flag) {
       int frame_id_len = seq_params->frame_id_length;
       aom_wb_write_literal(wb, cm->current_frame_id, frame_id_len);
@@ -3055,118 +3059,110 @@
     }
   }
 
-  if (current_frame->frame_type == KEY_FRAME) {
+  if (frame_is_intra_only(cm)) {
     write_frame_size(cm, frame_size_override_flag, wb);
     assert(!av1_superres_scaled(cm) || !features->allow_intrabc);
     if (features->allow_screen_content_tools && !av1_superres_scaled(cm))
       aom_wb_write_bit(wb, features->allow_intrabc);
-  } else {
-    if (current_frame->frame_type == INTRA_ONLY_FRAME) {
-      write_frame_size(cm, frame_size_override_flag, wb);
-      assert(!av1_superres_scaled(cm) || !features->allow_intrabc);
-      if (features->allow_screen_content_tools && !av1_superres_scaled(cm))
-        aom_wb_write_bit(wb, features->allow_intrabc);
-    } else if (current_frame->frame_type == INTER_FRAME ||
-               frame_is_sframe(cm)) {
-      MV_REFERENCE_FRAME ref_frame;
+  } else if (current_frame->frame_type == INTER_FRAME || frame_is_sframe(cm)) {
+    MV_REFERENCE_FRAME ref_frame;
 
-      // NOTE: Error resilient mode turns off frame_refs_short_signaling
-      //       automatically.
+    // NOTE: Error resilient mode turns off frame_refs_short_signaling
+    //       automatically.
 #define FRAME_REFS_SHORT_SIGNALING 0
 #if FRAME_REFS_SHORT_SIGNALING
-      current_frame->frame_refs_short_signaling =
-          seq_params->order_hint_info.enable_order_hint;
+    current_frame->frame_refs_short_signaling =
+        seq_params->order_hint_info.enable_order_hint;
 #endif  // FRAME_REFS_SHORT_SIGNALING
 
-      if (current_frame->frame_refs_short_signaling) {
-        //    In rtc case when cpi->sf.rt_sf.enable_ref_short_signaling is true,
-        //    we turn on frame_refs_short_signaling when the current frame and
-        //    golden frame are in the same order_hint group, and their relative
-        //    distance is <= 64 (in order to be decodable).
+    if (current_frame->frame_refs_short_signaling) {
+      //    In rtc case when cpi->sf.rt_sf.enable_ref_short_signaling is true,
+      //    we turn on frame_refs_short_signaling when the current frame and
+      //    golden frame are in the same order_hint group, and their relative
+      //    distance is <= 64 (in order to be decodable).
 
-        //    For other cases, an example solution for encoder-side
-        //    implementation on frame_refs_short_signaling is also provided in
-        //    this function, where frame_refs_short_signaling is only turned on
-        //    when the encoder side decision on ref frames is identical to that
-        //    at the decoder side.
+      //    For other cases, an example solution for encoder-side
+      //    implementation on frame_refs_short_signaling is also provided in
+      //    this function, where frame_refs_short_signaling is only turned on
+      //    when the encoder side decision on ref frames is identical to that
+      //    at the decoder side.
 
-        current_frame->frame_refs_short_signaling =
-            check_frame_refs_short_signaling(
-                cm, cpi->sf.rt_sf.enable_ref_short_signaling);
-      }
+      current_frame->frame_refs_short_signaling =
+          check_frame_refs_short_signaling(
+              cm, cpi->sf.rt_sf.enable_ref_short_signaling);
+    }
 
-      if (seq_params->order_hint_info.enable_order_hint)
-        aom_wb_write_bit(wb, current_frame->frame_refs_short_signaling);
+    if (seq_params->order_hint_info.enable_order_hint)
+      aom_wb_write_bit(wb, current_frame->frame_refs_short_signaling);
 
-      if (current_frame->frame_refs_short_signaling) {
-        const int lst_ref = get_ref_frame_map_idx(cm, LAST_FRAME);
-        aom_wb_write_literal(wb, lst_ref, REF_FRAMES_LOG2);
+    if (current_frame->frame_refs_short_signaling) {
+      const int lst_ref = get_ref_frame_map_idx(cm, LAST_FRAME);
+      aom_wb_write_literal(wb, lst_ref, REF_FRAMES_LOG2);
 
-        const int gld_ref = get_ref_frame_map_idx(cm, GOLDEN_FRAME);
-        aom_wb_write_literal(wb, gld_ref, REF_FRAMES_LOG2);
-      }
-      int first_ref_map_idx = INVALID_IDX;
-      if (cpi->ppi->rtc_ref.set_ref_frame_config) {
-        for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) {
-          if (cpi->ppi->rtc_ref.reference[ref_frame - 1] == 1) {
-            first_ref_map_idx = cpi->ppi->rtc_ref.ref_idx[ref_frame - 1];
-            break;
-          }
-        }
-      }
+      const int gld_ref = get_ref_frame_map_idx(cm, GOLDEN_FRAME);
+      aom_wb_write_literal(wb, gld_ref, REF_FRAMES_LOG2);
+    }
+    int first_ref_map_idx = INVALID_IDX;
+    if (cpi->ppi->rtc_ref.set_ref_frame_config) {
       for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) {
-        assert(get_ref_frame_map_idx(cm, ref_frame) != INVALID_IDX);
-        if (!current_frame->frame_refs_short_signaling) {
-          if (cpi->ppi->rtc_ref.set_ref_frame_config &&
-              first_ref_map_idx != INVALID_IDX &&
-              cpi->svc.number_spatial_layers == 1 &&
-              !seq_params->order_hint_info.enable_order_hint) {
-            // For the usage of set_ref_frame_config:
-            // for any reference not used set their ref_map_idx
-            // to the first used reference.
-            const int map_idx = cpi->ppi->rtc_ref.reference[ref_frame - 1]
-                                    ? get_ref_frame_map_idx(cm, ref_frame)
-                                    : first_ref_map_idx;
-            aom_wb_write_literal(wb, map_idx, REF_FRAMES_LOG2);
-          } else {
-            aom_wb_write_literal(wb, get_ref_frame_map_idx(cm, ref_frame),
-                                 REF_FRAMES_LOG2);
-          }
-        }
-        if (seq_params->frame_id_numbers_present_flag) {
-          int i = get_ref_frame_map_idx(cm, ref_frame);
-          int frame_id_len = seq_params->frame_id_length;
-          int diff_len = seq_params->delta_frame_id_length;
-          int delta_frame_id_minus_1 =
-              ((cm->current_frame_id - cm->ref_frame_id[i] +
-                (1 << frame_id_len)) %
-               (1 << frame_id_len)) -
-              1;
-          if (delta_frame_id_minus_1 < 0 ||
-              delta_frame_id_minus_1 >= (1 << diff_len)) {
-            aom_internal_error(cm->error, AOM_CODEC_ERROR,
-                               "Invalid delta_frame_id_minus_1");
-          }
-          aom_wb_write_literal(wb, delta_frame_id_minus_1, diff_len);
+        if (cpi->ppi->rtc_ref.reference[ref_frame - 1] == 1) {
+          first_ref_map_idx = cpi->ppi->rtc_ref.ref_idx[ref_frame - 1];
+          break;
         }
       }
-
-      if (!features->error_resilient_mode && frame_size_override_flag) {
-        write_frame_size_with_refs(cm, wb);
-      } else {
-        write_frame_size(cm, frame_size_override_flag, wb);
+    }
+    for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) {
+      assert(get_ref_frame_map_idx(cm, ref_frame) != INVALID_IDX);
+      if (!current_frame->frame_refs_short_signaling) {
+        if (cpi->ppi->rtc_ref.set_ref_frame_config &&
+            first_ref_map_idx != INVALID_IDX &&
+            cpi->svc.number_spatial_layers == 1 &&
+            !seq_params->order_hint_info.enable_order_hint) {
+          // For the usage of set_ref_frame_config:
+          // for any reference not used set their ref_map_idx
+          // to the first used reference.
+          const int map_idx = cpi->ppi->rtc_ref.reference[ref_frame - 1]
+                                  ? get_ref_frame_map_idx(cm, ref_frame)
+                                  : first_ref_map_idx;
+          aom_wb_write_literal(wb, map_idx, REF_FRAMES_LOG2);
+        } else {
+          aom_wb_write_literal(wb, get_ref_frame_map_idx(cm, ref_frame),
+                               REF_FRAMES_LOG2);
+        }
       }
-
-      if (!features->cur_frame_force_integer_mv)
-        aom_wb_write_bit(wb, features->allow_high_precision_mv);
-      write_frame_interp_filter(features->interp_filter, wb);
-      aom_wb_write_bit(wb, features->switchable_motion_mode);
-      if (frame_might_allow_ref_frame_mvs(cm)) {
-        aom_wb_write_bit(wb, features->allow_ref_frame_mvs);
-      } else {
-        assert(features->allow_ref_frame_mvs == 0);
+      if (seq_params->frame_id_numbers_present_flag) {
+        int i = get_ref_frame_map_idx(cm, ref_frame);
+        int frame_id_len = seq_params->frame_id_length;
+        int diff_len = seq_params->delta_frame_id_length;
+        int delta_frame_id_minus_1 =
+            ((cm->current_frame_id - cm->ref_frame_id[i] +
+              (1 << frame_id_len)) %
+             (1 << frame_id_len)) -
+            1;
+        if (delta_frame_id_minus_1 < 0 ||
+            delta_frame_id_minus_1 >= (1 << diff_len)) {
+          aom_internal_error(cm->error, AOM_CODEC_ERROR,
+                             "Invalid delta_frame_id_minus_1");
+        }
+        aom_wb_write_literal(wb, delta_frame_id_minus_1, diff_len);
       }
     }
+
+    if (!features->error_resilient_mode && frame_size_override_flag) {
+      write_frame_size_with_refs(cm, wb);
+    } else {
+      write_frame_size(cm, frame_size_override_flag, wb);
+    }
+
+    if (!features->cur_frame_force_integer_mv)
+      aom_wb_write_bit(wb, features->allow_high_precision_mv);
+    write_frame_interp_filter(features->interp_filter, wb);
+    aom_wb_write_bit(wb, features->switchable_motion_mode);
+    if (frame_might_allow_ref_frame_mvs(cm)) {
+      aom_wb_write_bit(wb, features->allow_ref_frame_mvs);
+    } else {
+      assert(features->allow_ref_frame_mvs == 0);
+    }
   }
 
   const int might_bwd_adapt = !(seq_params->reduced_still_picture_hdr) &&
diff --git a/av1/encoder/encode_strategy.c b/av1/encoder/encode_strategy.c
index 85931ea..6f9715c 100644
--- a/av1/encoder/encode_strategy.c
+++ b/av1/encoder/encode_strategy.c
@@ -1771,8 +1771,7 @@
 
 #if CONFIG_TUNE_VMAF
   if (!is_stat_generation_stage(cpi) &&
-      (oxcf->tune_cfg.tuning >= AOM_TUNE_VMAF_WITH_PREPROCESSING &&
-       oxcf->tune_cfg.tuning <= AOM_TUNE_VMAF_NEG_MAX_GAIN)) {
+      is_vmaf_tuning_mode(oxcf->tune_cfg.tuning)) {
     av1_update_vmaf_curve(cpi);
   }
 #endif
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index e3ef6eb..da81788 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -2446,13 +2446,15 @@
   mt_info->pack_bs_mt_enabled = AOMMIN(mt_info->num_mod_workers[MOD_PACK_BS],
                                        cm->tiles.cols * cm->tiles.rows) > 1;
 
-  if (oxcf->row_mt && (mt_info->num_workers > 1)) {
+  if (oxcf->row_mt && (mt_info->num_mod_workers[MOD_ENC] > 1)) {
     mt_info->row_mt_enabled = 1;
     enc_row_mt->sync_read_ptr = av1_row_mt_sync_read;
     enc_row_mt->sync_write_ptr = av1_row_mt_sync_write;
     av1_encode_tiles_row_mt(cpi);
   } else {
-    if (AOMMIN(mt_info->num_workers, cm->tiles.cols * cm->tiles.rows) > 1) {
+    const int num_workers = AOMMIN(mt_info->num_mod_workers[MOD_ENC],
+                                   cm->tiles.cols * cm->tiles.rows);
+    if (num_workers > 1) {
       av1_encode_tiles_mt(cpi);
     } else {
       // Preallocate the pc_tree for realtime coding to reduce the cost of
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 3842190..6ee258a 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -429,6 +429,22 @@
   av1_calculate_tile_rows(seq_params, mi_params->mi_rows, tiles);
 }
 
+static inline void init_frame_info(FRAME_INFO *frame_info,
+                                   const AV1_COMMON *const cm) {
+  const CommonModeInfoParams *const mi_params = &cm->mi_params;
+  const SequenceHeader *const seq_params = cm->seq_params;
+  frame_info->frame_width = cm->width;
+  frame_info->frame_height = cm->height;
+  frame_info->mi_cols = mi_params->mi_cols;
+  frame_info->mi_rows = mi_params->mi_rows;
+  frame_info->mb_cols = mi_params->mb_cols;
+  frame_info->mb_rows = mi_params->mb_rows;
+  frame_info->num_mbs = mi_params->MBs;
+  frame_info->bit_depth = seq_params->bit_depth;
+  frame_info->subsampling_x = seq_params->subsampling_x;
+  frame_info->subsampling_y = seq_params->subsampling_y;
+}
+
 void av1_update_frame_size(AV1_COMP *cpi) {
   AV1_COMMON *const cm = &cpi->common;
   MACROBLOCKD *const xd = &cpi->td.mb.e_mbd;
@@ -446,6 +462,7 @@
                                    cpi->ppi->number_spatial_layers));
 
   set_tile_info(cm, &cpi->oxcf.tile_cfg);
+  init_frame_info(&cpi->frame_info, cm);
 }
 
 static inline int does_level_match(int width, int height, double fps,
@@ -646,7 +663,7 @@
   seq->enable_intra_edge_filter = oxcf->intra_mode_cfg.enable_intra_edge_filter;
   seq->enable_filter_intra = oxcf->intra_mode_cfg.enable_filter_intra;
 
-  set_bitstream_level_tier(ppi, frm_dim_cfg->width, frm_dim_cfg->height,
+  set_bitstream_level_tier(ppi, seq->max_frame_width, seq->max_frame_height,
                            oxcf->input_cfg.init_framerate);
   av1_set_svc_seq_params(ppi);
 }
@@ -741,8 +758,12 @@
 
   alloc_compressor_data(cpi);
 
-  cpi->data_alloc_width = cm->width;
-  cpi->data_alloc_height = cm->height;
+  cpi->data_alloc_width = oxcf->frm_dim_cfg.forced_max_frame_width
+                              ? oxcf->frm_dim_cfg.forced_max_frame_width
+                              : cm->width;
+  cpi->data_alloc_height = oxcf->frm_dim_cfg.forced_max_frame_height
+                               ? oxcf->frm_dim_cfg.forced_max_frame_height
+                               : cm->height;
   cpi->frame_size_related_setup_done = false;
 
   // Single thread case: use counts in common.
@@ -979,9 +1000,13 @@
   }
 
   if (x->upsample_pred == NULL) {
+    // The buffer 'upsampled_pred' is used to store the output of horizontal
+    // filtering in aom_(highbd_)upsampled_pred() function. As the length of the
+    // interpolation filter used is SUBPEL_TAPS a buffer size of (MAX_SB_SIZE +
+    // SUBPEL_TAPS) * MAX_SB_SIZE is allocated.
     CHECK_MEM_ERROR(
         cm, x->upsample_pred,
-        aom_memalign(16, (1 + is_highbitdepth) * ((MAX_SB_SIZE + 16) + 16) *
+        aom_memalign(16, (1 + is_highbitdepth) * (MAX_SB_SIZE + SUBPEL_TAPS) *
                              MAX_SB_SIZE * sizeof(*x->upsample_pred)));
     x->e_mbd.tmp_upsample_pred = x->upsample_pred;
   }
@@ -1038,8 +1063,12 @@
     cpi->td.firstpass_ctx = NULL;
     alloc_compressor_data(cpi);
     realloc_segmentation_maps(cpi);
-    cpi->data_alloc_width = cm->width;
-    cpi->data_alloc_height = cm->height;
+    cpi->data_alloc_width = oxcf->frm_dim_cfg.forced_max_frame_width
+                                ? oxcf->frm_dim_cfg.forced_max_frame_width
+                                : cm->width;
+    cpi->data_alloc_height = oxcf->frm_dim_cfg.forced_max_frame_height
+                                 ? oxcf->frm_dim_cfg.forced_max_frame_height
+                                 : cm->height;
     cpi->frame_size_related_setup_done = false;
   }
   av1_update_frame_size(cpi);
@@ -1048,6 +1077,12 @@
     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;
+      if (cpi->oxcf.frm_dim_cfg.forced_max_frame_width) {
+        mi_cols = size_in_mi(cpi->oxcf.frm_dim_cfg.forced_max_frame_width);
+      }
+      if (cpi->oxcf.frm_dim_cfg.forced_max_frame_height) {
+        mi_rows = size_in_mi(cpi->oxcf.frm_dim_cfg.forced_max_frame_height);
+      }
       aom_free(cpi->cyclic_refresh->map);
       CHECK_MEM_ERROR(
           cm, cpi->cyclic_refresh->map,
@@ -1093,22 +1128,6 @@
 #endif  // CONFIG_REALTIME_ONLY
 }
 
-static inline void init_frame_info(FRAME_INFO *frame_info,
-                                   const AV1_COMMON *const cm) {
-  const CommonModeInfoParams *const mi_params = &cm->mi_params;
-  const SequenceHeader *const seq_params = cm->seq_params;
-  frame_info->frame_width = cm->width;
-  frame_info->frame_height = cm->height;
-  frame_info->mi_cols = mi_params->mi_cols;
-  frame_info->mi_rows = mi_params->mi_rows;
-  frame_info->mb_cols = mi_params->mb_cols;
-  frame_info->mb_rows = mi_params->mb_rows;
-  frame_info->num_mbs = mi_params->MBs;
-  frame_info->bit_depth = seq_params->bit_depth;
-  frame_info->subsampling_x = seq_params->subsampling_x;
-  frame_info->subsampling_y = seq_params->subsampling_y;
-}
-
 static inline void init_frame_index_set(FRAME_INDEX_SET *frame_index_set) {
   frame_index_set->show_frame_count = 0;
 }
@@ -1616,7 +1635,9 @@
   cpi->consec_zero_mv_alloc_size = consec_zero_mv_alloc_size;
 
   cpi->mb_weber_stats = NULL;
+  cpi->mb_weber_stats_alloc_size = 0;
   cpi->mb_delta_q = NULL;
+  cpi->mb_delta_q_alloc_size = 0;
   cpi->palette_pixel_num = 0;
   cpi->scaled_last_source_available = 0;
 
@@ -2625,8 +2646,12 @@
     cpi->td.firstpass_ctx = NULL;
     alloc_compressor_data(cpi);
     realloc_segmentation_maps(cpi);
-    cpi->data_alloc_width = cm->width;
-    cpi->data_alloc_height = cm->height;
+    cpi->data_alloc_width = cpi->oxcf.frm_dim_cfg.forced_max_frame_width
+                                ? cpi->oxcf.frm_dim_cfg.forced_max_frame_width
+                                : cm->width;
+    cpi->data_alloc_height = cpi->oxcf.frm_dim_cfg.forced_max_frame_height
+                                 ? cpi->oxcf.frm_dim_cfg.forced_max_frame_height
+                                 : cm->height;
     cpi->frame_size_related_setup_done = false;
   }
   alloc_mb_mode_info_buffers(cpi);
@@ -3392,8 +3417,7 @@
     }
 
 #if CONFIG_TUNE_VMAF
-    if (oxcf->tune_cfg.tuning >= AOM_TUNE_VMAF_WITH_PREPROCESSING &&
-        oxcf->tune_cfg.tuning <= AOM_TUNE_VMAF_NEG_MAX_GAIN) {
+    if (is_vmaf_tuning_mode(oxcf->tune_cfg.tuning)) {
       cpi->vmaf_info.original_qindex = q;
       q = av1_get_vmaf_base_qindex(cpi, q);
     }
@@ -3597,8 +3621,7 @@
     }
 
 #if CONFIG_TUNE_VMAF
-    if (oxcf->tune_cfg.tuning >= AOM_TUNE_VMAF_WITH_PREPROCESSING &&
-        oxcf->tune_cfg.tuning <= AOM_TUNE_VMAF_NEG_MAX_GAIN) {
+    if (is_vmaf_tuning_mode(oxcf->tune_cfg.tuning)) {
       q = cpi->vmaf_info.original_qindex;
     }
 #endif
@@ -4326,8 +4349,7 @@
   }
 #endif
 #if CONFIG_TUNE_VMAF
-  else if (oxcf->tune_cfg.tuning == AOM_TUNE_VMAF_WITHOUT_PREPROCESSING ||
-           oxcf->tune_cfg.tuning == AOM_TUNE_VMAF_MAX_GAIN ||
+  else if (oxcf->tune_cfg.tuning == AOM_TUNE_VMAF_MAX_GAIN ||
            oxcf->tune_cfg.tuning == AOM_TUNE_VMAF_NEG_MAX_GAIN) {
     av1_set_mb_vmaf_rdmult_scaling(cpi);
   }
@@ -4770,11 +4792,9 @@
 
 #if CONFIG_TUNE_VMAF
   if (!is_stat_generation_stage(cpi) &&
-      cpi->oxcf.tune_cfg.tuning == AOM_TUNE_VMAF_WITH_PREPROCESSING) {
-    av1_vmaf_frame_preprocessing(cpi, sd);
-  }
-  if (!is_stat_generation_stage(cpi) &&
       cpi->oxcf.tune_cfg.tuning == AOM_TUNE_VMAF_MAX_GAIN) {
+    // Future work: frame-level preprocessing can be used here if the
+    // performance is similar.
     av1_vmaf_blk_preprocessing(cpi, sd);
   }
 #endif
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 82e9cab..1b13fc9 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -3586,6 +3586,12 @@
   WeberStats *mb_weber_stats;
 
   /*!
+   * Allocated memory size (number of elements) for |mb_weber_stats| (and
+   * |prep_rate_estimates|, |ext_rate_distribution|).
+   */
+  int mb_weber_stats_alloc_size;
+
+  /*!
    * Buffer to store rate cost estimates for each macro block (8x8) in the
    * preprocessing stage used in allintra mode.
    */
@@ -3618,6 +3624,11 @@
   int *mb_delta_q;
 
   /*!
+   * Allocated memory size (number of elements) for |mb_delta_q|.
+   */
+  int mb_delta_q_alloc_size;
+
+  /*!
    * Flag to indicate that current frame is dropped.
    */
   bool is_dropped_frame;
@@ -4444,6 +4455,12 @@
          cm->show_frame && !cpi->is_dropped_frame;
 }
 
+// Check if VMAF tuning is enabled.
+static inline int is_vmaf_tuning_mode(const aom_tune_metric tuning) {
+  return tuning == AOM_TUNE_VMAF_MAX_GAIN ||
+         tuning == AOM_TUNE_VMAF_NEG_MAX_GAIN;
+}
+
 static inline int is_frame_resize_pending(const AV1_COMP *const cpi) {
   const ResizePendingParams *const resize_pending_params =
       &cpi->resize_pending_params;
diff --git a/av1/encoder/encoder_alloc.h b/av1/encoder/encoder_alloc.h
index 97b5273..ac4a873 100644
--- a/av1/encoder/encoder_alloc.h
+++ b/av1/encoder/encoder_alloc.h
@@ -15,6 +15,7 @@
 #include "av1/encoder/block.h"
 #include "av1/encoder/encodeframe_utils.h"
 #include "av1/encoder/encoder.h"
+#include "av1/encoder/encoder_utils.h"
 #include "av1/encoder/encodetxb.h"
 #include "av1/encoder/ethread.h"
 #include "av1/encoder/global_motion_facade.h"
@@ -105,22 +106,29 @@
 static inline void realloc_segmentation_maps(AV1_COMP *cpi) {
   AV1_COMMON *const cm = &cpi->common;
   CommonModeInfoParams *const mi_params = &cm->mi_params;
+  int max_mi_cols = mi_params->mi_cols;
+  int max_mi_rows = mi_params->mi_rows;
+  if (cpi->oxcf.frm_dim_cfg.forced_max_frame_width) {
+    max_mi_cols = size_in_mi(cpi->oxcf.frm_dim_cfg.forced_max_frame_width);
+  }
+  if (cpi->oxcf.frm_dim_cfg.forced_max_frame_height) {
+    max_mi_rows = size_in_mi(cpi->oxcf.frm_dim_cfg.forced_max_frame_height);
+  }
 
   // Create the encoder segmentation map and set all entries to 0
   aom_free(cpi->enc_seg.map);
   CHECK_MEM_ERROR(cm, cpi->enc_seg.map,
-                  aom_calloc(mi_params->mi_rows * mi_params->mi_cols, 1));
+                  aom_calloc(max_mi_rows * max_mi_cols, 1));
 
   // Create a map used for cyclic background refresh.
   if (cpi->cyclic_refresh) av1_cyclic_refresh_free(cpi->cyclic_refresh);
-  CHECK_MEM_ERROR(
-      cm, cpi->cyclic_refresh,
-      av1_cyclic_refresh_alloc(mi_params->mi_rows, mi_params->mi_cols));
+  CHECK_MEM_ERROR(cm, cpi->cyclic_refresh,
+                  av1_cyclic_refresh_alloc(max_mi_rows, max_mi_cols));
 
   // Create a map used to mark inactive areas.
   aom_free(cpi->active_map.map);
   CHECK_MEM_ERROR(cm, cpi->active_map.map,
-                  aom_calloc(mi_params->mi_rows * mi_params->mi_cols, 1));
+                  aom_calloc(max_mi_rows * max_mi_cols, 1));
 }
 
 static inline void alloc_obmc_buffers(OBMCBuffer *obmc_buffer,
@@ -365,6 +373,7 @@
 
   aom_free(cpi->mb_weber_stats);
   cpi->mb_weber_stats = NULL;
+  cpi->mb_weber_stats_alloc_size = 0;
 
   if (cpi->oxcf.enable_rate_guide_deltaq) {
     aom_free(cpi->prep_rate_estimates);
@@ -376,6 +385,7 @@
 
   aom_free(cpi->mb_delta_q);
   cpi->mb_delta_q = NULL;
+  cpi->mb_delta_q_alloc_size = 0;
 
 #if !CONFIG_REALTIME_ONLY
   av1_free_tpl_gop_stats(&cpi->extrc_tpl_gop_stats);
diff --git a/av1/encoder/encodetxb.c b/av1/encoder/encodetxb.c
index f0607b6..6066f9e 100644
--- a/av1/encoder/encodetxb.c
+++ b/av1/encoder/encodetxb.c
@@ -21,6 +21,7 @@
 #include "av1/encoder/bitstream.h"
 #include "av1/encoder/cost.h"
 #include "av1/encoder/encodeframe.h"
+#include "av1/encoder/encoder_utils.h"
 #include "av1/encoder/hash.h"
 #include "av1/encoder/rdopt.h"
 #include "av1/encoder/tokenize.h"
@@ -28,10 +29,18 @@
 void av1_alloc_txb_buf(AV1_COMP *cpi) {
   AV1_COMMON *cm = &cpi->common;
   CoeffBufferPool *coeff_buf_pool = &cpi->coeff_buffer_pool;
+  int mi_rows = cm->mi_params.mi_rows;
+  int mi_cols = cm->mi_params.mi_cols;
+  if (cpi->oxcf.frm_dim_cfg.forced_max_frame_width) {
+    mi_cols = size_in_mi(cpi->oxcf.frm_dim_cfg.forced_max_frame_width);
+  }
+  if (cpi->oxcf.frm_dim_cfg.forced_max_frame_height) {
+    mi_rows = size_in_mi(cpi->oxcf.frm_dim_cfg.forced_max_frame_height);
+  }
   const int num_sb_rows =
-      CEIL_POWER_OF_TWO(cm->mi_params.mi_rows, cm->seq_params->mib_size_log2);
+      CEIL_POWER_OF_TWO(mi_rows, cm->seq_params->mib_size_log2);
   const int num_sb_cols =
-      CEIL_POWER_OF_TWO(cm->mi_params.mi_cols, cm->seq_params->mib_size_log2);
+      CEIL_POWER_OF_TWO(mi_cols, cm->seq_params->mib_size_log2);
   const int size = num_sb_rows * num_sb_cols;
   const int num_planes = av1_num_planes(cm);
   const int subsampling_x = cm->seq_params->subsampling_x;
@@ -899,8 +908,11 @@
                                          int mi_col) {
   const AV1_COMMON *const cm = &cpi->common;
   const int mib_size_log2 = cm->seq_params->mib_size_log2;
-  const int stride =
-      CEIL_POWER_OF_TWO(cm->mi_params.mi_cols, cm->seq_params->mib_size_log2);
+  int mi_cols = cm->mi_params.mi_cols;
+  if (cpi->oxcf.frm_dim_cfg.forced_max_frame_width) {
+    mi_cols = size_in_mi(cpi->oxcf.frm_dim_cfg.forced_max_frame_width);
+  }
+  const int stride = CEIL_POWER_OF_TWO(mi_cols, cm->seq_params->mib_size_log2);
   const int offset =
       (mi_row >> mib_size_log2) * stride + (mi_col >> mib_size_log2);
   return cpi->coeff_buffer_base + offset;
diff --git a/av1/encoder/ethread.c b/av1/encoder/ethread.c
index d46b3ee..581294c 100644
--- a/av1/encoder/ethread.c
+++ b/av1/encoder/ethread.c
@@ -750,8 +750,9 @@
   return 1;
 }
 
-static int enc_worker_hook(void *arg1, void *unused) {
+static int enc_worker_hook(void *arg1, void *arg2) {
   EncWorkerData *const thread_data = (EncWorkerData *)arg1;
+  const int num_workers = (int)(intptr_t)arg2;
   AV1_COMP *const cpi = thread_data->cpi;
   MACROBLOCKD *const xd = &thread_data->td->mb.e_mbd;
   struct aom_internal_error_info *const error_info = &thread_data->error_info;
@@ -760,8 +761,6 @@
   const int tile_rows = cm->tiles.rows;
   int t;
 
-  (void)unused;
-
   xd->error_info = error_info;
 
   // The jmp_buf is valid only for the duration of the function that calls
@@ -784,8 +783,7 @@
     thread_data->td->pc_root = NULL;
   }
 
-  for (t = thread_data->start; t < tile_rows * tile_cols;
-       t += cpi->mt_info.num_workers) {
+  for (t = thread_data->start; t < tile_rows * tile_cols; t += num_workers) {
     int tile_row = t / tile_cols;
     int tile_col = t % tile_cols;
 
@@ -996,7 +994,7 @@
 
       AOM_CHECK_MEM_ERROR(
           &ppi->error, td->upsample_pred,
-          aom_memalign(16, (1 + is_highbitdepth) * ((MAX_SB_SIZE + 16) + 16) *
+          aom_memalign(16, (1 + is_highbitdepth) * (MAX_SB_SIZE + SUBPEL_TAPS) *
                                MAX_SB_SIZE * sizeof(*td->upsample_pred)));
 
       if (!is_first_pass && i < num_enc_workers) {
@@ -1039,12 +1037,10 @@
           }
         }
 
-        if (is_gradient_caching_for_hog_enabled(ppi->cpi)) {
-          const int plane_types = PLANE_TYPES >> ppi->seq_params.monochrome;
-          AOM_CHECK_MEM_ERROR(&ppi->error, td->pixel_gradient_info,
-                              aom_malloc(sizeof(*td->pixel_gradient_info) *
-                                         plane_types * MAX_SB_SQUARE));
-        }
+        const int plane_types = PLANE_TYPES >> ppi->seq_params.monochrome;
+        AOM_CHECK_MEM_ERROR(&ppi->error, td->pixel_gradient_info,
+                            aom_malloc(sizeof(*td->pixel_gradient_info) *
+                                       plane_types * MAX_SB_SQUARE));
 
         if (is_src_var_for_4x4_sub_blocks_caching_enabled(ppi->cpi)) {
           const BLOCK_SIZE sb_size = ppi->cpi->common.seq_params->sb_size;
@@ -1588,7 +1584,7 @@
 
     worker->hook = hook;
     worker->data1 = thread_data;
-    worker->data2 = NULL;
+    worker->data2 = (void *)(intptr_t)num_workers;
 
     thread_data->thread_id = i;
     // Set the starting tile for each thread.
diff --git a/av1/encoder/intra_mode_search.c b/av1/encoder/intra_mode_search.c
index db8d9c8..cad0cdc 100644
--- a/av1/encoder/intra_mode_search.c
+++ b/av1/encoder/intra_mode_search.c
@@ -998,6 +998,19 @@
                 intra_mode_info_cost_uv(cpi, x, mbmi, bsize, mode_cost);
     this_rd = RDCOST(x->rdmult, this_rate, tokenonly_rd_stats.dist);
 
+#if CONFIG_AV1_HIGHBITDEPTH
+    if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+      const int is_smooth_uv_mode =
+          (uv_mode == UV_SMOOTH_PRED || uv_mode == UV_SMOOTH_V_PRED ||
+           uv_mode == UV_SMOOTH_H_PRED);
+      if (is_smooth_uv_mode) {
+        if (this_rd < INT64_MAX) {
+          this_rd += (this_rd >> 2);
+        }
+      }
+    }
+#endif
+
     if (this_rd < best_rd) {
       best_mbmi = *mbmi;
       best_rd = this_rd;
diff --git a/av1/encoder/lookahead.c b/av1/encoder/lookahead.c
index ee2e065..7dcabf9 100644
--- a/av1/encoder/lookahead.c
+++ b/av1/encoder/lookahead.c
@@ -105,7 +105,7 @@
   int uv_height = src->uv_crop_height;
   int subsampling_x = src->subsampling_x;
   int subsampling_y = src->subsampling_y;
-  int larger_dimensions, new_dimensions;
+  int new_dimensions;
 
   assert(ctx->read_ctxs[ENCODE_STAGE].valid == 1);
   if (ctx->read_ctxs[ENCODE_STAGE].sz + ctx->max_pre_frames > ctx->max_sz)
@@ -122,31 +122,13 @@
                    height != buf->img.y_crop_height ||
                    uv_width != buf->img.uv_crop_width ||
                    uv_height != buf->img.uv_crop_height;
-  larger_dimensions =
-      width > buf->img.y_crop_width || height > buf->img.y_crop_height ||
-      uv_width > buf->img.uv_crop_width || uv_height > buf->img.uv_crop_height;
-  assert(!larger_dimensions || new_dimensions);
 
-  if (larger_dimensions) {
-    YV12_BUFFER_CONFIG new_img;
-    memset(&new_img, 0, sizeof(new_img));
-    if (aom_alloc_frame_buffer(&new_img, width, height, subsampling_x,
-                               subsampling_y, use_highbitdepth,
-                               AOM_BORDER_IN_PIXELS, 0, alloc_pyramid, 0))
+  if (new_dimensions) {
+    if (aom_realloc_frame_buffer(&buf->img, width, height, subsampling_x,
+                                 subsampling_y, use_highbitdepth,
+                                 AOM_BORDER_IN_PIXELS, 0, NULL, NULL, NULL,
+                                 alloc_pyramid, 0))
       return 1;
-    aom_free_frame_buffer(&buf->img);
-    buf->img = new_img;
-  } else if (new_dimensions) {
-    buf->img.y_width = src->y_width;
-    buf->img.y_height = src->y_height;
-    buf->img.uv_width = src->uv_width;
-    buf->img.uv_height = src->uv_height;
-    buf->img.y_crop_width = src->y_crop_width;
-    buf->img.y_crop_height = src->y_crop_height;
-    buf->img.uv_crop_width = src->uv_crop_width;
-    buf->img.uv_crop_height = src->uv_crop_height;
-    buf->img.subsampling_x = src->subsampling_x;
-    buf->img.subsampling_y = src->subsampling_y;
   }
   av1_copy_and_extend_frame(src, &buf->img);
 
diff --git a/av1/encoder/partition_search.c b/av1/encoder/partition_search.c
index b5f1d6a..c038838 100644
--- a/av1/encoder/partition_search.c
+++ b/av1/encoder/partition_search.c
@@ -637,8 +637,7 @@
   }
 #endif
 #if CONFIG_TUNE_VMAF
-  else if (cpi->oxcf.tune_cfg.tuning == AOM_TUNE_VMAF_WITHOUT_PREPROCESSING ||
-           cpi->oxcf.tune_cfg.tuning == AOM_TUNE_VMAF_MAX_GAIN ||
+  else if (cpi->oxcf.tune_cfg.tuning == AOM_TUNE_VMAF_MAX_GAIN ||
            cpi->oxcf.tune_cfg.tuning == AOM_TUNE_VMAF_NEG_MAX_GAIN) {
     av1_set_vmaf_rdmult(cpi, x, bsize, mi_row, mi_col, &x->rdmult);
   }
diff --git a/av1/encoder/pass2_strategy.c b/av1/encoder/pass2_strategy.c
index 7af5bcf..42e9186 100644
--- a/av1/encoder/pass2_strategy.c
+++ b/av1/encoder/pass2_strategy.c
@@ -4541,8 +4541,7 @@
       p_rc->active_best_quality[i] = cpi->common.quant_params.base_qindex;
 #if CONFIG_TUNE_VMAF
       if (cpi->vmaf_info.original_qindex != -1 &&
-          (cpi->oxcf.tune_cfg.tuning >= AOM_TUNE_VMAF_WITH_PREPROCESSING &&
-           cpi->oxcf.tune_cfg.tuning <= AOM_TUNE_VMAF_NEG_MAX_GAIN)) {
+          is_vmaf_tuning_mode(cpi->oxcf.tune_cfg.tuning)) {
         p_rc->active_best_quality[i] = cpi->vmaf_info.original_qindex;
       }
 #endif
@@ -4601,13 +4600,12 @@
       }
       twopass->extend_maxq -= 1;
       // Overshoot
-    } else if ((rc_cfg->over_shoot_pct < 100) &&
+    } else if ((rc_cfg->over_shoot_pct <= 100) &&
                (p_rc->rolling_actual_bits > p_rc->rolling_target_bits)) {
       int pct_error =
           ((p_rc->rolling_actual_bits - p_rc->rolling_target_bits) * 100) /
           p_rc->rolling_target_bits;
 
-      pct_error = clamp(pct_error, 0, 100);
       if ((pct_error >= rc_cfg->over_shoot_pct) &&
           (p_rc->rate_error_estimate < 0)) {
         twopass->extend_maxq += 1;
@@ -4633,13 +4631,21 @@
     // frame is unexpectedly almost perfectly predicted by the ARF or GF
     // but not very well predcited by the previous frame.
     if (!frame_is_kf_gf_arf(cpi) && !cpi->rc.is_src_frame_alt_ref) {
-      int fast_extra_thresh = rc->base_frame_target / HIGH_UNDERSHOOT_RATIO;
-      if (rc->projected_frame_size < fast_extra_thresh) {
-        p_rc->vbr_bits_off_target_fast +=
-            fast_extra_thresh - rc->projected_frame_size;
-        p_rc->vbr_bits_off_target_fast =
-            AOMMIN(p_rc->vbr_bits_off_target_fast,
-                   (4 * (int64_t)rc->avg_frame_bandwidth));
+#if CONFIG_AV1_HIGHBITDEPTH
+      if (cpi->common.seq_params->bit_depth > 8 &&
+          cpi->oxcf.algo_cfg.sharpness == 3 && p_rc->vbr_bits_off_target < 0) {
+        p_rc->vbr_bits_off_target_fast = 0;
+      } else
+#endif
+      {
+        int fast_extra_thresh = rc->base_frame_target / HIGH_UNDERSHOOT_RATIO;
+        if (rc->projected_frame_size < fast_extra_thresh) {
+          p_rc->vbr_bits_off_target_fast +=
+              fast_extra_thresh - rc->projected_frame_size;
+          p_rc->vbr_bits_off_target_fast =
+              AOMMIN(p_rc->vbr_bits_off_target_fast,
+                     (4 * (int64_t)rc->avg_frame_bandwidth));
+        }
       }
     }
 
diff --git a/av1/encoder/pickcdef.c b/av1/encoder/pickcdef.c
index 8ece9331..ed6db25 100644
--- a/av1/encoder/pickcdef.c
+++ b/av1/encoder/pickcdef.c
@@ -899,7 +899,7 @@
   // Allocate CDEF search context buffers.
   cdef_alloc_data(cm, cdef_search_ctx);
   // Frame level mse calculation.
-  if (cpi->mt_info.num_workers > 1) {
+  if (cpi->mt_info.num_mod_workers[MOD_CDEF_SEARCH] > 1) {
     av1_cdef_mse_calc_frame_mt(cpi);
   } else {
     cdef_mse_calc_frame(cdef_search_ctx, cm->error, adaptive_cdef_mode);
diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c
index f68c78f..03cb5c9 100644
--- a/av1/encoder/ratectrl.c
+++ b/av1/encoder/ratectrl.c
@@ -365,8 +365,9 @@
         LAYER_IDS_TO_IDX(svc->spatial_layer_id, i, svc->number_temporal_layers);
     LAYER_CONTEXT *lc = &svc->layer_context[layer];
     PRIMARY_RATE_CONTROL *lp_rc = &lc->p_rc;
-    lp_rc->bits_off_target +=
-        (int)round(lc->target_bandwidth / lc->framerate) - encoded_frame_size;
+    lp_rc->bits_off_target += (int64_t)saturate_cast_double_to_int(
+                                  round(lc->target_bandwidth / lc->framerate)) -
+                              encoded_frame_size;
     // Clip buffer level to maximum buffer size for the layer.
     lp_rc->bits_off_target =
         AOMMIN(lp_rc->bits_off_target, lp_rc->maximum_buffer_size);
@@ -2788,14 +2789,18 @@
       simulate_parallel_frame ? cpi->ppi->p_rc.temp_vbr_bits_off_target_fast
                               : p_rc->vbr_bits_off_target_fast;
 #endif
-  // Fast redistribution of bits arising from massive local undershoot.
-  // Don't do it for kf,arf,gf or overlay frames.
   if (!frame_is_kf_gf_arf(cpi) &&
 #if CONFIG_FPMT_TEST
       vbr_bits_off_target_fast &&
 #else
       p_rc->vbr_bits_off_target_fast &&
 #endif
+#if CONFIG_AV1_HIGHBITDEPTH
+      (cpi->common.seq_params->bit_depth > 8 &&
+               cpi->oxcf.algo_cfg.sharpness == 3
+           ? vbr_bits_off_target >= 0
+           : 1) &&
+#endif
       !rc->is_src_frame_alt_ref) {
     int64_t one_frame_bits = AOMMAX(rc->avg_frame_bandwidth, frame_target);
     int64_t fast_extra_bits;
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index b0ea574..053edda 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -644,8 +644,8 @@
 }
 #endif  // CONFIG_AV1_HIGHBITDEPTH
 
-static void get_variance_stats(const MACROBLOCK *x, int64_t *src_var,
-                               int64_t *rec_var) {
+void av1_get_variance_stats(const MACROBLOCK *x, int64_t *src_var,
+                            int64_t *rec_var) {
 #if CONFIG_AV1_HIGHBITDEPTH
   if (is_cur_buf_hbd(&x->e_mbd)) {
     get_variance_stats_hbd(x, src_var, rec_var);
@@ -665,6 +665,54 @@
   *src_var = aom_calc_variance_stat(p->src.buf, p->src.stride, bw, bh);
 }
 
+int av1_is_skip_txfm_penalized(const AV1_COMP *cpi, const MACROBLOCK *x,
+                               BLOCK_SIZE bsize) {
+#if CONFIG_AV1_HIGHBITDEPTH
+  const MACROBLOCKD *xd = &x->e_mbd;
+  if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3 &&
+      !frame_is_kf_gf_arf(cpi)) {
+    int64_t src_var, rec_var;
+    av1_get_variance_stats(x, &src_var, &rec_var);
+    if (src_var > rec_var) {
+      const int num_pixels = block_size_wide[bsize] * block_size_high[bsize];
+      const int64_t src_var_per_px = src_var / num_pixels;
+      if (src_var_per_px >= 0) return 1;
+    }
+  }
+#else
+  (void)cpi;
+  (void)x;
+  (void)bsize;
+#endif
+  return 0;
+}
+
+void av1_get_tx_skip_dist(const AV1_COMP *cpi, const MACROBLOCK *x,
+                          BLOCK_SIZE bsize, int64_t dist, int64_t sse,
+                          int64_t *no_skip_dist, int64_t *skip_dist) {
+  *no_skip_dist = dist;
+  *skip_dist = sse;
+#if CONFIG_AV1_HIGHBITDEPTH
+  const MACROBLOCKD *xd = &x->e_mbd;
+  if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3 &&
+      !frame_is_kf_gf_arf(cpi)) {
+    int64_t src_var, rec_var;
+    av1_get_variance_stats(x, &src_var, &rec_var);
+    if (src_var > rec_var) {
+      int64_t var_offset = src_var - rec_var;
+      const int num_pixels = block_size_wide[bsize] * block_size_high[bsize];
+      const int64_t src_var_per_px = src_var / num_pixels;
+      *no_skip_dist += var_offset;
+      *skip_dist += (src_var_per_px >= 0) ? (var_offset * 4) : var_offset;
+    }
+  }
+#else
+  (void)cpi;
+  (void)x;
+  (void)bsize;
+#endif
+}
+
 static void adjust_rdcost(const AV1_COMP *cpi, const MACROBLOCK *x,
                           RD_STATS *rd_cost, bool is_inter_pred) {
   if ((cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ ||
@@ -688,12 +736,56 @@
     return;
   }
 
+#if CONFIG_AV1_HIGHBITDEPTH
+  const MACROBLOCKD *xd = &x->e_mbd;
+  if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+    if (frame_is_intra_only(&cpi->common)) return;
+
+    int64_t src_var, rec_var;
+    av1_get_variance_stats(x, &src_var, &rec_var);
+
+    int64_t var_offset = (src_var > rec_var) ? (src_var - rec_var) : 0;
+
+    const MB_MODE_INFO *mbmi = xd->mi[0];
+    const int is_smooth_intra_mode =
+        (mbmi->mode == DC_PRED || mbmi->mode == SMOOTH_PRED ||
+         mbmi->mode == SMOOTH_V_PRED || mbmi->mode == SMOOTH_H_PRED ||
+         mbmi->mode == PAETH_PRED);
+
+    const int is_skip_txfm = mbmi->skip_txfm || rd_cost->skip_txfm;
+    const int num_pixels =
+        block_size_wide[mbmi->bsize] * block_size_high[mbmi->bsize];
+    const int64_t src_var_per_px = src_var / num_pixels;
+
+    if (var_offset > 0 &&
+        ((is_skip_txfm && src_var_per_px >= 0) || is_smooth_intra_mode ||
+         is_interintra_mode(mbmi) || has_second_ref(mbmi))) {
+      var_offset *= 4;
+    }
+
+    if (is_inter_pred && !has_second_ref(mbmi)) {
+      const int mv_mag =
+          abs(mbmi->mv[0].as_mv.row) + abs(mbmi->mv[0].as_mv.col);
+      if (mv_mag > 0 && src_var_per_px < 64) {
+        var_offset += (int64_t)mv_mag * (64 - src_var_per_px);
+      }
+    }
+
+    if (var_offset <= 0) return;
+
+    rd_cost->dist += var_offset;
+
+    rd_cost->rdcost = RDCOST(x->rdmult, rd_cost->rate, rd_cost->dist);
+    return;
+  }
+#endif
+
   if (cpi->oxcf.algo_cfg.sharpness != 3) return;
 
   if (frame_is_kf_gf_arf(cpi)) return;
 
   int64_t src_var, rec_var;
-  get_variance_stats(x, &src_var, &rec_var);
+  av1_get_variance_stats(x, &src_var, &rec_var);
 
   if (src_var <= rec_var) return;
 
@@ -713,12 +805,54 @@
     return;
   }
 
+#if CONFIG_AV1_HIGHBITDEPTH
+  const MACROBLOCKD *xd = &x->e_mbd;
+  if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+    if (frame_is_intra_only(&cpi->common)) return;
+
+    int64_t src_var, rec_var;
+    av1_get_variance_stats(x, &src_var, &rec_var);
+
+    int64_t var_offset = (src_var > rec_var) ? (src_var - rec_var) : 0;
+
+    const MB_MODE_INFO *mbmi = xd->mi[0];
+    const int is_smooth_intra_mode =
+        (mbmi->mode == DC_PRED || mbmi->mode == SMOOTH_PRED ||
+         mbmi->mode == SMOOTH_V_PRED || mbmi->mode == SMOOTH_H_PRED ||
+         mbmi->mode == PAETH_PRED);
+
+    const int is_skip_txfm = mbmi->skip_txfm;
+    const int num_pixels =
+        block_size_wide[mbmi->bsize] * block_size_high[mbmi->bsize];
+    const int64_t src_var_per_px = src_var / num_pixels;
+
+    if (var_offset > 0 &&
+        ((is_skip_txfm && src_var_per_px >= 0) || is_smooth_intra_mode ||
+         is_interintra_mode(mbmi) || has_second_ref(mbmi))) {
+      var_offset *= 4;
+    }
+
+    if (is_inter_pred && !has_second_ref(mbmi)) {
+      const int mv_mag =
+          abs(mbmi->mv[0].as_mv.row) + abs(mbmi->mv[0].as_mv.col);
+      if (mv_mag > 0 && src_var_per_px < 64) {
+        var_offset += (int64_t)mv_mag * (64 - src_var_per_px);
+      }
+    }
+
+    if (var_offset <= 0) return;
+
+    *rd_cost += RDCOST(x->rdmult, 0, var_offset);
+    return;
+  }
+#endif
+
   if (cpi->oxcf.algo_cfg.sharpness != 3) return;
 
   if (frame_is_kf_gf_arf(cpi)) return;
 
   int64_t src_var, rec_var;
-  get_variance_stats(x, &src_var, &rec_var);
+  av1_get_variance_stats(x, &src_var, &rec_var);
 
   if (src_var <= rec_var) return;
 
@@ -1712,6 +1846,8 @@
                               rd_stats_uv, mbmi);
       }
       mbmi->skip_txfm = 0;
+      rd_stats->skip_txfm = 0;
+      if (rd_stats_y) rd_stats_y->skip_txfm = 0;
       increase_motion_mode_rdstats(cpi, mbmi, rd_stats, NULL, NULL);
 
     } else {
@@ -3853,14 +3989,18 @@
         this_sse = rd_stats_y.sse + rd_stats_uv.sse * 15 / 16;
       }
 
+      int64_t no_skip_dist, skip_sse;
+      av1_get_tx_skip_dist(cpi, x, mbmi->bsize, this_dist, this_sse,
+                           &no_skip_dist, &skip_sse);
+
       if (is_inter_mode(mbmi->mode) &&
           (!cpi->oxcf.algo_cfg.sharpness || !comp_pred) &&
           RDCOST(x->rdmult,
                  mode_costs->skip_txfm_cost[skip_ctx][0] + rd_stats_y.rate +
                      rd_stats_uv.rate,
-                 this_dist) > RDCOST(x->rdmult,
-                                     mode_costs->skip_txfm_cost[skip_ctx][1],
-                                     this_sse)) {
+                 no_skip_dist) > RDCOST(x->rdmult,
+                                        mode_costs->skip_txfm_cost[skip_ctx][1],
+                                        skip_sse)) {
         skip_blk = 1;
         rd_stats_y.rate = mode_costs->skip_txfm_cost[skip_ctx][1];
         rd_stats_uv.rate = 0;
@@ -3875,7 +4015,23 @@
                                    &rd_stats_uv);
       int this_rate = rd_stats.rate + rd_stats_y.rate + rd_stats_uv.rate -
                       winner_rate_y - winner_rate_uv;
-      int64_t this_rd = RDCOST(x->rdmult, this_rate, this_dist);
+      int64_t this_rd;
+#if CONFIG_AV1_HIGHBITDEPTH
+      if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+        RD_STATS tmp_rd_stats;
+        av1_init_rd_stats(&tmp_rd_stats);
+        tmp_rd_stats.rate = this_rate;
+        tmp_rd_stats.dist = this_dist;
+        tmp_rd_stats.sse = this_sse;
+        tmp_rd_stats.skip_txfm = skip_blk;
+        adjust_rdcost(cpi, x, &tmp_rd_stats, is_inter_mode(mbmi->mode));
+        this_rd = tmp_rd_stats.rdcost;
+        this_dist = tmp_rd_stats.dist;
+      } else
+#endif
+      {
+        this_rd = RDCOST(x->rdmult, this_rate, this_dist);
+      }
       if (best_rd > this_rd) {
         *best_mbmode = *mbmi;
         *best_mode_index = winner_mode_index;
@@ -4704,6 +4860,7 @@
   mbmi->motion_mode = SIMPLE_TRANSLATION;
   mbmi->interintra_mode = (INTERINTRA_MODE)(II_DC_PRED - 1);
   set_default_interp_filters(mbmi, cm->features.interp_filter);
+  mbmi->skip_txfm = 0;
 }
 
 static inline void collect_single_states(MACROBLOCK *x,
@@ -5663,7 +5820,13 @@
     top_intra_model_rd[i] = INT64_MAX;
   }
 
-  if (cpi->oxcf.algo_cfg.sharpness) {
+#if CONFIG_AV1_HIGHBITDEPTH
+  const int allow_larger_intra =
+      xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3;
+#else
+  const int allow_larger_intra = 0;
+#endif
+  if (cpi->oxcf.algo_cfg.sharpness && !allow_larger_intra) {
     int bh = mi_size_high[bsize];
     int bw = mi_size_wide[bsize];
     if (bh > 4 || bw > 4) return;
@@ -6215,6 +6378,7 @@
     const int comp_pred = second_ref_frame > INTRA_FRAME;
 
     txfm_info->skip_txfm = 0;
+    mbmi->skip_txfm = 0;
     sf_args.num_single_modes_processed += is_single_pred;
 #if CONFIG_COLLECT_COMPONENT_TIMING
     start_timing(cpi, skip_inter_mode_time);
@@ -6300,9 +6464,6 @@
       ref_frame_rd[ref_frame] = this_rd;
     }
 
-    adjust_cost(cpi, x, &this_rd, /*is_inter_pred=*/true);
-    adjust_rdcost(cpi, x, &rd_stats, /*is_inter_pred=*/true);
-
     // Did this mode help, i.e., is it the new best mode
     if (this_rd < search_state.best_rd) {
       assert(IMPLIES(comp_pred,
diff --git a/av1/encoder/rdopt_utils.h b/av1/encoder/rdopt_utils.h
index 04d46d0..2082163 100644
--- a/av1/encoder/rdopt_utils.h
+++ b/av1/encoder/rdopt_utils.h
@@ -901,6 +901,16 @@
   return txfm_rd_gate_level[TX_SEARCH_DEFAULT];
 }
 
+void av1_get_variance_stats(const MACROBLOCK *x, int64_t *src_var,
+                            int64_t *rec_var);
+
+void av1_get_tx_skip_dist(const struct AV1_COMP *cpi, const MACROBLOCK *x,
+                          BLOCK_SIZE bsize, int64_t dist, int64_t sse,
+                          int64_t *no_skip_dist, int64_t *skip_dist);
+
+int av1_is_skip_txfm_penalized(const struct AV1_COMP *cpi, const MACROBLOCK *x,
+                               BLOCK_SIZE bsize);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/av1/encoder/reconinter_enc.c b/av1/encoder/reconinter_enc.c
index f169721..825e17c 100644
--- a/av1/encoder/reconinter_enc.c
+++ b/av1/encoder/reconinter_enc.c
@@ -491,7 +491,7 @@
         av1_get_interp_filter_subpel_kernel(filter, subpel_y_q3 << 1);
     const int intermediate_height =
         (((height - 1) * 8 + subpel_y_q3) >> 3) + filter->taps;
-    assert(intermediate_height <= (MAX_SB_SIZE * 2 + 16) + 16);
+    assert(intermediate_height < (MAX_SB_SIZE + SUBPEL_TAPS));
     aom_convolve8_horiz_c(ref - ref_stride * ((filter->taps >> 1) - 1),
                           ref_stride, temp, MAX_SB_SIZE, kernel_x, 16, NULL, -1,
                           width, intermediate_height);
@@ -574,7 +574,7 @@
         av1_get_interp_filter_subpel_kernel(filter, subpel_y_q3 << 1);
     const int intermediate_height =
         (((height - 1) * 8 + subpel_y_q3) >> 3) + filter->taps;
-    assert(intermediate_height <= (MAX_SB_SIZE * 2 + 16) + 16);
+    assert(intermediate_height < (MAX_SB_SIZE + SUBPEL_TAPS));
     aom_highbd_convolve8_horiz_c(
         CONVERT_TO_BYTEPTR(ref - ref_stride * ((filter->taps >> 1) - 1)),
         ref_stride, CONVERT_TO_BYTEPTR(temp), MAX_SB_SIZE, kernel_x, 16, NULL,
diff --git a/av1/encoder/svc_layercontext.c b/av1/encoder/svc_layercontext.c
index 8b8d4c5..af22f8f 100644
--- a/av1/encoder/svc_layercontext.c
+++ b/av1/encoder/svc_layercontext.c
@@ -141,8 +141,8 @@
       lp_rc->buffer_level =
           AOMMIN(lp_rc->buffer_level, lp_rc->maximum_buffer_size);
       lc->framerate = cpi->framerate / lc->framerate_factor;
-      lrc->avg_frame_bandwidth =
-          (int)round(lc->target_bandwidth / lc->framerate);
+      lrc->avg_frame_bandwidth = saturate_cast_double_to_int(
+          round(lc->target_bandwidth / lc->framerate));
       lrc->max_frame_bandwidth = rc->max_frame_bandwidth;
       lrc->rtc_external_ratectrl = rc->rtc_external_ratectrl;
       lrc->worst_quality = av1_quantizer_to_qindex(lc->max_q);
@@ -203,11 +203,12 @@
         cpi->framerate / lcprev->framerate_factor;
     const int64_t prev_layer_target_bandwidth = lcprev->layer_target_bitrate;
     if (lc->framerate > prev_layer_framerate) {
-      lc->avg_frame_size =
-          (int)round((lc->target_bandwidth - prev_layer_target_bandwidth) /
-                     (lc->framerate - prev_layer_framerate));
+      lc->avg_frame_size = saturate_cast_double_to_int(
+          round((lc->target_bandwidth - prev_layer_target_bandwidth) /
+                (lc->framerate - prev_layer_framerate)));
     } else {
-      lc->avg_frame_size = (int)round(lc->target_bandwidth / lc->framerate);
+      lc->avg_frame_size = saturate_cast_double_to_int(
+          round(lc->target_bandwidth / lc->framerate));
     }
   }
 }
diff --git a/av1/encoder/temporal_filter.c b/av1/encoder/temporal_filter.c
index be7ff38..97be8fc 100644
--- a/av1/encoder/temporal_filter.c
+++ b/av1/encoder/temporal_filter.c
@@ -382,6 +382,27 @@
     *is_dc_diff_large = 50 * error < sse;
     if (src_var <= 2 * (int64_t)distortion) *is_low_cntras = 1;
 
+#if CONFIG_AV1_HIGHBITDEPTH
+    const MACROBLOCKD *xd = &mb->e_mbd;
+    if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+      // Favor zero motion vector (0,0) for stationary/near-stationary regions
+      // when zero-motion error is close to the best found error or sufficiently
+      // small.
+      unsigned int zero_sse;
+      unsigned int zero_error = cpi->ppi->fn_ptr[block_size].vf(
+          ref_frame->y_buffer + y_offset, y_stride,
+          frame_to_filter->y_buffer + y_offset, y_stride, &zero_sse);
+      if (zero_error <= error + (error >> 4) || (zero_error / mb_pels) < 16) {
+        best_mv.as_mv = kZeroMv;
+        block_mv = kZeroMv;
+        *ref_mv = kZeroMv;
+        error = zero_error;
+        distortion = (int)zero_error;
+        block_mse = DIVIDE_AND_ROUND(error, mb_pels);
+      }
+    }
+#endif
+
     // On 4 mid-blocks in the 64x64 tf block.
     if (allow_me_for_sub_blks) {
       // midblock_size is 32x32, which corresponds to each 32x32 block in the
@@ -1092,7 +1113,12 @@
     // partition only if the difference between max and min source variance of
     // 4x4 blocks is greater than a threshold (which is derived empirically).
     bool allow_me_for_sub_blks = true;
-    if (cpi->sf.hl_sf.allow_sub_blk_me_in_tf) {
+    // In high bit-depth sharpness mode, disable sub-block motion estimation to
+    // avoid inconsistent filtering across sub-partitions and preserve
+    // sharpness.
+    if (mbd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+      allow_me_for_sub_blks = false;
+    } else if (cpi->sf.hl_sf.allow_sub_blk_me_in_tf) {
       const int is_hbd = is_frame_high_bitdepth(frame_to_filter);
       // Initialize minimum variance to a large value and maximum variance to 0.
       double blk_4x4_var_min = DBL_MAX;
@@ -1139,7 +1165,7 @@
         filter_strength = AOMMIN(filter_strength, 1);
 
       if (cpi->oxcf.algo_cfg.sharpness == 3 && is_low_cntras)
-        filter_strength = AOMMIN(filter_strength, 3);
+        filter_strength = AOMMIN(filter_strength, mbd->bd > 8 ? 0 : 3);
 
       // Perform weighted averaging.
       if (frame == filter_frame_idx) {  // Frame to be filtered.
diff --git a/av1/encoder/tpl_model.c b/av1/encoder/tpl_model.c
index ba54d55..34cf2bf 100644
--- a/av1/encoder/tpl_model.c
+++ b/av1/encoder/tpl_model.c
@@ -854,6 +854,20 @@
           cpi, x, src_mb_buffer, ref_mb, src_stride, ref_stride, src_width,
           ref_width, bsize, center_mvs[idx].mv.as_mv, &this_mv);
 
+#if CONFIG_AV1_HIGHBITDEPTH
+      if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+        // Bias towards (0,0) / short motion vectors in stationary background.
+        // Require a noticeable SAD improvement before replacing (0,0) with a
+        // non-zero MV.
+        if (bestsme != UINT32_MAX && best_rfidx_mv.as_int == 0 &&
+            this_mv.as_int != 0) {
+          if (thissme + (thissme >> 4) >= bestsme) {
+            continue;
+          }
+        }
+      }
+#endif
+
       if (thissme < bestsme) {
         bestsme = thissme;
         best_rfidx_mv = this_mv;
@@ -1724,7 +1738,7 @@
     if (frame_update_type == LF_UPDATE) {
       *pframe_qindex = gf_group->q_val[gf_index];
       leaf_frame_qindex = *pframe_qindex;
-      if (cpi->oxcf.rc_cfg.mode == AOM_VBR) {
+      if (cpi->oxcf.rc_cfg.mode == AOM_VBR || cpi->oxcf.rc_cfg.mode == AOM_Q) {
         const int bit_depth = cm->seq_params->bit_depth;
         const double q_val = av1_convert_qindex_to_q(*pframe_qindex, bit_depth);
         const double qindex_ratio = (double)(*pframe_qindex) / MAXQ;
diff --git a/av1/encoder/tune_vmaf.c b/av1/encoder/tune_vmaf.c
index 7784202..981033d 100644
--- a/av1/encoder/tune_vmaf.c
+++ b/av1/encoder/tune_vmaf.c
@@ -402,45 +402,6 @@
   aom_free_frame_buffer(&blurred);
 }
 
-void av1_vmaf_frame_preprocessing(AV1_COMP *const cpi,
-                                  const YV12_BUFFER_CONFIG *const source) {
-  const AV1_COMMON *const cm = &cpi->common;
-  const int bit_depth = cpi->td.mb.e_mbd.bd;
-  const int width = source->y_width;
-  const int height = source->y_height;
-
-  YV12_BUFFER_CONFIG source_extended, blurred;
-  memset(&source_extended, 0, sizeof(source_extended));
-  memset(&blurred, 0, sizeof(blurred));
-  aom_alloc_frame_buffer(
-      &source_extended, width, height, source->subsampling_x,
-      source->subsampling_y, cm->seq_params->use_highbitdepth,
-      cpi->oxcf.border_in_pixels, cm->features.byte_alignment, false, 0);
-  aom_alloc_frame_buffer(
-      &blurred, width, height, source->subsampling_x, source->subsampling_y,
-      cm->seq_params->use_highbitdepth, cpi->oxcf.border_in_pixels,
-      cm->features.byte_alignment, false, 0);
-
-  av1_copy_and_extend_frame(source, &source_extended);
-  gaussian_blur(bit_depth, &source_extended, &blurred);
-  aom_free_frame_buffer(&source_extended);
-
-  const GF_GROUP *const gf_group = &cpi->ppi->gf_group;
-  const int layer_depth =
-      AOMMIN(gf_group->layer_depth[cpi->gf_frame_index], MAX_ARF_LAYERS - 1);
-  const double last_frame_unsharp_amount =
-      get_layer_value(cpi->vmaf_info.last_frame_unsharp_amount, layer_depth);
-
-  const double best_frame_unsharp_amount = find_best_frame_unsharp_amount(
-      cpi, source, &blurred, last_frame_unsharp_amount, 0.05, 20, 1.01);
-
-  cpi->vmaf_info.last_frame_unsharp_amount[layer_depth] =
-      best_frame_unsharp_amount;
-
-  unsharp(cpi, source, &blurred, source, best_frame_unsharp_amount);
-  aom_free_frame_buffer(&blurred);
-}
-
 void av1_vmaf_blk_preprocessing(AV1_COMP *const cpi,
                                 const YV12_BUFFER_CONFIG *const source) {
   const AV1_COMMON *const cm = &cpi->common;
diff --git a/av1/encoder/tune_vmaf.h b/av1/encoder/tune_vmaf.h
index 7e01435..a79f90f 100644
--- a/av1/encoder/tune_vmaf.h
+++ b/av1/encoder/tune_vmaf.h
@@ -45,9 +45,6 @@
 void av1_vmaf_blk_preprocessing(struct AV1_COMP *cpi,
                                 const YV12_BUFFER_CONFIG *source);
 
-void av1_vmaf_frame_preprocessing(struct AV1_COMP *cpi,
-                                  const YV12_BUFFER_CONFIG *source);
-
 void av1_vmaf_neg_preprocessing(struct AV1_COMP *cpi,
                                 const YV12_BUFFER_CONFIG *source);
 
diff --git a/av1/encoder/tx_search.c b/av1/encoder/tx_search.c
index 70e862c..6281191 100644
--- a/av1/encoder/tx_search.c
+++ b/av1/encoder/tx_search.c
@@ -190,12 +190,20 @@
 // Uses simple features on top of DCT coefficients to quickly predict
 // whether optimal RD decision is to skip encoding the residual.
 // The sse value is stored in dist.
-static int predict_skip_txfm(MACROBLOCK *x, BLOCK_SIZE bsize, int64_t *dist,
+static int predict_skip_txfm(const AV1_COMP *cpi, MACROBLOCK *x,
+                             BLOCK_SIZE bsize, int64_t *dist,
                              int reduced_tx_set) {
+  const MACROBLOCKD *xd = &x->e_mbd;
+#if CONFIG_AV1_HIGHBITDEPTH
+  if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+    if (av1_is_skip_txfm_penalized(cpi, x, bsize)) return 0;
+  }
+#else
+  (void)cpi;
+#endif
   const TxfmSearchParams *txfm_params = &x->txfm_search_params;
   const int bw = block_size_wide[bsize];
   const int bh = block_size_high[bsize];
-  const MACROBLOCKD *xd = &x->e_mbd;
   const int16_t dc_q = av1_dc_quant_QTX(x->qindex, 0, xd->bd);
 
   *dist = av1_pixel_diff_dist(x, 0, 0, 0, bsize, bsize, NULL);
@@ -2953,13 +2961,33 @@
   }
   // Check if forcing the block to skip transform leads to smaller RD cost.
   if (is_inter && !rd_stats->skip_txfm && !xd->lossless[mbmi->segment_id]) {
-    int64_t temp_skip_txfm_rd =
-        RDCOST(x->rdmult, skip_txfm_rate, rd_stats->sse);
-    if (temp_skip_txfm_rd <= rd) {
-      rd = temp_skip_txfm_rd;
-      rd_stats->rate = 0;
-      rd_stats->dist = rd_stats->sse;
-      rd_stats->skip_txfm = 1;
+#if CONFIG_AV1_HIGHBITDEPTH
+    if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+      int64_t no_skip_dist, skip_dist;
+      av1_get_tx_skip_dist(cpi, x, bs, rd_stats->dist, rd_stats->sse,
+                           &no_skip_dist, &skip_dist);
+      const int64_t temp_skip_txfm_rd =
+          RDCOST(x->rdmult, skip_txfm_rate, skip_dist);
+      const int64_t temp_no_skip_txfm_rd =
+          RDCOST(x->rdmult, rd_stats->rate + no_skip_txfm_rate + tx_size_rate,
+                 no_skip_dist);
+      if (temp_skip_txfm_rd <= temp_no_skip_txfm_rd) {
+        rd = temp_skip_txfm_rd;
+        rd_stats->rate = 0;
+        rd_stats->dist = rd_stats->sse;
+        rd_stats->skip_txfm = 1;
+      }
+    } else
+#endif
+    {
+      const int64_t temp_skip_txfm_rd =
+          RDCOST(x->rdmult, skip_txfm_rate, rd_stats->sse);
+      if (temp_skip_txfm_rd <= rd) {
+        rd = temp_skip_txfm_rd;
+        rd_stats->rate = 0;
+        rd_stats->dist = rd_stats->sse;
+        rd_stats->skip_txfm = 1;
+      }
     }
   }
 
@@ -3600,7 +3628,7 @@
   // context and terminate early.
   int64_t dist;
   if (txfm_params->skip_txfm_level &&
-      predict_skip_txfm(x, bsize, &dist,
+      predict_skip_txfm(cpi, x, bsize, &dist,
                         cpi->common.features.reduced_tx_set_used)) {
     set_skip_txfm(x, rd_stats, bsize, dist);
     // Save the RD search results into mb_rd_record.
@@ -3673,7 +3701,7 @@
   int64_t dist;
   if (tx_params->skip_txfm_level && is_inter &&
       !xd->lossless[mbmi->segment_id] &&
-      predict_skip_txfm(x, bs, &dist,
+      predict_skip_txfm(cpi, x, bs, &dist,
                         cpi->common.features.reduced_tx_set_used)) {
     // Populate rdstats as per skip decision
     set_skip_txfm(x, rd_stats, bs, dist);
@@ -3874,14 +3902,23 @@
 
   int choose_skip_txfm = rd_stats->skip_txfm;
   if (!choose_skip_txfm && !xd->lossless[mbmi->segment_id]) {
+    int64_t no_skip_dist = rd_stats->dist;
+    int64_t skip_dist = rd_stats->sse;
+#if CONFIG_AV1_HIGHBITDEPTH
+    if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+      av1_get_tx_skip_dist(cpi, x, bsize, rd_stats->dist, rd_stats->sse,
+                           &no_skip_dist, &skip_dist);
+    }
+#endif
     const int64_t rdcost_no_skip_txfm = RDCOST(
         x->rdmult, rd_stats_y->rate + rd_stats_uv->rate + skip_txfm_cost[0],
-        rd_stats->dist);
+        no_skip_dist);
     const int64_t rdcost_skip_txfm =
-        RDCOST(x->rdmult, skip_txfm_cost[1], rd_stats->sse);
+        RDCOST(x->rdmult, skip_txfm_cost[1], skip_dist);
     if (rdcost_no_skip_txfm >= rdcost_skip_txfm) choose_skip_txfm = 1;
   }
   if (choose_skip_txfm) {
+    const int naturally_skip = rd_stats->skip_txfm;
     rd_stats_y->rate = 0;
     rd_stats_uv->rate = 0;
     rd_stats->rate = mode_rate + skip_txfm_cost[1];
@@ -3889,13 +3926,15 @@
     rd_stats_y->dist = rd_stats_y->sse;
     rd_stats_uv->dist = rd_stats_uv->sse;
     mbmi->skip_txfm = 1;
-    if (rd_stats->skip_txfm) {
+    rd_stats->skip_txfm = 1;
+    if (naturally_skip) {
       const int64_t tmprd = RDCOST(x->rdmult, rd_stats->rate, rd_stats->dist);
       if (tmprd > ref_best_rd) return 0;
     }
   } else {
     rd_stats->rate += skip_txfm_cost[0];
     mbmi->skip_txfm = 0;
+    rd_stats->skip_txfm = 0;
   }
 
   return 1;
diff --git a/av1/encoder/x86/reconinter_enc_sse2.c b/av1/encoder/x86/reconinter_enc_sse2.c
index e67ac1b..55a3ee9 100644
--- a/av1/encoder/x86/reconinter_enc_sse2.c
+++ b/av1/encoder/x86/reconinter_enc_sse2.c
@@ -111,7 +111,7 @@
     uint8_t *temp_start_vert = temp + MAX_SB_SIZE * ((filter->taps >> 1) - 1);
     int intermediate_height =
         (((height - 1) * 8 + subpel_y_q3) >> 3) + filter_taps;
-    assert(intermediate_height <= (MAX_SB_SIZE * 2 + 16) + 16);
+    assert(intermediate_height < (MAX_SB_SIZE + SUBPEL_TAPS));
     aom_convolve8_horiz(ref_start, ref_stride, temp_start_horiz, MAX_SB_SIZE,
                         kernel_x, 16, NULL, -1, width, intermediate_height);
     aom_convolve8_vert(temp_start_vert, MAX_SB_SIZE, comp_pred, width, NULL, -1,
@@ -175,6 +175,17 @@
     aom_highbd_convolve8_vert(ref8, ref_stride, comp_pred8, width, NULL, -1,
                               kernel, 16, width, height, bd);
   } else {
+    // The src and dst parameters of 'aom_highbd_convolve8_vert()' call are the
+    // same buffer ('comp_pred8') although they start from different offsets in
+    // the buffer. For any given row 'y', the 8-tap vertical filter reads a
+    // window of input rows from 'y-3' to 'y+4' (stride = MAX_SB_SIZE) and the
+    // result is written to row 'y-3' (stride = width) of the `comp_pred8`
+    // buffer. Since 'width <= MAX_SB_SIZE', output is written only after its
+    // corresponding input has already been read, eliminating any risk of
+    // overwriting data required for future iterations. The function
+    // 'aom_highbd_convolve8_vert()' must process the rows from top to bottom in
+    // increading order of row index, otherwise it will break the in-place
+    // processing of 'comp_pred8'.
     uint16_t *temp = CONVERT_TO_SHORTPTR(comp_pred8);
     const uint16_t *ref = CONVERT_TO_SHORTPTR(ref8);
     const int16_t *const kernel_x =
@@ -188,7 +199,7 @@
     uint16_t *temp_start_vert = temp + MAX_SB_SIZE * ((filter->taps >> 1) - 1);
     const int intermediate_height =
         (((height - 1) * 8 + subpel_y_q3) >> 3) + filter_taps;
-    assert(intermediate_height <= (MAX_SB_SIZE * 2 + 16) + 16);
+    assert(intermediate_height < (MAX_SB_SIZE + SUBPEL_TAPS));
     aom_highbd_convolve8_horiz(CONVERT_TO_BYTEPTR(ref_start), ref_stride,
                                CONVERT_TO_BYTEPTR(temp_start_horiz),
                                MAX_SB_SIZE, kernel_x, 16, NULL, -1, width,
diff --git a/av1/ratectrl_rtc.cc b/av1/ratectrl_rtc.cc
index 238484f..966f569 100644
--- a/av1/ratectrl_rtc.cc
+++ b/av1/ratectrl_rtc.cc
@@ -63,6 +63,7 @@
   config->min_quantizers[0] = config->min_quantizer;
   config->scaling_factor_num[0] = 1;
   config->scaling_factor_den[0] = 1;
+  config->bit_depth = 8;
 }
 
 }  // namespace
@@ -156,8 +157,10 @@
   AV1_COMMON *cm = &cpi_->common;
   AV1EncoderConfig *oxcf = &cpi_->oxcf;
   RATE_CONTROL *const rc = &cpi_->rc;
-  cm->seq_params->profile = PROFILE_0;
-  cm->seq_params->bit_depth = AOM_BITS_8;
+  const int bit_depth = rc_cfg.bit_depth == 0 ? 8 : rc_cfg.bit_depth;
+  if (bit_depth != 8 && bit_depth != 10 && bit_depth != 12) return false;
+  cm->seq_params->profile = (bit_depth == 12) ? PROFILE_2 : PROFILE_0;
+  cm->seq_params->bit_depth = static_cast<aom_bit_depth_t>(bit_depth);
   cm->show_frame = 1;
   oxcf->profile = cm->seq_params->profile;
   oxcf->mode = REALTIME;
@@ -171,7 +174,7 @@
         ceil(cpi_->framerate * rc_cfg.max_consec_drop_ms / 1000));
   }
   cpi_->svc.framedrop_mode = AOM_FULL_SUPERFRAME_DROP;
-  oxcf->tool_cfg.bit_depth = AOM_BITS_8;
+  oxcf->tool_cfg.bit_depth = cm->seq_params->bit_depth;
   oxcf->tool_cfg.superblock_size = AOM_SUPERBLOCK_SIZE_DYNAMIC;
   oxcf->algo_cfg.loopfilter_control = LOOPFILTER_ALL;
   cm->current_frame.frame_number = 0;
@@ -207,6 +210,12 @@
     rc_is_valid_ = false;
     return false;
   }
+  if (rc_cfg.bit_depth != 0 &&
+      rc_cfg.bit_depth !=
+          static_cast<int>(cpi_->common.seq_params->bit_depth)) {
+    rc_is_valid_ = false;
+    return false;
+  }
   if (cpi_->svc.number_spatial_layers != rc_cfg.ss_number_layers ||
       cpi_->svc.number_temporal_layers != rc_cfg.ts_number_layers)
     av1_free_svc_cyclic_refresh(cpi_);
diff --git a/av1/ratectrl_rtc.h b/av1/ratectrl_rtc.h
index 0eeaa68..f3bbe3b 100644
--- a/av1/ratectrl_rtc.h
+++ b/av1/ratectrl_rtc.h
@@ -97,6 +97,8 @@
   int min_quantizers[kAomAV1MaxLayers];
   int scaling_factor_num[kAomAV1MaxSpatialLayers];
   int scaling_factor_den[kAomAV1MaxSpatialLayers];
+  // Bit-depth (8, 10, or 12).
+  int bit_depth;
 } AomAV1RateControlRtcConfig;
 
 struct AomAV1RateControlRTC;
diff --git a/examples/av1_enc_fuzzer.cc b/examples/av1_enc_fuzzer.cc
new file mode 100644
index 0000000..6868190
--- /dev/null
+++ b/examples/av1_enc_fuzzer.cc
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2026, Alliance for Open Media. All rights reserved.
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+/*
+ * See build_av1_enc_fuzzer.sh for building instructions.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "aom/aom_encoder.h"
+#include "aom/aom_image.h"
+#include "aom/aomcx.h"
+
+namespace {
+
+constexpr unsigned int kMaxDimension = 1024;
+constexpr size_t kMinHeaderSize = 16;
+
+// Bit masks derived from the mode_flags byte. The low two bits are used to
+// select the encoding usage (see PickUsage).
+enum ModeFlag {
+  kLossless = 1u << 2,
+  kRowMt = 1u << 3,
+  kUseSecondResolution = 1u << 4,
+  kKeyFrameSecond = 1u << 5,
+  kErrorResilient = 1u << 6,
+};
+
+// Number of frames to encode for a given cpu_used value. Lower cpu_used
+// values encode more slowly, so fewer frames are used to stay within the
+// fuzzer's execution time budget (libFuzzer defaults to one second per
+// input). This can be refined based on measured fuzzer performance.
+constexpr unsigned int kNumFramesForCpuUsed[12] = {
+  50,  // cpu_used 0
+  50,  // cpu_used 1
+  50,  // cpu_used 2
+  60,  // cpu_used 3
+  60,  // cpu_used 4
+  70,  // cpu_used 5
+  70,  // cpu_used 6
+  80,  // cpu_used 7
+  80,  // cpu_used 8
+  90,  // cpu_used 9
+  90,  // cpu_used 10
+  100  // cpu_used 11
+};
+
+struct FuzzReader {
+  const uint8_t *data;
+  size_t size;
+};
+
+uint8_t ReadU8(FuzzReader *reader) {
+  if (reader->size == 0) return 0;
+  const uint8_t value = *reader->data++;
+  --reader->size;
+  return value;
+}
+
+uint16_t ReadU16(FuzzReader *reader) {
+  if (reader->size < 2) return 0;
+  const uint16_t value = reader->data[0] | (reader->data[1] << 8);
+  reader->data += 2;
+  reader->size -= 2;
+  return value;
+}
+
+unsigned int UsageToIndex(unsigned int usage) {
+  switch (usage) {
+    case AOM_USAGE_GOOD_QUALITY: return 0;
+    case AOM_USAGE_REALTIME: return 1;
+    case AOM_USAGE_ALL_INTRA: return 2;
+    default: return 0;
+  }
+}
+
+unsigned int PickUsage(uint8_t raw) {
+  switch (raw % 3) {
+    case 0: return AOM_USAGE_GOOD_QUALITY;
+    case 1: return AOM_USAGE_REALTIME;
+    default: return AOM_USAGE_ALL_INTRA;
+  }
+}
+
+aom_rc_mode PickRcMode(uint8_t raw) {
+  switch (raw % 4) {
+    case 0: return AOM_VBR;
+    case 1: return AOM_CBR;
+    case 2: return AOM_CQ;
+    default: return AOM_Q;
+  }
+}
+
+unsigned int PickDimension(uint16_t raw) { return 1u + (raw % kMaxDimension); }
+
+int PickCpuUsed(unsigned int usage, uint8_t raw) {
+  const int max_cpu_used = usage == AOM_USAGE_REALTIME ? 11 : 9;
+  return raw % (max_cpu_used + 1);
+}
+
+bool DrainPackets(aom_codec_ctx_t *codec) {
+  bool got_data = false;
+  aom_codec_iter_t iter = nullptr;
+  while (aom_codec_get_cx_data(codec, &iter) != nullptr) {
+    got_data = true;
+  }
+  return got_data;
+}
+
+bool InitDefaultConfig(aom_codec_iface_t *iface, unsigned int usage,
+                       aom_codec_enc_cfg_t *cfg) {
+  const unsigned int usages[3] = { AOM_USAGE_GOOD_QUALITY, AOM_USAGE_REALTIME,
+                                   AOM_USAGE_ALL_INTRA };
+  const unsigned int requested_index = UsageToIndex(usage);
+  for (unsigned int i = 0; i < 3; ++i) {
+    const unsigned int index = (requested_index + i) % 3;
+    if (aom_codec_enc_config_default(iface, cfg, usages[index]) ==
+        AOM_CODEC_OK) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Deterministic PRNG so that the frame content is derived from the fuzzer
+// input without requiring the reader to supply a full plane-sized chunk of
+// data for every frame.
+uint32_t Rand(uint32_t *state) {
+  *state = *state * 1664525u + 1013904223u;
+  return *state;
+}
+
+void FillPlane(uint8_t *plane, int stride, unsigned int width,
+               unsigned int height, uint32_t *state) {
+  for (unsigned int row = 0; row < height; ++row) {
+    for (unsigned int col = 0; col < width; ++col) {
+      plane[static_cast<size_t>(row) * stride + col] =
+          static_cast<uint8_t>(Rand(state) >> 24);
+    }
+  }
+}
+
+bool BuildImage(FuzzReader *reader, unsigned int width, unsigned int height,
+                aom_image_t *image, unsigned int frame_index) {
+  if (aom_img_alloc(image, AOM_IMG_FMT_I420, width, height, 1) == nullptr) {
+    return false;
+  }
+
+  // Seed a deterministic PRNG from the fuzzer input and the frame index so
+  // that each encoded frame differs without consuming a plane-sized chunk of
+  // input per frame.
+  uint32_t state = 0;
+  for (unsigned int i = 0; i < 4; ++i) {
+    state = (state << 8) | ReadU8(reader);
+  }
+  const uint32_t golden_ratio = 0x9e3779b9u;
+  state ^= frame_index * golden_ratio;
+
+  const unsigned int uv_width = (width + 1) / 2;
+  const unsigned int uv_height = (height + 1) / 2;
+  FillPlane(image->planes[AOM_PLANE_Y], image->stride[AOM_PLANE_Y], width,
+            height, &state);
+  FillPlane(image->planes[AOM_PLANE_U], image->stride[AOM_PLANE_U], uv_width,
+            uv_height, &state);
+  FillPlane(image->planes[AOM_PLANE_V], image->stride[AOM_PLANE_V], uv_width,
+            uv_height, &state);
+  return true;
+}
+
+void ApplyControls(aom_codec_ctx_t *codec, unsigned int usage,
+                   uint8_t mode_flags, uint8_t cpu_used_raw, uint8_t ctl0,
+                   uint8_t ctl1) {
+  const int cpu_used = PickCpuUsed(usage, cpu_used_raw);
+  const unsigned int lossless = (mode_flags & kLossless) != 0;
+  const unsigned int row_mt = (mode_flags & kRowMt) != 0;
+  const unsigned int aq_mode = ctl0 % 4;
+  const unsigned int deltaq_mode = ctl1 % 4;
+  const unsigned int tile_columns = (ctl0 >> 4) & 0x3;
+  const unsigned int tile_rows = (ctl1 >> 4) & 0x3;
+  const unsigned int enable_cdef = (ctl0 >> 2) & 1;
+  const unsigned int enable_restoration = (ctl1 >> 2) & 1;
+
+  (void)AOM_CODEC_CONTROL_TYPECHECKED(codec, AOME_SET_CPUUSED, cpu_used);
+  (void)AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1E_SET_LOSSLESS, lossless);
+  (void)AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1E_SET_ROW_MT, row_mt);
+  (void)AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1E_SET_AQ_MODE, aq_mode);
+  (void)AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1E_SET_DELTAQ_MODE, deltaq_mode);
+  (void)AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1E_SET_TILE_COLUMNS,
+                                      tile_columns);
+  (void)AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1E_SET_TILE_ROWS, tile_rows);
+  (void)AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1E_SET_ENABLE_CDEF, enable_cdef);
+  (void)AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1E_SET_ENABLE_RESTORATION,
+                                      enable_restoration);
+}
+
+bool EncodeFrame(aom_codec_ctx_t *codec, FuzzReader *reader, unsigned int width,
+                 unsigned int height, aom_codec_pts_t pts,
+                 aom_enc_frame_flags_t flags) {
+  aom_image_t image;
+  memset(&image, 0, sizeof(image));
+
+  if (!BuildImage(reader, width, height, &image, pts)) {
+    return false;
+  }
+
+  const aom_codec_err_t enc_ret =
+      aom_codec_encode(codec, &image, pts, 1, flags);
+  if (enc_ret == AOM_CODEC_OK) {
+    (void)DrainPackets(codec);
+  }
+
+  aom_img_free(&image);
+  return true;
+}
+
+}  // namespace
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  if (iface == nullptr || size < kMinHeaderSize) return 0;
+
+  FuzzReader reader = { data, size };
+  const uint8_t mode_flags = ReadU8(&reader);
+  const uint8_t cpu_used_raw = ReadU8(&reader);
+  const uint8_t ctl0 = ReadU8(&reader);
+  const uint8_t ctl1 = ReadU8(&reader);
+  const unsigned int usage = PickUsage(mode_flags);
+  const unsigned int width0 = PickDimension(ReadU16(&reader));
+  const unsigned int height0 = PickDimension(ReadU16(&reader));
+  const unsigned int bitrate = 1u + (ReadU16(&reader) % 4000u);
+  const unsigned int width1 = PickDimension(ReadU16(&reader));
+  const unsigned int height1 = PickDimension(ReadU16(&reader));
+  const unsigned int threads0 = ReadU8(&reader) % 9;
+  const unsigned int threads1 = ReadU8(&reader) % 9;
+
+  aom_codec_enc_cfg_t cfg;
+  if (!InitDefaultConfig(iface, usage, &cfg)) return 0;
+
+  cfg.g_usage = usage;
+  cfg.g_w = width0;
+  cfg.g_h = height0;
+  cfg.g_threads = threads0;
+  cfg.g_forced_max_frame_width = kMaxDimension;
+  cfg.g_forced_max_frame_height = kMaxDimension;
+  cfg.g_timebase.num = 1;
+  cfg.g_timebase.den = 1000000;
+  cfg.g_pass = AOM_RC_ONE_PASS;
+  cfg.g_lag_in_frames = 0;
+  cfg.g_error_resilient =
+      (mode_flags & kErrorResilient) ? AOM_ERROR_RESILIENT_DEFAULT : 0;
+  cfg.rc_end_usage = PickRcMode(ctl0);
+  cfg.rc_target_bitrate = bitrate;
+
+  aom_codec_ctx_t codec;
+  memset(&codec, 0, sizeof(codec));
+  if (aom_codec_enc_init(&codec, iface, &cfg, 0) != AOM_CODEC_OK) return 0;
+
+  const int cpu_used = PickCpuUsed(usage, cpu_used_raw);
+  ApplyControls(&codec, usage, mode_flags, cpu_used_raw, ctl0, ctl1);
+
+  const unsigned int num_frames = kNumFramesForCpuUsed[cpu_used];
+  bool config_failed = false;
+  for (unsigned int frame = 0; frame < num_frames; ++frame) {
+    aom_enc_frame_flags_t flags = 0;
+    if (frame == 1 && (mode_flags & kKeyFrameSecond)) {
+      flags |= AOM_EFLAG_FORCE_KF;
+    }
+
+    unsigned int width = width0;
+    unsigned int height = height0;
+    if (mode_flags & kUseSecondResolution) {
+      // Vary the switch frame based on the input so different encodings
+      // exercise the resolution change at different points in the stream.
+      // num_frames >= 50 for every cpu_used, so switch_frame and
+      // restore_frame stay within [1, num_frames - 1].
+      const unsigned int switch_frame = 1u + (ctl0 % (num_frames / 4u));
+      const unsigned int restore_frame =
+          switch_frame + 1u + (ctl1 % (num_frames / 4u));
+      if (frame == switch_frame) {
+        cfg.g_w = width1;
+        cfg.g_h = height1;
+        cfg.g_threads = threads1;
+        cfg.rc_end_usage = PickRcMode(ctl1);
+        if (aom_codec_enc_config_set(&codec, &cfg) != AOM_CODEC_OK) {
+          config_failed = true;
+          break;
+        }
+        width = width1;
+        height = height1;
+      } else if (frame == restore_frame) {
+        cfg.g_w = width0;
+        cfg.g_h = height0;
+        cfg.g_threads = threads0;
+        cfg.rc_end_usage = PickRcMode(ctl0);
+        if (aom_codec_enc_config_set(&codec, &cfg) != AOM_CODEC_OK) {
+          config_failed = true;
+          break;
+        }
+        width = width0;
+        height = height0;
+      }
+    }
+
+    if (!EncodeFrame(&codec, &reader, width, height, frame, flags)) {
+      config_failed = true;
+      break;
+    }
+  }
+
+  if (!config_failed) {
+    while (aom_codec_encode(&codec, nullptr, 0, 0, 0) == AOM_CODEC_OK &&
+           DrainPackets(&codec)) {
+    }
+  }
+
+  (void)aom_codec_destroy(&codec);
+  return 0;
+}
diff --git a/examples/build_av1_enc_fuzzer.sh b/examples/build_av1_enc_fuzzer.sh
new file mode 100755
index 0000000..a11de16
--- /dev/null
+++ b/examples/build_av1_enc_fuzzer.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# Copyright (c) 2026, Alliance for Open Media. All rights reserved.
+#
+# This source code is subject to the terms of the BSD 2 Clause License and
+# the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+# was not distributed with this source code in the LICENSE file, you can
+# obtain it at www.aomedia.org/license/software. If the Alliance for Open
+# Media Patent License 1.0 was not distributed with this source code in the
+# PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+#
+###############################################################################
+# Fuzzer for libaom AV1 encoder public API.
+# =========================================
+# Requirements
+# ---------------------
+# Clang6.0 or above (must support -fsanitize=fuzzer -fsanitize=fuzzer-no-link)
+#
+# References:
+# ---------------------
+# http://llvm.org/docs/LibFuzzer.html
+# https://github.com/google/oss-fuzz
+#
+# Steps to build / run
+# ---------------------
+
+set -eu
+
+# Have a copy of AOM and a build directory ready.
+if [[ $# -ne 2 ]]; then
+  echo "Pass in the AOM source tree as first argument, and a build directory "
+  echo "as the second argument. The AOM source tree can be obtained via: "
+  echo "  git clone https://aomedia.googlesource.com/aom"
+  exit 2
+fi
+if [[ -z "${CC:-}" ]]; then
+  echo "Set the CC environment variable to point to your C compiler."
+  exit 2
+fi
+if [[ -z "${CXX:-}" ]]; then
+  echo "Set the CXX environment variable to point to your C++ compiler."
+  exit 2
+fi
+
+AOM_DIR=$1
+BUILD_DIR=$2
+# Run CMake with address sanitizer enabled and build the codec.
+# Enable DO_RANGE_CHECK_CLAMP to suppress the noise of integer overflows
+# in the transform functions. Also set memory limits.
+EXTRA_C_FLAGS='-UNDEBUG -DDO_RANGE_CHECK_CLAMP=1 -DAOM_MAX_ALLOCABLE_MEMORY=1073741824'
+cd "${BUILD_DIR}"
+cmake "${AOM_DIR}" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONFIG_PIC=1 \
+  -DCONFIG_AV1_ENCODER=1 \
+  -DENABLE_APPS=0 -DENABLE_EXAMPLES=0 -DENABLE_DOCS=0 -DENABLE_TESTS=0 \
+  -DAOM_EXTRA_C_FLAGS="${EXTRA_C_FLAGS}" \
+  -DAOM_EXTRA_CXX_FLAGS="${EXTRA_C_FLAGS}" -DSANITIZE=fuzzer-no-link,address
+
+# Build the codec.
+make -j$(nproc)
+
+# Build the av1 encoder fuzzer
+$CXX -std=c++17 -I${AOM_DIR} -I${BUILD_DIR} \
+    -g -fsanitize=fuzzer,address \
+    ${AOM_DIR}/examples/av1_enc_fuzzer.cc -o ${BUILD_DIR}/av1_enc_fuzzer \
+    ${BUILD_DIR}/libaom.a
+
+echo "Fuzzer built at ${BUILD_DIR}/av1_enc_fuzzer."
+echo "Create a corpus directory, and run:"
+echo "  av1_enc_fuzzer CORPUS_DIR"
diff --git a/test/av1_convolve_test.cc b/test/av1_convolve_test.cc
index 4b444bd..7d1bef4 100644
--- a/test/av1_convolve_test.cc
+++ b/test/av1_convolve_test.cc
@@ -1376,7 +1376,7 @@
 
  private:
   void TestConvolveVmaf() {
-    // 8-tap Gaussian blur filter used by tune=vmaf_with_preprocessing
+    // 8-tap Gaussian blur filter used by tune=vmaf
     // in av1/encoder/tune_vmaf.c.
     // The array is of size 9 to allow passing kGaussFilter + 1 to
     // _mm_loadu_si128() in prepare_coeffs_6t().
diff --git a/test/av1_ext_tile_test.cc b/test/av1_ext_tile_test.cc
index 1eb6ced..51a1d32 100644
--- a/test/av1_ext_tile_test.cc
+++ b/test/av1_ext_tile_test.cc
@@ -29,6 +29,8 @@
 // Fake width and height so that they can be multiples of the tile size.
 const int kImgWidth = 704;
 const int kImgHeight = 576;
+const int kInvalidTileImgSize = 256;
+const int kOutOfRangeTileCol = 2;
 
 // This test tests large scale tile coding case. Non-large-scale tile coding
 // is tested by the tile_independence test.
@@ -78,7 +80,8 @@
                           ::libaom_test::Encoder *encoder) override {
     if (video->frame() == 0) {
       // Encode setting
-      encoder->Control(AOME_SET_CPUUSED, set_cpu_used_);
+      encoder->Control(AOME_SET_CPUUSED,
+                       test_invalid_tile_ ? 8 : set_cpu_used_);
       encoder->Control(AOME_SET_ENABLEAUTOALTREF, 0);
       encoder->Control(AV1E_SET_FRAME_PARALLEL_DECODING, 1);
 
@@ -88,8 +91,8 @@
       encoder->Control(AV1E_SET_SUPERBLOCK_SIZE, AOM_SUPERBLOCK_SIZE_64X64);
       // Set tile_columns and tile_rows to MAX values, which guarantees the tile
       // size of 64 x 64 pixels(i.e. 1 SB) for <= 4k resolution.
-      encoder->Control(AV1E_SET_TILE_COLUMNS, 6);
-      encoder->Control(AV1E_SET_TILE_ROWS, 6);
+      encoder->Control(AV1E_SET_TILE_COLUMNS, test_invalid_tile_ ? 1 : 6);
+      encoder->Control(AV1E_SET_TILE_ROWS, test_invalid_tile_ ? 1 : 6);
     } else if (video->frame() == 1) {
       frame_flags_ =
           AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF;
@@ -109,6 +112,16 @@
   }
 
   void FramePktHook(const aom_codec_cx_pkt_t *pkt) override {
+    if (test_invalid_tile_) {
+      decoder_->Control(AV1_SET_DECODE_TILE_ROW, 0);
+      decoder_->Control(AV1_SET_DECODE_TILE_COL, kOutOfRangeTileCol);
+      EXPECT_EQ(AOM_CODEC_INVALID_PARAM,
+                decoder_->DecodeFrame(
+                    reinterpret_cast<uint8_t *>(pkt->data.frame.buf),
+                    pkt->data.frame.sz));
+      return;
+    }
+
     // Skip decoding 1 frame.
     if (pkt->data.frame.pts == (aom_codec_pts_t)kSkip) return;
 
@@ -186,12 +199,28 @@
     ASSERT_EQ(md5_, tile_md5_);
   }
 
+  void TestInvalidTile() {
+    ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv",
+                                         kInvalidTileImgSize,
+                                         kInvalidTileImgSize, 30, 1, 0, 1);
+    cfg_.rc_target_bitrate = 500;
+    cfg_.g_error_resilient = AOM_ERROR_RESILIENT_DEFAULT;
+    cfg_.large_scale_tile = 1;
+    cfg_.g_lag_in_frames = 0;
+    cfg_.g_threads = 1;
+    test_invalid_tile_ = true;
+
+    init_flags_ = AOM_CODEC_USE_PSNR;
+    ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+  }
+
   ::libaom_test::TestMode encoding_mode_;
   int set_cpu_used_;
   ::libaom_test::Decoder *decoder_;
   aom_image_t tile_img_;
   std::vector<std::string> md5_;
   std::vector<std::string> tile_md5_;
+  bool test_invalid_tile_ = false;
 };
 
 TEST_P(AV1ExtTileTest, DecoderResultTest) { TestRoundTrip(); }
@@ -205,6 +234,8 @@
 
 TEST_P(AV1ExtTileTestLarge, DecoderResultTest) { TestRoundTrip(); }
 
+TEST_P(AV1ExtTileTestLarge, RejectsInvalidDecodeTile) { TestInvalidTile(); }
+
 AV1_INSTANTIATE_TEST_SUITE(
     // Now only test 2-pass mode.
     AV1ExtTileTestLarge, ::testing::Values(::libaom_test::kTwoPassGood),
diff --git a/test/av1_key_value_api_test.cc b/test/av1_key_value_api_test.cc
index d3e9dc1..8444907 100644
--- a/test/av1_key_value_api_test.cc
+++ b/test/av1_key_value_api_test.cc
@@ -104,6 +104,7 @@
   std::make_tuple("auto-intra-tools-off", "1"),  // uint
   std::make_tuple("min-gf-interval", "10"),      // uint
   std::make_tuple("min-partition-size", "4"),    // int
+  std::make_tuple("resize-mode", "3"),           // uint
   std::make_tuple("tune", "psnr"),               // enum
 };
 
@@ -121,6 +122,7 @@
   std::make_tuple("tune", "PsnR1"),
   // out of range
   std::make_tuple("cq-level", "1000"),
+  std::make_tuple("resize-mode", "10"),
 };
 
 INSTANTIATE_TEST_SUITE_P(KeyValAPI, EncValidTest,
diff --git a/test/comp_mask_pred_test.cc b/test/comp_mask_pred_test.cc
index f57160c..be6a1fb 100644
--- a/test/comp_mask_pred_test.cc
+++ b/test/comp_mask_pred_test.cc
@@ -82,10 +82,10 @@
   rnd_.Reset(libaom_test::ACMRandom::DeterministicSeed());
   av1_init_wedge_masks();
   comp_pred1_ =
-      (uint8_t *)aom_memalign(16, ((MAX_SB_SIZE + 16) + 16) * MAX_SB_SIZE);
+      (uint8_t *)aom_memalign(16, (MAX_SB_SIZE + SUBPEL_TAPS) * MAX_SB_SIZE);
   ASSERT_NE(comp_pred1_, nullptr);
   comp_pred2_ =
-      (uint8_t *)aom_memalign(16, ((MAX_SB_SIZE + 16) + 16) * MAX_SB_SIZE);
+      (uint8_t *)aom_memalign(16, (MAX_SB_SIZE + SUBPEL_TAPS) * MAX_SB_SIZE);
   ASSERT_NE(comp_pred2_, nullptr);
   pred_ = (uint8_t *)aom_memalign(16, MAX_SB_SQUARE);
   ASSERT_NE(pred_, nullptr);
@@ -458,10 +458,10 @@
   av1_init_wedge_masks();
 
   comp_pred1_ = (uint16_t *)aom_memalign(
-      16, ((MAX_SB_SIZE + 16) + 16) * MAX_SB_SIZE * sizeof(*comp_pred1_));
+      16, (MAX_SB_SIZE + SUBPEL_TAPS) * MAX_SB_SIZE * sizeof(*comp_pred1_));
   ASSERT_NE(comp_pred1_, nullptr);
   comp_pred2_ = (uint16_t *)aom_memalign(
-      16, ((MAX_SB_SIZE + 16) + 16) * MAX_SB_SIZE * sizeof(*comp_pred2_));
+      16, (MAX_SB_SIZE + SUBPEL_TAPS) * MAX_SB_SIZE * sizeof(*comp_pred2_));
   ASSERT_NE(comp_pred2_, nullptr);
   pred_ = (uint16_t *)aom_memalign(16, MAX_SB_SQUARE * sizeof(*pred_));
   ASSERT_NE(pred_, nullptr);
diff --git a/test/disflow_test.cc b/test/disflow_test.cc
index d881f7b..f852608 100644
--- a/test/disflow_test.cc
+++ b/test/disflow_test.cc
@@ -9,6 +9,7 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 
+#include "aom_dsp/flow_estimation/corner_match.h"
 #include "aom_dsp/flow_estimation/disflow.h"
 
 #include "gtest/gtest.h"
@@ -21,9 +22,10 @@
 
 namespace {
 
-using ComputeFlowAtPointFunc = void (*)(const uint8_t *src, const uint8_t *ref,
+using ComputeFlowAtPointFunc = void (*)(const uint8_t *src, int src_stride,
+                                        const uint8_t *ref, int ref_stride,
                                         int x, int y, int width, int height,
-                                        int stride, double *u, double *v);
+                                        double *u, double *v);
 
 class ComputeFlowTest
     : public ::testing::TestWithParam<ComputeFlowAtPointFunc> {
@@ -74,16 +76,17 @@
 
   aom_usec_timer ref_timer, test_timer;
 
-  aom_compute_flow_at_point_c(src, ref, x, y, kWidth, kHeight, kWidth, &u_ref,
-                              &v_ref);
+  aom_compute_flow_at_point_c(src, kWidth, ref, kWidth, x, y, kWidth, kHeight,
+                              &u_ref, &v_ref);
 
-  target_func_(src, ref, x, y, kWidth, kHeight, kWidth, &u_test, &v_test);
+  target_func_(src, kWidth, ref, kWidth, x, y, kWidth, kHeight, &u_test,
+               &v_test);
 
   if (run_times > 1) {
     aom_usec_timer_start(&ref_timer);
     for (int i = 0; i < run_times; ++i) {
-      aom_compute_flow_at_point_c(src, ref, x, y, kWidth, kHeight, kWidth,
-                                  &u_ref, &v_ref);
+      aom_compute_flow_at_point_c(src, kWidth, ref, kWidth, x, y, kWidth,
+                                  kHeight, &u_ref, &v_ref);
     }
     aom_usec_timer_mark(&ref_timer);
     const double elapsed_time_c =
@@ -91,7 +94,8 @@
 
     aom_usec_timer_start(&test_timer);
     for (int i = 0; i < run_times; ++i) {
-      target_func_(src, ref, x, y, kWidth, kHeight, kWidth, &u_test, &v_test);
+      target_func_(src, kWidth, ref, kWidth, x, y, kWidth, kHeight, &u_test,
+                   &v_test);
     }
     aom_usec_timer_mark(&test_timer);
     const double elapsed_time_simd =
@@ -129,4 +133,52 @@
                          ::testing::Values(aom_compute_flow_at_point_sve));
 #endif
 
+#if CONFIG_AV1_ENCODER && !CONFIG_REALTIME_ONLY
+TEST(DisflowTest, NarrowDimensions) {
+  YV12_BUFFER_CONFIG src = {};
+  YV12_BUFFER_CONFIG ref = {};
+
+  constexpr int kWidth = 17;
+  constexpr int kHeight = 1;
+  ASSERT_EQ(aom_alloc_frame_buffer(&src, kWidth, kHeight, 1, 1, 0,
+                                   AOM_BORDER_IN_PIXELS, 0, true, 0),
+            0);
+  ASSERT_EQ(aom_alloc_frame_buffer(&ref, kWidth, kHeight, 1, 1, 0,
+                                   AOM_BORDER_IN_PIXELS, 0, true, 0),
+            0);
+
+  MotionModel motion_models[1];
+  bool mem_alloc_failed = false;
+  bool ret = av1_compute_global_motion_disflow(
+      TRANSLATION, &src, &ref, 8, 0, motion_models, 1, &mem_alloc_failed);
+  EXPECT_FALSE(ret);
+  EXPECT_FALSE(mem_alloc_failed);
+
+  aom_free_frame_buffer(&src);
+  aom_free_frame_buffer(&ref);
+}
+
+TEST(DisflowTest, MismatchedDimensions) {
+  YV12_BUFFER_CONFIG src = {};
+  YV12_BUFFER_CONFIG ref = {};
+
+  ASSERT_EQ(aom_alloc_frame_buffer(&src, 128, 96, 1, 1, 0, AOM_BORDER_IN_PIXELS,
+                                   0, true, 0),
+            0);
+  ASSERT_EQ(aom_alloc_frame_buffer(&ref, 320, 240, 1, 1, 0,
+                                   AOM_BORDER_IN_PIXELS, 0, true, 0),
+            0);
+
+  MotionModel motion_models[1];
+  bool mem_alloc_failed = false;
+  bool ret = av1_compute_global_motion_disflow(
+      TRANSLATION, &src, &ref, 8, 0, motion_models, 1, &mem_alloc_failed);
+  EXPECT_FALSE(ret);
+  EXPECT_FALSE(mem_alloc_failed);
+
+  aom_free_frame_buffer(&src);
+  aom_free_frame_buffer(&ref);
+}
+#endif  // CONFIG_AV1_ENCODER && !CONFIG_REALTIME_ONLY
+
 }  // namespace
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc
index e672822..5621933 100644
--- a/test/encode_api_test.cc
+++ b/test/encode_api_test.cc
@@ -29,6 +29,9 @@
 #include "aom/aom_image.h"
 #include "aom_mem/aom_mem.h"
 
+#include "av1/common/blockd.h"
+#include "av1/common/reconintra.h"
+#include "av1/encoder/allintra_vis.h"
 #include "test/codec_factory.h"
 #include "test/encode_test_driver.h"
 #include "test/util.h"
@@ -2684,4 +2687,422 @@
   ASSERT_EQ(aom_codec_destroy(&codec), AOM_CODEC_OK);
 }
 
+#if !CONFIG_REALTIME_ONLY
+TEST(EncodeAPI, Buganizer558434716) {
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  aom_codec_enc_cfg_t cfg;
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_GOOD_QUALITY),
+            AOM_CODEC_OK);
+
+  cfg.g_w = 128;
+  cfg.g_h = 96;
+  cfg.g_forced_max_frame_width = 1920;
+  cfg.g_forced_max_frame_height = 1080;
+  cfg.g_pass = AOM_RC_ONE_PASS;
+  cfg.g_lag_in_frames = 0;
+  cfg.g_threads = 1;
+
+  aom_codec_ctx_t enc;
+  ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
+
+  aom_image_t *img_small = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 128, 96, 1);
+  ASSERT_NE(img_small, nullptr);
+  FillImage(img_small, 128);
+
+  aom_image_t *img_large =
+      aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 320, 240, 1);
+  ASSERT_NE(img_large, nullptr);
+  FillImage(img_large, 128);
+
+  // Frame 0 at 128x96
+  ASSERT_EQ(aom_codec_encode(&enc, img_small, 0, 1, 0), AOM_CODEC_OK);
+
+  // Switch to larger resolution
+  cfg.g_w = 320;
+  cfg.g_h = 240;
+  ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK);
+
+  // Frame 1 at 320x240
+  ASSERT_EQ(aom_codec_encode(&enc, img_large, 1, 1, AOM_EFLAG_FORCE_KF),
+            AOM_CODEC_OK);
+
+  // Switch back to smaller resolution
+  cfg.g_w = 128;
+  cfg.g_h = 96;
+  ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK);
+
+  // Frame 2 at 128x96 (keyframe)
+  ASSERT_EQ(aom_codec_encode(&enc, img_small, 2, 1, AOM_EFLAG_FORCE_KF),
+            AOM_CODEC_OK);
+
+  // Frames 3+ at 128x96 (interframes referencing frame 2)
+  for (int i = 3; i < 8; ++i) {
+    ASSERT_EQ(aom_codec_encode(&enc, img_small, i, 1, 0), AOM_CODEC_OK);
+  }
+
+  aom_img_free(img_small);
+  aom_img_free(img_large);
+  ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+}
+
+TEST(EncodeAPI, PerceptualAIDynamicResolutionChange) {
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  aom_codec_enc_cfg_t cfg;
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_ALL_INTRA),
+            AOM_CODEC_OK);
+
+  cfg.g_w = 17;
+  cfg.g_h = 1;
+  cfg.g_forced_max_frame_width = 320;
+  cfg.g_forced_max_frame_height = 240;
+  cfg.g_pass = AOM_RC_ONE_PASS;
+  cfg.g_lag_in_frames = 0;
+  cfg.g_threads = 1;
+
+  aom_codec_ctx_t enc;
+  ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_DELTAQ_MODE, 3), AOM_CODEC_OK);
+
+  aom_image_t *img_small = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 17, 1, 1);
+  ASSERT_NE(img_small, nullptr);
+  FillImage(img_small, 128);
+
+  aom_image_t *img_large =
+      aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 320, 240, 1);
+  ASSERT_NE(img_large, nullptr);
+  FillImage(img_large, 128);
+
+  // Frame 0 at 17x1
+  ASSERT_EQ(aom_codec_encode(&enc, img_small, 0, 1, 0), AOM_CODEC_OK);
+
+  // Switch to larger resolution
+  cfg.g_w = 320;
+  cfg.g_h = 240;
+  ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK);
+
+  // Frame 1 at 320x240
+  ASSERT_EQ(aom_codec_encode(&enc, img_large, 1, 1, 0), AOM_CODEC_OK);
+
+  // Switch back to 17x1
+  cfg.g_w = 17;
+  cfg.g_h = 1;
+  ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK);
+
+  // Frame 2 at 17x1
+  ASSERT_EQ(aom_codec_encode(&enc, img_small, 2, 1, 0), AOM_CODEC_OK);
+
+  aom_img_free(img_small);
+  aom_img_free(img_large);
+  ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+}
+
+// Test for OSS-Fuzz Issue 559079132: Integer-overflow in
+// av1_caq_select_segment. When COMPLEXITY_AQ (aq_mode 2) is enabled with a high
+// target bitrate, sb64_target_rate is large, causing signed integer overflow
+// when multiplying sb64_target_rate * xmis * ymis in 32-bit arithmetic and when
+// casting target_rate to int.
+TEST(EncodeAPI, Issue559079132) {
+  aom_codec_iface_t *const iface = aom_codec_av1_cx();
+  aom_codec_ctx_t enc;
+  aom_codec_enc_cfg_t cfg;
+
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_GOOD_QUALITY),
+            AOM_CODEC_OK);
+
+  cfg.g_w = 64;
+  cfg.g_h = 64;
+  cfg.g_timebase.num = 1;
+  cfg.g_timebase.den = 1;
+  cfg.rc_target_bitrate = 2000000;
+  cfg.rc_end_usage = AOM_CBR;
+  cfg.g_lag_in_frames = 0;
+
+  ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_AQ_MODE, 2), AOM_CODEC_OK);
+
+  aom_image_t *img = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 64, 64, 1);
+  ASSERT_NE(img, nullptr);
+  FillImageRandom(img);
+
+  EncodeOne(&enc, img, 0);
+
+  // Flush encoder.
+  ASSERT_EQ(aom_codec_encode(&enc, nullptr, 0, 0, 0), AOM_CODEC_OK);
+  aom_codec_iter_t iter = nullptr;
+  while (aom_codec_get_cx_data(&enc, &iter) != nullptr) {
+  }
+
+  aom_img_free(img);
+  ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+}
+#endif  // !CONFIG_REALTIME_ONLY
+
+// Tests for OSS-Fuzz Issues 558463888, 559075253, 559225640:
+// When dynamically reducing threads with multiple tiles and row_mt=0, the
+// primary worker pool size (mt_info->num_workers) remains at the higher
+// capacity allocated previously while the module workers for the encode stage
+// (mt_info->num_mod_workers[MOD_ENC]) is reduced. If encode_frame_internal()
+// uses mt_info->num_workers instead of mt_info->num_mod_workers[MOD_ENC], it
+// can erroneously call av1_encode_tiles_mt() or stride with the wrong worker
+// count, causing tiles to be skipped and leading to a crash in bitstream
+// packing.
+void TestDynamicThreadReductionWithTiles(int tile_rows, int tile_columns,
+                                         int init_threads, int max_threads) {
+  aom_codec_iface_t *const iface = aom_codec_av1_cx();
+  aom_codec_ctx_t enc;
+  aom_codec_enc_cfg_t cfg;
+
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME),
+            AOM_CODEC_OK);
+
+  cfg.g_w = 256;
+  cfg.g_h = 256;
+  // Allow dynamic resolution changes up to 512x512.
+  cfg.g_forced_max_frame_width = 512;
+  cfg.g_forced_max_frame_height = 512;
+  cfg.g_threads = init_threads;
+  cfg.g_lag_in_frames = 0;
+  cfg.g_error_resilient = 1;
+
+  ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
+
+  // Disable row-mt and configure tiling.
+  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ROW_MT, 0), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&enc, AOME_SET_CPUUSED, 7), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_TILE_ROWS, tile_rows),
+            AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_TILE_COLUMNS, tile_columns),
+            AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&enc, AOME_SET_ENABLEAUTOALTREF, 0),
+            AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_AQ_MODE, 3), AOM_CODEC_OK);
+
+  aom_image_t *img_256 = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 256, 256, 1);
+  ASSERT_NE(img_256, nullptr);
+  FillImageRandom(img_256);
+
+  aom_image_t *img_512 = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 512, 512, 1);
+  ASSERT_NE(img_512, nullptr);
+  FillImageRandom(img_512);
+
+  // Frame 0: Encode at 256x256 with init_threads.
+  EncodeOne(&enc, img_256, 0);
+
+  // Frame 1: Switch to 512x512 with max_threads. This allocates max_threads
+  // workers in the primary worker pool (mt_info->num_workers = max_threads).
+  cfg.g_w = 512;
+  cfg.g_h = 512;
+  cfg.g_threads = max_threads;
+  ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK);
+  EncodeOne(&enc, img_512, 1);
+
+  // Frame 2: Switch back to 256x256 with init_threads. The worker pool retains
+  // mt_info->num_workers = max_threads, but mt_info->num_mod_workers[MOD_ENC]
+  // is init_threads. This verifies that tile encoding properly handles the
+  // reduced worker count without skipping tiles.
+  cfg.g_w = 256;
+  cfg.g_h = 256;
+  cfg.g_threads = init_threads;
+  ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK);
+  EncodeOne(&enc, img_256, 2);
+
+  // Flush encoder.
+  ASSERT_EQ(aom_codec_encode(&enc, nullptr, 0, 0, 0), AOM_CODEC_OK);
+  aom_codec_iter_t iter = nullptr;
+  while (aom_codec_get_cx_data(&enc, &iter) != nullptr) {
+  }
+
+  aom_img_free(img_256);
+  aom_img_free(img_512);
+  ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+}
+
+TEST(EncodeAPI, Issue558463888) {
+  TestDynamicThreadReductionWithTiles(/*tile_rows=*/1, /*tile_columns=*/0,
+                                      /*init_threads=*/1, /*max_threads=*/2);
+}
+
+TEST(EncodeAPI, Issue559075253) {
+  TestDynamicThreadReductionWithTiles(/*tile_rows=*/1, /*tile_columns=*/1,
+                                      /*init_threads=*/2, /*max_threads=*/4);
+}
+
+TEST(EncodeAPI, Issue559225640) {
+  TestDynamicThreadReductionWithTiles(/*tile_rows=*/0, /*tile_columns=*/1,
+                                      /*init_threads=*/1, /*max_threads=*/4);
+}
+
+#if !CONFIG_REALTIME_ONLY
+TEST(EncodeAPI, Buganizer558446054) {
+#if !CONFIG_SHARED
+  av1_init_intra_predictors();
+
+  MACROBLOCKD xd = {};
+  YV12_BUFFER_CONFIG cur_buf = {};
+  xd.cur_buf = &cur_buf;
+  MB_MODE_INFO mbmi = {};
+  mbmi.bsize = BLOCK_8X8;
+  mbmi.partition = PARTITION_NONE;
+  mbmi.mode = D45_PRED;
+  MB_MODE_INFO *mbmi_ptr = &mbmi;
+  xd.mi = &mbmi_ptr;
+  xd.bd = 8;
+  xd.left_available = 1;
+  xd.up_available = 1;
+  xd.tile.mi_row_end = 100;
+  xd.tile.mi_col_end = 100;
+  // Set mb_to_bottom_edge and mb_to_right_edge such that
+  // yd + txhpx < 0 and xr < 0:
+  // yd + txhpx = (mb_to_bottom_edge >> 3) + hpx - y = -16 + 8 - 0 = -8 < 0
+  // xr = (mb_to_right_edge >> 3) + wpx - x - txwpx = -5 + 8 - 0 - 4 = -1 < 0
+  // xr + txwpx = 3 > 0 (so n_top_px = 3).
+  xd.mb_to_bottom_edge = -16 * 8;
+  xd.mb_to_right_edge = -5 * 8;
+
+  uint8_t ref_buf[64 * 64];
+  memset(ref_buf, 200, sizeof(ref_buf));
+  uint8_t dst_buf[64 * 64] = { 0 };
+  av1_predict_intra_block(&xd, BLOCK_64X64, /*enable_intra_edge_filter=*/0,
+                          /*wpx=*/8, /*hpx=*/8, TX_4X4, D45_PRED,
+                          /*angle_delta=*/0, /*use_palette=*/0,
+                          FILTER_INTRA_MODES, ref_buf + 64 * 8 + 8, 64, dst_buf,
+                          64, /*col_off=*/0, /*row_off=*/0, /*plane=*/0);
+  // In Debug builds (-UNDEBUG), reverting clamp() triggers:
+  //   Assertion `n_left_px >= 0' failed.
+  // In Release builds (-DNDEBUG), reverting clamp() causes n_topright_px < 0
+  // (-4 < 0), skipping top-right extension of above_row[3]=200 into
+  // above_row[4..7] and leaving dst_buf[3] == 127 instead of 200.
+  ASSERT_EQ(dst_buf[3], 200);
+#endif  // !CONFIG_SHARED
+
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  aom_codec_enc_cfg_t cfg;
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_ALL_INTRA),
+            AOM_CODEC_OK);
+
+  cfg.g_w = 65;
+  cfg.g_h = 33;
+  cfg.g_threads = 0;
+  cfg.g_lag_in_frames = 0;
+  cfg.g_pass = AOM_RC_ONE_PASS;
+  cfg.rc_end_usage = AOM_Q;
+
+  aom_codec_ctx_t codec;
+  ASSERT_EQ(aom_codec_enc_init(&codec, iface, &cfg, 0), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&codec, AOME_SET_CPUUSED, 1), AOM_CODEC_OK);
+
+  aom_image_t raw;
+  ASSERT_NE(aom_img_alloc(&raw, AOM_IMG_FMT_I420, 65, 33, 1), nullptr);
+  FillImageRandom(&raw);
+
+  ASSERT_EQ(aom_codec_encode(&codec, &raw, 0, 1, 0), AOM_CODEC_OK);
+
+  aom_img_free(&raw);
+  ASSERT_EQ(aom_codec_destroy(&codec), AOM_CODEC_OK);
+}
+#endif  // !CONFIG_REALTIME_ONLY
+
+#if !CONFIG_REALTIME_ONLY
+TEST(EncodeAPI, Buganizer558463892_558589747) {
+#if !CONFIG_SHARED
+  std::unique_ptr<AV1_COMP> cpi_test(new AV1_COMP());
+  struct aom_internal_error_info error = {};
+  if (setjmp(error.jmp)) FAIL();
+  error.setjmp = 1;
+  cpi_test->common.error = &error;
+  cpi_test->frame_info.mi_rows = 16;
+  cpi_test->frame_info.mi_cols = 16;
+  av1_init_mb_wiener_var_buffer(cpi_test.get());
+  ASSERT_NE(cpi_test->mb_weber_stats, nullptr);
+  cpi_test->mb_weber_stats[0].satd = 12345;
+
+  // Increase dimensions to 64x64 MI units: mb_weber_stats must be reallocated.
+  cpi_test->frame_info.mi_rows = 64;
+  cpi_test->frame_info.mi_cols = 64;
+  av1_init_mb_wiener_var_buffer(cpi_test.get());
+  EXPECT_EQ(cpi_test->mb_weber_stats[0].satd, 0);
+  aom_free(cpi_test->mb_weber_stats);
+#endif  // !CONFIG_SHARED
+
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  aom_codec_enc_cfg_t cfg;
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_ALL_INTRA),
+            AOM_CODEC_OK);
+
+  cfg.g_w = 64;
+  cfg.g_h = 64;
+  cfg.g_forced_max_frame_width = 1024;
+  cfg.g_forced_max_frame_height = 1024;
+  cfg.g_threads = 0;
+  cfg.g_lag_in_frames = 0;
+  cfg.g_pass = AOM_RC_ONE_PASS;
+  cfg.rc_end_usage = AOM_Q;
+
+  aom_codec_ctx_t codec;
+  ASSERT_EQ(aom_codec_enc_init(&codec, iface, &cfg, 0), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&codec, AV1E_SET_DELTAQ_MODE, 3), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&codec, AOME_SET_CPUUSED, 0), AOM_CODEC_OK);
+
+  // Encode frame 0 at 64x64
+  aom_image_t raw;
+  ASSERT_NE(aom_img_alloc(&raw, AOM_IMG_FMT_I420, 64, 64, 1), nullptr);
+  FillImageRandom(&raw);
+  ASSERT_EQ(aom_codec_encode(&codec, &raw, 0, 1, 0), AOM_CODEC_OK);
+  aom_img_free(&raw);
+
+  // Change resolution to 256x256 mid-stream
+  cfg.g_w = 256;
+  cfg.g_h = 256;
+  ASSERT_EQ(aom_codec_enc_config_set(&codec, &cfg), AOM_CODEC_OK);
+
+  // Encode frame 1 at 256x256
+  ASSERT_NE(aom_img_alloc(&raw, AOM_IMG_FMT_I420, 256, 256, 1), nullptr);
+  FillImageRandom(&raw);
+  ASSERT_EQ(aom_codec_encode(&codec, &raw, 1, 1, 0), AOM_CODEC_OK);
+  aom_img_free(&raw);
+
+  ASSERT_EQ(aom_codec_destroy(&codec), AOM_CODEC_OK);
+}
+#endif  // !CONFIG_REALTIME_ONLY
+
+#if !CONFIG_REALTIME_ONLY
+TEST(EncodeAPI, Buganizer558417547) {
+#if !CONFIG_SHARED
+  std::unique_ptr<AV1_COMP> cpi_test(new AV1_COMP());
+  SequenceHeader seq_params = {};
+  seq_params.bit_depth = AOM_BITS_8;
+  seq_params.sb_size = BLOCK_64X64;
+  cpi_test->common.seq_params = &seq_params;
+  cpi_test->common.mi_params.mi_rows = 16;
+  cpi_test->common.mi_params.mi_cols = 16;
+  cpi_test->common.quant_params.base_qindex = 128;
+  cpi_test->common.delta_q_info.delta_q_res = 4;
+  cpi_test->frame_info.mi_rows = 16;
+  cpi_test->frame_info.mi_cols = 16;
+  cpi_test->norm_wiener_variance = 100;
+  struct aom_internal_error_info error = {};
+  if (setjmp(error.jmp)) FAIL();
+  error.setjmp = 1;
+  cpi_test->common.error = &error;
+  av1_init_mb_wiener_var_buffer(cpi_test.get());
+  ASSERT_NE(cpi_test->mb_weber_stats, nullptr);
+
+  // Set large SATD and distortion values exceeding INT32_MAX.
+  for (int i = 0; i < 16 * 16; ++i) {
+    cpi_test->mb_weber_stats[i].satd = 3000000000LL;
+    cpi_test->mb_weber_stats[i].distortion = 3000000000LL;
+    cpi_test->mb_weber_stats[i].rec_pix_max = 255;
+  }
+
+  // Test out-of-bounds / negative mi_row and mi_col without crashing or SIGFPE.
+  int q_neg = av1_get_sbq_perceptual_ai(cpi_test.get(), BLOCK_64X64, -16, -16);
+  EXPECT_GE(q_neg, 0);
+  int q_oob = av1_get_sbq_perceptual_ai(cpi_test.get(), BLOCK_64X64, 100, 100);
+  EXPECT_GE(q_oob, 0);
+
+  aom_free(cpi_test->mb_weber_stats);
+#endif  // !CONFIG_SHARED
+}
+#endif  // !CONFIG_REALTIME_ONLY
+
 }  // namespace
diff --git a/test/end_to_end_psnr_test.cc b/test/end_to_end_psnr_test.cc
index b006509..26c286b 100644
--- a/test/end_to_end_psnr_test.cc
+++ b/test/end_to_end_psnr_test.cc
@@ -31,11 +31,11 @@
 const unsigned int kCqLevel = 18;
 // List of psnr thresholds for speed settings 0-8 and 4 encoding modes
 const double kPsnrThreshold[][4] = {
-  { 34.9, 44.4, 39.5, 41.9 }, { 34.9, 44.4, 39.5, 41.9 },
-  { 34.9, 44.4, 39.4, 41.9 }, { 34.9, 44.4, 39.1, 41.8 },
-  { 34.9, 44.4, 39.1, 41.8 }, { 34.9, 44.29, 38.5, 41.8 },
-  { 34.9, 44.3, 38.5, 41.3 }, { 34.9, 44.3, 38.5, 40.8 },
-  { 34.9, 44.3, 38.5, 40.8 }
+  { 34.1, 44.4, 39.5, 41.9 }, { 34.1, 44.4, 39.5, 41.9 },
+  { 34.1, 44.4, 39.4, 41.9 }, { 34.1, 44.4, 39.1, 41.8 },
+  { 34.1, 44.4, 39.1, 41.8 }, { 34.1, 44.29, 38.5, 41.8 },
+  { 34.1, 44.3, 38.5, 41.3 }, { 34.1, 44.3, 38.5, 40.8 },
+  { 34.1, 44.3, 38.5, 40.8 }
 };
 
 struct TestVideoParam {
diff --git a/test/forced_max_frame_width_height_test.cc b/test/forced_max_frame_width_height_test.cc
index 5211d66..762de80 100644
--- a/test/forced_max_frame_width_height_test.cc
+++ b/test/forced_max_frame_width_height_test.cc
@@ -22,6 +22,10 @@
 #include "aom/aomcx.h"
 #include "aom/aom_encoder.h"
 #include "config/aom_config.h"
+#if CONFIG_AV1_DECODER
+#include "aom/aom_decoder.h"
+#include "aom/aomdx.h"
+#endif
 #include "gtest/gtest.h"
 
 namespace {
@@ -277,4 +281,292 @@
   EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
 }
 
+// A regression test for bug 555350218.
+TEST(EncodeForcedMaxFrameWidthHeight, ReducedStillPictureHeader) {
+  constexpr size_t kImageDataSize = 128 * 128 + 2 * 64 * 64;
+  std::unique_ptr<unsigned char[]> img_data(new unsigned char[kImageDataSize]);
+  ASSERT_NE(img_data, nullptr);
+  memset(img_data.get(), 128, kImageDataSize);
+  aom_image_t img;
+  EXPECT_EQ(&img,
+            aom_img_wrap(&img, AOM_IMG_FMT_I420, 128, 128, 1, img_data.get()));
+
+  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_limit = 1;
+
+  aom_codec_ctx_t enc;
+
+  cfg.g_forced_max_frame_width = 128;
+  cfg.g_forced_max_frame_height = 128;
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, iface, &cfg, 0));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
+
+  cfg.g_forced_max_frame_width = 256;
+  cfg.g_forced_max_frame_height = 256;
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, iface, &cfg, 0));
+  EXPECT_NE(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
+}
+
+TEST(EncodeForcedMaxFrameWidthHeight, DynamicUpscaleDoesNotForceKeyframe) {
+  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);
+
+  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;
+  cfg.g_lag_in_frames = 0;
+  cfg.kf_mode = AOM_KF_DISABLED;
+  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_control(&enc, AOME_SET_CPUUSED, 7));
+
+#if CONFIG_AV1_DECODER
+  aom_codec_ctx_t dec;
+  EXPECT_EQ(AOM_CODEC_OK,
+            aom_codec_dec_init(&dec, aom_codec_av1_dx(), nullptr, 0));
+#endif
+
+  // Frame 0: 128x128 keyframe.
+  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));
+
+  aom_codec_iter_t iter = nullptr;
+  const aom_codec_cx_pkt_t *pkt = aom_codec_get_cx_data(&enc, &iter);
+  ASSERT_NE(pkt, nullptr);
+  EXPECT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
+  EXPECT_TRUE(pkt->data.frame.flags & AOM_FRAME_IS_KEY);
+
+#if CONFIG_AV1_DECODER
+  EXPECT_EQ(
+      AOM_CODEC_OK,
+      aom_codec_decode(&dec, static_cast<const uint8_t *>(pkt->data.frame.buf),
+                       pkt->data.frame.sz, nullptr));
+  aom_codec_iter_t dec_iter = nullptr;
+  aom_image_t *dec_img = aom_codec_get_frame(&dec, &dec_iter);
+  ASSERT_NE(dec_img, nullptr);
+  EXPECT_EQ(dec_img->d_w, 128u);
+  EXPECT_EQ(dec_img->d_h, 128u);
+#endif
+
+  // Frame 1: Upscale to 256x256.
+  cfg.g_w = 256;
+  cfg.g_h = 256;
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_set(&enc, &cfg));
+
+  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, 1, 1, 0));
+
+  iter = nullptr;
+  pkt = aom_codec_get_cx_data(&enc, &iter);
+  ASSERT_NE(pkt, nullptr);
+  EXPECT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
+  EXPECT_FALSE(pkt->data.frame.flags & AOM_FRAME_IS_KEY);
+
+#if CONFIG_AV1_DECODER
+  EXPECT_EQ(
+      AOM_CODEC_OK,
+      aom_codec_decode(&dec, static_cast<const uint8_t *>(pkt->data.frame.buf),
+                       pkt->data.frame.sz, nullptr));
+  dec_iter = nullptr;
+  dec_img = aom_codec_get_frame(&dec, &dec_iter);
+  ASSERT_NE(dec_img, nullptr);
+  EXPECT_EQ(dec_img->d_w, 256u);
+  EXPECT_EQ(dec_img->d_h, 256u);
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&dec));
+#endif
+
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, nullptr, 0, 0, 0));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
+}
+
+TEST(EncodeForcedMaxFrameWidthHeight, DynamicUpscaleUpTo16x) {
+  constexpr size_t kImageDataSize = 2048 * 2048 + 2 * 1024 * 1024;
+  std::unique_ptr<unsigned char[]> img_data(new unsigned char[kImageDataSize]);
+  ASSERT_NE(img_data, nullptr);
+  memset(img_data.get(), 128, kImageDataSize);
+
+  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 = 2048;
+  cfg.g_forced_max_frame_height = 2048;
+  cfg.g_lag_in_frames = 0;
+  cfg.kf_mode = AOM_KF_DISABLED;
+  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_control(&enc, AOME_SET_CPUUSED, 7));
+
+#if CONFIG_AV1_DECODER
+  aom_codec_ctx_t dec;
+  EXPECT_EQ(AOM_CODEC_OK,
+            aom_codec_dec_init(&dec, aom_codec_av1_dx(), nullptr, 0));
+#endif
+
+  // Frame 0: 128x128 keyframe.
+  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));
+
+  aom_codec_iter_t iter = nullptr;
+  const aom_codec_cx_pkt_t *pkt = aom_codec_get_cx_data(&enc, &iter);
+  ASSERT_NE(pkt, nullptr);
+  EXPECT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
+  EXPECT_TRUE(pkt->data.frame.flags & AOM_FRAME_IS_KEY);
+
+#if CONFIG_AV1_DECODER
+  EXPECT_EQ(
+      AOM_CODEC_OK,
+      aom_codec_decode(&dec, static_cast<const uint8_t *>(pkt->data.frame.buf),
+                       pkt->data.frame.sz, nullptr));
+#endif
+
+  // Frame 1: Upscale 16x to 2048x2048.
+  cfg.g_w = 2048;
+  cfg.g_h = 2048;
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_set(&enc, &cfg));
+
+  EXPECT_EQ(&img, aom_img_wrap(&img, AOM_IMG_FMT_I420, 2048, 2048, 1,
+                               img_data.get()));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 1, 1, 0));
+
+  iter = nullptr;
+  pkt = aom_codec_get_cx_data(&enc, &iter);
+  ASSERT_NE(pkt, nullptr);
+  EXPECT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
+  EXPECT_FALSE(pkt->data.frame.flags & AOM_FRAME_IS_KEY);
+
+#if CONFIG_AV1_DECODER
+  EXPECT_EQ(
+      AOM_CODEC_OK,
+      aom_codec_decode(&dec, static_cast<const uint8_t *>(pkt->data.frame.buf),
+                       pkt->data.frame.sz, nullptr));
+  aom_codec_iter_t dec_iter = nullptr;
+  aom_image_t *dec_img = aom_codec_get_frame(&dec, &dec_iter);
+  ASSERT_NE(dec_img, nullptr);
+  EXPECT_EQ(dec_img->d_w, 2048u);
+  EXPECT_EQ(dec_img->d_h, 2048u);
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&dec));
+#endif
+
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, nullptr, 0, 0, 0));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
+}
+
+// Values of the AV1_LEVEL enum in av1/common/enums.h, which is not part of the
+// public API.
+//
+// Passing kLevelKeepStats as the target level only switches on level statistics
+// collection. Unlike a real target level it does not constrain the encoder, so
+// it does not change what the rest of the test measures.
+constexpr int kSeqLevelIdx5_0 = 12;
+constexpr int kLevelKeepStats = 32;
+constexpr int kMaxOperatingPoints = 32;
+
+TEST(EncodeForcedMaxFrameWidthHeight,
+     SequenceLevelIndexUsesForcedMaxDimensions) {
+  constexpr size_t kImageDataSize = 2560 * 1536 + 2 * 1280 * 768;
+  std::unique_ptr<unsigned char[]> img_data(new unsigned char[kImageDataSize]);
+  ASSERT_NE(img_data, nullptr);
+  memset(img_data.get(), 128, kImageDataSize);
+
+  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 = 640;
+  cfg.g_h = 384;
+  cfg.g_forced_max_frame_width = 2560;
+  cfg.g_forced_max_frame_height = 1536;
+  cfg.g_lag_in_frames = 0;
+  cfg.kf_mode = AOM_KF_DISABLED;
+  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_control(&enc, AOME_SET_CPUUSED, 7));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_control(&enc, AV1E_SET_TARGET_SEQ_LEVEL_IDX,
+                                            kLevelKeepStats));
+
+#if CONFIG_AV1_DECODER
+  aom_codec_ctx_t dec;
+  EXPECT_EQ(AOM_CODEC_OK,
+            aom_codec_dec_init(&dec, aom_codec_av1_dx(), nullptr, 0));
+#endif
+
+  // Frame 0: 640x384 keyframe.
+  aom_image_t img;
+  EXPECT_EQ(&img,
+            aom_img_wrap(&img, AOM_IMG_FMT_I420, 640, 384, 1, img_data.get()));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 0, 1, 0));
+
+  aom_codec_iter_t iter = nullptr;
+  const aom_codec_cx_pkt_t *pkt = aom_codec_get_cx_data(&enc, &iter);
+  ASSERT_NE(pkt, nullptr);
+  EXPECT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
+
+#if CONFIG_AV1_DECODER
+  EXPECT_EQ(
+      AOM_CODEC_OK,
+      aom_codec_decode(&dec, static_cast<const uint8_t *>(pkt->data.frame.buf),
+                       pkt->data.frame.sz, nullptr));
+#endif
+
+  // Frame 1: Upscale 4x to 2560x1536.
+  cfg.g_w = 2560;
+  cfg.g_h = 1536;
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_set(&enc, &cfg));
+
+  EXPECT_EQ(&img, aom_img_wrap(&img, AOM_IMG_FMT_I420, 2560, 1536, 1,
+                               img_data.get()));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, &img, 1, 1, 0));
+
+  iter = nullptr;
+  pkt = aom_codec_get_cx_data(&enc, &iter);
+  ASSERT_NE(pkt, nullptr);
+  EXPECT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
+  EXPECT_FALSE(pkt->data.frame.flags & AOM_FRAME_IS_KEY);
+
+#if CONFIG_AV1_DECODER
+  EXPECT_EQ(
+      AOM_CODEC_OK,
+      aom_codec_decode(&dec, static_cast<const uint8_t *>(pkt->data.frame.buf),
+                       pkt->data.frame.sz, nullptr));
+  aom_codec_iter_t dec_iter = nullptr;
+  aom_image_t *dec_img = aom_codec_get_frame(&dec, &dec_iter);
+  ASSERT_NE(dec_img, nullptr);
+  EXPECT_EQ(dec_img->d_w, 2560u);
+  EXPECT_EQ(dec_img->d_h, 1536u);
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&dec));
+#endif
+
+  // The level the encoder derives from the statistics it collected has to
+  // account for the upscaled frame, not just the initial 640x384 one.
+  int seq_level_idx[kMaxOperatingPoints];
+  EXPECT_EQ(AOM_CODEC_OK,
+            aom_codec_control(&enc, AV1E_GET_SEQ_LEVEL_IDX, seq_level_idx));
+  EXPECT_EQ(seq_level_idx[0], kSeqLevelIdx5_0);
+
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, nullptr, 0, 0, 0));
+  EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));
+}
+
 }  // namespace
diff --git a/test/frame_size_tests.cc b/test/frame_size_tests.cc
index 4ed8354..96d5448 100644
--- a/test/frame_size_tests.cc
+++ b/test/frame_size_tests.cc
@@ -222,11 +222,25 @@
       iter = nullptr;
       while ((pkt = aom_codec_get_cx_data(enc.get(), &iter)) != nullptr) {
         ASSERT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
-        // The frame following a resolution change should be a keyframe as the
-        // change is too extreme to allow previous references to be used.
-        if (i == 0 || usage_ == AOM_USAGE_ALL_INTRA) {
+        // All the resolution changes above are within the reference frame
+        // scaling limits (up to 16x up and 2x down). In single pass realtime
+        // mode without lookahead, and with the maximum frame size declared up
+        // front via g_forced_max_frame_width/height, such changes are coded as
+        // inter frames that scale their references, so only the very first
+        // frame is a keyframe. Other modes force a keyframe on every
+        // resolution change.
+        const bool scales_references = usage_ == AOM_USAGE_REALTIME;
+        if (usage_ == AOM_USAGE_ALL_INTRA || frame_count == 0) {
           EXPECT_NE(pkt->data.frame.flags & AOM_FRAME_IS_KEY, 0u)
               << "frame " << frame_count;
+        } else if (i == 0) {
+          if (scales_references) {
+            EXPECT_EQ(pkt->data.frame.flags & AOM_FRAME_IS_KEY, 0u)
+                << "frame " << frame_count;
+          } else {
+            EXPECT_NE(pkt->data.frame.flags & AOM_FRAME_IS_KEY, 0u)
+                << "frame " << frame_count;
+          }
         }
         frame_count++;
       }
diff --git a/test/ratectrl_rtc_test.cc b/test/ratectrl_rtc_test.cc
index 4760d39..9120122 100644
--- a/test/ratectrl_rtc_test.cc
+++ b/test/ratectrl_rtc_test.cc
@@ -551,6 +551,8 @@
   void TestGetCdefInfoRateControl();
   void TestCreateRateControlConfig();
   void TestDestroyRateControlRTC();
+  void TestBitDepthRateControl();
+  void TestLoopFilterLevelBitDepth();
   void SetConfig();
 
  private:
@@ -694,7 +696,7 @@
   av1_ratecontrol_rtc_init_ratecontrol_config(&config);
   ASSERT_EQ(config.width, 1280);
   ASSERT_EQ(config.height, 720);
-  // only width and height is checked. can be extended
+  ASSERT_EQ(config.bit_depth, 8);
 
   av1_ratecontrol_rtc_destroy(controller);
 }
@@ -706,6 +708,72 @@
   av1_ratecontrol_rtc_destroy(controller);
 }
 
+void RcExternMethodsInterfaceTest::TestBitDepthRateControl() {
+  // Invalid bit-depth values should fail.
+  rc_cfg_.bit_depth = 9;
+  AomAV1RateControlRTC *invalid_controller =
+      av1_ratecontrol_rtc_create(&rc_cfg_);
+  ASSERT_EQ(invalid_controller, nullptr);
+
+  rc_cfg_.bit_depth = -1;
+  invalid_controller = av1_ratecontrol_rtc_create(&rc_cfg_);
+  ASSERT_EQ(invalid_controller, nullptr);
+
+  rc_cfg_.bit_depth = 14;
+  invalid_controller = av1_ratecontrol_rtc_create(&rc_cfg_);
+  ASSERT_EQ(invalid_controller, nullptr);
+
+  // 8-bit depth is always supported.
+  rc_cfg_.bit_depth = 8;
+  AomAV1RateControlRTC *controller_8 = av1_ratecontrol_rtc_create(&rc_cfg_);
+  ASSERT_NE(controller_8, nullptr);
+  // Attempting to change bit-depth dynamically should fail.
+  rc_cfg_.bit_depth = 10;
+  ASSERT_FALSE(av1_ratecontrol_rtc_update(controller_8, &rc_cfg_));
+  av1_ratecontrol_rtc_destroy(controller_8);
+
+  rc_cfg_.bit_depth = 10;
+  AomAV1RateControlRTC *controller_10 = av1_ratecontrol_rtc_create(&rc_cfg_);
+  ASSERT_NE(controller_10, nullptr);
+  rc_cfg_.bit_depth = 8;
+  ASSERT_FALSE(av1_ratecontrol_rtc_update(controller_10, &rc_cfg_));
+  av1_ratecontrol_rtc_destroy(controller_10);
+
+  rc_cfg_.bit_depth = 12;
+  AomAV1RateControlRTC *controller_12 = av1_ratecontrol_rtc_create(&rc_cfg_);
+  ASSERT_NE(controller_12, nullptr);
+  av1_ratecontrol_rtc_destroy(controller_12);
+}
+
+void RcExternMethodsInterfaceTest::TestLoopFilterLevelBitDepth() {
+  frame_params_.spatial_layer_id = 0;
+  frame_params_.temporal_layer_id = 0;
+  frame_params_.frame_type = kAomKeyFrame;
+
+  rc_cfg_.bit_depth = 8;
+  AomAV1RateControlRTC *controller_8 = av1_ratecontrol_rtc_create(&rc_cfg_);
+  ASSERT_NE(controller_8, nullptr);
+  ASSERT_EQ(av1_ratecontrol_rtc_compute_qp(controller_8, &frame_params_),
+            kAomFrameDropDecisionOk);
+  const AomAV1LoopfilterLevel lpf_8 =
+      av1_ratecontrol_rtc_get_loop_filter_level(controller_8);
+  av1_ratecontrol_rtc_destroy(controller_8);
+
+  rc_cfg_.bit_depth = 10;
+  AomAV1RateControlRTC *controller_10 = av1_ratecontrol_rtc_create(&rc_cfg_);
+  ASSERT_NE(controller_10, nullptr);
+  ASSERT_EQ(av1_ratecontrol_rtc_compute_qp(controller_10, &frame_params_),
+            kAomFrameDropDecisionOk);
+  const AomAV1LoopfilterLevel lpf_10 =
+      av1_ratecontrol_rtc_get_loop_filter_level(controller_10);
+  av1_ratecontrol_rtc_destroy(controller_10);
+
+  // For 10-bit AV1 encoding, the estimated loopfilter level should be stronger
+  // than for 8-bit at the same configuration.
+  EXPECT_GT(lpf_10.filter_level[0], lpf_8.filter_level[0]);
+  EXPECT_GT(lpf_10.filter_level[1], lpf_8.filter_level[1]);
+}
+
 TEST_P(RcInterfaceTest, OneLayer) { RunOneLayer(); }
 
 TEST_P(RcInterfaceTest, OneLayerDropFramesCBR) { RunOneLayerDropFramesCBR(); }
@@ -764,6 +832,14 @@
   TestDestroyRateControlRTC();
 }
 
+TEST_P(RcExternMethodsInterfaceTest, BitDepthRateControllerTest) {
+  TestBitDepthRateControl();
+}
+
+TEST_P(RcExternMethodsInterfaceTest, LoopFilterLevelBitDepthTest) {
+  TestLoopFilterLevelBitDepth();
+}
+
 AV1_INSTANTIATE_TEST_SUITE(RcInterfaceTest, ::testing::Values(0, 3));
 AV1_INSTANTIATE_TEST_SUITE(RcExternMethodsInterfaceTest,
                            ::testing::Values(0, 3));