Snap for 11383711 from 4c1c882d56675a5d82eef3de3b199ba0a41b6da1 to mainline-ipsec-release

Change-Id: I904877e16e32cf96c3f004d771e15a33220b6f58
diff --git a/AUTHORS b/AUTHORS
index ade7a1a..509c0d1 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -235,6 +235,7 @@
 Rostislav Pehlivanov <rpehlivanov@mozilla.com>
 Ruiling Song <ruiling.song@intel.com>
 Rui Ueyama <ruiu@google.com>
+Ruoyu Zhong <zhongruoyu@outlook.com>
 Rupert Swarbrick <rupert.swarbrick@argondesign.com>
 Ryan Lei <ryanlei@fb.com>
 Ryan Overbeck <rover@google.com>
diff --git a/CHANGELOG b/CHANGELOG
index 9f0cabb..daa83a1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,24 @@
+2024-01-17 v3.8.1
+  This release includes several bug fixes. This release is ABI
+  compatible with the last release. See
+  https://aomedia.googlesource.com/aom/+log/v3.8.0..v3.8.1 for all the
+  commits in this release.
+
+  - Bug Fixes
+    * aomedia:3520: get_cubic_kernel_dbl: Assertion `0 <= x && x < 1'
+      failed.
+    * aomedia:3526: alloc_compressor_data() is called during every
+      aom_codec_control() call on the encoder.
+    * aomedia:3527: aom/av1/encoder/mcomp.c:1810: av1_full_pixel_search:
+      Assertion `ms_params->ms_buffers.ref->width ==
+      ms_params->ms_buffers.src->width' failed.
+    * aomedia:3534: libaom encoder crashed by AOM_USAGE_ALL_INTRA and
+      AOM_EFLAG_NO_REF_LAST flags.
+    * b/310455204: Recreate workers if necessary.
+    * b/310548198: Update frame size in actual encoding.
+    * b/314858909: Do not use adaptive error estimate.
+    * Fix a hang of cmake on arm64 macOS with cmake 3.27.0 or later.
+
 2023-11-30 v3.8.0
   This release includes new codec interfaces, compression efficiency and
   perceptual improvements, speedup and memory optimizations and many bug
@@ -70,7 +91,7 @@
     * aomedia:3478: GCC 12.2.0 emits a -Wstringop-overflow warning on
       aom/av1/encoder/motion_search_facade.c
     * aomedia:3489: Detect encoder and image high bit depth mismatch
-    * aomedia:3491: heap-buffer-overflow on frame size change
+    * aomedia:3491: heap-buffer-overflow on frame size change (CVE-2023-6879)
     * b/303023614:  Segfault at encoding time for high bit depth images
 
 2023-08-10 v3.7.0
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c97ccd1..308a93d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -59,7 +59,7 @@
 #
 # We set SO_FILE_VERSION = [c-a].a.r
 set(LT_CURRENT 11)
-set(LT_REVISION 0)
+set(LT_REVISION 1)
 set(LT_AGE 8)
 math(EXPR SO_VERSION "${LT_CURRENT} - ${LT_AGE}")
 set(SO_FILE_VERSION "${SO_VERSION}.${LT_AGE}.${LT_REVISION}")
diff --git a/METADATA b/METADATA
index 54033f1..35df8b8 100644
--- a/METADATA
+++ b/METADATA
@@ -20,10 +20,10 @@
     type: GIT
     value: "https://aomedia.googlesource.com/aom/"
   }
-  version: "v3.8.0"
+  version: "v3.8.1"
   last_upgrade_date {
-    year: 2023
-    month: 12
-    day: 6
+    year: 2024
+    month: 1
+    day: 22
   }
 }
diff --git a/README.android b/README.android
index 09091d5..50860bf 100644
--- a/README.android
+++ b/README.android
@@ -1,12 +1,12 @@
 Name: libaom
 URL: https://aomedia.org
-Version: v3.8.0
+Version: v3.8.1
 License: BSD
 License File: libaom/LICENSE
 
-Date: Wednesday December 06 2023
-Branch: v3.8.0
-Commit: b681eac83963950afc7be55df56c22fa5210aaa2
+Date: Monday January 22 2024
+Branch: v3.8.1
+Commit: bb6430482199eaefbeaaa396600935082bc43f66
 
 Description:
 Contains the sources used to compile libaom.
diff --git a/README.version b/README.version
index c6bee0a..3b99200 100644
--- a/README.version
+++ b/README.version
@@ -1,3 +1,3 @@
 URL: https://aomedia.googlesource.com/aom/
-Version: v3.8.0
+Version: v3.8.1
 Local Modifications:
diff --git a/aom_dsp/flow_estimation/arm/disflow_neon.c b/aom_dsp/flow_estimation/arm/disflow_neon.c
index e2ba0e0..f091366 100644
--- a/aom_dsp/flow_estimation/arm/disflow_neon.c
+++ b/aom_dsp/flow_estimation/arm/disflow_neon.c
@@ -20,7 +20,14 @@
 #include "config/aom_dsp_rtcd.h"
 
 static INLINE void get_cubic_kernel_dbl(double x, double *kernel) {
-  assert(0 <= x && x < 1);
+  // Check that the fractional position is in range.
+  //
+  // Note: x is calculated from (eg.) `u_frac = u - floor(u)`.
+  // Mathematically, this implies that 0 <= x < 1. However, in practice it is
+  // possible to have x == 1 due to floating point rounding. This is fine,
+  // and we still interpolate correctly if we allow x = 1.
+  assert(0 <= x && x <= 1);
+
   double x2 = x * x;
   double x3 = x2 * x;
   kernel[0] = -0.5 * x + x2 - 0.5 * x3;
diff --git a/aom_dsp/flow_estimation/disflow.c b/aom_dsp/flow_estimation/disflow.c
index a010c81..ed5559c 100644
--- a/aom_dsp/flow_estimation/disflow.c
+++ b/aom_dsp/flow_estimation/disflow.c
@@ -53,7 +53,14 @@
 #define UPSAMPLE_CENTER_OFFSET ((DOWNSAMPLE_FACTOR - 1) / 2)
 
 static INLINE void get_cubic_kernel_dbl(double x, double *kernel) {
-  assert(0 <= x && x < 1);
+  // Check that the fractional position is in range.
+  //
+  // Note: x is calculated from (eg.) `u_frac = u - floor(u)`.
+  // Mathematically, this implies that 0 <= x < 1. However, in practice it is
+  // possible to have x == 1 due to floating point rounding. This is fine,
+  // and we still interpolate correctly if we allow x = 1.
+  assert(0 <= x && x <= 1);
+
   double x2 = x * x;
   double x3 = x2 * x;
   kernel[0] = -0.5 * x + x2 - 0.5 * x3;
diff --git a/aom_dsp/flow_estimation/x86/disflow_sse4.c b/aom_dsp/flow_estimation/x86/disflow_sse4.c
index 77784ee..3c2159a 100644
--- a/aom_dsp/flow_estimation/x86/disflow_sse4.c
+++ b/aom_dsp/flow_estimation/x86/disflow_sse4.c
@@ -28,7 +28,14 @@
 
 // Note: Max sum(+ve coefficients) = 1.125 * scale
 static INLINE void get_cubic_kernel_dbl(double x, double *kernel) {
-  assert(0 <= x && x < 1);
+  // Check that the fractional position is in range.
+  //
+  // Note: x is calculated from (eg.) `u_frac = u - floor(u)`.
+  // Mathematically, this implies that 0 <= x < 1. However, in practice it is
+  // possible to have x == 1 due to floating point rounding. This is fine,
+  // and we still interpolate correctly if we allow x = 1.
+  assert(0 <= x && x <= 1);
+
   double x2 = x * x;
   double x3 = x2 * x;
   kernel[0] = -0.5 * x + x2 - 0.5 * x3;
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index 7256f48..1175a32 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -1499,20 +1499,27 @@
 
 static aom_codec_err_t encoder_set_config(aom_codec_alg_priv_t *ctx,
                                           const aom_codec_enc_cfg_t *cfg) {
-  InitialDimensions *const initial_dimensions =
-      &ctx->ppi->cpi->initial_dimensions;
   aom_codec_err_t res;
   int force_key = 0;
 
   if (cfg->g_w != ctx->cfg.g_w || cfg->g_h != ctx->cfg.g_h) {
     if (cfg->g_lag_in_frames > 1 || cfg->g_pass != AOM_RC_ONE_PASS)
       ERROR("Cannot change width or height after initialization");
-    if (!valid_ref_frame_size(ctx->cfg.g_w, ctx->cfg.g_h, cfg->g_w, cfg->g_h) ||
-        (initial_dimensions->width &&
-         (int)cfg->g_w > initial_dimensions->width) ||
-        (initial_dimensions->height &&
-         (int)cfg->g_h > initial_dimensions->height))
+    // Note: function encoder_set_config() is allowed to be called multiple
+    // times. However, when the original frame width or height is less than two
+    // times of the new frame width or height, a forced key frame should be
+    // used. To make sure the correct detection of a forced key frame, we need
+    // to update the frame width and height only when the actual encoding is
+    // performed. cpi->last_coded_width and cpi->last_coded_height are used to
+    // track the actual coded frame size.
+    if (ctx->ppi->cpi->last_coded_width && ctx->ppi->cpi->last_coded_height &&
+        (!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))) {
       force_key = 1;
+    }
   }
 
   if (ctx->monochrome_on_init && cfg->monochrome == 0) {
@@ -2924,6 +2931,9 @@
   AV1_COMP *cpi_lap = ppi->cpi_lap;
   if (ppi->cpi == NULL) return AOM_CODEC_INVALID_PARAM;
 
+  ppi->cpi->last_coded_width = ppi->cpi->oxcf.frm_dim_cfg.width;
+  ppi->cpi->last_coded_height = ppi->cpi->oxcf.frm_dim_cfg.height;
+
   if (ppi->lap_enabled && cpi_lap == NULL &&
       ppi->cpi->oxcf.pass == AOM_RC_ONE_PASS)
     return AOM_CODEC_INVALID_PARAM;
@@ -3123,12 +3133,25 @@
       av1_compute_num_workers_for_mt(cpi);
       num_workers = av1_get_max_num_workers(cpi);
     }
-    if ((num_workers > 1) && (ppi->p_mt_info.num_workers == 0)) {
+    if (num_workers > 1 && ppi->p_mt_info.num_workers < num_workers) {
       // Obtain the maximum no. of frames that can be supported in a parallel
       // encode set.
       if (is_stat_consumption_stage(cpi)) {
         ppi->num_fp_contexts = av1_compute_num_fp_contexts(ppi, &cpi->oxcf);
       }
+      if (ppi->p_mt_info.num_workers > 0) {
+        av1_terminate_workers(ppi);
+        free_thread_data(ppi);
+        aom_free(ppi->p_mt_info.tile_thr_data);
+        ppi->p_mt_info.tile_thr_data = NULL;
+        aom_free(ppi->p_mt_info.workers);
+        ppi->p_mt_info.workers = NULL;
+        ppi->p_mt_info.num_workers = 0;
+        for (int j = 0; j < ppi->num_fp_contexts; j++) {
+          aom_free(ppi->parallel_cpi[j]->td.tctx);
+          ppi->parallel_cpi[j]->td.tctx = NULL;
+        }
+      }
       av1_create_workers(ppi, num_workers);
       av1_init_tile_thread_data(ppi, cpi->oxcf.pass == AOM_RC_FIRST_PASS);
     }
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 6a4332c..07b6ffe 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -642,6 +642,12 @@
   cm->height = oxcf->frm_dim_cfg.height;
   cpi->is_dropped_frame = false;
 
+  InitialDimensions *const initial_dimensions = &cpi->initial_dimensions;
+  initial_dimensions->width = cm->width;
+  initial_dimensions->height = cm->height;
+
+  cpi->frame_size_related_setup_done = false;
+
   alloc_compressor_data(cpi);
 
   // Single thread case: use counts in common.
@@ -916,14 +922,14 @@
     cpi->td.firstpass_ctx = NULL;
     alloc_compressor_data(cpi);
     realloc_segmentation_maps(cpi);
-    initial_dimensions->width = initial_dimensions->height = 0;
+    initial_dimensions->width = cm->width;
+    initial_dimensions->height = cm->height;
+    cpi->frame_size_related_setup_done = false;
   }
   av1_update_frame_size(cpi);
 
   rc->is_src_frame_alt_ref = 0;
 
-  set_tile_info(cm, &cpi->oxcf.tile_cfg);
-
   if (!cpi->ppi->rtc_ref.set_ref_frame_config)
     cpi->ext_flags.refresh_frame.update_pending = 0;
   cpi->ext_flags.refresh_frame_context_pending = 0;
@@ -1614,17 +1620,6 @@
   snprintf((H) + strlen(H), sizeof(H) - strlen(H), (T), (V))
 #endif  // CONFIG_INTERNAL_STATS
 
-// This function will change the state and free the mutex of corresponding
-// workers and terminate the object. The object can not be re-used unless a call
-// to reset() is made.
-static AOM_INLINE void terminate_worker_data(AV1_PRIMARY *ppi) {
-  PrimaryMultiThreadInfo *const p_mt_info = &ppi->p_mt_info;
-  for (int t = p_mt_info->num_workers - 1; t >= 0; --t) {
-    AVxWorker *const worker = &p_mt_info->workers[t];
-    aom_get_worker_interface()->end(worker);
-  }
-}
-
 void av1_remove_primary_compressor(AV1_PRIMARY *ppi) {
   if (!ppi) return;
 #if !CONFIG_REALTIME_ONLY
@@ -1652,11 +1647,14 @@
   av1_tpl_dealloc(&tpl_data->tpl_mt_sync);
 #endif
 
-  terminate_worker_data(ppi);
+  av1_terminate_workers(ppi);
   free_thread_data(ppi);
 
   aom_free(ppi->p_mt_info.tile_thr_data);
+  ppi->p_mt_info.tile_thr_data = NULL;
   aom_free(ppi->p_mt_info.workers);
+  ppi->p_mt_info.workers = NULL;
+  ppi->p_mt_info.num_workers = 0;
 
   aom_free(ppi);
 }
@@ -2071,13 +2069,15 @@
 #endif
 }
 
+// TODO(chengchen): consider renaming this function as it is necessary
+// for the encoder to setup critical parameters, and it does not
+// deal with initial width any longer.
 void av1_check_initial_width(AV1_COMP *cpi, int use_highbitdepth,
                              int subsampling_x, int subsampling_y) {
   AV1_COMMON *const cm = &cpi->common;
   SequenceHeader *const seq_params = cm->seq_params;
-  InitialDimensions *const initial_dimensions = &cpi->initial_dimensions;
 
-  if (!initial_dimensions->width ||
+  if (!cpi->frame_size_related_setup_done ||
       seq_params->use_highbitdepth != use_highbitdepth ||
       seq_params->subsampling_x != subsampling_x ||
       seq_params->subsampling_y != subsampling_y) {
@@ -2097,9 +2097,8 @@
 
     init_motion_estimation(cpi);  // TODO(agrange) This can be removed.
 
-    initial_dimensions->width = cm->width;
-    initial_dimensions->height = cm->height;
     cpi->initial_mbs = cm->mi_params.MBs;
+    cpi->frame_size_related_setup_done = true;
   }
 }
 
@@ -2136,18 +2135,18 @@
   setup_denoiser_buffer(cpi);
 #endif
 
-  if (initial_dimensions->width && initial_dimensions->height &&
-      (cm->width > initial_dimensions->width ||
-       cm->height > initial_dimensions->height)) {
+  if (cm->width > initial_dimensions->width ||
+      cm->height > initial_dimensions->height) {
     av1_free_context_buffers(cm);
     av1_free_shared_coeff_buffer(&cpi->td.shared_coeff_buf);
     av1_free_sms_tree(&cpi->td);
     av1_free_pmc(cpi->td.firstpass_ctx, av1_num_planes(cm));
     cpi->td.firstpass_ctx = NULL;
-    alloc_mb_mode_info_buffers(cpi);
     alloc_compressor_data(cpi);
     realloc_segmentation_maps(cpi);
-    initial_dimensions->width = initial_dimensions->height = 0;
+    initial_dimensions->width = cm->width;
+    initial_dimensions->height = cm->height;
+    cpi->frame_size_related_setup_done = false;
   }
   alloc_mb_mode_info_buffers(cpi);
   av1_update_frame_size(cpi);
@@ -2229,6 +2228,7 @@
 
   init_motion_estimation(cpi);
 
+  int has_valid_ref_frame = 0;
   for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) {
     RefCntBuffer *const buf = get_ref_frame_buf(cm, ref_frame);
     if (buf != NULL) {
@@ -2236,9 +2236,15 @@
       av1_setup_scale_factors_for_frame(sf, buf->buf.y_crop_width,
                                         buf->buf.y_crop_height, cm->width,
                                         cm->height);
+      has_valid_ref_frame |= av1_is_valid_scale(sf);
       if (av1_is_scaled(sf)) aom_extend_frame_borders(&buf->buf, num_planes);
     }
   }
+  if (!frame_is_intra_only(cm) && !has_valid_ref_frame) {
+    aom_internal_error(
+        cm->error, AOM_CODEC_CORRUPT_FRAME,
+        "Can't find at least one reference frame with valid size");
+  }
 
   av1_setup_scale_factors_for_frame(&cm->sf_identity, cm->width, cm->height,
                                     cm->width, cm->height);
@@ -2582,14 +2588,18 @@
     if (cpi->ref_frame_flags & av1_ref_frame_flag_list[GOLDEN_FRAME]) {
       const YV12_BUFFER_CONFIG *const ref =
           get_ref_frame_yv12_buf(cm, GOLDEN_FRAME);
-      if (ref->y_crop_width != cm->width || ref->y_crop_height != cm->height)
+      if (ref == NULL || ref->y_crop_width != cm->width ||
+          ref->y_crop_height != cm->height) {
         cpi->ref_frame_flags ^= AOM_GOLD_FLAG;
+      }
     }
     if (cpi->ref_frame_flags & av1_ref_frame_flag_list[ALTREF_FRAME]) {
       const YV12_BUFFER_CONFIG *const ref =
           get_ref_frame_yv12_buf(cm, ALTREF_FRAME);
-      if (ref->y_crop_width != cm->width || ref->y_crop_height != cm->height)
+      if (ref == NULL || ref->y_crop_width != cm->width ||
+          ref->y_crop_height != cm->height) {
         cpi->ref_frame_flags ^= AOM_ALT_FLAG;
+      }
     }
   }
 
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index adec789..0a8bcde 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -3163,7 +3163,9 @@
   FRAME_INDEX_SET frame_index_set;
 
   /*!
-   * Structure to store the dimensions of current frame.
+   * Structure to store the cm->width and cm->height in the last call
+   * of alloc_compressor_data().
+   * TODO(chengchen): rename this variable or delete it.
    */
   InitialDimensions initial_dimensions;
 
@@ -3176,6 +3178,24 @@
   int initial_mbs;
 
   /*!
+   * Flag to indicate whether the frame size inforamation has been
+   * setup and propagated to associated allocations.
+   */
+  bool frame_size_related_setup_done;
+
+  /*!
+   * The width of the frame that is lastly encoded.
+   * It is updated in the function "encoder_encode()".
+   */
+  int last_coded_width;
+
+  /*!
+   * The height of the frame that is lastly encoded.
+   * It is updated in the function "encoder_encode()".
+   */
+  int last_coded_height;
+
+  /*!
    * Resize related parameters.
    */
   ResizePendingParams resize_pending_params;
@@ -3582,6 +3602,8 @@
 
   /*!
    * SSE between the current frame and the reconstructed last frame
+   * It is only used for CBR mode.
+   * It is not used if the reference frame has a different frame size.
    */
   uint64_t rec_sse;
 
diff --git a/av1/encoder/ethread.c b/av1/encoder/ethread.c
index b878797..c15e396 100644
--- a/av1/encoder/ethread.c
+++ b/av1/encoder/ethread.c
@@ -1067,6 +1067,7 @@
 void av1_create_workers(AV1_PRIMARY *ppi, int num_workers) {
   PrimaryMultiThreadInfo *const p_mt_info = &ppi->p_mt_info;
   const AVxWorkerInterface *const winterface = aom_get_worker_interface();
+  assert(p_mt_info->num_workers == 0);
 
   AOM_CHECK_MEM_ERROR(&ppi->error, p_mt_info->workers,
                       aom_malloc(num_workers * sizeof(*p_mt_info->workers)));
@@ -1098,6 +1099,17 @@
   }
 }
 
+// This function will change the state and free the mutex of corresponding
+// workers and terminate the object. The object can not be re-used unless a call
+// to reset() is made.
+void av1_terminate_workers(AV1_PRIMARY *ppi) {
+  PrimaryMultiThreadInfo *const p_mt_info = &ppi->p_mt_info;
+  for (int t = 0; t < p_mt_info->num_workers; ++t) {
+    AVxWorker *const worker = &p_mt_info->workers[t];
+    aom_get_worker_interface()->end(worker);
+  }
+}
+
 // This function returns 1 if frame parallel encode is supported for
 // the current configuration. Returns 0 otherwise.
 static AOM_INLINE int is_fpmt_config(AV1_PRIMARY *ppi, AV1EncoderConfig *oxcf) {
diff --git a/av1/encoder/ethread.h b/av1/encoder/ethread.h
index 4f4c232..f3f8629 100644
--- a/av1/encoder/ethread.h
+++ b/av1/encoder/ethread.h
@@ -88,6 +88,8 @@
 
 void av1_create_workers(AV1_PRIMARY *ppi, int num_workers);
 
+void av1_terminate_workers(AV1_PRIMARY *ppi);
+
 void av1_init_frame_mt(AV1_PRIMARY *ppi, AV1_COMP *cpi);
 
 void av1_init_cdef_worker(AV1_COMP *cpi);
diff --git a/av1/encoder/firstpass.c b/av1/encoder/firstpass.c
index 7ddc3e3..3631113 100644
--- a/av1/encoder/firstpass.c
+++ b/av1/encoder/firstpass.c
@@ -252,9 +252,9 @@
 
 // Refine the motion search range according to the frame dimension
 // for first pass test.
-static int get_search_range(const InitialDimensions *initial_dimensions) {
+static int get_search_range(int width, int height) {
   int sr = 0;
-  const int dim = AOMMIN(initial_dimensions->width, initial_dimensions->height);
+  const int dim = AOMMIN(width, height);
 
   while ((dim << sr) < MAX_FULL_PEL_VAL) ++sr;
   return sr;
@@ -293,18 +293,19 @@
                                                 const MV *ref_mv,
                                                 FULLPEL_MV *best_mv,
                                                 int *best_motion_err) {
+  AV1_COMMON *const cm = &cpi->common;
   MACROBLOCKD *const xd = &x->e_mbd;
   FULLPEL_MV start_mv = get_fullmv_from_mv(ref_mv);
   int tmp_err;
   const BLOCK_SIZE bsize = xd->mi[0]->bsize;
   const int new_mv_mode_penalty = NEW_MV_MODE_PENALTY;
-  const int sr = get_search_range(&cpi->initial_dimensions);
+  const int sr = get_search_range(cm->width, cm->height);
   const int step_param = cpi->sf.fp_sf.reduce_mv_step_param + sr;
 
   const search_site_config *first_pass_search_sites =
       av1_get_first_pass_search_site_config(cpi, x, NSTEP);
   const int fine_search_interval =
-      cpi->is_screen_content_type && cpi->common.features.allow_intrabc;
+      cpi->is_screen_content_type && cm->features.allow_intrabc;
   FULLPEL_MOTION_SEARCH_PARAMS ms_params;
   av1_make_default_fullpel_ms_params(&ms_params, cpi, x, bsize, ref_mv,
                                      start_mv, first_pass_search_sites, NSTEP,
diff --git a/av1/encoder/mcomp.c b/av1/encoder/mcomp.c
index a8b0d10..2462f1b 100644
--- a/av1/encoder/mcomp.c
+++ b/av1/encoder/mcomp.c
@@ -1807,7 +1807,6 @@
   }
 
   assert(ms_params->ms_buffers.ref->stride == ms_params->search_sites->stride);
-  assert(ms_params->ms_buffers.ref->width == ms_params->ms_buffers.src->width);
 
   switch (search_method) {
     case FAST_BIGDIA:
diff --git a/av1/encoder/pass2_strategy.c b/av1/encoder/pass2_strategy.c
index 3641c8b..d85440d 100644
--- a/av1/encoder/pass2_strategy.c
+++ b/av1/encoder/pass2_strategy.c
@@ -999,10 +999,9 @@
                                 GF_GROUP_STATS *gf_stats) {
   RATE_CONTROL *const rc = &cpi->rc;
   TWO_PASS *const twopass = &cpi->ppi->twopass;
-  InitialDimensions *const initial_dimensions = &cpi->initial_dimensions;
+  AV1_COMMON *const cm = &cpi->common;
   // Motion breakout threshold for loop below depends on image size.
-  const double mv_ratio_accumulator_thresh =
-      (initial_dimensions->height + initial_dimensions->width) / 4.0;
+  const double mv_ratio_accumulator_thresh = (cm->height + cm->width) / 4.0;
 
   if (!flash_detected) {
     // Break clause to detect very still sections after motion. For example,
diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c
index 83c5944..9062136 100644
--- a/av1/encoder/ratectrl.c
+++ b/av1/encoder/ratectrl.c
@@ -188,8 +188,7 @@
          correction_factor >= MIN_BPB_FACTOR);
 
   if (cpi->oxcf.rc_cfg.mode == AOM_CBR && frame_type != KEY_FRAME &&
-      accurate_estimate) {
-    assert(cpi->rec_sse != UINT64_MAX);
+      accurate_estimate && cpi->rec_sse != UINT64_MAX) {
     const int mbs = cm->mi_params.MBs;
     const double sse_sqrt =
         (double)((int)sqrt((double)(cpi->rec_sse)) << BPER_MB_NORMBITS) /
@@ -2086,6 +2085,13 @@
   // TODO(yunqing): support scaled reference frames.
   if (cpi->scaled_ref_buf[LAST_FRAME - 1]) return;
 
+  for (int i = 0; i < 2; ++i) {
+    if (unscaled_src->widths[i] != yv12->widths[i] ||
+        unscaled_src->heights[i] != yv12->heights[i]) {
+      return;
+    }
+  }
+
   const int num_mi_cols = cm->mi_params.mi_cols;
   const int num_mi_rows = cm->mi_params.mi_rows;
   const BLOCK_SIZE bsize = BLOCK_64X64;
diff --git a/build/cmake/aom_configure.cmake b/build/cmake/aom_configure.cmake
index 6c932e8..917e7ca 100644
--- a/build/cmake/aom_configure.cmake
+++ b/build/cmake/aom_configure.cmake
@@ -184,7 +184,9 @@
   string(STRIP "${AOM_AS_FLAGS}" AOM_AS_FLAGS)
 elseif(AOM_TARGET_CPU MATCHES "arm")
   if(AOM_TARGET_SYSTEM STREQUAL "Darwin")
-    set(CMAKE_ASM_COMPILER as)
+    if(NOT CMAKE_ASM_COMPILER)
+      set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+    endif()
     set(AOM_AS_FLAGS -arch ${AOM_TARGET_CPU} -isysroot ${CMAKE_OSX_SYSROOT})
   elseif(AOM_TARGET_SYSTEM STREQUAL "Windows")
     if(NOT CMAKE_ASM_COMPILER)
diff --git a/config/arm/config/aom_config.asm b/config/arm/config/aom_config.asm
index 0cdc0cf..5bacc30 100644
--- a/config/arm/config/aom_config.asm
+++ b/config/arm/config/aom_config.asm
@@ -1,5 +1,5 @@
 ;
-; Copyright (c) 2023, Alliance for Open Media. All rights reserved
+; Copyright (c) 2024, 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
diff --git a/config/arm/config/aom_config.c b/config/arm/config/aom_config.c
index 691e782..03251c9 100644
--- a/config/arm/config/aom_config.c
+++ b/config/arm/config/aom_config.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2024, 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
diff --git a/config/arm/config/aom_config.h b/config/arm/config/aom_config.h
index f59dc31..0d089d5 100644
--- a/config/arm/config/aom_config.h
+++ b/config/arm/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2024, 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
diff --git a/config/arm64/config/aom_config.asm b/config/arm64/config/aom_config.asm
index 108fa0b..8d1117e 100644
--- a/config/arm64/config/aom_config.asm
+++ b/config/arm64/config/aom_config.asm
@@ -1,5 +1,5 @@
 ;
-; Copyright (c) 2023, Alliance for Open Media. All rights reserved
+; Copyright (c) 2024, 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
diff --git a/config/arm64/config/aom_config.c b/config/arm64/config/aom_config.c
index 17df4f5..c600ad2 100644
--- a/config/arm64/config/aom_config.c
+++ b/config/arm64/config/aom_config.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2024, 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
diff --git a/config/arm64/config/aom_config.h b/config/arm64/config/aom_config.h
index d81df21..4782c89 100644
--- a/config/arm64/config/aom_config.h
+++ b/config/arm64/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2024, 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
diff --git a/config/config/aom_version.h b/config/config/aom_version.h
index 46d20c2..51e763c 100644
--- a/config/config/aom_version.h
+++ b/config/config/aom_version.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2024, 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
@@ -11,9 +11,9 @@
 
 #define VERSION_MAJOR 3
 #define VERSION_MINOR 8
-#define VERSION_PATCH 0
-#define VERSION_EXTRA "292-gd854021ff4"
+#define VERSION_PATCH 1
+#define VERSION_EXTRA "324-gd0d0651f9b"
 #define VERSION_PACKED \
   ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH))
-#define VERSION_STRING_NOSP "3.8.0-292-gd854021ff4"
-#define VERSION_STRING " 3.8.0-292-gd854021ff4"
+#define VERSION_STRING_NOSP "3.8.1-324-gd0d0651f9b"
+#define VERSION_STRING " 3.8.1-324-gd0d0651f9b"
diff --git a/config/riscv64/config/aom_config.asm b/config/riscv64/config/aom_config.asm
index d286d42..594d55d 100644
--- a/config/riscv64/config/aom_config.asm
+++ b/config/riscv64/config/aom_config.asm
@@ -1,5 +1,5 @@
 ;
-; Copyright (c) 2023, Alliance for Open Media. All rights reserved
+; Copyright (c) 2024, 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
diff --git a/config/riscv64/config/aom_config.c b/config/riscv64/config/aom_config.c
index 07609ac..c785346 100644
--- a/config/riscv64/config/aom_config.c
+++ b/config/riscv64/config/aom_config.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2024, 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
diff --git a/config/riscv64/config/aom_config.h b/config/riscv64/config/aom_config.h
index 7e99639..9c37899 100644
--- a/config/riscv64/config/aom_config.h
+++ b/config/riscv64/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2024, 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
diff --git a/config/x86/config/aom_config.c b/config/x86/config/aom_config.c
index f6f929d..46444fa 100644
--- a/config/x86/config/aom_config.c
+++ b/config/x86/config/aom_config.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2024, 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
diff --git a/config/x86/config/aom_config.h b/config/x86/config/aom_config.h
index fa4223a..0edc5c5 100644
--- a/config/x86/config/aom_config.h
+++ b/config/x86/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2024, 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
diff --git a/config/x86_64/config/aom_config.c b/config/x86_64/config/aom_config.c
index 8a75212..ee37f5b 100644
--- a/config/x86_64/config/aom_config.c
+++ b/config/x86_64/config/aom_config.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2024, 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
diff --git a/config/x86_64/config/aom_config.h b/config/x86_64/config/aom_config.h
index ead2b02..765481e 100644
--- a/config/x86_64/config/aom_config.h
+++ b/config/x86_64/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2024, 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
diff --git a/libaom_blocklist.txt b/libaom_blocklist.txt
index 06a721b..02b8508 100644
--- a/libaom_blocklist.txt
+++ b/libaom_blocklist.txt
@@ -26,3 +26,5 @@
 fun:highbd_10_variance_sse2
 # libaom/av1/encoder/encodeframe_utils.c: indirect call to assembly code on x86/x86_64 platform
 fun:fast_detect_non_zero_motion
+# libaom/av1/common/reconintra.c: indirect call to assembly code on x86/x86_64 platform
+fun:highbd_build_intra_predictors
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc
index 07b2fc8..9a447b5 100644
--- a/test/encode_api_test.cc
+++ b/test/encode_api_test.cc
@@ -9,6 +9,7 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 
+#include <cassert>
 #include <cstdlib>
 #include <cstring>
 #include <tuple>
@@ -320,37 +321,20 @@
   return image;
 }
 
-// Run this test to reproduce the bug in fuzz test: ASSERT: cpi->rec_sse !=
-// UINT64_MAX in av1_rc_bits_per_mb.
-TEST(EncodeAPI, Buganizer310766628) {
+TEST(EncodeAPI, Buganizer310548198) {
   aom_codec_iface_t *const iface = aom_codec_av1_cx();
   aom_codec_enc_cfg_t cfg;
-
-  struct Config {
-    unsigned int thread;
-    unsigned int width;
-    unsigned int height;
-    aom_rc_mode end_usage;
-  };
-
-  struct Config init_config = { 16, 759, 383, AOM_CBR };
   const unsigned int usage = AOM_USAGE_REALTIME;
   ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, usage), AOM_CODEC_OK);
-
-  cfg.g_threads = init_config.thread;
-  cfg.g_w = init_config.width;
-  cfg.g_h = init_config.height;
-  cfg.g_timebase.num = 1;
-  cfg.g_timebase.den = 1000 * 1000;  // microseconds
+  cfg.g_w = 1;
+  cfg.g_h = 444;
   cfg.g_pass = AOM_RC_ONE_PASS;
   cfg.g_lag_in_frames = 0;
-  cfg.rc_end_usage = init_config.end_usage;
-  cfg.rc_min_quantizer = 2;
-  cfg.rc_max_quantizer = 58;
 
   aom_codec_ctx_t enc;
-  const int speed = 7;
   ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
+
+  const int speed = 6;
   ASSERT_EQ(aom_codec_control(&enc, AOME_SET_CPUUSED, speed), AOM_CODEC_OK);
 
   const aom_enc_frame_flags_t flags = 0;
@@ -368,19 +352,18 @@
   }
   aom_img_free(image);
 
-  struct Config encode_config = { 2, 759, 383, AOM_VBR };
-
-  cfg.g_threads = encode_config.thread;
-  cfg.g_w = encode_config.width;
-  cfg.g_h = encode_config.height;
-  cfg.rc_end_usage = encode_config.end_usage;
-
+  cfg.g_w = 1;
+  cfg.g_h = 254;
   ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK)
       << aom_codec_error_detail(&enc);
 
-  // Encode a frame. This will trigger the assertion failure.
+  cfg.g_w = 1;
+  cfg.g_h = 154;
+  ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK)
+      << aom_codec_error_detail(&enc);
+
+  // Encode a frame.
   image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h);
-  ASSERT_NE(image, nullptr);
   ASSERT_EQ(aom_codec_encode(&enc, image, frame_index, 1, flags), AOM_CODEC_OK);
   frame_index++;
   iter = nullptr;
@@ -404,88 +387,173 @@
   ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
 }
 
-// Run this test to reproduce the bug in fuzz test: Float-cast-overflow in
-// av1_rc_bits_per_mb.
-TEST(EncodeAPI, Buganizer310457427) {
-  aom_codec_iface_t *const iface = aom_codec_av1_cx();
-  aom_codec_enc_cfg_t cfg;
+// Emulates the WebCodecs VideoEncoder interface.
+class AV1Encoder {
+ public:
+  explicit AV1Encoder(int speed) : speed_(speed) {}
+  ~AV1Encoder();
 
-  struct Config {
-    unsigned int thread;
-    unsigned int width;
-    unsigned int height;
-    aom_rc_mode end_usage;
-  };
+  void Configure(unsigned int threads, unsigned int width, unsigned int height,
+                 aom_rc_mode end_usage, unsigned int usage);
+  void Encode(bool key_frame);
 
-  struct Config init_config = { 12, 896, 1076, AOM_CBR };
-  const unsigned int usage = AOM_USAGE_REALTIME;
-  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, usage), AOM_CODEC_OK);
+ private:
+  // Flushes the encoder. Should be called after all the Encode() calls.
+  void Flush();
 
-  cfg.g_threads = init_config.thread;
-  cfg.g_w = init_config.width;
-  cfg.g_h = init_config.height;
-  cfg.g_timebase.num = 1;
-  cfg.g_timebase.den = 1000 * 1000;  // microseconds
-  cfg.g_pass = AOM_RC_ONE_PASS;
-  cfg.g_lag_in_frames = 0;
-  cfg.rc_end_usage = init_config.end_usage;
-  cfg.rc_min_quantizer = 2;
-  cfg.rc_max_quantizer = 58;
+  const int speed_;
+  bool initialized_ = false;
+  aom_codec_enc_cfg_t cfg_;
+  aom_codec_ctx_t enc_;
+  int frame_index_ = 0;
+};
 
-  aom_codec_ctx_t enc;
-  const int speed = 7;
-  ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
-  ASSERT_EQ(aom_codec_control(&enc, AOME_SET_CPUUSED, speed), AOM_CODEC_OK);
+AV1Encoder::~AV1Encoder() {
+  if (initialized_) {
+    Flush();
+    EXPECT_EQ(aom_codec_destroy(&enc_), AOM_CODEC_OK);
+  }
+}
 
-  struct Config encode_config = { 6, 609, 1076, AOM_VBR };
-  cfg.g_threads = encode_config.thread;
-  cfg.g_w = encode_config.width;
-  cfg.g_h = encode_config.height;
-  cfg.rc_end_usage = encode_config.end_usage;
+void AV1Encoder::Configure(unsigned int threads, unsigned int width,
+                           unsigned int height, aom_rc_mode end_usage,
+                           unsigned int usage) {
+  if (!initialized_) {
+    aom_codec_iface_t *const iface = aom_codec_av1_cx();
+    ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg_, usage), AOM_CODEC_OK);
+    cfg_.g_threads = threads;
+    cfg_.g_w = width;
+    cfg_.g_h = height;
+    cfg_.g_forced_max_frame_width = cfg_.g_w;
+    cfg_.g_forced_max_frame_height = cfg_.g_h;
+    cfg_.g_timebase.num = 1;
+    cfg_.g_timebase.den = 1000 * 1000;  // microseconds
+    cfg_.g_pass = AOM_RC_ONE_PASS;
+    cfg_.g_lag_in_frames = 0;
+    cfg_.rc_end_usage = end_usage;
+    cfg_.rc_min_quantizer = 2;
+    cfg_.rc_max_quantizer = 58;
+    ASSERT_EQ(aom_codec_enc_init(&enc_, iface, &cfg_, 0), AOM_CODEC_OK);
+    ASSERT_EQ(aom_codec_control(&enc_, AOME_SET_CPUUSED, speed_), AOM_CODEC_OK);
+    initialized_ = true;
+    return;
+  }
 
-  ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK)
-      << aom_codec_error_detail(&enc);
+  ASSERT_EQ(usage, cfg_.g_usage);
+  cfg_.g_threads = threads;
+  cfg_.g_w = width;
+  cfg_.g_h = height;
+  cfg_.rc_end_usage = end_usage;
+  ASSERT_EQ(aom_codec_enc_config_set(&enc_, &cfg_), AOM_CODEC_OK)
+      << aom_codec_error_detail(&enc_);
+}
 
-  const aom_enc_frame_flags_t flags = 0;
-  int frame_index = 0;
-
-  // Encode a frame.
-  aom_image_t *image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h);
+void AV1Encoder::Encode(bool key_frame) {
+  assert(initialized_);
+  // TODO(wtc): Support high bit depths and other YUV formats.
+  aom_image_t *const image =
+      CreateGrayImage(AOM_IMG_FMT_I420, cfg_.g_w, cfg_.g_h);
   ASSERT_NE(image, nullptr);
-  ASSERT_EQ(aom_codec_encode(&enc, image, frame_index, 1, flags), AOM_CODEC_OK);
-  frame_index++;
+  const aom_enc_frame_flags_t flags = key_frame ? AOM_EFLAG_FORCE_KF : 0;
+  ASSERT_EQ(aom_codec_encode(&enc_, image, frame_index_, 1, flags),
+            AOM_CODEC_OK);
+  frame_index_++;
   const aom_codec_cx_pkt_t *pkt;
   aom_codec_iter_t iter = nullptr;
-  while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) {
+  while ((pkt = aom_codec_get_cx_data(&enc_, &iter)) != nullptr) {
     ASSERT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
+    if (key_frame) {
+      ASSERT_EQ(pkt->data.frame.flags & AOM_FRAME_IS_KEY, AOM_FRAME_IS_KEY);
+    }
   }
   aom_img_free(image);
+}
 
-  // Encode a frame. This will trigger the float-cast-overflow bug which was
-  // caused by division by zero.
-  image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h);
-  ASSERT_NE(image, nullptr);
-  ASSERT_EQ(aom_codec_encode(&enc, image, frame_index, 1, flags), AOM_CODEC_OK);
-  frame_index++;
-  iter = nullptr;
-  while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) {
-    ASSERT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
-  }
-  aom_img_free(image);
-
-  // Flush the encoder.
+void AV1Encoder::Flush() {
   bool got_data;
   do {
-    ASSERT_EQ(aom_codec_encode(&enc, nullptr, 0, 0, 0), AOM_CODEC_OK);
+    ASSERT_EQ(aom_codec_encode(&enc_, nullptr, 0, 0, 0), AOM_CODEC_OK);
     got_data = false;
-    iter = nullptr;
-    while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) {
+    const aom_codec_cx_pkt_t *pkt;
+    aom_codec_iter_t iter = nullptr;
+    while ((pkt = aom_codec_get_cx_data(&enc_, &iter)) != nullptr) {
       ASSERT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT);
       got_data = true;
     }
   } while (got_data);
+}
 
-  ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+TEST(EncodeAPI, Buganizer314858909) {
+  AV1Encoder encoder(7);
+
+  encoder.Configure(6, 1582, 750, AOM_CBR, AOM_USAGE_REALTIME);
+
+  // Encode a frame.
+  encoder.Encode(false);
+
+  encoder.Configure(0, 1582, 23, AOM_CBR, AOM_USAGE_REALTIME);
+
+  // Encode a frame..
+  encoder.Encode(false);
+
+  encoder.Configure(16, 1542, 363, AOM_CBR, AOM_USAGE_REALTIME);
+
+  // Encode a frame..
+  encoder.Encode(false);
+}
+
+// Run this test to reproduce the bug in fuzz test: ASSERT: cpi->rec_sse !=
+// UINT64_MAX in av1_rc_bits_per_mb.
+TEST(EncodeAPI, Buganizer310766628) {
+  AV1Encoder encoder(7);
+
+  encoder.Configure(16, 759, 383, AOM_CBR, AOM_USAGE_REALTIME);
+
+  // Encode a frame.
+  encoder.Encode(false);
+
+  encoder.Configure(2, 759, 383, AOM_VBR, AOM_USAGE_REALTIME);
+
+  // Encode a frame. This will trigger the assertion failure.
+  encoder.Encode(false);
+}
+
+// This test covers a possible use case where the change of frame sizes and
+// thread numbers happens before and after the first frame coding.
+TEST(EncodeAPI, Buganizer310455204) {
+  AV1Encoder encoder(7);
+
+  encoder.Configure(0, 1915, 503, AOM_VBR, AOM_USAGE_REALTIME);
+
+  encoder.Configure(4, 1, 1, AOM_VBR, AOM_USAGE_REALTIME);
+
+  encoder.Configure(6, 559, 503, AOM_CBR, AOM_USAGE_REALTIME);
+
+  // Encode a frame.
+  encoder.Encode(false);
+
+  // Increase the number of threads.
+  encoder.Configure(16, 1915, 503, AOM_CBR, AOM_USAGE_REALTIME);
+
+  // Encode a frame.
+  encoder.Encode(false);
+}
+
+// Run this test to reproduce the bug in fuzz test: Float-cast-overflow in
+// av1_rc_bits_per_mb.
+TEST(EncodeAPI, Buganizer310457427) {
+  AV1Encoder encoder(7);
+
+  encoder.Configure(12, 896, 1076, AOM_CBR, AOM_USAGE_REALTIME);
+
+  encoder.Configure(6, 609, 1076, AOM_VBR, AOM_USAGE_REALTIME);
+
+  // Encode a frame.
+  encoder.Encode(false);
+
+  // Encode a frame. This will trigger the float-cast-overflow bug which was
+  // caused by division by zero.
+  encoder.Encode(false);
 }
 
 class EncodeAPIParameterized
@@ -586,6 +654,26 @@
   cfg.kf_max_dist = 1;
   EXPECT_EQ(AOM_CODEC_INVALID_PARAM, aom_codec_enc_init(&enc, iface, &cfg, 0));
 }
-#endif
+
+// A test that reproduces bug aomedia:3534.
+TEST(EncodeAPI, AllIntraAndNoRefLast) {
+  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);
+
+  aom_codec_ctx_t enc;
+  ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
+
+  aom_image_t *image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h);
+  ASSERT_NE(image, nullptr);
+
+  ASSERT_EQ(aom_codec_encode(&enc, image, 0, 1, AOM_EFLAG_NO_REF_LAST),
+            AOM_CODEC_OK);
+
+  aom_img_free(image);
+  ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+}
+#endif  // !CONFIG_REALTIME_ONLY
 
 }  // namespace