diff --git a/aom_scale/aom_scale.h b/aom_scale/aom_scale.h
index 6e089f5aaa13e98896c47a42f8f6f60f69b58c50..a4aef6c654e089b8eaeefbceed2e4b77eda66682 100644
--- a/aom_scale/aom_scale.h
+++ b/aom_scale/aom_scale.h
@@ -18,6 +18,6 @@ extern void aom_scale_frame(YV12_BUFFER_CONFIG *src, YV12_BUFFER_CONFIG *dst,
                             unsigned char *temp_area, unsigned char temp_height,
                             unsigned int hscale, unsigned int hratio,
                             unsigned int vscale, unsigned int vratio,
-                            unsigned int interlaced);
+                            unsigned int interlaced, const int num_planes);
 
 #endif  // AOM_SCALE_AOM_SCALE_H_
diff --git a/aom_scale/aom_scale_rtcd.pl b/aom_scale/aom_scale_rtcd.pl
index 62c6b3cb7ca358c8db96b011647029892c6ddac2..f6cfe1322c7cc1c712aaf68d9c3a1ae6398801da 100644
--- a/aom_scale/aom_scale_rtcd.pl
+++ b/aom_scale/aom_scale_rtcd.pl
@@ -26,9 +26,9 @@ if (aom_config("CONFIG_SPATIAL_RESAMPLING") eq "yes") {
   add_proto qw/void aom_vertical_band_2_1_scale_i/, "unsigned char *source, int src_pitch, unsigned char *dest, int dest_pitch, unsigned int dest_width";
 }
 
-add_proto qw/void aom_yv12_extend_frame_borders/, "struct yv12_buffer_config *ybf";
+add_proto qw/void aom_yv12_extend_frame_borders/, "struct yv12_buffer_config *ybf, const int num_planes";
 
-add_proto qw/void aom_yv12_copy_frame/, "const struct yv12_buffer_config *src_bc, struct yv12_buffer_config *dst_bc";
+add_proto qw/void aom_yv12_copy_frame/, "const struct yv12_buffer_config *src_bc, struct yv12_buffer_config *dst_bc, const int num_planes";
 
 add_proto qw/void aom_yv12_copy_y/, "const struct yv12_buffer_config *src_ybc, struct yv12_buffer_config *dst_ybc";
 
@@ -37,10 +37,10 @@ add_proto qw/void aom_yv12_copy_u/, "const struct yv12_buffer_config *src_bc, st
 add_proto qw/void aom_yv12_copy_v/, "const struct yv12_buffer_config *src_bc, struct yv12_buffer_config *dst_bc";
 
 if (aom_config("CONFIG_AV1") eq "yes") {
-  add_proto qw/void aom_extend_frame_borders/, "struct yv12_buffer_config *ybf";
+  add_proto qw/void aom_extend_frame_borders/, "struct yv12_buffer_config *ybf, const int num_planes";
   specialize qw/aom_extend_frame_borders dspr2/;
 
-  add_proto qw/void aom_extend_frame_inner_borders/, "struct yv12_buffer_config *ybf";
+  add_proto qw/void aom_extend_frame_inner_borders/, "struct yv12_buffer_config *ybf, const int num_planes";
   specialize qw/aom_extend_frame_inner_borders dspr2/;
 
   add_proto qw/void aom_extend_frame_borders_y/, "struct yv12_buffer_config *ybf";
diff --git a/aom_scale/generic/aom_scale.c b/aom_scale/generic/aom_scale.c
index d124832b79b8b60470c5199874dab95a53c5f292..1e4adadf65fa5a5fe997d07d3d0cf4250156be21 100644
--- a/aom_scale/generic/aom_scale.c
+++ b/aom_scale/generic/aom_scale.c
@@ -475,11 +475,11 @@ void aom_scale_frame(YV12_BUFFER_CONFIG *src, YV12_BUFFER_CONFIG *dst,
                      unsigned char *temp_area, unsigned char temp_height,
                      unsigned int hscale, unsigned int hratio,
                      unsigned int vscale, unsigned int vratio,
-                     unsigned int interlaced) {
+                     unsigned int interlaced, const int num_planes) {
   const int dw = (hscale - 1 + src->y_width * hratio) / hscale;
   const int dh = (vscale - 1 + src->y_height * vratio) / vscale;
 
-  for (int plane = 0; plane < 3; ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     const int is_uv = plane > 0;
     const int plane_dw = dw >> is_uv;
     const int plane_dh = dh >> is_uv;
diff --git a/aom_scale/generic/yv12extend.c b/aom_scale/generic/yv12extend.c
index 3cfed30217a2e38a5b8a6a48cc69690741f9410e..e06022fcbd661868fb3074e5eaba9fcf7b7240a5 100644
--- a/aom_scale/generic/yv12extend.c
+++ b/aom_scale/generic/yv12extend.c
@@ -98,7 +98,8 @@ static void extend_plane_high(uint8_t *const src8, int src_stride, int width,
   }
 }
 
-void aom_yv12_extend_frame_borders_c(YV12_BUFFER_CONFIG *ybf) {
+void aom_yv12_extend_frame_borders_c(YV12_BUFFER_CONFIG *ybf,
+                                     const int num_planes) {
   assert(ybf->border % 2 == 0);
   assert(ybf->y_height - ybf->y_crop_height < 16);
   assert(ybf->y_width - ybf->y_crop_width < 16);
@@ -106,7 +107,7 @@ void aom_yv12_extend_frame_borders_c(YV12_BUFFER_CONFIG *ybf) {
   assert(ybf->y_width - ybf->y_crop_width >= 0);
 
   if (ybf->flags & YV12_FLAG_HIGHBITDEPTH) {
-    for (int plane = 0; plane < 3; ++plane) {
+    for (int plane = 0; plane < num_planes; ++plane) {
       const int is_uv = plane > 0;
       const int plane_border = ybf->border >> is_uv;
       extend_plane_high(
@@ -117,7 +118,7 @@ void aom_yv12_extend_frame_borders_c(YV12_BUFFER_CONFIG *ybf) {
     }
     return;
   }
-  for (int plane = 0; plane < 3; ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     const int is_uv = plane > 0;
     const int plane_border = ybf->border >> is_uv;
     extend_plane(ybf->buffers[plane], ybf->strides[is_uv],
@@ -129,7 +130,8 @@ void aom_yv12_extend_frame_borders_c(YV12_BUFFER_CONFIG *ybf) {
 }
 
 #if CONFIG_AV1
-static void extend_frame(YV12_BUFFER_CONFIG *const ybf, int ext_size) {
+static void extend_frame(YV12_BUFFER_CONFIG *const ybf, int ext_size,
+                         const int num_planes) {
   const int ss_x = ybf->uv_width < ybf->y_width;
   const int ss_y = ybf->uv_height < ybf->y_height;
 
@@ -139,7 +141,7 @@ static void extend_frame(YV12_BUFFER_CONFIG *const ybf, int ext_size) {
   assert(ybf->y_width - ybf->y_crop_width >= 0);
 
   if (ybf->flags & YV12_FLAG_HIGHBITDEPTH) {
-    for (int plane = 0; plane < 3; ++plane) {
+    for (int plane = 0; plane < num_planes; ++plane) {
       const int is_uv = plane > 0;
       const int top = ext_size >> (is_uv ? ss_y : 0);
       const int left = ext_size >> (is_uv ? ss_x : 0);
@@ -151,7 +153,7 @@ static void extend_frame(YV12_BUFFER_CONFIG *const ybf, int ext_size) {
     }
     return;
   }
-  for (int plane = 0; plane < 3; ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     const int is_uv = plane > 0;
     const int top = ext_size >> (is_uv ? ss_y : 0);
     const int left = ext_size >> (is_uv ? ss_x : 0);
@@ -163,15 +165,16 @@ static void extend_frame(YV12_BUFFER_CONFIG *const ybf, int ext_size) {
   }
 }
 
-void aom_extend_frame_borders_c(YV12_BUFFER_CONFIG *ybf) {
-  extend_frame(ybf, ybf->border);
+void aom_extend_frame_borders_c(YV12_BUFFER_CONFIG *ybf, const int num_planes) {
+  extend_frame(ybf, ybf->border, num_planes);
 }
 
-void aom_extend_frame_inner_borders_c(YV12_BUFFER_CONFIG *ybf) {
+void aom_extend_frame_inner_borders_c(YV12_BUFFER_CONFIG *ybf,
+                                      const int num_planes) {
   const int inner_bw = (ybf->border > AOMINNERBORDERINPIXELS)
                            ? AOMINNERBORDERINPIXELS
                            : ybf->border;
-  extend_frame(ybf, inner_bw);
+  extend_frame(ybf, inner_bw, num_planes);
 }
 
 void aom_extend_frame_borders_y_c(YV12_BUFFER_CONFIG *ybf) {
@@ -205,7 +208,7 @@ static void memcpy_short_addr(uint8_t *dst8, const uint8_t *src8, int num) {
 // destination's UMV borders.
 // Note: The frames are assumed to be identical in size.
 void aom_yv12_copy_frame_c(const YV12_BUFFER_CONFIG *src_bc,
-                           YV12_BUFFER_CONFIG *dst_bc) {
+                           YV12_BUFFER_CONFIG *dst_bc, const int num_planes) {
 #if 0
   /* These assertions are valid in the codec, but the libaom-tester uses
    * this code slightly differently.
@@ -218,7 +221,7 @@ void aom_yv12_copy_frame_c(const YV12_BUFFER_CONFIG *src_bc,
          (dst_bc->flags & YV12_FLAG_HIGHBITDEPTH));
 
   if (src_bc->flags & YV12_FLAG_HIGHBITDEPTH) {
-    for (int plane = 0; plane < 3; ++plane) {
+    for (int plane = 0; plane < num_planes; ++plane) {
       const uint8_t *plane_src = src_bc->buffers[plane];
       uint8_t *plane_dst = dst_bc->buffers[plane];
       const int is_uv = plane > 0;
@@ -229,10 +232,10 @@ void aom_yv12_copy_frame_c(const YV12_BUFFER_CONFIG *src_bc,
         plane_dst += dst_bc->strides[is_uv];
       }
     }
-    aom_yv12_extend_frame_borders_c(dst_bc);
+    aom_yv12_extend_frame_borders_c(dst_bc, num_planes);
     return;
   }
-  for (int plane = 0; plane < 3; ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     const uint8_t *plane_src = src_bc->buffers[plane];
     uint8_t *plane_dst = dst_bc->buffers[plane];
     const int is_uv = plane > 0;
@@ -243,7 +246,7 @@ void aom_yv12_copy_frame_c(const YV12_BUFFER_CONFIG *src_bc,
       plane_dst += dst_bc->strides[is_uv];
     }
   }
-  aom_yv12_extend_frame_borders_c(dst_bc);
+  aom_yv12_extend_frame_borders_c(dst_bc, num_planes);
 }
 
 void aom_yv12_copy_y_c(const YV12_BUFFER_CONFIG *src_ybc,
diff --git a/aom_scale/mips/dspr2/yv12extend_dspr2.c b/aom_scale/mips/dspr2/yv12extend_dspr2.c
index 51192f7b93ec8857bef514f34015deccf8f85059..d03884821808437f5515066750dab490734050c6 100644
--- a/aom_scale/mips/dspr2/yv12extend_dspr2.c
+++ b/aom_scale/mips/dspr2/yv12extend_dspr2.c
@@ -126,14 +126,16 @@ static void extend_frame(YV12_BUFFER_CONFIG *const ybf, int ext_size) {
   extend_plane(ybf->v_buffer, ybf->uv_stride, c_w, c_h, c_et, c_el, c_eb, c_er);
 }
 
-void aom_extend_frame_borders_dspr2(YV12_BUFFER_CONFIG *ybf) {
-  extend_frame(ybf, ybf->border);
+void aom_extend_frame_borders_dspr2(YV12_BUFFER_CONFIG *ybf,
+                                    const int num_planes) {
+  extend_frame(ybf, ybf->border, num_planes);
 }
 
-void aom_extend_frame_inner_borders_dspr2(YV12_BUFFER_CONFIG *ybf) {
+void aom_extend_frame_inner_borders_dspr2(YV12_BUFFER_CONFIG *ybf,
+                                          const int num_planes) {
   const int inner_bw = (ybf->border > AOMINNERBORDERINPIXELS)
                            ? AOMINNERBORDERINPIXELS
                            : ybf->border;
-  extend_frame(ybf, inner_bw);
+  extend_frame(ybf, inner_bw, num_planes);
 }
 #endif
diff --git a/aom_util/debug_util.c b/aom_util/debug_util.c
index 4f9bdc1df208f9eff09c494cca1644e2215a5e72..cf3fb24d838709841ced6fe75f5d28871e27142d 100644
--- a/aom_util/debug_util.c
+++ b/aom_util/debug_util.c
@@ -98,8 +98,8 @@ void mismatch_move_frame_idx_w() {
   }
 }
 
-void mismatch_reset_frame() {
-  for (int plane = 0; plane < 3; ++plane) {
+void mismatch_reset_frame(int num_planes) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     memset(frame_pre[frame_buf_idx_w][plane], 0,
            sizeof(frame_pre[frame_buf_idx_w][plane][0]) * frame_size);
     memset(frame_tx[frame_buf_idx_w][plane], 0,
diff --git a/aom_util/debug_util.h b/aom_util/debug_util.h
index 11c1296e0bf69e6e5f7a8a701f1fefe5b858c824..0b2ea050dfd58619787e5889b08320d8b8818656 100644
--- a/aom_util/debug_util.h
+++ b/aom_util/debug_util.h
@@ -46,7 +46,7 @@ void bitstream_queue_set_skip_read(int skip);
 #if CONFIG_MISMATCH_DEBUG
 void mismatch_move_frame_idx_w();
 void mismatch_move_frame_idx_r();
-void mismatch_reset_frame();
+void mismatch_reset_frame(int num_planes);
 void mismatch_record_block_pre(const uint8_t *src, int src_stride, int plane,
                                int pixel_c, int pixel_r, int blk_w, int blk_h);
 void mismatch_record_block_tx(const uint8_t *src, int src_stride, int plane,
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c
index ab0f98b423fac476f55c30237d6d7a005f360be7..ccc7851f4f9b62bb133be8419fc3203c3d2fc733 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -886,6 +886,7 @@ static aom_image_t *decoder_get_frame(aom_codec_alg_priv_t *ctx,
           yuvconfig2image(&ctx->img, &sd, frame_worker_data->user_priv);
 
 #if CONFIG_EXT_TILE
+          const int num_planes = av1_num_planes(cm);
           if (cm->single_tile_decoding &&
               frame_worker_data->pbi->dec_tile_row >= 0) {
             const int tile_row =
@@ -894,9 +895,11 @@ static aom_image_t *decoder_get_frame(aom_codec_alg_priv_t *ctx,
             const int ssy = ctx->img.y_chroma_shift;
             int plane;
             ctx->img.planes[0] += mi_row * MI_SIZE * ctx->img.stride[0];
-            for (plane = 1; plane < MAX_MB_PLANE; ++plane) {
-              ctx->img.planes[plane] +=
-                  mi_row * (MI_SIZE >> ssy) * ctx->img.stride[plane];
+            if (num_planes > 1) {
+              for (plane = 1; plane < MAX_MB_PLANE; ++plane) {
+                ctx->img.planes[plane] +=
+                    mi_row * (MI_SIZE >> ssy) * ctx->img.stride[plane];
+              }
             }
             ctx->img.d_h =
                 AOMMIN(cm->tile_height, cm->mi_rows - mi_row) * MI_SIZE;
@@ -910,8 +913,10 @@ static aom_image_t *decoder_get_frame(aom_codec_alg_priv_t *ctx,
             const int ssx = ctx->img.x_chroma_shift;
             int plane;
             ctx->img.planes[0] += mi_col * MI_SIZE;
-            for (plane = 1; plane < MAX_MB_PLANE; ++plane) {
-              ctx->img.planes[plane] += mi_col * (MI_SIZE >> ssx);
+            if (num_planes > 1) {
+              for (plane = 1; plane < MAX_MB_PLANE; ++plane) {
+                ctx->img.planes[plane] += mi_col * (MI_SIZE >> ssx);
+              }
             }
             ctx->img.d_w =
                 AOMMIN(cm->tile_width, cm->mi_cols - mi_col) * MI_SIZE;
diff --git a/av1/common/alloccommon.c b/av1/common/alloccommon.c
index 5e072bc240e54130a1cae82baaacf1b7d25cd993..5e3e68152fb559b4c229343b6dfcf44eafa51a17 100644
--- a/av1/common/alloccommon.c
+++ b/av1/common/alloccommon.c
@@ -109,7 +109,8 @@ void av1_free_ref_frame_buffers(BufferPool *pool) {
 #if CONFIG_LOOP_RESTORATION
 // Assumes cm->rst_info[p].restoration_unit_size is already initialized
 void av1_alloc_restoration_buffers(AV1_COMMON *cm) {
-  for (int p = 0; p < MAX_MB_PLANE; ++p)
+  const int num_planes = av1_num_planes(cm);
+  for (int p = 0; p < num_planes; ++p)
     av1_alloc_restoration_struct(cm, &cm->rst_info[p], p > 0);
   aom_free(cm->rst_tmpbuf);
   CHECK_MEM_ERROR(cm, cm->rst_tmpbuf,
@@ -148,7 +149,7 @@ void av1_alloc_restoration_buffers(AV1_COMMON *cm) {
 #endif  // CONFIG_HORZONLY_FRAME_SUPERRES
   const int use_highbd = cm->use_highbitdepth ? 1 : 0;
 
-  for (int p = 0; p < MAX_MB_PLANE; ++p) {
+  for (int p = 0; p < num_planes; ++p) {
     const int is_uv = p > 0;
     const int ss_x = is_uv && cm->subsampling_x;
     const int plane_w = ((frame_w + ss_x) >> ss_x) + 2 * RESTORATION_EXTRA_HORZ;
@@ -170,13 +171,14 @@ void av1_alloc_restoration_buffers(AV1_COMMON *cm) {
 }
 
 void av1_free_restoration_buffers(AV1_COMMON *cm) {
+  const int num_planes = av1_num_planes(cm);
   int p;
-  for (p = 0; p < MAX_MB_PLANE; ++p)
+  for (p = 0; p < num_planes; ++p)
     av1_free_restoration_struct(&cm->rst_info[p]);
   aom_free(cm->rst_tmpbuf);
   cm->rst_tmpbuf = NULL;
 #if CONFIG_STRIPED_LOOP_RESTORATION
-  for (p = 0; p < MAX_MB_PLANE; ++p) {
+  for (p = 0; p < num_planes; ++p) {
     RestorationStripeBoundaries *boundaries = &cm->rst_info[p].boundaries;
     aom_free(boundaries->stripe_boundary_above);
     aom_free(boundaries->stripe_boundary_below);
@@ -188,6 +190,7 @@ void av1_free_restoration_buffers(AV1_COMMON *cm) {
 #endif  // CONFIG_LOOP_RESTORATION
 
 void av1_free_context_buffers(AV1_COMMON *cm) {
+  const int num_planes = av1_num_planes(cm);
   int i;
   cm->free_mi(cm);
 
@@ -198,7 +201,7 @@ void av1_free_context_buffers(AV1_COMMON *cm) {
 #if !CONFIG_SEGMENT_PRED_LAST
   free_seg_map(cm);
 #endif
-  for (i = 0; i < MAX_MB_PLANE; i++) {
+  for (i = 0; i < num_planes; i++) {
     aom_free(cm->above_context[i]);
     cm->above_context[i] = NULL;
   }
@@ -208,13 +211,14 @@ void av1_free_context_buffers(AV1_COMMON *cm) {
   aom_free(cm->above_txfm_context);
   cm->above_txfm_context = NULL;
 
-  for (i = 0; i < MAX_MB_PLANE; ++i) {
+  for (i = 0; i < num_planes; ++i) {
     aom_free(cm->top_txfm_context[i]);
     cm->top_txfm_context[i] = NULL;
   }
 }
 
 int av1_alloc_context_buffers(AV1_COMMON *cm, int width, int height) {
+  const int num_planes = av1_num_planes(cm);
   int new_mi_size;
 
   av1_set_mb_mi(cm, width, height);
@@ -250,7 +254,7 @@ int av1_alloc_context_buffers(AV1_COMMON *cm, int width, int height) {
         ALIGN_POWER_OF_TWO(cm->mi_cols, MAX_MIB_SIZE_LOG2);
     int i;
 
-    for (i = 0; i < MAX_MB_PLANE; i++) {
+    for (i = 0; i < num_planes; i++) {
       aom_free(cm->above_context[i]);
       cm->above_context[i] = (ENTROPY_CONTEXT *)aom_calloc(
           aligned_mi_cols << (MI_SIZE_LOG2 - tx_size_wide_log2[0]),
@@ -268,7 +272,7 @@ int av1_alloc_context_buffers(AV1_COMMON *cm, int width, int height) {
         aligned_mi_cols << TX_UNIT_WIDE_LOG2, sizeof(*cm->above_txfm_context));
     if (!cm->above_txfm_context) goto fail;
 
-    for (i = 0; i < MAX_MB_PLANE; ++i) {
+    for (i = 0; i < num_planes; ++i) {
       aom_free(cm->top_txfm_context[i]);
       cm->top_txfm_context[i] =
           (TXFM_CONTEXT *)aom_calloc(aligned_mi_cols << TX_UNIT_WIDE_LOG2,
diff --git a/av1/common/av1_loopfilter.c b/av1/common/av1_loopfilter.c
index 805133d45f1b91bb45b63d946918a4e46119be89..eef33dbd55536a3b21a5b9c52aaa5e3f76ec925d 100644
--- a/av1/common/av1_loopfilter.c
+++ b/av1/common/av1_loopfilter.c
@@ -2394,6 +2394,7 @@ static void av1_filter_block_plane_horz(
 void av1_loop_filter_rows(YV12_BUFFER_CONFIG *frame_buffer, AV1_COMMON *cm,
                           struct macroblockd_plane *planes, int start, int stop,
                           int y_only) {
+  const int num_planes = av1_num_planes(cm);
 #if CONFIG_LOOPFILTER_LEVEL
   // y_only no longer has its original meaning.
   // Here it means which plane to filter
@@ -2402,9 +2403,9 @@ void av1_loop_filter_rows(YV12_BUFFER_CONFIG *frame_buffer, AV1_COMMON *cm,
   const int plane_start = y_only;
   const int plane_end = plane_start + 1;
 #else
-  const int num_planes = y_only ? 1 : MAX_MB_PLANE;
+  const int nplanes = y_only ? 1 : num_planes;
   const int plane_start = 0;
-  const int plane_end = num_planes;
+  const int plane_end = nplanes;
 #endif  // CONFIG_LOOPFILTER_LEVEL
 #if CONFIG_PARALLEL_DEBLOCKING
   const int col_start = 0;
@@ -2414,11 +2415,11 @@ void av1_loop_filter_rows(YV12_BUFFER_CONFIG *frame_buffer, AV1_COMMON *cm,
   int plane;
 
 #if !CONFIG_PARALLEL_DEBLOCKING
-  for (int i = 0; i < MAX_MB_PLANE; ++i)
+  for (int i = 0; i < nplanes; ++i)
     memset(cm->top_txfm_context[i], TX_32X32, cm->mi_cols << TX_UNIT_WIDE_LOG2);
   for (mi_row = start; mi_row < stop; mi_row += cm->mib_size) {
     MODE_INFO **mi = cm->mi_grid_visible + mi_row * cm->mi_stride;
-    for (int i = 0; i < MAX_MB_PLANE; ++i)
+    for (int i = 0; i < nplanes; ++i)
       memset(cm->left_txfm_context[i], TX_32X32,
              MAX_MIB_SIZE << TX_UNIT_HIGH_LOG2);
     for (mi_col = 0; mi_col < cm->mi_cols; mi_col += cm->mib_size) {
@@ -2437,7 +2438,8 @@ void av1_loop_filter_rows(YV12_BUFFER_CONFIG *frame_buffer, AV1_COMMON *cm,
   // filter all vertical edges in every 64x64 super block
   for (mi_row = start; mi_row < stop; mi_row += MAX_MIB_SIZE) {
     for (mi_col = col_start; mi_col < col_end; mi_col += MAX_MIB_SIZE) {
-      av1_setup_dst_planes(planes, cm->sb_size, frame_buffer, mi_row, mi_col);
+      av1_setup_dst_planes(planes, cm->sb_size, frame_buffer, mi_row, mi_col,
+                           num_planes);
       for (plane = plane_start; plane < plane_end; ++plane) {
         av1_filter_block_plane_vert(cm, plane, &planes[plane], mi_row, mi_col);
       }
@@ -2447,7 +2449,8 @@ void av1_loop_filter_rows(YV12_BUFFER_CONFIG *frame_buffer, AV1_COMMON *cm,
   // filter all horizontal edges in every 64x64 super block
   for (mi_row = start; mi_row < stop; mi_row += MAX_MIB_SIZE) {
     for (mi_col = col_start; mi_col < col_end; mi_col += MAX_MIB_SIZE) {
-      av1_setup_dst_planes(planes, cm->sb_size, frame_buffer, mi_row, mi_col);
+      av1_setup_dst_planes(planes, cm->sb_size, frame_buffer, mi_row, mi_col,
+                           num_planes);
       for (plane = plane_start; plane < plane_end; ++plane) {
         av1_filter_block_plane_horz(cm, plane, &planes[plane], mi_row, mi_col);
       }
diff --git a/av1/common/blockd.c b/av1/common/blockd.c
index 83a41d7e52dc349b35907aa55112cdd56030b3ec..d6cc975818a58cfc55949cfc567d30ab25717256 100644
--- a/av1/common/blockd.c
+++ b/av1/common/blockd.c
@@ -85,10 +85,8 @@ void av1_foreach_transformed_block_in_plane(
 void av1_foreach_transformed_block(const MACROBLOCKD *const xd,
                                    BLOCK_SIZE bsize, int mi_row, int mi_col,
                                    foreach_transformed_block_visitor visit,
-                                   void *arg) {
-  int plane;
-
-  for (plane = 0; plane < MAX_MB_PLANE; ++plane) {
+                                   void *arg, const int num_planes) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     if (!is_chroma_reference(mi_row, mi_col, bsize,
                              xd->plane[plane].subsampling_x,
                              xd->plane[plane].subsampling_y))
@@ -136,14 +134,14 @@ void av1_set_contexts(const MACROBLOCKD *xd, struct macroblockd_plane *pd,
   }
 }
 void av1_reset_skip_context(MACROBLOCKD *xd, int mi_row, int mi_col,
-                            BLOCK_SIZE bsize) {
+                            BLOCK_SIZE bsize, const int num_planes) {
   int i;
   int nplanes;
   int chroma_ref;
   chroma_ref =
       is_chroma_reference(mi_row, mi_col, bsize, xd->plane[1].subsampling_x,
                           xd->plane[1].subsampling_y);
-  nplanes = 1 + (MAX_MB_PLANE - 1) * chroma_ref;
+  nplanes = 1 + (num_planes - 1) * chroma_ref;
   for (i = 0; i < nplanes; i++) {
     struct macroblockd_plane *const pd = &xd->plane[i];
     const BLOCK_SIZE plane_bsize = get_plane_block_size(bsize, pd);
@@ -155,18 +153,19 @@ void av1_reset_skip_context(MACROBLOCKD *xd, int mi_row, int mi_col,
 }
 
 #if CONFIG_LOOP_RESTORATION
-void av1_reset_loop_restoration(MACROBLOCKD *xd) {
-  for (int p = 0; p < MAX_MB_PLANE; ++p) {
+void av1_reset_loop_restoration(MACROBLOCKD *xd, const int num_planes) {
+  for (int p = 0; p < num_planes; ++p) {
     set_default_wiener(xd->wiener_info + p);
     set_default_sgrproj(xd->sgrproj_info + p);
   }
 }
 #endif  // CONFIG_LOOP_RESTORATION
 
-void av1_setup_block_planes(MACROBLOCKD *xd, int ss_x, int ss_y) {
+void av1_setup_block_planes(MACROBLOCKD *xd, int ss_x, int ss_y,
+                            const int num_planes) {
   int i;
 
-  for (i = 0; i < MAX_MB_PLANE; i++) {
+  for (i = 0; i < num_planes; i++) {
     xd->plane[i].plane_type = get_plane_type(i);
     xd->plane[i].subsampling_x = i ? ss_x : 0;
     xd->plane[i].subsampling_y = i ? ss_y : 0;
diff --git a/av1/common/blockd.h b/av1/common/blockd.h
index 3008958cf97b7d38f75b64cec0771b52fbf8a762..5c5d91cc43edfd3e5e066b066e042df4ae9c794c 100644
--- a/av1/common/blockd.h
+++ b/av1/common/blockd.h
@@ -990,7 +990,8 @@ static INLINE TX_TYPE av1_get_tx_type(PLANE_TYPE plane_type,
   return intra_type;
 }
 
-void av1_setup_block_planes(MACROBLOCKD *xd, int ss_x, int ss_y);
+void av1_setup_block_planes(MACROBLOCKD *xd, int ss_x, int ss_y,
+                            const int num_planes);
 
 static INLINE int bsize_to_max_depth(BLOCK_SIZE bsize, int is_inter) {
   TX_SIZE tx_size = get_max_rect_tx_size(bsize, is_inter);
@@ -1055,10 +1056,10 @@ static INLINE TX_SIZE av1_get_tx_size(int plane, const MACROBLOCKD *xd) {
 }
 
 void av1_reset_skip_context(MACROBLOCKD *xd, int mi_row, int mi_col,
-                            BLOCK_SIZE bsize);
+                            BLOCK_SIZE bsize, const int num_planes);
 
 #if CONFIG_LOOP_RESTORATION
-void av1_reset_loop_restoration(MACROBLOCKD *xd);
+void av1_reset_loop_restoration(MACROBLOCKD *xd, const int num_planes);
 #endif  // CONFIG_LOOP_RESTORATION
 
 typedef void (*foreach_transformed_block_visitor)(int plane, int block,
@@ -1074,7 +1075,7 @@ void av1_foreach_transformed_block_in_plane(
 void av1_foreach_transformed_block(const MACROBLOCKD *const xd,
                                    BLOCK_SIZE bsize, int mi_row, int mi_col,
                                    foreach_transformed_block_visitor visit,
-                                   void *arg);
+                                   void *arg, const int num_planes);
 #endif
 
 void av1_set_contexts(const MACROBLOCKD *xd, struct macroblockd_plane *pd,
diff --git a/av1/common/cdef.c b/av1/common/cdef.c
index d82d060e8c1f65f758599bfb120d94d84e928c02..bd37b2bd4530f46e5f3b804c79cbee8721484177 100644
--- a/av1/common/cdef.c
+++ b/av1/common/cdef.c
@@ -150,6 +150,7 @@ static INLINE void copy_rect(uint16_t *dst, int dstride, const uint16_t *src,
 
 void av1_cdef_frame(YV12_BUFFER_CONFIG *frame, AV1_COMMON *cm,
                     MACROBLOCKD *xd) {
+  const int num_planes = av1_num_planes(cm);
   DECLARE_ALIGNED(16, uint16_t, src[CDEF_INBUF_SIZE]);
   uint16_t *linebuf[3];
   uint16_t *colbuf[3];
@@ -163,22 +164,21 @@ void av1_cdef_frame(YV12_BUFFER_CONFIG *frame, AV1_COMMON *cm,
   int xdec[3];
   int ydec[3];
   int coeff_shift = AOMMAX(cm->bit_depth - 8, 0);
-  int nplanes = av1_num_planes(cm);
   const int nvfb = (cm->mi_rows + MI_SIZE_64X64 - 1) / MI_SIZE_64X64;
   const int nhfb = (cm->mi_cols + MI_SIZE_64X64 - 1) / MI_SIZE_64X64;
-  av1_setup_dst_planes(xd->plane, cm->sb_size, frame, 0, 0);
+  av1_setup_dst_planes(xd->plane, cm->sb_size, frame, 0, 0, num_planes);
   row_cdef = aom_malloc(sizeof(*row_cdef) * (nhfb + 2) * 2);
   memset(row_cdef, 1, sizeof(*row_cdef) * (nhfb + 2) * 2);
   prev_row_cdef = row_cdef + 1;
   curr_row_cdef = prev_row_cdef + nhfb + 2;
-  for (int pli = 0; pli < nplanes; pli++) {
+  for (int pli = 0; pli < num_planes; pli++) {
     xdec[pli] = xd->plane[pli].subsampling_x;
     ydec[pli] = xd->plane[pli].subsampling_y;
     mi_wide_l2[pli] = MI_SIZE_LOG2 - xd->plane[pli].subsampling_x;
     mi_high_l2[pli] = MI_SIZE_LOG2 - xd->plane[pli].subsampling_y;
   }
   const int stride = (cm->mi_cols << MI_SIZE_LOG2) + 2 * CDEF_HBORDER;
-  for (int pli = 0; pli < nplanes; pli++) {
+  for (int pli = 0; pli < num_planes; pli++) {
     linebuf[pli] = aom_malloc(sizeof(*linebuf) * CDEF_VBORDER * stride);
     colbuf[pli] =
         aom_malloc(sizeof(*colbuf) *
@@ -186,7 +186,7 @@ void av1_cdef_frame(YV12_BUFFER_CONFIG *frame, AV1_COMMON *cm,
                    CDEF_HBORDER);
   }
   for (int fbr = 0; fbr < nvfb; fbr++) {
-    for (int pli = 0; pli < nplanes; pli++) {
+    for (int pli = 0; pli < num_planes; pli++) {
       const int block_height =
           (MI_SIZE_64X64 << mi_high_l2[pli]) + 2 * CDEF_VBORDER;
       fill_rect(colbuf[pli], CDEF_HBORDER, block_height, CDEF_HBORDER,
@@ -276,7 +276,7 @@ void av1_cdef_frame(YV12_BUFFER_CONFIG *frame, AV1_COMMON *cm,
       }
 
       curr_row_cdef[fbc] = 1;
-      for (int pli = 0; pli < nplanes; pli++) {
+      for (int pli = 0; pli < num_planes; pli++) {
         int coffset;
         int rend, cend;
         int pri_damping = cm->cdef_pri_damping;
@@ -423,7 +423,7 @@ void av1_cdef_frame(YV12_BUFFER_CONFIG *frame, AV1_COMMON *cm,
     }
   }
   aom_free(row_cdef);
-  for (int pli = 0; pli < nplanes; pli++) {
+  for (int pli = 0; pli < num_planes; pli++) {
     aom_free(linebuf[pli]);
     aom_free(colbuf[pli]);
   }
diff --git a/av1/common/obmc.h b/av1/common/obmc.h
index 5ad4d2980cdb85590e25e6f0afdf583fdceea814..53301e7ff6d03d474c99b607d90f68393c3ff3af 100644
--- a/av1/common/obmc.h
+++ b/av1/common/obmc.h
@@ -14,13 +14,14 @@
 
 typedef void (*overlappable_nb_visitor_t)(MACROBLOCKD *xd, int rel_mi_pos,
                                           uint8_t nb_mi_size, MODE_INFO *nb_mi,
-                                          void *fun_ctxt);
+                                          void *fun_ctxt, const int num_planes);
 
 static INLINE void foreach_overlappable_nb_above(const AV1_COMMON *cm,
                                                  MACROBLOCKD *xd, int mi_col,
                                                  int nb_max,
                                                  overlappable_nb_visitor_t fun,
                                                  void *fun_ctxt) {
+  const int num_planes = av1_num_planes(cm);
   if (!xd->up_available) return;
 
   int nb_count = 0;
@@ -49,7 +50,7 @@ static INLINE void foreach_overlappable_nb_above(const AV1_COMMON *cm,
     if (is_neighbor_overlappable(above_mbmi)) {
       ++nb_count;
       fun(xd, above_mi_col - mi_col, AOMMIN(xd->n8_w, mi_step), *above_mi,
-          fun_ctxt);
+          fun_ctxt, num_planes);
     }
   }
 }
@@ -59,6 +60,7 @@ static INLINE void foreach_overlappable_nb_left(const AV1_COMMON *cm,
                                                 int nb_max,
                                                 overlappable_nb_visitor_t fun,
                                                 void *fun_ctxt) {
+  const int num_planes = av1_num_planes(cm);
   if (!xd->left_available) return;
 
   int nb_count = 0;
@@ -82,7 +84,7 @@ static INLINE void foreach_overlappable_nb_left(const AV1_COMMON *cm,
     if (is_neighbor_overlappable(left_mbmi)) {
       ++nb_count;
       fun(xd, left_mi_row - mi_row, AOMMIN(xd->n8_h, mi_step), *left_mi,
-          fun_ctxt);
+          fun_ctxt, num_planes);
     }
   }
 }
diff --git a/av1/common/onyxc_int.h b/av1/common/onyxc_int.h
index d8d3ed0e8e05a277e1ab757a034dc8f335fc7e60..dbf08398a583b01368983ee1999fe5401a133d8f 100644
--- a/av1/common/onyxc_int.h
+++ b/av1/common/onyxc_int.h
@@ -757,7 +757,8 @@ static INLINE int av1_num_planes(const AV1_COMMON *cm) {
 
 static INLINE void av1_init_macroblockd(AV1_COMMON *cm, MACROBLOCKD *xd,
                                         tran_low_t *dqcoeff) {
-  for (int i = 0; i < MAX_MB_PLANE; ++i) {
+  const int num_planes = av1_num_planes(cm);
+  for (int i = 0; i < num_planes; ++i) {
     xd->plane[i].dqcoeff = dqcoeff;
     xd->above_context[i] = cm->above_context[i];
     if (xd->plane[i].plane_type == PLANE_TYPE_Y) {
@@ -807,11 +808,12 @@ static INLINE void av1_init_macroblockd(AV1_COMMON *cm, MACROBLOCKD *xd,
 #endif
 }
 
-static INLINE void set_skip_context(MACROBLOCKD *xd, int mi_row, int mi_col) {
+static INLINE void set_skip_context(MACROBLOCKD *xd, int mi_row, int mi_col,
+                                    const int num_planes) {
   int i;
   int row_offset = mi_row;
   int col_offset = mi_col;
-  for (i = 0; i < MAX_MB_PLANE; ++i) {
+  for (i = 0; i < num_planes; ++i) {
     struct macroblockd_plane *const pd = &xd->plane[i];
     // Offset the buffer pointer
     const BLOCK_SIZE bsize = xd->mi[0]->mbmi.sb_type;
@@ -832,9 +834,10 @@ static INLINE int calc_mi_size(int len) {
   return len + MAX_MIB_SIZE;
 }
 
-static INLINE void set_plane_n4(MACROBLOCKD *const xd, int bw, int bh) {
+static INLINE void set_plane_n4(MACROBLOCKD *const xd, int bw, int bh,
+                                const int num_planes) {
   int i;
-  for (i = 0; i < MAX_MB_PLANE; i++) {
+  for (i = 0; i < num_planes; i++) {
     xd->plane[i].width = (bw * MI_SIZE) >> xd->plane[i].subsampling_x;
     xd->plane[i].height = (bh * MI_SIZE) >> xd->plane[i].subsampling_y;
 
@@ -1160,6 +1163,7 @@ static INLINE int max_intra_block_height(const MACROBLOCKD *xd,
 
 static INLINE void av1_zero_above_context(AV1_COMMON *const cm,
                                           int mi_col_start, int mi_col_end) {
+  const int num_planes = av1_num_planes(cm);
   const int width = mi_col_end - mi_col_start;
   const int aligned_width = ALIGN_POWER_OF_TWO(width, cm->mib_size_log2);
 
@@ -1169,8 +1173,10 @@ static INLINE void av1_zero_above_context(AV1_COMMON *const cm,
   const int width_uv = width_y >> cm->subsampling_x;
 
   av1_zero_array(cm->above_context[0] + offset_y, width_y);
-  av1_zero_array(cm->above_context[1] + offset_uv, width_uv);
-  av1_zero_array(cm->above_context[2] + offset_uv, width_uv);
+  if (num_planes > 1) {
+    av1_zero_array(cm->above_context[1] + offset_uv, width_uv);
+    av1_zero_array(cm->above_context[2] + offset_uv, width_uv);
+  }
 
   av1_zero_array(cm->above_seg_context + mi_col_start, aligned_width);
 
diff --git a/av1/common/quant_common.c b/av1/common/quant_common.c
index c9241169957af5c3415d0058f325fed1c679749a..bb9997a12d3aeb1e3ff040e9e2ce1486696ed919 100644
--- a/av1/common/quant_common.c
+++ b/av1/common/quant_common.c
@@ -600,10 +600,11 @@ static const uint16_t wt_matrix_ref[NUM_QM_LEVELS][2][QM_TOTAL_SIZE];
 static const uint16_t iwt_matrix_ref[NUM_QM_LEVELS][2][QM_TOTAL_SIZE];
 
 void aom_qm_init(AV1_COMMON *cm) {
+  const int num_planes = av1_num_planes(cm);
   int q, c, t;
   int current;
   for (q = 0; q < NUM_QM_LEVELS; ++q) {
-    for (c = 0; c < av1_num_planes(cm); ++c) {
+    for (c = 0; c < num_planes; ++c) {
       current = 0;
       for (t = 0; t < TX_SIZES_ALL; ++t) {
         const int size = tx_size_2d[t];
diff --git a/av1/common/reconinter.c b/av1/common/reconinter.c
index e96abd4c29bc7dfd867f1caaf0a382fe5d3f41cb..b709793653db3073830310f33e50bf3f67e47707 100644
--- a/av1/common/reconinter.c
+++ b/av1/common/reconinter.c
@@ -1304,46 +1304,39 @@ void av1_build_inter_predictors_sbuv(const AV1_COMMON *cm, MACROBLOCKD *xd,
 void av1_build_inter_predictors_sb(const AV1_COMMON *cm, MACROBLOCKD *xd,
                                    int mi_row, int mi_col, BUFFER_SET *ctx,
                                    BLOCK_SIZE bsize) {
+  const int num_planes = av1_num_planes(cm);
   av1_build_inter_predictors_sby(cm, xd, mi_row, mi_col, ctx, bsize);
-  av1_build_inter_predictors_sbuv(cm, xd, mi_row, mi_col, ctx, bsize);
+  if (num_planes > 1)
+    av1_build_inter_predictors_sbuv(cm, xd, mi_row, mi_col, ctx, bsize);
 }
 
 void av1_setup_dst_planes(struct macroblockd_plane *planes, BLOCK_SIZE bsize,
-                          const YV12_BUFFER_CONFIG *src, int mi_row,
-                          int mi_col) {
-  const int widths[MAX_MB_PLANE] = { src->y_crop_width, src->uv_crop_width,
-                                     src->uv_crop_width };
-  const int heights[MAX_MB_PLANE] = { src->y_crop_height, src->uv_crop_height,
-                                      src->uv_crop_height };
-  const int strides[MAX_MB_PLANE] = { src->y_stride, src->uv_stride,
-                                      src->uv_stride };
-  int i;
-
-  for (i = 0; i < MAX_MB_PLANE; ++i) {
+                          const YV12_BUFFER_CONFIG *src, int mi_row, int mi_col,
+                          const int num_planes) {
+  // We use AOMMIN(num_planes, MAX_MB_PLANE) instead of num_planes to quiet
+  // the static analysis warnings.
+  for (int i = 0; i < AOMMIN(num_planes, MAX_MB_PLANE); ++i) {
     struct macroblockd_plane *const pd = &planes[i];
-    setup_pred_plane(&pd->dst, bsize, src->buffers[i], widths[i], heights[i],
-                     strides[i], mi_row, mi_col, NULL, pd->subsampling_x,
-                     pd->subsampling_y);
+    const int is_uv = i > 0;
+    setup_pred_plane(&pd->dst, bsize, src->buffers[i], src->crop_widths[is_uv],
+                     src->crop_heights[is_uv], src->strides[is_uv], mi_row,
+                     mi_col, NULL, pd->subsampling_x, pd->subsampling_y);
   }
 }
 
 void av1_setup_pre_planes(MACROBLOCKD *xd, int idx,
                           const YV12_BUFFER_CONFIG *src, int mi_row, int mi_col,
-                          const struct scale_factors *sf) {
+                          const struct scale_factors *sf,
+                          const int num_planes) {
   if (src != NULL) {
-    int i;
-    uint8_t *const buffers[MAX_MB_PLANE] = { src->y_buffer, src->u_buffer,
-                                             src->v_buffer };
-    const int widths[MAX_MB_PLANE] = { src->y_crop_width, src->uv_crop_width,
-                                       src->uv_crop_width };
-    const int heights[MAX_MB_PLANE] = { src->y_crop_height, src->uv_crop_height,
-                                        src->uv_crop_height };
-    const int strides[MAX_MB_PLANE] = { src->y_stride, src->uv_stride,
-                                        src->uv_stride };
-    for (i = 0; i < MAX_MB_PLANE; ++i) {
+    // We use AOMMIN(num_planes, MAX_MB_PLANE) instead of num_planes to quiet
+    // the static analysis warnings.
+    for (int i = 0; i < AOMMIN(num_planes, MAX_MB_PLANE); ++i) {
       struct macroblockd_plane *const pd = &xd->plane[i];
-      setup_pred_plane(&pd->pre[idx], xd->mi[0]->mbmi.sb_type, buffers[i],
-                       widths[i], heights[i], strides[i], mi_row, mi_col, sf,
+      const int is_uv = i > 0;
+      setup_pred_plane(&pd->pre[idx], xd->mi[0]->mbmi.sb_type, src->buffers[i],
+                       src->crop_widths[is_uv], src->crop_heights[is_uv],
+                       src->strides[is_uv], mi_row, mi_col, sf,
                        pd->subsampling_x, pd->subsampling_y);
     }
   }
@@ -1392,12 +1385,13 @@ const uint8_t *av1_get_obmc_mask(int length) {
 
 static INLINE void increment_int_ptr(MACROBLOCKD *xd, int rel_mi_rc,
                                      uint8_t mi_hw, MODE_INFO *mi,
-                                     void *fun_ctxt) {
+                                     void *fun_ctxt, const int num_planes) {
   (void)xd;
   (void)rel_mi_rc;
   (void)mi_hw;
   (void)mi;
   ++*(int *)fun_ctxt;
+  (void)num_planes;
 }
 
 void av1_count_overlappable_neighbors(const AV1_COMMON *cm, MACROBLOCKD *xd,
@@ -1448,7 +1442,8 @@ struct obmc_inter_pred_ctxt {
 static INLINE void build_obmc_inter_pred_above(MACROBLOCKD *xd, int rel_mi_col,
                                                uint8_t above_mi_width,
                                                MODE_INFO *above_mi,
-                                               void *fun_ctxt) {
+                                               void *fun_ctxt,
+                                               const int num_planes) {
   (void)above_mi;
   struct obmc_inter_pred_ctxt *ctxt = (struct obmc_inter_pred_ctxt *)fun_ctxt;
   const BLOCK_SIZE bsize = xd->mi[0]->mbmi.sb_type;
@@ -1456,7 +1451,7 @@ static INLINE void build_obmc_inter_pred_above(MACROBLOCKD *xd, int rel_mi_col,
   const int overlap =
       AOMMIN(block_size_high[bsize], block_size_high[BLOCK_64X64]) >> 1;
 
-  for (int plane = 0; plane < MAX_MB_PLANE; ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     const struct macroblockd_plane *pd = &xd->plane[plane];
     const int bw = (above_mi_width * MI_SIZE) >> pd->subsampling_x;
     const int bh = overlap >> pd->subsampling_y;
@@ -1482,7 +1477,8 @@ static INLINE void build_obmc_inter_pred_above(MACROBLOCKD *xd, int rel_mi_col,
 static INLINE void build_obmc_inter_pred_left(MACROBLOCKD *xd, int rel_mi_row,
                                               uint8_t left_mi_height,
                                               MODE_INFO *left_mi,
-                                              void *fun_ctxt) {
+                                              void *fun_ctxt,
+                                              const int num_planes) {
   (void)left_mi;
   struct obmc_inter_pred_ctxt *ctxt = (struct obmc_inter_pred_ctxt *)fun_ctxt;
   const BLOCK_SIZE bsize = xd->mi[0]->mbmi.sb_type;
@@ -1490,7 +1486,7 @@ static INLINE void build_obmc_inter_pred_left(MACROBLOCKD *xd, int rel_mi_row,
       AOMMIN(block_size_wide[bsize], block_size_wide[BLOCK_64X64]) >> 1;
   const int is_hbd = (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) ? 1 : 0;
 
-  for (int plane = 0; plane < MAX_MB_PLANE; ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     const struct macroblockd_plane *pd = &xd->plane[plane];
     const int bw = overlap >> pd->subsampling_x;
     const int bh = (left_mi_height * MI_SIZE) >> pd->subsampling_y;
@@ -1561,11 +1557,9 @@ struct build_prediction_ctxt {
   int mb_to_far_edge;
 };
 
-static INLINE void build_prediction_by_above_pred(MACROBLOCKD *xd,
-                                                  int rel_mi_col,
-                                                  uint8_t above_mi_width,
-                                                  MODE_INFO *above_mi,
-                                                  void *fun_ctxt) {
+static INLINE void build_prediction_by_above_pred(
+    MACROBLOCKD *xd, int rel_mi_col, uint8_t above_mi_width,
+    MODE_INFO *above_mi, void *fun_ctxt, const int num_planes) {
   MB_MODE_INFO *above_mbmi = &above_mi->mbmi;
   const BLOCK_SIZE a_bsize = AOMMAX(BLOCK_8X8, above_mbmi->sb_type);
   struct build_prediction_ctxt *ctxt = (struct build_prediction_ctxt *)fun_ctxt;
@@ -1574,7 +1568,7 @@ static INLINE void build_prediction_by_above_pred(MACROBLOCKD *xd,
   MB_MODE_INFO backup_mbmi = *above_mbmi;
   modify_neighbor_predictor_for_obmc(above_mbmi);
 
-  for (int j = 0; j < MAX_MB_PLANE; ++j) {
+  for (int j = 0; j < num_planes; ++j) {
     struct macroblockd_plane *const pd = &xd->plane[j];
     setup_pred_plane(&pd->dst, a_bsize, ctxt->tmp_buf[j], ctxt->tmp_width[j],
                      ctxt->tmp_height[j], ctxt->tmp_stride[j], 0, rel_mi_col,
@@ -1593,7 +1587,7 @@ static INLINE void build_prediction_by_above_pred(MACROBLOCKD *xd,
       aom_internal_error(xd->error_info, AOM_CODEC_UNSUP_BITSTREAM,
                          "Reference frame has invalid dimensions");
     av1_setup_pre_planes(xd, ref, ref_buf->buf, ctxt->mi_row, above_mi_col,
-                         &ref_buf->sf);
+                         &ref_buf->sf, num_planes);
   }
 
   xd->mb_to_left_edge = 8 * MI_SIZE * (-above_mi_col);
@@ -1605,7 +1599,7 @@ static INLINE void build_prediction_by_above_pred(MACROBLOCKD *xd,
 
   const BLOCK_SIZE bsize = xd->mi[0]->mbmi.sb_type;
 
-  for (int j = 0; j < MAX_MB_PLANE; ++j) {
+  for (int j = 0; j < num_planes; ++j) {
     const struct macroblockd_plane *pd = &xd->plane[j];
     int bw = (above_mi_width * MI_SIZE) >> pd->subsampling_x;
     int bh = clamp(block_size_high[bsize] >> (pd->subsampling_y + 1), 4,
@@ -1647,11 +1641,9 @@ void av1_build_prediction_by_above_preds(const AV1_COMMON *cm, MACROBLOCKD *xd,
   xd->mb_to_bottom_edge -= (this_height - pred_height) * 8;
 }
 
-static INLINE void build_prediction_by_left_pred(MACROBLOCKD *xd,
-                                                 int rel_mi_row,
-                                                 uint8_t left_mi_height,
-                                                 MODE_INFO *left_mi,
-                                                 void *fun_ctxt) {
+static INLINE void build_prediction_by_left_pred(
+    MACROBLOCKD *xd, int rel_mi_row, uint8_t left_mi_height, MODE_INFO *left_mi,
+    void *fun_ctxt, const int num_planes) {
   MB_MODE_INFO *left_mbmi = &left_mi->mbmi;
   const BLOCK_SIZE l_bsize = AOMMAX(BLOCK_8X8, left_mbmi->sb_type);
   struct build_prediction_ctxt *ctxt = (struct build_prediction_ctxt *)fun_ctxt;
@@ -1660,7 +1652,7 @@ static INLINE void build_prediction_by_left_pred(MACROBLOCKD *xd,
   MB_MODE_INFO backup_mbmi = *left_mbmi;
   modify_neighbor_predictor_for_obmc(left_mbmi);
 
-  for (int j = 0; j < MAX_MB_PLANE; ++j) {
+  for (int j = 0; j < num_planes; ++j) {
     struct macroblockd_plane *const pd = &xd->plane[j];
     setup_pred_plane(&pd->dst, l_bsize, ctxt->tmp_buf[j], ctxt->tmp_width[j],
                      ctxt->tmp_height[j], ctxt->tmp_stride[j], rel_mi_row, 0,
@@ -1679,7 +1671,7 @@ static INLINE void build_prediction_by_left_pred(MACROBLOCKD *xd,
       aom_internal_error(xd->error_info, AOM_CODEC_UNSUP_BITSTREAM,
                          "Reference frame has invalid dimensions");
     av1_setup_pre_planes(xd, ref, ref_buf->buf, left_mi_row, ctxt->mi_col,
-                         &ref_buf->sf);
+                         &ref_buf->sf, num_planes);
   }
 
   xd->mb_to_top_edge = 8 * MI_SIZE * (-left_mi_row);
@@ -1692,7 +1684,7 @@ static INLINE void build_prediction_by_left_pred(MACROBLOCKD *xd,
 
   const BLOCK_SIZE bsize = xd->mi[0]->mbmi.sb_type;
 
-  for (int j = 0; j < MAX_MB_PLANE; ++j) {
+  for (int j = 0; j < num_planes; ++j) {
     const struct macroblockd_plane *pd = &xd->plane[j];
     int bw = clamp(block_size_wide[bsize] >> (pd->subsampling_x + 1), 4,
                    block_size_wide[BLOCK_64X64] >> (pd->subsampling_x + 1));
@@ -1736,6 +1728,7 @@ void av1_build_prediction_by_left_preds(const AV1_COMMON *cm, MACROBLOCKD *xd,
 
 void av1_build_obmc_inter_predictors_sb(const AV1_COMMON *cm, MACROBLOCKD *xd,
                                         int mi_row, int mi_col) {
+  const int num_planes = av1_num_planes(cm);
   DECLARE_ALIGNED(16, uint8_t, tmp_buf1[2 * MAX_MB_PLANE * MAX_SB_SQUARE]);
   DECLARE_ALIGNED(16, uint8_t, tmp_buf2[2 * MAX_MB_PLANE * MAX_SB_SQUARE]);
   uint8_t *dst_buf1[MAX_MB_PLANE], *dst_buf2[MAX_MB_PLANE];
@@ -1767,7 +1760,7 @@ void av1_build_obmc_inter_predictors_sb(const AV1_COMMON *cm, MACROBLOCKD *xd,
   av1_build_prediction_by_left_preds(cm, xd, mi_row, mi_col, dst_buf2,
                                      dst_width2, dst_height2, dst_stride2);
   av1_setup_dst_planes(xd->plane, xd->mi[0]->mbmi.sb_type,
-                       get_frame_new_buffer(cm), mi_row, mi_col);
+                       get_frame_new_buffer(cm), mi_row, mi_col, num_planes);
   av1_build_obmc_inter_prediction(cm, xd, mi_row, mi_col, dst_buf1, dst_stride1,
                                   dst_buf2, dst_stride2);
 }
diff --git a/av1/common/reconinter.h b/av1/common/reconinter.h
index cd6e8bb4c1f4e1621b349653dade1890010063f7..df92cfe53a43412749cebf92796f6fb6084e387a 100644
--- a/av1/common/reconinter.h
+++ b/av1/common/reconinter.h
@@ -355,12 +355,12 @@ static INLINE void setup_pred_plane(struct buf_2d *dst, BLOCK_SIZE bsize,
 }
 
 void av1_setup_dst_planes(struct macroblockd_plane *planes, BLOCK_SIZE bsize,
-                          const YV12_BUFFER_CONFIG *src, int mi_row,
-                          int mi_col);
+                          const YV12_BUFFER_CONFIG *src, int mi_row, int mi_col,
+                          const int num_planes);
 
 void av1_setup_pre_planes(MACROBLOCKD *xd, int idx,
                           const YV12_BUFFER_CONFIG *src, int mi_row, int mi_col,
-                          const struct scale_factors *sf);
+                          const struct scale_factors *sf, const int num_planes);
 
 // Detect if the block have sub-pixel level motion vectors
 // per component.
diff --git a/av1/common/resize.c b/av1/common/resize.c
index 0bbecbc7956a351ece725d0fed3557a8663b9de2..5abdabb29e4bc1e633212c9b738073fef342e1f6 100644
--- a/av1/common/resize.c
+++ b/av1/common/resize.c
@@ -1080,33 +1080,26 @@ void av1_highbd_resize_frame444(const uint8_t *const y, int y_stride,
 }
 
 void av1_resize_and_extend_frame(const YV12_BUFFER_CONFIG *src,
-                                 YV12_BUFFER_CONFIG *dst, int bd) {
+                                 YV12_BUFFER_CONFIG *dst, int bd,
+                                 const int num_planes) {
   // TODO(dkovalev): replace YV12_BUFFER_CONFIG with aom_image_t
-  int i;
-  const uint8_t *const srcs[3] = { src->y_buffer, src->u_buffer,
-                                   src->v_buffer };
-  const int src_strides[3] = { src->y_stride, src->uv_stride, src->uv_stride };
-  const int src_widths[3] = { src->y_crop_width, src->uv_crop_width,
-                              src->uv_crop_width };
-  const int src_heights[3] = { src->y_crop_height, src->uv_crop_height,
-                               src->uv_crop_height };
-  uint8_t *const dsts[3] = { dst->y_buffer, dst->u_buffer, dst->v_buffer };
-  const int dst_strides[3] = { dst->y_stride, dst->uv_stride, dst->uv_stride };
-  const int dst_widths[3] = { dst->y_crop_width, dst->uv_crop_width,
-                              dst->uv_crop_width };
-  const int dst_heights[3] = { dst->y_crop_height, dst->uv_crop_height,
-                               dst->uv_crop_height };
-
-  for (i = 0; i < MAX_MB_PLANE; ++i) {
+
+  // We use AOMMIN(num_planes, MAX_MB_PLANE) instead of num_planes to quiet
+  // the static analysis warnings.
+  for (int i = 0; i < AOMMIN(num_planes, MAX_MB_PLANE); ++i) {
+    const int is_uv = i > 0;
     if (src->flags & YV12_FLAG_HIGHBITDEPTH)
-      highbd_resize_plane(srcs[i], src_heights[i], src_widths[i],
-                          src_strides[i], dsts[i], dst_heights[i],
-                          dst_widths[i], dst_strides[i], bd);
+      highbd_resize_plane(src->buffers[i], src->crop_heights[is_uv],
+                          src->crop_widths[is_uv], src->strides[is_uv],
+                          dst->buffers[i], dst->crop_heights[is_uv],
+                          dst->crop_widths[is_uv], dst->strides[is_uv], bd);
     else
-      resize_plane(srcs[i], src_heights[i], src_widths[i], src_strides[i],
-                   dsts[i], dst_heights[i], dst_widths[i], dst_strides[i]);
+      resize_plane(src->buffers[i], src->crop_heights[is_uv],
+                   src->crop_widths[is_uv], src->strides[is_uv],
+                   dst->buffers[i], dst->crop_heights[is_uv],
+                   dst->crop_widths[is_uv], dst->strides[is_uv]);
   }
-  aom_extend_frame_borders(dst);
+  aom_extend_frame_borders(dst, num_planes);
 }
 
 #if CONFIG_HORZONLY_FRAME_SUPERRES
@@ -1183,23 +1176,26 @@ void av1_upscale_normative_rows(const AV1_COMMON *cm, const uint8_t *src,
 void av1_upscale_normative_and_extend_frame(const AV1_COMMON *cm,
                                             const YV12_BUFFER_CONFIG *src,
                                             YV12_BUFFER_CONFIG *dst) {
-  for (int i = 0; i < MAX_MB_PLANE; ++i) {
+  const int num_planes = av1_num_planes(cm);
+  for (int i = 0; i < num_planes; ++i) {
     const int is_uv = (i > 0);
     av1_upscale_normative_rows(cm, src->buffers[i], src->strides[is_uv],
                                dst->buffers[i], dst->strides[is_uv], i,
                                src->crop_heights[is_uv]);
   }
 
-  aom_extend_frame_borders(dst);
+  aom_extend_frame_borders(dst, num_planes);
 }
 #endif  // CONFIG_HORZONLY_FRAME_SUPERRES
 
 YV12_BUFFER_CONFIG *av1_scale_if_required(AV1_COMMON *cm,
                                           YV12_BUFFER_CONFIG *unscaled,
                                           YV12_BUFFER_CONFIG *scaled) {
+  const int num_planes = av1_num_planes(cm);
   if (cm->width != unscaled->y_crop_width ||
       cm->height != unscaled->y_crop_height) {
-    av1_resize_and_extend_frame(unscaled, scaled, (int)cm->bit_depth);
+    av1_resize_and_extend_frame(unscaled, scaled, (int)cm->bit_depth,
+                                num_planes);
     return scaled;
   } else {
     return unscaled;
@@ -1243,6 +1239,7 @@ void av1_calculate_unscaled_superres_size(int *width, int *height, int denom) {
 // TODO(afergs): aom_ vs av1_ functions? Which can I use?
 // Upscale decoded image.
 void av1_superres_upscale(AV1_COMMON *cm, BufferPool *const pool) {
+  const int num_planes = av1_num_planes(cm);
   if (av1_superres_unscaled(cm)) return;
 
   YV12_BUFFER_CONFIG copy_buffer;
@@ -1258,7 +1255,7 @@ void av1_superres_upscale(AV1_COMMON *cm, BufferPool *const pool) {
                        "Failed to allocate copy buffer for superres upscaling");
 
   // Copy function assumes the frames are the same size, doesn't copy bit_depth.
-  aom_yv12_copy_frame(frame_to_show, &copy_buffer);
+  aom_yv12_copy_frame(frame_to_show, &copy_buffer, num_planes);
   copy_buffer.bit_depth = frame_to_show->bit_depth;
   assert(copy_buffer.y_crop_width == cm->width);
   assert(copy_buffer.y_crop_height == cm->height);
diff --git a/av1/common/resize.h b/av1/common/resize.h
index 7c7505e21014269d83824a9de97d19cb93e1ebb1..e54eff8dba78ac5b16299ee10bb0ec732e970f81 100644
--- a/av1/common/resize.h
+++ b/av1/common/resize.h
@@ -61,7 +61,8 @@ void av1_highbd_resize_frame444(const uint8_t *const y, int y_stride,
                                 uint8_t *ov, int ouv_stride, int oheight,
                                 int owidth, int bd);
 void av1_resize_and_extend_frame(const YV12_BUFFER_CONFIG *src,
-                                 YV12_BUFFER_CONFIG *dst, int bd);
+                                 YV12_BUFFER_CONFIG *dst, int bd,
+                                 const int num_planes);
 
 #if CONFIG_HORZONLY_FRAME_SUPERRES
 void av1_upscale_normative_rows(const AV1_COMMON *cm, const uint8_t *src,
diff --git a/av1/common/restoration.c b/av1/common/restoration.c
index 45a516c2ae5ccef5e773ec2ee594ed57673b52e3..946725a46756c5b2b2e5afefc7d6dd17995ca476 100644
--- a/av1/common/restoration.c
+++ b/av1/common/restoration.c
@@ -1447,6 +1447,7 @@ static void filter_frame_on_unit(const RestorationTileLimits *limits,
 
 void av1_loop_restoration_filter_frame(YV12_BUFFER_CONFIG *frame,
                                        AV1_COMMON *cm) {
+  const int num_planes = av1_num_planes(cm);
   typedef void (*copy_fun)(const YV12_BUFFER_CONFIG *src,
                            YV12_BUFFER_CONFIG *dst);
   static const copy_fun copy_funs[3] = { aom_yv12_copy_y, aom_yv12_copy_u,
@@ -1469,7 +1470,7 @@ void av1_loop_restoration_filter_frame(YV12_BUFFER_CONFIG *frame,
   const int bit_depth = cm->bit_depth;
   const int highbd = cm->use_highbitdepth;
 
-  for (int plane = 0; plane < 3; ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     const RestorationInfo *rsi = &cm->rst_info[plane];
     RestorationType rtype = rsi->frame_restoration_type;
     if (rtype == RESTORE_NONE) {
@@ -1505,7 +1506,7 @@ void av1_loop_restoration_filter_frame(YV12_BUFFER_CONFIG *frame,
                                    filter_frame_on_unit, &ctxt);
   }
 
-  for (int plane = 0; plane < 3; ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     copy_funs[plane](&dst, frame);
   }
   aom_free_frame_buffer(&dst);
@@ -1936,8 +1937,9 @@ static void save_tile_row_boundary_lines(const YV12_BUFFER_CONFIG *frame,
 // lines are saved in rst_internal.stripe_boundary_lines
 void av1_loop_restoration_save_boundary_lines(const YV12_BUFFER_CONFIG *frame,
                                               AV1_COMMON *cm, int after_cdef) {
+  const int num_planes = av1_num_planes(cm);
   const int use_highbd = cm->use_highbitdepth;
-  for (int p = 0; p < MAX_MB_PLANE; ++p) {
+  for (int p = 0; p < num_planes; ++p) {
     TileInfo tile_info;
     for (int tile_row = 0; tile_row < cm->tile_rows; ++tile_row) {
       av1_tile_init(&tile_info, cm, tile_row, 0);
diff --git a/av1/common/thread_common.c b/av1/common/thread_common.c
index d515912312f119388052bcb1366dc04bd5f5748a..0f8a06e5c466575df6bd705858a21313df928174 100644
--- a/av1/common/thread_common.c
+++ b/av1/common/thread_common.c
@@ -161,7 +161,8 @@ static int loop_filter_ver_row_worker(AV1LfSync *const lf_sync,
       int plane;
 
       av1_setup_dst_planes(lf_data->planes, lf_data->cm->sb_size,
-                           lf_data->frame_buffer, mi_row, mi_col);
+                           lf_data->frame_buffer, mi_row, mi_col,
+                           av1_num_planes(lf_data->cm));
       av1_setup_mask(lf_data->cm, mi_row, mi_col, mi + mi_col,
                      lf_data->cm->mi_stride, &lfm);
 
@@ -207,7 +208,8 @@ static int loop_filter_hor_row_worker(AV1LfSync *const lf_sync,
       sync_read(lf_sync, r, c);
 
       av1_setup_dst_planes(lf_data->planes, lf_data->cm->sb_size,
-                           lf_data->frame_buffer, mi_row, mi_col);
+                           lf_data->frame_buffer, mi_row, mi_col,
+                           av1_num_planes(lf_data->cm));
       av1_setup_mask(lf_data->cm, mi_row, mi_col, mi + mi_col,
                      lf_data->cm->mi_stride, &lfm);
 #if CONFIG_EXT_PARTITION_TYPES
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index d2d5ae7f3f73fa067eb2e5ee6b48d85231af31cc..025afdf122d4450b6e6fddecca8fdef3cd44b20e 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -282,6 +282,8 @@ static void decode_reconstruct_tx(AV1_COMMON *cm, MACROBLOCKD *const xd,
 static void set_offsets(AV1_COMMON *const cm, MACROBLOCKD *const xd,
                         BLOCK_SIZE bsize, int mi_row, int mi_col, int bw,
                         int bh, int x_mis, int y_mis) {
+  const int num_planes = av1_num_planes(cm);
+
   const int offset = mi_row * cm->mi_stride + mi_col;
   const TileInfo *const tile = &xd->tile;
 
@@ -307,8 +309,8 @@ static void set_offsets(AV1_COMMON *const cm, MACROBLOCKD *const xd,
     idx += cm->mi_stride;
   }
 
-  set_plane_n4(xd, bw, bh);
-  set_skip_context(xd, mi_row, mi_col);
+  set_plane_n4(xd, bw, bh, num_planes);
+  set_skip_context(xd, mi_row, mi_col, num_planes);
 
   // Distance of Mb to the various image edges. These are specified to 8th pel
   // as they are always compared to values that are in 1/8th pel units
@@ -319,7 +321,7 @@ static void set_offsets(AV1_COMMON *const cm, MACROBLOCKD *const xd,
                  cm->mi_rows, cm->mi_cols);
 
   av1_setup_dst_planes(xd->plane, bsize, get_frame_new_buffer(cm), mi_row,
-                       mi_col);
+                       mi_col, num_planes);
 }
 
 static void decode_mbmi_block(AV1Decoder *const pbi, MACROBLOCKD *const xd,
@@ -359,6 +361,7 @@ static void decode_token_and_recon_block(AV1Decoder *const pbi,
                                          int mi_col, aom_reader *r,
                                          BLOCK_SIZE bsize) {
   AV1_COMMON *const cm = &pbi->common;
+  const int num_planes = av1_num_planes(cm);
   const int bw = mi_size_wide[bsize];
   const int bh = mi_size_high[bsize];
   const int x_mis = AOMMIN(bw, cm->mi_cols - mi_col);
@@ -380,7 +383,7 @@ static void decode_token_and_recon_block(AV1Decoder *const pbi,
 #else
       const int current_qindex = xd->current_qindex;
 #endif  // CONFIG_EXT_DELTA_Q
-      for (int j = 0; j < av1_num_planes(cm); ++j) {
+      for (int j = 0; j < num_planes; ++j) {
         const int dc_delta_q =
             j == 0 ? cm->y_dc_delta_q
                    : (j == 1 ? cm->u_dc_delta_q : cm->v_dc_delta_q);
@@ -393,10 +396,9 @@ static void decode_token_and_recon_block(AV1Decoder *const pbi,
       }
     }
   }
-  if (mbmi->skip) av1_reset_skip_context(xd, mi_row, mi_col, bsize);
+  if (mbmi->skip) av1_reset_skip_context(xd, mi_row, mi_col, bsize, num_planes);
 
   if (!is_inter_block(mbmi)) {
-    const int num_planes = av1_num_planes(cm);
     for (int plane = 0; plane < AOMMIN(2, num_planes); ++plane) {
       if (mbmi->palette_mode_info.palette_size[plane])
         av1_decode_palette_tokens(xd, plane, r);
@@ -461,7 +463,7 @@ static void decode_token_and_recon_block(AV1Decoder *const pbi,
           aom_internal_error(xd->error_info, AOM_CODEC_UNSUP_BITSTREAM,
                              "Reference frame has invalid dimensions");
         av1_setup_pre_planes(xd, ref, ref_buf->buf, mi_row, mi_col,
-                             &ref_buf->sf);
+                             &ref_buf->sf, num_planes);
       }
     }
 
@@ -472,7 +474,7 @@ static void decode_token_and_recon_block(AV1Decoder *const pbi,
     }
 
 #if CONFIG_MISMATCH_DEBUG
-    for (int plane = 0; plane < 3; ++plane) {
+    for (int plane = 0; plane < num_planes; ++plane) {
       const struct macroblockd_plane *pd = &xd->plane[plane];
       int pixel_c, pixel_r;
       mi_to_pixel_loc(&pixel_c, &pixel_r, mi_col, mi_row, 0, 0,
@@ -505,7 +507,7 @@ static void decode_token_and_recon_block(AV1Decoder *const pbi,
 
       for (row = 0; row < max_blocks_high; row += mu_blocks_high) {
         for (col = 0; col < max_blocks_wide; col += mu_blocks_wide) {
-          for (int plane = 0; plane < av1_num_planes(cm); ++plane) {
+          for (int plane = 0; plane < num_planes; ++plane) {
             const struct macroblockd_plane *const pd = &xd->plane[plane];
             if (!is_chroma_reference(mi_row, mi_col, bsize, pd->subsampling_x,
                                      pd->subsampling_y))
@@ -521,7 +523,6 @@ static void decode_token_and_recon_block(AV1Decoder *const pbi,
             int block = 0;
             int step =
                 tx_size_wide_unit[max_tx_size] * tx_size_high_unit[max_tx_size];
-
             int blk_row, blk_col;
             const int unit_height = ROUND_POWER_OF_TWO(
                 AOMMIN(mu_blocks_high + row, max_blocks_high),
@@ -607,6 +608,7 @@ static void decode_partition(AV1Decoder *const pbi, MACROBLOCKD *const xd,
                              int mi_row, int mi_col, aom_reader *r,
                              BLOCK_SIZE bsize) {
   AV1_COMMON *const cm = &pbi->common;
+  const int num_planes = av1_num_planes(cm);
   const int num_8x8_wh = mi_size_wide[bsize];
   const int hbs = num_8x8_wh >> 1;
   PARTITION_TYPE partition;
@@ -621,7 +623,7 @@ static void decode_partition(AV1Decoder *const pbi, MACROBLOCKD *const xd,
   if (mi_row >= cm->mi_rows || mi_col >= cm->mi_cols) return;
 
 #if CONFIG_LOOP_RESTORATION
-  for (int plane = 0; plane < av1_num_planes(cm); ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     int rcol0, rcol1, rrow0, rrow1, tile_tl_idx;
     if (av1_loop_restoration_corners_in_sb(cm, plane, mi_row, mi_col, bsize,
                                            &rcol0, &rcol1, &rrow0, &rrow1,
@@ -820,11 +822,12 @@ static void setup_segmentation(AV1_COMMON *const cm,
 #if CONFIG_LOOP_RESTORATION
 static void decode_restoration_mode(AV1_COMMON *cm,
                                     struct aom_read_bit_buffer *rb) {
+  const int num_planes = av1_num_planes(cm);
 #if CONFIG_INTRABC
   if (cm->allow_intrabc && NO_FILTER_FOR_IBC) return;
 #endif  // CONFIG_INTRABC
   int all_none = 1, chroma_none = 1;
-  for (int p = 0; p < av1_num_planes(cm); ++p) {
+  for (int p = 0; p < num_planes; ++p) {
     RestorationInfo *rsi = &cm->rst_info[p];
     if (aom_rb_read_bit(rb)) {
       rsi->frame_restoration_type =
@@ -840,7 +843,7 @@ static void decode_restoration_mode(AV1_COMMON *cm,
   }
   if (!all_none) {
     const int qsize = RESTORATION_TILESIZE_MAX >> 2;
-    for (int p = 0; p < MAX_MB_PLANE; ++p)
+    for (int p = 0; p < num_planes; ++p)
       cm->rst_info[p].restoration_unit_size = qsize;
 
     RestorationInfo *rsi = &cm->rst_info[0];
@@ -850,11 +853,11 @@ static void decode_restoration_mode(AV1_COMMON *cm,
     }
   } else {
     const int size = RESTORATION_TILESIZE_MAX;
-    for (int p = 0; p < MAX_MB_PLANE; ++p)
+    for (int p = 0; p < num_planes; ++p)
       cm->rst_info[p].restoration_unit_size = size;
   }
 
-  if (av1_num_planes(cm) > 1) {
+  if (num_planes > 1) {
     int s = AOMMIN(cm->subsampling_x, cm->subsampling_y);
     if (s && !chroma_none) {
       cm->rst_info[1].restoration_unit_size =
@@ -987,6 +990,7 @@ static void loop_restoration_read_sb_coeffs(const AV1_COMMON *const cm,
 #endif  // CONFIG_LOOP_RESTORATION
 
 static void setup_loopfilter(AV1_COMMON *cm, struct aom_read_bit_buffer *rb) {
+  const int num_planes = av1_num_planes(cm);
 #if CONFIG_INTRABC
   if (cm->allow_intrabc && NO_FILTER_FOR_IBC) return;
 #endif  // CONFIG_INTRABC
@@ -994,7 +998,7 @@ static void setup_loopfilter(AV1_COMMON *cm, struct aom_read_bit_buffer *rb) {
 #if CONFIG_LOOPFILTER_LEVEL
   lf->filter_level[0] = aom_rb_read_literal(rb, 6);
   lf->filter_level[1] = aom_rb_read_literal(rb, 6);
-  if (av1_num_planes(cm) > 1) {
+  if (num_planes > 1) {
     if (lf->filter_level[0] || lf->filter_level[1]) {
       lf->filter_level_u = aom_rb_read_literal(rb, 6);
       lf->filter_level_v = aom_rb_read_literal(rb, 6);
@@ -1025,6 +1029,7 @@ static void setup_loopfilter(AV1_COMMON *cm, struct aom_read_bit_buffer *rb) {
 }
 
 static void setup_cdef(AV1_COMMON *cm, struct aom_read_bit_buffer *rb) {
+  const int num_planes = av1_num_planes(cm);
 #if CONFIG_INTRABC
   if (cm->allow_intrabc && NO_FILTER_FOR_IBC) return;
 #endif  // CONFIG_INTRABC
@@ -1033,9 +1038,8 @@ static void setup_cdef(AV1_COMMON *cm, struct aom_read_bit_buffer *rb) {
   cm->nb_cdef_strengths = 1 << cm->cdef_bits;
   for (int i = 0; i < cm->nb_cdef_strengths; i++) {
     cm->cdef_strengths[i] = aom_rb_read_literal(rb, CDEF_STRENGTH_BITS);
-    cm->cdef_uv_strengths[i] = av1_num_planes(cm) > 1
-                                   ? aom_rb_read_literal(rb, CDEF_STRENGTH_BITS)
-                                   : 0;
+    cm->cdef_uv_strengths[i] =
+        num_planes > 1 ? aom_rb_read_literal(rb, CDEF_STRENGTH_BITS) : 0;
   }
 }
 
@@ -1045,9 +1049,10 @@ static INLINE int read_delta_q(struct aom_read_bit_buffer *rb) {
 
 static void setup_quantization(AV1_COMMON *const cm,
                                struct aom_read_bit_buffer *rb) {
+  const int num_planes = av1_num_planes(cm);
   cm->base_qindex = aom_rb_read_literal(rb, QINDEX_BITS);
   cm->y_dc_delta_q = read_delta_q(rb);
-  if (av1_num_planes(cm) > 1) {
+  if (num_planes > 1) {
     int diff_uv_delta = 0;
 #if CONFIG_EXT_QM
     if (cm->separate_uv_delta_q) diff_uv_delta = aom_rb_read_bit(rb);
@@ -1916,6 +1921,7 @@ static const uint8_t *decode_tiles(AV1Decoder *pbi, const uint8_t *data,
                                    const uint8_t *data_end, int startTile,
                                    int endTile) {
   AV1_COMMON *const cm = &pbi->common;
+  const int num_planes = av1_num_planes(cm);
 #if !CONFIG_LOOPFILTER_LEVEL
   const AVxWorkerInterface *const winterface = aom_get_worker_interface();
 #endif
@@ -2075,7 +2081,7 @@ static const uint8_t *decode_tiles(AV1Decoder *pbi, const uint8_t *data,
       av1_zero_above_context(cm, tile_info.mi_col_start, tile_info.mi_col_end);
 #endif
 #if CONFIG_LOOP_RESTORATION
-      av1_reset_loop_restoration(&td->xd);
+      av1_reset_loop_restoration(&td->xd, num_planes);
 #endif  // CONFIG_LOOP_RESTORATION
 
 #if CONFIG_LOOPFILTERING_ACROSS_TILES || CONFIG_LOOPFILTERING_ACROSS_TILES_EXT
@@ -2122,12 +2128,14 @@ static const uint8_t *decode_tiles(AV1Decoder *pbi, const uint8_t *data,
         av1_loop_filter_frame(get_frame_new_buffer(cm), cm, &pbi->mb,
                               cm->lf.filter_level[0], cm->lf.filter_level[1], 0,
                               0);
-        av1_loop_filter_frame(get_frame_new_buffer(cm), cm, &pbi->mb,
-                              cm->lf.filter_level_u, cm->lf.filter_level_u, 1,
-                              0);
-        av1_loop_filter_frame(get_frame_new_buffer(cm), cm, &pbi->mb,
-                              cm->lf.filter_level_v, cm->lf.filter_level_v, 2,
-                              0);
+        if (num_planes > 1) {
+          av1_loop_filter_frame(get_frame_new_buffer(cm), cm, &pbi->mb,
+                                cm->lf.filter_level_u, cm->lf.filter_level_u, 1,
+                                0);
+          av1_loop_filter_frame(get_frame_new_buffer(cm), cm, &pbi->mb,
+                                cm->lf.filter_level_v, cm->lf.filter_level_v, 2,
+                                0);
+        }
       }
 #else
       av1_loop_filter_frame(get_frame_new_buffer(cm), cm, &pbi->mb,
@@ -3263,6 +3271,7 @@ int av1_decode_frame_headers_and_setup(AV1Decoder *pbi, const uint8_t *data,
                                        const uint8_t *data_end,
                                        const uint8_t **p_data_end) {
   AV1_COMMON *const cm = &pbi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *const xd = &pbi->mb;
 
 #if CONFIG_BITSTREAM_DEBUG
@@ -3338,7 +3347,7 @@ int av1_decode_frame_headers_and_setup(AV1Decoder *pbi, const uint8_t *data,
   av1_setup_motion_field(cm);
 #endif  // CONFIG_MFMV
 
-  av1_setup_block_planes(xd, cm->subsampling_x, cm->subsampling_y);
+  av1_setup_block_planes(xd, cm->subsampling_x, cm->subsampling_y, num_planes);
 #if CONFIG_NO_FRAME_CONTEXT_SIGNALING
   if (cm->error_resilient_mode || frame_is_intra_only(cm)) {
     // use the default frame context values
@@ -3410,6 +3419,7 @@ void av1_decode_tg_tiles_and_wrapup(AV1Decoder *pbi, const uint8_t *data,
                                     const uint8_t **p_data_end, int startTile,
                                     int endTile, int initialize_flag) {
   AV1_COMMON *const cm = &pbi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *const xd = &pbi->mb;
 
   if (initialize_flag) setup_frame_info(pbi);
@@ -3418,7 +3428,7 @@ void av1_decode_tg_tiles_and_wrapup(AV1Decoder *pbi, const uint8_t *data,
 
 #if CONFIG_MONO_VIDEO
   // If the bit stream is monochrome, set the U and V buffers to a constant.
-  if (av1_num_planes(cm) < 3) {
+  if (num_planes < 3) {
     const int bytes_per_sample = cm->use_highbitdepth ? 2 : 1;
 
     YV12_BUFFER_CONFIG *cur_buf = (YV12_BUFFER_CONFIG *)xd->cur_buf;
diff --git a/av1/decoder/decoder.c b/av1/decoder/decoder.c
index 326015b4c4a20f8af8510996b8b617eed3e470cf..1e38429693b95aea35f86083d3d0c5efb9e4c8c2 100644
--- a/av1/decoder/decoder.c
+++ b/av1/decoder/decoder.c
@@ -175,6 +175,7 @@ static int equal_dimensions(const YV12_BUFFER_CONFIG *a,
 aom_codec_err_t av1_copy_reference_dec(AV1Decoder *pbi, int idx,
                                        YV12_BUFFER_CONFIG *sd) {
   AV1_COMMON *cm = &pbi->common;
+  const int num_planes = av1_num_planes(cm);
 
   const YV12_BUFFER_CONFIG *const cfg = get_ref_frame(cm, idx);
   if (cfg == NULL) {
@@ -185,13 +186,14 @@ aom_codec_err_t av1_copy_reference_dec(AV1Decoder *pbi, int idx,
     aom_internal_error(&cm->error, AOM_CODEC_ERROR,
                        "Incorrect buffer dimensions");
   else
-    aom_yv12_copy_frame(cfg, sd);
+    aom_yv12_copy_frame(cfg, sd, num_planes);
 
   return cm->error.error_code;
 }
 
 aom_codec_err_t av1_set_reference_dec(AV1_COMMON *cm, int idx,
                                       YV12_BUFFER_CONFIG *sd) {
+  const int num_planes = av1_num_planes(cm);
   YV12_BUFFER_CONFIG *ref_buf = NULL;
 
   // Get the destination reference buffer.
@@ -207,7 +209,7 @@ aom_codec_err_t av1_set_reference_dec(AV1_COMMON *cm, int idx,
                        "Incorrect buffer dimensions");
   } else {
     // Overwrite the reference frame buffer.
-    aom_yv12_copy_frame(sd, ref_buf);
+    aom_yv12_copy_frame(sd, ref_buf, num_planes);
   }
 
   return cm->error.error_code;
@@ -268,6 +270,7 @@ static void swap_frame_buffers(AV1Decoder *pbi) {
 int av1_receive_compressed_data(AV1Decoder *pbi, size_t size,
                                 const uint8_t **psource) {
   AV1_COMMON *volatile const cm = &pbi->common;
+  volatile const int num_planes = av1_num_planes(cm);
   BufferPool *volatile const pool = cm->buffer_pool;
   RefCntBuffer *volatile const frame_bufs = cm->buffer_pool->frame_bufs;
   const uint8_t *source = *psource;
@@ -406,8 +409,8 @@ int av1_receive_compressed_data(AV1Decoder *pbi, size_t size,
 #endif  // CONFIG_EXT_TILE
     // TODO(debargha): Fix encoder side mv range, so that we can use the
     // inner border extension. As of now use the larger extension.
-    // aom_extend_frame_inner_borders(cm->frame_to_show);
-    aom_extend_frame_borders(cm->frame_to_show);
+    // aom_extend_frame_inner_borders(cm->frame_to_show, num_planes);
+    aom_extend_frame_borders(cm->frame_to_show, num_planes);
 
   aom_clear_system_state();
 
diff --git a/av1/encoder/aq_complexity.c b/av1/encoder/aq_complexity.c
index 201691d2afb845ab7fc8d55280f5e27134c855ce..cb08ef598446ea7b30027c2d24caddacc379b3fb 100644
--- a/av1/encoder/aq_complexity.c
+++ b/av1/encoder/aq_complexity.c
@@ -122,6 +122,7 @@ void av1_setup_in_frame_q_adj(AV1_COMP *cpi) {
 void av1_caq_select_segment(const AV1_COMP *cpi, MACROBLOCK *mb, BLOCK_SIZE bs,
                             int mi_row, int mi_col, int projected_rate) {
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
 
   const int mi_offset = mi_row * cm->mi_cols + mi_col;
   const int xmis = AOMMIN(cm->mi_cols - mi_col, mi_size_wide[bs]);
@@ -148,7 +149,7 @@ void av1_caq_select_segment(const AV1_COMP *cpi, MACROBLOCK *mb, BLOCK_SIZE bs,
                                                     MIN_DEFAULT_LV_THRESH)
                                            : DEFAULT_LV_THRESH;
 
-    av1_setup_src_planes(mb, cpi->source, mi_row, mi_col);
+    av1_setup_src_planes(mb, cpi->source, mi_row, mi_col, num_planes);
     logvar = av1_log_block_var(cpi, mb, bs);
 
     segment = AQ_C_SEGMENTS - 1;  // Just in case no break out below.
diff --git a/av1/encoder/bgsprite.c b/av1/encoder/bgsprite.c
index 0a79f33544fb049f42ebf7a6586cb0a2fd19738f..36f31feaacc200585ba2dc91150ef7a71ce14404 100644
--- a/av1/encoder/bgsprite.c
+++ b/av1/encoder/bgsprite.c
@@ -943,6 +943,8 @@ static void stitch_images(AV1_COMP *cpi, YV12_BUFFER_CONFIG **const frames,
                           const int *const y_min, const int *const y_max,
                           int pano_x_min, int pano_x_max, int pano_y_min,
                           int pano_y_max, YV12_BUFFER_CONFIG *panorama) {
+  AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   const int width = pano_x_max - pano_x_min + 1;
   const int height = pano_y_max - pano_y_min + 1;
 
@@ -984,7 +986,7 @@ static void stitch_images(AV1_COMP *cpi, YV12_BUFFER_CONFIG **const frames,
                          frames[0]->subsampling_x, frames[0]->subsampling_y,
                          frames[0]->flags & YV12_FLAG_HIGHBITDEPTH,
                          frames[0]->border, 0);
-  aom_yv12_copy_frame(frames[0], &bgsprite);
+  aom_yv12_copy_frame(frames[0], &bgsprite, num_planes);
   bgsprite.bit_depth = frames[0]->bit_depth;
   resample_panorama(blended_img, center_idx, x_min, y_min, pano_x_min,
                     pano_x_max, pano_y_min, pano_y_max, &bgsprite);
@@ -996,7 +998,7 @@ static void stitch_images(AV1_COMP *cpi, YV12_BUFFER_CONFIG **const frames,
       &temporal_bgsprite, frames[0]->y_width, frames[0]->y_height,
       frames[0]->subsampling_x, frames[0]->subsampling_y,
       frames[0]->flags & YV12_FLAG_HIGHBITDEPTH, frames[0]->border, 0);
-  aom_yv12_copy_frame(frames[0], &temporal_bgsprite);
+  aom_yv12_copy_frame(frames[0], &temporal_bgsprite, num_planes);
   temporal_bgsprite.bit_depth = frames[0]->bit_depth;
 
   av1_temporal_filter(cpi, &bgsprite, &temporal_bgsprite, distance);
@@ -1033,7 +1035,7 @@ static void stitch_images(AV1_COMP *cpi, YV12_BUFFER_CONFIG **const frames,
                          frames[0]->subsampling_x, frames[0]->subsampling_y,
                          frames[0]->flags & YV12_FLAG_HIGHBITDEPTH,
                          frames[0]->border, 0);
-  aom_yv12_copy_frame(frames[0], &temporal_arf);
+  aom_yv12_copy_frame(frames[0], &temporal_arf, num_planes);
   temporal_arf.bit_depth = frames[0]->bit_depth;
   av1_temporal_filter(cpi, NULL, &temporal_arf, distance);
 
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 25eac0a3abe27498273e2365dba7c39c9eb6be63..cbcb346e68de794d273c83c8a20aa0e459c930f7 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -970,6 +970,7 @@ static void write_palette_colors_uv(const MACROBLOCKD *const xd,
 static void write_palette_mode_info(const AV1_COMMON *cm, const MACROBLOCKD *xd,
                                     const MODE_INFO *const mi, int mi_row,
                                     int mi_col, aom_writer *w) {
+  const int num_planes = av1_num_planes(cm);
   const MB_MODE_INFO *const mbmi = &mi->mbmi;
   const BLOCK_SIZE bsize = mbmi->sb_type;
   assert(av1_allow_palette(cm->allow_screen_content_tools, bsize));
@@ -991,7 +992,7 @@ static void write_palette_mode_info(const AV1_COMMON *cm, const MACROBLOCKD *xd,
   }
 
   const int uv_dc_pred =
-      av1_num_planes(cm) > 1 && mbmi->uv_mode == UV_DC_PRED &&
+      num_planes > 1 && mbmi->uv_mode == UV_DC_PRED &&
       is_chroma_reference(mi_row, mi_col, bsize, xd->plane[1].subsampling_x,
                           xd->plane[1].subsampling_y);
   if (uv_dc_pred) {
@@ -1869,6 +1870,7 @@ static void write_tokens_b(AV1_COMP *cpi, const TileInfo *const tile,
                            const TOKENEXTRA *const tok_end, int mi_row,
                            int mi_col) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *const xd = &cpi->td.mb.e_mbd;
   const int mi_offset = mi_row * cm->mi_stride + mi_col;
   MODE_INFO *const m = *(cm->mi_grid_visible + mi_offset);
@@ -1895,7 +1897,6 @@ static void write_tokens_b(AV1_COMP *cpi, const TileInfo *const tile,
 #endif  // CONFIG_DEPENDENT_HORZTILES
                  cm->mi_rows, cm->mi_cols);
 
-  const int num_planes = av1_num_planes(cm);
   for (plane = 0; plane < AOMMIN(2, num_planes); ++plane) {
     const uint8_t palette_size_plane =
         mbmi->palette_mode_info.palette_size[plane];
@@ -2025,6 +2026,7 @@ static void write_modes_sb(AV1_COMP *const cpi, const TileInfo *const tile,
                            const TOKENEXTRA *const tok_end, int mi_row,
                            int mi_col, BLOCK_SIZE bsize) {
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *const xd = &cpi->td.mb.e_mbd;
   const int hbs = mi_size_wide[bsize] / 2;
 #if CONFIG_EXT_PARTITION_TYPES
@@ -2037,7 +2039,7 @@ static void write_modes_sb(AV1_COMP *const cpi, const TileInfo *const tile,
   if (mi_row >= cm->mi_rows || mi_col >= cm->mi_cols) return;
 
 #if CONFIG_LOOP_RESTORATION
-  for (int plane = 0; plane < av1_num_planes(cm); ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     int rcol0, rcol1, rrow0, rrow1, tile_tl_idx;
     if (av1_loop_restoration_corners_in_sb(cm, plane, mi_row, mi_col, bsize,
                                            &rcol0, &rcol1, &rrow0, &rrow1,
@@ -2172,11 +2174,12 @@ static void write_modes(AV1_COMP *const cpi, const TileInfo *const tile,
 #if CONFIG_LOOP_RESTORATION
 static void encode_restoration_mode(AV1_COMMON *cm,
                                     struct aom_write_bit_buffer *wb) {
+  const int num_planes = av1_num_planes(cm);
 #if CONFIG_INTRABC
   if (cm->allow_intrabc && NO_FILTER_FOR_IBC) return;
 #endif  // CONFIG_INTRABC
   int all_none = 1, chroma_none = 1;
-  for (int p = 0; p < av1_num_planes(cm); ++p) {
+  for (int p = 0; p < num_planes; ++p) {
     RestorationInfo *rsi = &cm->rst_info[p];
     if (rsi->frame_restoration_type != RESTORE_NONE) {
       all_none = 0;
@@ -2212,7 +2215,7 @@ static void encode_restoration_mode(AV1_COMMON *cm,
     }
   }
 
-  if (av1_num_planes(cm) > 1) {
+  if (num_planes > 1) {
     int s = AOMMIN(cm->subsampling_x, cm->subsampling_y);
     if (s && !chroma_none) {
       aom_wb_write_bit(wb,
@@ -2333,6 +2336,7 @@ static void loop_restoration_write_sb_coeffs(const AV1_COMMON *const cm,
 #endif  // CONFIG_LOOP_RESTORATION
 
 static void encode_loopfilter(AV1_COMMON *cm, struct aom_write_bit_buffer *wb) {
+  const int num_planes = av1_num_planes(cm);
 #if CONFIG_INTRABC
   if (cm->allow_intrabc && NO_FILTER_FOR_IBC) return;
 #endif  // CONFIG_INTRABC
@@ -2343,7 +2347,7 @@ static void encode_loopfilter(AV1_COMMON *cm, struct aom_write_bit_buffer *wb) {
 #if CONFIG_LOOPFILTER_LEVEL
   aom_wb_write_literal(wb, lf->filter_level[0], 6);
   aom_wb_write_literal(wb, lf->filter_level[1], 6);
-  if (av1_num_planes(cm) > 1) {
+  if (num_planes > 1) {
     if (lf->filter_level[0] || lf->filter_level[1]) {
       aom_wb_write_literal(wb, lf->filter_level_u, 6);
       aom_wb_write_literal(wb, lf->filter_level_v, 6);
@@ -2385,6 +2389,7 @@ static void encode_loopfilter(AV1_COMMON *cm, struct aom_write_bit_buffer *wb) {
 }
 
 static void encode_cdef(const AV1_COMMON *cm, struct aom_write_bit_buffer *wb) {
+  const int num_planes = av1_num_planes(cm);
 #if CONFIG_INTRABC
   if (cm->allow_intrabc && NO_FILTER_FOR_IBC) return;
 #endif  // CONFIG_INTRABC
@@ -2394,7 +2399,7 @@ static void encode_cdef(const AV1_COMMON *cm, struct aom_write_bit_buffer *wb) {
   aom_wb_write_literal(wb, cm->cdef_bits, 2);
   for (i = 0; i < cm->nb_cdef_strengths; i++) {
     aom_wb_write_literal(wb, cm->cdef_strengths[i], CDEF_STRENGTH_BITS);
-    if (av1_num_planes(cm) > 1)
+    if (num_planes > 1)
       aom_wb_write_literal(wb, cm->cdef_uv_strengths[i], CDEF_STRENGTH_BITS);
   }
 }
@@ -2410,9 +2415,11 @@ static void write_delta_q(struct aom_write_bit_buffer *wb, int delta_q) {
 
 static void encode_quantization(const AV1_COMMON *const cm,
                                 struct aom_write_bit_buffer *wb) {
+  const int num_planes = av1_num_planes(cm);
+
   aom_wb_write_literal(wb, cm->base_qindex, QINDEX_BITS);
   write_delta_q(wb, cm->y_dc_delta_q);
-  if (av1_num_planes(cm) > 1) {
+  if (num_planes > 1) {
     int diff_uv_delta = (cm->u_dc_delta_q != cm->v_dc_delta_q) ||
                         (cm->u_ac_delta_q != cm->v_ac_delta_q);
 #if CONFIG_EXT_QM
@@ -4404,6 +4411,7 @@ static uint32_t write_tiles_in_tg_obus(AV1_COMP *const cpi, uint8_t *const dst,
 #endif
                                        int insert_frame_header_obu_flag) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   aom_writer mode_bc;
   int tile_row, tile_col;
   TOKENEXTRA *(*const tok_buffers)[MAX_TILE_COLS] = cpi->tile_tok;
@@ -4602,7 +4610,7 @@ static uint32_t write_tiles_in_tg_obus(AV1_COMP *const cpi, uint8_t *const dst,
         cpi->td.mb.e_mbd.tile_ctx = &this_tile->tctx;
         mode_bc.allow_update_cdf = 1;
 #if CONFIG_LOOP_RESTORATION
-        av1_reset_loop_restoration(&cpi->td.mb.e_mbd);
+        av1_reset_loop_restoration(&cpi->td.mb.e_mbd, num_planes);
 #endif  // CONFIG_LOOP_RESTORATION
 
         aom_start_encode(&mode_bc, dst + total_size);
diff --git a/av1/encoder/context_tree.c b/av1/encoder/context_tree.c
index ec68cb1ba0c982fd6f93f8c1e20e70dc5ec5cb7d..c06df8b05f42e1e0c65626efd82e1b3837f346f9 100644
--- a/av1/encoder/context_tree.c
+++ b/av1/encoder/context_tree.c
@@ -21,11 +21,12 @@ static const BLOCK_SIZE square[MAX_SB_SIZE_LOG2 - 1] = {
 
 static void alloc_mode_context(AV1_COMMON *cm, int num_pix,
                                PICK_MODE_CONTEXT *ctx) {
+  const int num_planes = av1_num_planes(cm);
   int i;
   const int num_blk = num_pix / 16;
   ctx->num_4x4_blk = num_blk;
 
-  for (i = 0; i < MAX_MB_PLANE; ++i) {
+  for (i = 0; i < num_planes; ++i) {
     CHECK_MEM_ERROR(cm, ctx->blk_skip[i], aom_calloc(num_blk, sizeof(uint8_t)));
     CHECK_MEM_ERROR(cm, ctx->coeff[i],
                     aom_memalign(32, num_pix * sizeof(*ctx->coeff[i])));
@@ -51,9 +52,9 @@ static void alloc_mode_context(AV1_COMMON *cm, int num_pix,
   }
 }
 
-static void free_mode_context(PICK_MODE_CONTEXT *ctx) {
+static void free_mode_context(PICK_MODE_CONTEXT *ctx, const int num_planes) {
   int i;
-  for (i = 0; i < MAX_MB_PLANE; ++i) {
+  for (i = 0; i < num_planes; ++i) {
     aom_free(ctx->blk_skip[i]);
     ctx->blk_skip[i] = 0;
     aom_free(ctx->coeff[i]);
@@ -112,25 +113,25 @@ static void alloc_tree_contexts(AV1_COMMON *cm, PC_TREE *tree, int num_pix,
 #endif  // CONFIG_EXT_PARTITION_TYPES
 }
 
-static void free_tree_contexts(PC_TREE *tree) {
+static void free_tree_contexts(PC_TREE *tree, const int num_planes) {
 #if CONFIG_EXT_PARTITION_TYPES
   int i;
   for (i = 0; i < 3; i++) {
-    free_mode_context(&tree->horizontala[i]);
-    free_mode_context(&tree->horizontalb[i]);
-    free_mode_context(&tree->verticala[i]);
-    free_mode_context(&tree->verticalb[i]);
+    free_mode_context(&tree->horizontala[i], num_planes);
+    free_mode_context(&tree->horizontalb[i], num_planes);
+    free_mode_context(&tree->verticala[i], num_planes);
+    free_mode_context(&tree->verticalb[i], num_planes);
   }
   for (i = 0; i < 4; ++i) {
-    free_mode_context(&tree->horizontal4[i]);
-    free_mode_context(&tree->vertical4[i]);
+    free_mode_context(&tree->horizontal4[i], num_planes);
+    free_mode_context(&tree->vertical4[i], num_planes);
   }
 #endif  // CONFIG_EXT_PARTITION_TYPES
-  free_mode_context(&tree->none);
-  free_mode_context(&tree->horizontal[0]);
-  free_mode_context(&tree->horizontal[1]);
-  free_mode_context(&tree->vertical[0]);
-  free_mode_context(&tree->vertical[1]);
+  free_mode_context(&tree->none, num_planes);
+  free_mode_context(&tree->horizontal[0], num_planes);
+  free_mode_context(&tree->horizontal[1], num_planes);
+  free_mode_context(&tree->vertical[0], num_planes);
+  free_mode_context(&tree->vertical[1], num_planes);
 }
 
 // This function sets up a tree of contexts such that at each square
@@ -193,7 +194,7 @@ void av1_setup_pc_tree(AV1_COMMON *cm, ThreadData *td) {
   }
 }
 
-void av1_free_pc_tree(ThreadData *td) {
+void av1_free_pc_tree(ThreadData *td, const int num_planes) {
 #if CONFIG_EXT_PARTITION
   const int tree_nodes_inc = 1024;
 #else
@@ -206,7 +207,8 @@ void av1_free_pc_tree(ThreadData *td) {
   const int tree_nodes = tree_nodes_inc + 64 + 16 + 4 + 1;
 #endif  // CONFIG_EXT_PARTITION
   int i;
-  for (i = 0; i < tree_nodes; ++i) free_tree_contexts(&td->pc_tree[i]);
+  for (i = 0; i < tree_nodes; ++i)
+    free_tree_contexts(&td->pc_tree[i], num_planes);
   aom_free(td->pc_tree);
   td->pc_tree = NULL;
 }
diff --git a/av1/encoder/context_tree.h b/av1/encoder/context_tree.h
index 385225185e7a0964a45c8ea0db4570511243c923..d374ec819cad27a89be9cb795040b3a7a274d70e 100644
--- a/av1/encoder/context_tree.h
+++ b/av1/encoder/context_tree.h
@@ -81,7 +81,7 @@ typedef struct PC_TREE {
 } PC_TREE;
 
 void av1_setup_pc_tree(struct AV1Common *cm, struct ThreadData *td);
-void av1_free_pc_tree(struct ThreadData *td);
+void av1_free_pc_tree(struct ThreadData *td, const int num_planes);
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index 84ea271d7c47f9a9325ee000ecbe7d46646fa756..81b3e29afc9dca6db4cbe07c154fab5fb5b9a096 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -253,13 +253,14 @@ static void set_offsets_without_segment_id(const AV1_COMP *const cpi,
                                            MACROBLOCK *const x, int mi_row,
                                            int mi_col, BLOCK_SIZE bsize) {
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *const xd = &x->e_mbd;
   const int mi_width = mi_size_wide[bsize];
   const int mi_height = mi_size_high[bsize];
 
   set_mode_info_offsets(cpi, x, xd, mi_row, mi_col);
 
-  set_skip_context(xd, mi_row, mi_col);
+  set_skip_context(xd, mi_row, mi_col, num_planes);
   xd->above_txfm_context =
       cm->above_txfm_context + (mi_col << TX_UNIT_WIDE_LOG2);
   xd->left_txfm_context = xd->left_txfm_context_buffer +
@@ -267,7 +268,7 @@ static void set_offsets_without_segment_id(const AV1_COMP *const cpi,
 
   // Set up destination pointers.
   av1_setup_dst_planes(xd->plane, bsize, get_frame_new_buffer(cm), mi_row,
-                       mi_col);
+                       mi_col, num_planes);
 
   // Set up limit values for MV components.
   // Mv beyond the range do not produce new/different prediction block.
@@ -277,7 +278,7 @@ static void set_offsets_without_segment_id(const AV1_COMP *const cpi,
   x->mv_limits.row_max = (cm->mi_rows - mi_row) * MI_SIZE + AOM_INTERP_EXTEND;
   x->mv_limits.col_max = (cm->mi_cols - mi_col) * MI_SIZE + AOM_INTERP_EXTEND;
 
-  set_plane_n4(xd, mi_width, mi_height);
+  set_plane_n4(xd, mi_width, mi_height, num_planes);
 
   // Set up distance of MB to edge of frame in 1/8th pel units.
   assert(!(mi_col & (mi_width - 1)) && !(mi_row & (mi_height - 1)));
@@ -288,7 +289,7 @@ static void set_offsets_without_segment_id(const AV1_COMP *const cpi,
                  cm->mi_rows, cm->mi_cols);
 
   // Set up source buffers.
-  av1_setup_src_planes(x, cpi->source, mi_row, mi_col);
+  av1_setup_src_planes(x, cpi->source, mi_row, mi_col, num_planes);
 
   // R/D setup.
   x->rdmult = cpi->rd.RDMULT;
@@ -446,6 +447,7 @@ static void update_state(const AV1_COMP *const cpi, TileDataEnc *tile_data,
                          int mi_col, BLOCK_SIZE bsize, RUN_TYPE dry_run) {
   int i, x_idx, y;
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   RD_COUNTS *const rdc = &td->rd_counts;
   MACROBLOCK *const x = &td->mb;
   MACROBLOCKD *const xd = &x->e_mbd;
@@ -494,7 +496,7 @@ static void update_state(const AV1_COMP *const cpi, TileDataEnc *tile_data,
     }
   }
 
-  for (i = 0; i < MAX_MB_PLANE; ++i) {
+  for (i = 0; i < num_planes; ++i) {
     p[i].coeff = ctx->coeff[i];
     p[i].qcoeff = ctx->qcoeff[i];
     pd[i].dqcoeff = ctx->dqcoeff[i];
@@ -587,32 +589,28 @@ static void update_state(const AV1_COMP *const cpi, TileDataEnc *tile_data,
 }
 
 void av1_setup_src_planes(MACROBLOCK *x, const YV12_BUFFER_CONFIG *src,
-                          int mi_row, int mi_col) {
-  uint8_t *const buffers[3] = { src->y_buffer, src->u_buffer, src->v_buffer };
-  const int widths[3] = { src->y_crop_width, src->uv_crop_width,
-                          src->uv_crop_width };
-  const int heights[3] = { src->y_crop_height, src->uv_crop_height,
-                           src->uv_crop_height };
-  const int strides[3] = { src->y_stride, src->uv_stride, src->uv_stride };
-  int i;
-
+                          int mi_row, int mi_col, const int num_planes) {
   // Set current frame pointer.
   x->e_mbd.cur_buf = src;
 
-  for (i = 0; i < MAX_MB_PLANE; i++)
-    setup_pred_plane(&x->plane[i].src, x->e_mbd.mi[0]->mbmi.sb_type, buffers[i],
-                     widths[i], heights[i], strides[i], mi_row, mi_col, NULL,
-                     x->e_mbd.plane[i].subsampling_x,
+  // We use AOMMIN(num_planes, MAX_MB_PLANE) instead of num_planes to quiet
+  // the static analysis warnings.
+  for (int i = 0; i < AOMMIN(num_planes, MAX_MB_PLANE); i++) {
+    const int is_uv = i > 0;
+    setup_pred_plane(&x->plane[i].src, x->e_mbd.mi[0]->mbmi.sb_type,
+                     src->buffers[i], src->crop_widths[is_uv],
+                     src->crop_heights[is_uv], src->strides[is_uv], mi_row,
+                     mi_col, NULL, x->e_mbd.plane[i].subsampling_x,
                      x->e_mbd.plane[i].subsampling_y);
+  }
 }
 
 static int set_segment_rdmult(const AV1_COMP *const cpi, MACROBLOCK *const x,
                               int8_t segment_id) {
-  int segment_qindex;
   const AV1_COMMON *const cm = &cpi->common;
   av1_init_plane_quantizers(cpi, x, segment_id);
   aom_clear_system_state();
-  segment_qindex = av1_get_qindex(&cm->seg, segment_id, cm->base_qindex);
+  int segment_qindex = av1_get_qindex(&cm->seg, segment_id, cm->base_qindex);
   return av1_compute_rd_mult(cpi, segment_qindex + cm->y_dc_delta_q);
 }
 
@@ -625,6 +623,7 @@ static void rd_pick_sb_modes(const AV1_COMP *const cpi, TileDataEnc *tile_data,
                              BLOCK_SIZE bsize, PICK_MODE_CONTEXT *ctx,
                              int64_t best_rd) {
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   TileInfo *const tile_info = &tile_data->tile_info;
   MACROBLOCKD *const xd = &x->e_mbd;
   MB_MODE_INFO *mbmi;
@@ -646,7 +645,7 @@ static void rd_pick_sb_modes(const AV1_COMP *const cpi, TileDataEnc *tile_data,
   mbmi->partition = partition;
 #endif
 
-  for (i = 0; i < MAX_MB_PLANE; ++i) {
+  for (i = 0; i < num_planes; ++i) {
     p[i].coeff = ctx->coeff[i];
     p[i].qcoeff = ctx->qcoeff[i];
     pd[i].dqcoeff = ctx->dqcoeff[i];
@@ -1356,7 +1355,8 @@ typedef struct {
 
 static void restore_context(MACROBLOCK *x,
                             const RD_SEARCH_MACROBLOCK_CONTEXT *ctx, int mi_row,
-                            int mi_col, BLOCK_SIZE bsize) {
+                            int mi_col, BLOCK_SIZE bsize,
+                            const int num_planes) {
   MACROBLOCKD *xd = &x->e_mbd;
   int p;
   const int num_4x4_blocks_wide =
@@ -1365,7 +1365,7 @@ static void restore_context(MACROBLOCK *x,
       block_size_high[bsize] >> tx_size_high_log2[0];
   int mi_width = mi_size_wide[bsize];
   int mi_height = mi_size_high[bsize];
-  for (p = 0; p < MAX_MB_PLANE; p++) {
+  for (p = 0; p < num_planes; p++) {
     int tx_col;
     int tx_row;
     tx_col = mi_col << (MI_SIZE_LOG2 - tx_size_wide_log2[0]);
@@ -1392,7 +1392,8 @@ static void restore_context(MACROBLOCK *x,
 }
 
 static void save_context(const MACROBLOCK *x, RD_SEARCH_MACROBLOCK_CONTEXT *ctx,
-                         int mi_row, int mi_col, BLOCK_SIZE bsize) {
+                         int mi_row, int mi_col, BLOCK_SIZE bsize,
+                         const int num_planes) {
   const MACROBLOCKD *xd = &x->e_mbd;
   int p;
   const int num_4x4_blocks_wide =
@@ -1403,7 +1404,7 @@ static void save_context(const MACROBLOCK *x, RD_SEARCH_MACROBLOCK_CONTEXT *ctx,
   int mi_height = mi_size_high[bsize];
 
   // buffer the above/left context information of the block in search.
-  for (p = 0; p < MAX_MB_PLANE; ++p) {
+  for (p = 0; p < num_planes; ++p) {
     int tx_col;
     int tx_row;
     tx_col = mi_col << (MI_SIZE_LOG2 - tx_size_wide_log2[0]);
@@ -1699,6 +1700,7 @@ static void rd_use_partition(AV1_COMP *cpi, ThreadData *td,
                              BLOCK_SIZE bsize, int *rate, int64_t *dist,
                              int do_recon, PC_TREE *pc_tree) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   TileInfo *const tile_info = &tile_data->tile_info;
   MACROBLOCK *const x = &td->mb;
   MACROBLOCKD *const xd = &x->e_mbd;
@@ -1735,7 +1737,7 @@ static void rd_use_partition(AV1_COMP *cpi, ThreadData *td,
       cm->above_txfm_context + (mi_col << TX_UNIT_WIDE_LOG2);
   xd->left_txfm_context = xd->left_txfm_context_buffer +
                           ((mi_row & MAX_MIB_MASK) << TX_UNIT_HIGH_LOG2);
-  save_context(x, &x_ctx, mi_row, mi_col, bsize);
+  save_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
 
   if (bsize == BLOCK_16X16 && cpi->vaq_refresh) {
     set_offsets(cpi, tile_info, x, mi_row, mi_col, bsize);
@@ -1774,7 +1776,7 @@ static void rd_use_partition(AV1_COMP *cpi, ThreadData *td,
         none_rdc.rdcost = RDCOST(x->rdmult, none_rdc.rate, none_rdc.dist);
       }
 
-      restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+      restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
       mib[0]->mbmi.sb_type = bs_type;
       pc_tree->partitioning = partition;
     }
@@ -1896,7 +1898,7 @@ static void rd_use_partition(AV1_COMP *cpi, ThreadData *td,
     chosen_rdc.rate = 0;
     chosen_rdc.dist = 0;
 
-    restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+    restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
     pc_tree->partitioning = PARTITION_SPLIT;
 
     // Split partition.
@@ -1908,7 +1910,7 @@ static void rd_use_partition(AV1_COMP *cpi, ThreadData *td,
       if ((mi_row + y_idx >= cm->mi_rows) || (mi_col + x_idx >= cm->mi_cols))
         continue;
 
-      save_context(x, &x_ctx, mi_row, mi_col, bsize);
+      save_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
       pc_tree->split[i]->partitioning = PARTITION_NONE;
       rd_pick_sb_modes(cpi, tile_data, x, mi_row + y_idx, mi_col + x_idx,
                        &tmp_rdc,
@@ -1917,7 +1919,7 @@ static void rd_use_partition(AV1_COMP *cpi, ThreadData *td,
 #endif
                        split_subsize, &pc_tree->split[i]->none, INT64_MAX);
 
-      restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+      restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
       if (tmp_rdc.rate == INT_MAX || tmp_rdc.dist == INT64_MAX) {
         av1_invalid_rd_stats(&chosen_rdc);
         break;
@@ -1950,7 +1952,7 @@ static void rd_use_partition(AV1_COMP *cpi, ThreadData *td,
     chosen_rdc = none_rdc;
   }
 
-  restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+  restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
 
   // We must have chosen a partitioning and encoding or we'll fail later on.
   // No other opportunities for success.
@@ -2352,6 +2354,8 @@ static void rd_test_partition3(const AV1_COMP *const cpi, ThreadData *td,
 static int64_t dist_8x8_yuv(const AV1_COMP *const cpi, MACROBLOCK *const x,
                             uint8_t *src_plane_8x8[MAX_MB_PLANE],
                             uint8_t *dst_plane_8x8[MAX_MB_PLANE]) {
+  const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *const xd = &x->e_mbd;
   int64_t dist_8x8, dist_8x8_uv, total_dist;
   const int src_stride = x->plane[0].src.stride;
@@ -2366,16 +2370,18 @@ static int64_t dist_8x8_yuv(const AV1_COMP *const cpi, MACROBLOCK *const x,
   // Compute chroma distortion for a luma 8x8 block
   dist_8x8_uv = 0;
 
-  for (plane = 1; plane < MAX_MB_PLANE; ++plane) {
-    unsigned sse;
-    const int src_stride_uv = x->plane[plane].src.stride;
-    const int dst_stride_uv = xd->plane[plane].dst.stride;
-    const BLOCK_SIZE plane_bsize =
-        get_plane_block_size(BLOCK_8X8, &xd->plane[plane]);
-
-    cpi->fn_ptr[plane_bsize].vf(src_plane_8x8[plane], src_stride_uv,
-                                dst_plane_8x8[plane], dst_stride_uv, &sse);
-    dist_8x8_uv += (int64_t)sse << 4;
+  if (num_planes > 1) {
+    for (plane = 1; plane < MAX_MB_PLANE; ++plane) {
+      unsigned sse;
+      const int src_stride_uv = x->plane[plane].src.stride;
+      const int dst_stride_uv = xd->plane[plane].dst.stride;
+      const BLOCK_SIZE plane_bsize =
+          get_plane_block_size(BLOCK_8X8, &xd->plane[plane]);
+
+      cpi->fn_ptr[plane_bsize].vf(src_plane_8x8[plane], src_stride_uv,
+                                  dst_plane_8x8[plane], dst_stride_uv, &sse);
+      dist_8x8_uv += (int64_t)sse << 4;
+    }
   }
 
   return total_dist = dist_8x8 + dist_8x8_uv;
@@ -2391,6 +2397,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
                               RD_STATS *rd_cost, int64_t best_rd,
                               PC_TREE *pc_tree, int64_t *none_rd) {
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   TileInfo *const tile_info = &tile_data->tile_info;
   MACROBLOCK *const x = &td->mb;
   MACROBLOCKD *const xd = &x->e_mbd;
@@ -2516,7 +2523,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
       cm->above_txfm_context + (mi_col << TX_UNIT_WIDE_LOG2);
   xd->left_txfm_context = xd->left_txfm_context_buffer +
                           ((mi_row & MAX_MIB_MASK) << TX_UNIT_HIGH_LOG2);
-  save_context(x, &x_ctx, mi_row, mi_col, bsize);
+  save_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
 
 #if CONFIG_FP_MB_STATS
   if (cpi->use_fp_mb_stats) {
@@ -2665,7 +2672,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
       }
     }
 
-    restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+    restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
   }
 
   // store estimated motion vector
@@ -2677,7 +2684,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
   uint8_t *src_plane_8x8[MAX_MB_PLANE], *dst_plane_8x8[MAX_MB_PLANE];
 
   if (x->using_dist_8x8 && bsize == BLOCK_8X8) {
-    for (int i = 0; i < MAX_MB_PLANE; i++) {
+    for (int i = 0; i < num_planes; i++) {
       src_plane_8x8[i] = x->plane[i].src.buf;
       dst_plane_8x8[i] = xd->plane[i].dst.buf;
     }
@@ -2743,7 +2750,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
       do_rectangular_split &= !partition_none_allowed;
     }
 
-    restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+    restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
   }  // if (do_split)
 
   // PARTITION_HORZ
@@ -2822,7 +2829,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
       }
     }
 
-    restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+    restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
   }
 
   // PARTITION_VERT
@@ -2903,7 +2910,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
       }
     }
 
-    restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+    restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
   }
 
 #if CONFIG_EXT_PARTITION_TYPES
@@ -2957,7 +2964,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
                        PARTITION_HORZ_A, mi_row, mi_col, bsize2, mi_row,
                        mi_col + mi_step, bsize2, mi_row + mi_step, mi_col,
                        subsize);
-    restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+    restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
   }
   // PARTITION_HORZ_B
   if (partition_horz_allowed && horzb_partition_allowed) {
@@ -2967,7 +2974,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
                        PARTITION_HORZ_B, mi_row, mi_col, subsize,
                        mi_row + mi_step, mi_col, bsize2, mi_row + mi_step,
                        mi_col + mi_step, bsize2);
-    restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+    restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
   }
 
   int verta_partition_allowed = vertab_partition_allowed;
@@ -2987,7 +2994,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
                        PARTITION_VERT_A, mi_row, mi_col, bsize2,
                        mi_row + mi_step, mi_col, bsize2, mi_row,
                        mi_col + mi_step, subsize);
-    restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+    restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
   }
   // PARTITION_VERT_B
   if (partition_vert_allowed && vertb_partition_allowed) {
@@ -2997,7 +3004,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
                        PARTITION_VERT_B, mi_row, mi_col, subsize, mi_row,
                        mi_col + mi_step, bsize2, mi_row + mi_step,
                        mi_col + mi_step, bsize2);
-    restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+    restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
   }
 
   // PARTITION_HORZ_4
@@ -3039,7 +3046,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
         pc_tree->partitioning = PARTITION_HORZ_4;
       }
     }
-    restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+    restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
   }
 
   // PARTITION_VERT_4
@@ -3081,7 +3088,7 @@ static void rd_pick_partition(const AV1_COMP *const cpi, ThreadData *td,
         pc_tree->partitioning = PARTITION_VERT_4;
       }
     }
-    restore_context(x, &x_ctx, mi_row, mi_col, bsize);
+    restore_context(x, &x_ctx, mi_row, mi_col, bsize, num_planes);
   }
 #endif  // CONFIG_EXT_PARTITION_TYPES
 
@@ -3130,6 +3137,7 @@ static void encode_rd_sb_row(AV1_COMP *cpi, ThreadData *td,
                              TileDataEnc *tile_data, int mi_row,
                              TOKENEXTRA **tp) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   const TileInfo *const tile_info = &tile_data->tile_info;
   MACROBLOCK *const x = &td->mb;
   MACROBLOCKD *const xd = &x->e_mbd;
@@ -3173,7 +3181,7 @@ static void encode_rd_sb_row(AV1_COMP *cpi, ThreadData *td,
     PC_TREE *const pc_root = td->pc_root[cm->mib_size_log2 - MIN_MIB_SIZE_LOG2];
 
 #if CONFIG_LV_MAP
-    av1_fill_coeff_costs(&td->mb, xd->tile_ctx);
+    av1_fill_coeff_costs(&td->mb, xd->tile_ctx, num_planes);
 #else
     av1_fill_token_costs_from_cdf(x->token_head_costs,
                                   x->e_mbd.tile_ctx->coef_head_cdfs);
@@ -3295,14 +3303,15 @@ static void encode_rd_sb_row(AV1_COMP *cpi, ThreadData *td,
 }
 
 static void init_encode_frame_mb_context(AV1_COMP *cpi) {
-  MACROBLOCK *const x = &cpi->td.mb;
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
+  MACROBLOCK *const x = &cpi->td.mb;
   MACROBLOCKD *const xd = &x->e_mbd;
 
   // Copy data over into macro block data structures.
-  av1_setup_src_planes(x, cpi->source, 0, 0);
+  av1_setup_src_planes(x, cpi->source, 0, 0, num_planes);
 
-  av1_setup_block_planes(xd, cm->subsampling_x, cm->subsampling_y);
+  av1_setup_block_planes(xd, cm->subsampling_x, cm->subsampling_y, num_planes);
 }
 
 static MV_REFERENCE_FRAME get_frame_type(const AV1_COMP *cpi) {
@@ -3346,6 +3355,7 @@ static TX_MODE select_tx_mode(const AV1_COMP *cpi) {
 
 void av1_init_tile_data(AV1_COMP *cpi) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   const int tile_cols = cm->tile_cols;
   const int tile_rows = cm->tile_rows;
   int tile_col, tile_row;
@@ -3383,7 +3393,7 @@ void av1_init_tile_data(AV1_COMP *cpi) {
       cpi->tile_tok[tile_row][tile_col] = pre_tok + tile_tok;
       pre_tok = cpi->tile_tok[tile_row][tile_col];
       tile_tok = allocated_tokens(*tile_info, cm->mib_size_log2 + MI_SIZE_LOG2,
-                                  av1_num_planes(cm));
+                                  num_planes);
 
 #if CONFIG_EXT_TILE
       tile_data->allow_update_cdf = !cm->large_scale_tile;
@@ -4099,6 +4109,7 @@ static void make_consistent_compound_tools(AV1_COMMON *cm) {
 
 void av1_encode_frame(AV1_COMP *cpi) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   // Indicates whether or not to use a default reduced set for ext-tx
   // rather than the potential full set of 16 transforms
   cm->reduced_tx_set_used = 0;
@@ -4130,7 +4141,9 @@ void av1_encode_frame(AV1_COMP *cpi) {
 #endif  // CONFIG_FRAME_MARKER
 
 #if CONFIG_MISMATCH_DEBUG
-  mismatch_reset_frame();
+  mismatch_reset_frame(num_planes);
+#else
+  (void)num_planes;
 #endif
 
   cpi->allow_comp_inter_inter = av1_is_compound_reference_allowed(cm);
@@ -4441,6 +4454,7 @@ static void encode_superblock(const AV1_COMP *const cpi, TileDataEnc *tile_data,
                               int mi_row, int mi_col, BLOCK_SIZE bsize,
                               int *rate) {
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCK *const x = &td->mb;
   MACROBLOCKD *const xd = &x->e_mbd;
   MODE_INFO **mi_8x8 = xd->mi;
@@ -4452,7 +4466,6 @@ static void encode_superblock(const AV1_COMP *const cpi, TileDataEnc *tile_data,
   const int mi_width = mi_size_wide[bsize];
   const int mi_height = mi_size_high[bsize];
   const int is_inter = is_inter_block(mbmi);
-  const int num_planes = av1_num_planes(cm);
 
   if (!is_inter) {
 #if CONFIG_CFL
@@ -4501,7 +4514,7 @@ static void encode_superblock(const AV1_COMP *const cpi, TileDataEnc *tile_data,
       assert(cfg != NULL);
 #endif  // !CONFIG_INTRABC
       av1_setup_pre_planes(xd, ref, cfg, mi_row, mi_col,
-                           &xd->block_refs[ref]->sf);
+                           &xd->block_refs[ref]->sf, num_planes);
     }
 
     av1_build_inter_predictors_sb(cm, xd, mi_row, mi_col, NULL, bsize);
@@ -4512,7 +4525,7 @@ static void encode_superblock(const AV1_COMP *const cpi, TileDataEnc *tile_data,
 
 #if CONFIG_MISMATCH_DEBUG
     if (dry_run == OUTPUT_ENABLED) {
-      for (int plane = 0; plane < 3; ++plane) {
+      for (int plane = 0; plane < num_planes; ++plane) {
         const struct macroblockd_plane *pd = &xd->plane[plane];
         int pixel_c, pixel_r;
         mi_to_pixel_loc(&pixel_c, &pixel_r, mi_col, mi_row, 0, 0,
@@ -4524,6 +4537,8 @@ static void encode_superblock(const AV1_COMP *const cpi, TileDataEnc *tile_data,
                                   pixel_r, pd->width, pd->height);
       }
     }
+#else
+    (void)num_planes;
 #endif
 
     av1_encode_sb(cpi, x, bsize, mi_row, mi_col, dry_run);
diff --git a/av1/encoder/encodeframe.h b/av1/encoder/encodeframe.h
index 38a2fbb142389e243b0b06881f555543e7d02517..e54b2783d0e255c8265adfde78f717339d58496b 100644
--- a/av1/encoder/encodeframe.h
+++ b/av1/encoder/encodeframe.h
@@ -27,7 +27,7 @@ struct ThreadData;
 
 void av1_setup_src_planes(struct macroblock *x,
                           const struct yv12_buffer_config *src, int mi_row,
-                          int mi_col);
+                          int mi_col, const int num_planes);
 
 void av1_encode_frame(struct AV1_COMP *cpi);
 
diff --git a/av1/encoder/encodemb.c b/av1/encoder/encodemb.c
index 1940badd45959e2117772d4f81ef65488851a083..1d08265c16e50810f6d36a90c8d9cd805fc51c9d 100644
--- a/av1/encoder/encodemb.c
+++ b/av1/encoder/encodemb.c
@@ -765,6 +765,8 @@ void av1_encode_sby_pass1(AV1_COMMON *cm, MACROBLOCK *x, BLOCK_SIZE bsize) {
 void av1_encode_sb(const struct AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize,
                    int mi_row, int mi_col, RUN_TYPE dry_run) {
   (void)dry_run;
+  const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *const xd = &x->e_mbd;
   struct optimize_ctx ctx;
   MB_MODE_INFO *mbmi = &xd->mi[0]->mbmi;
@@ -776,7 +778,7 @@ void av1_encode_sb(const struct AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize,
 
   if (x->skip) return;
 
-  for (plane = 0; plane < MAX_MB_PLANE; ++plane) {
+  for (plane = 0; plane < num_planes; ++plane) {
     const int subsampling_x = xd->plane[plane].subsampling_x;
     const int subsampling_y = xd->plane[plane].subsampling_y;
 
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 7a4e9b14763ec9d1c89ebe1ce125f00195785df8..ed8193179796763e17434e35036c973faf04f1b7 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -481,6 +481,7 @@ static void alloc_context_buffers_ext(AV1_COMP *cpi) {
 
 static void dealloc_compressor_data(AV1_COMP *cpi) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
 
   dealloc_context_buffers_ext(cpi);
 
@@ -533,7 +534,7 @@ static void dealloc_compressor_data(AV1_COMP *cpi) {
   aom_free(cpi->tile_tok[0][0]);
   cpi->tile_tok[0][0] = 0;
 
-  av1_free_pc_tree(&cpi->td);
+  av1_free_pc_tree(&cpi->td, num_planes);
 
   aom_free(cpi->td.mb.palette_buffer);
 }
@@ -803,6 +804,7 @@ static void alloc_util_frame_buffers(AV1_COMP *cpi) {
 
 static void alloc_compressor_data(AV1_COMP *cpi) {
   AV1_COMMON *cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
 
   av1_alloc_context_buffers(cm, cm->width, cm->height);
 
@@ -815,8 +817,8 @@ static void alloc_compressor_data(AV1_COMP *cpi) {
   aom_free(cpi->tile_tok[0][0]);
 
   {
-    unsigned int tokens = get_token_alloc(cm->mb_rows, cm->mb_cols,
-                                          MAX_SB_SIZE_LOG2, av1_num_planes(cm));
+    unsigned int tokens =
+        get_token_alloc(cm->mb_rows, cm->mb_cols, MAX_SB_SIZE_LOG2, num_planes);
     CHECK_MEM_ERROR(cm, cpi->tile_tok[0][0],
                     aom_calloc(tokens, sizeof(*cpi->tile_tok[0][0])));
   }
@@ -3074,6 +3076,7 @@ void set_compound_tools(AV1_COMMON *cm) {
 
 void av1_change_config(struct AV1_COMP *cpi, const AV1EncoderConfig *oxcf) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   RATE_CONTROL *const rc = &cpi->rc;
   MACROBLOCK *const x = &cpi->td.mb;
 
@@ -3179,7 +3182,7 @@ void av1_change_config(struct AV1_COMP *cpi, const AV1EncoderConfig *oxcf) {
     if (cm->width > cpi->initial_width || cm->height > cpi->initial_height ||
         cm->sb_size != sb_size) {
       av1_free_context_buffers(cm);
-      av1_free_pc_tree(&cpi->td);
+      av1_free_pc_tree(&cpi->td, num_planes);
       alloc_compressor_data(cpi);
       realloc_segmentation_maps(cpi);
       cpi->initial_width = cpi->initial_height = 0;
@@ -3789,6 +3792,8 @@ void av1_remove_compressor(AV1_COMP *cpi) {
   if (!cpi) return;
 
   cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
+
   if (cm->current_video_frame > 0) {
 #if CONFIG_ENTROPY_STATS
     if (cpi->oxcf.pass != 1) {
@@ -3893,7 +3898,7 @@ void av1_remove_compressor(AV1_COMP *cpi) {
       aom_free(thread_data->td->wsrc_buf);
       aom_free(thread_data->td->mask_buf);
       aom_free(thread_data->td->counts);
-      av1_free_pc_tree(thread_data->td);
+      av1_free_pc_tree(thread_data->td, num_planes);
       aom_free(thread_data->td);
     }
   }
@@ -3983,9 +3988,10 @@ void av1_update_reference(AV1_COMP *cpi, int ref_frame_upd_flags) {
 
 int av1_copy_reference_enc(AV1_COMP *cpi, int idx, YV12_BUFFER_CONFIG *sd) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   YV12_BUFFER_CONFIG *cfg = get_ref_frame(cm, idx);
   if (cfg) {
-    aom_yv12_copy_frame(cfg, sd);
+    aom_yv12_copy_frame(cfg, sd, num_planes);
     return 0;
   } else {
     return -1;
@@ -3994,9 +4000,10 @@ int av1_copy_reference_enc(AV1_COMP *cpi, int idx, YV12_BUFFER_CONFIG *sd) {
 
 int av1_set_reference_enc(AV1_COMP *cpi, int idx, YV12_BUFFER_CONFIG *sd) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   YV12_BUFFER_CONFIG *cfg = get_ref_frame(cm, idx);
   if (cfg) {
-    aom_yv12_copy_frame(sd, cfg);
+    aom_yv12_copy_frame(sd, cfg, num_planes);
     return 0;
   } else {
     return -1;
@@ -4544,6 +4551,7 @@ static INLINE void alloc_frame_mvs(AV1_COMMON *const cm, int buffer_idx) {
 
 static void scale_references(AV1_COMP *cpi) {
   AV1_COMMON *cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MV_REFERENCE_FRAME ref_frame;
   const AOM_REFFRAME ref_mask[INTER_REFS_PER_FRAME] = {
     AOM_LAST_FLAG, AOM_LAST2_FLAG, AOM_LAST3_FLAG, AOM_GOLD_FLAG,
@@ -4580,8 +4588,8 @@ static void scale_references(AV1_COMP *cpi) {
                   cm->byte_alignment, NULL, NULL, NULL))
             aom_internal_error(&cm->error, AOM_CODEC_MEM_ERROR,
                                "Failed to allocate frame buffer");
-          av1_resize_and_extend_frame(ref, &new_fb_ptr->buf,
-                                      (int)cm->bit_depth);
+          av1_resize_and_extend_frame(ref, &new_fb_ptr->buf, (int)cm->bit_depth,
+                                      num_planes);
           cpi->scaled_ref_idx[ref_frame - 1] = new_fb;
           alloc_frame_mvs(cm, new_fb);
         }
@@ -4852,6 +4860,7 @@ static void check_initial_width(AV1_COMP *cpi, int use_highbitdepth,
 // Returns 1 if the assigned width or height was <= 0.
 static int set_size_literal(AV1_COMP *cpi, int width, int height) {
   AV1_COMMON *cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   check_initial_width(cpi, cm->use_highbitdepth, cm->subsampling_x,
                       cm->subsampling_y);
 
@@ -4863,7 +4872,7 @@ static int set_size_literal(AV1_COMP *cpi, int width, int height) {
   if (cpi->initial_width && cpi->initial_height &&
       (cm->width > cpi->initial_width || cm->height > cpi->initial_height)) {
     av1_free_context_buffers(cm);
-    av1_free_pc_tree(&cpi->td);
+    av1_free_pc_tree(&cpi->td, num_planes);
     alloc_compressor_data(cpi);
     realloc_segmentation_maps(cpi);
     cpi->initial_width = cpi->initial_height = 0;
@@ -4875,6 +4884,7 @@ static int set_size_literal(AV1_COMP *cpi, int width, int height) {
 
 static void set_frame_size(AV1_COMP *cpi, int width, int height) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *const xd = &cpi->td.mb.e_mbd;
   int ref_frame;
 
@@ -4908,7 +4918,7 @@ static void set_frame_size(AV1_COMP *cpi, int width, int height) {
 #endif  // CONFIG_HORZONLY_FRAME_SUPERRES
   set_restoration_unit_size(frame_width, frame_height, cm->subsampling_x,
                             cm->subsampling_y, cm->rst_info);
-  for (int i = 0; i < MAX_MB_PLANE; ++i)
+  for (int i = 0; i < num_planes; ++i)
     cm->rst_info[i].frame_restoration_type = RESTORE_NONE;
 
   av1_alloc_restoration_buffers(cm);
@@ -4928,7 +4938,8 @@ static void set_frame_size(AV1_COMP *cpi, int width, int height) {
       av1_setup_scale_factors_for_frame(
           &ref_buf->sf, buf->y_crop_width, buf->y_crop_height, cm->width,
           cm->height, (buf->flags & YV12_FLAG_HIGHBITDEPTH) ? 1 : 0);
-      if (av1_is_scaled(&ref_buf->sf)) aom_extend_frame_borders(buf);
+      if (av1_is_scaled(&ref_buf->sf))
+        aom_extend_frame_borders(buf, num_planes);
     } else {
       ref_buf->buf = NULL;
     }
@@ -5123,6 +5134,7 @@ static void setup_frame_size(AV1_COMP *cpi) {
 #if CONFIG_HORZONLY_FRAME_SUPERRES
 static void superres_post_encode(AV1_COMP *cpi) {
   AV1_COMMON *cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
 
   if (av1_superres_unscaled(cm)) return;
 
@@ -5149,13 +5161,14 @@ static void superres_post_encode(AV1_COMP *cpi) {
     assert(cpi->scaled_source.y_crop_width == cm->superres_upscaled_width);
     assert(cpi->scaled_source.y_crop_height == cm->superres_upscaled_height);
     av1_resize_and_extend_frame(cpi->unscaled_source, &cpi->scaled_source,
-                                (int)cm->bit_depth);
+                                (int)cm->bit_depth, num_planes);
     cpi->source = &cpi->scaled_source;
   }
 }
 #endif  // CONFIG_HORZONLY_FRAME_SUPERRES
 
 static void loopfilter_frame(AV1_COMP *cpi, AV1_COMMON *cm) {
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *xd = &cpi->td.mb.e_mbd;
   struct loopfilter *lf = &cm->lf;
   int no_loopfilter = 0;
@@ -5218,10 +5231,12 @@ static void loopfilter_frame(AV1_COMP *cpi, AV1_COMMON *cm) {
 #if CONFIG_LOOPFILTER_LEVEL
     av1_loop_filter_frame(cm->frame_to_show, cm, xd, lf->filter_level[0],
                           lf->filter_level[1], 0, 0);
-    av1_loop_filter_frame(cm->frame_to_show, cm, xd, lf->filter_level_u,
-                          lf->filter_level_u, 1, 0);
-    av1_loop_filter_frame(cm->frame_to_show, cm, xd, lf->filter_level_v,
-                          lf->filter_level_v, 2, 0);
+    if (num_planes > 1) {
+      av1_loop_filter_frame(cm->frame_to_show, cm, xd, lf->filter_level_u,
+                            lf->filter_level_u, 1, 0);
+      av1_loop_filter_frame(cm->frame_to_show, cm, xd, lf->filter_level_v,
+                            lf->filter_level_v, 2, 0);
+    }
 
 #else
     av1_loop_filter_frame(cm->frame_to_show, cm, xd, lf->filter_level, 0, 0);
@@ -5269,8 +5284,8 @@ static void loopfilter_frame(AV1_COMP *cpi, AV1_COMMON *cm) {
   }
 #endif  // CONFIG_LOOP_RESTORATION
   // TODO(debargha): Fix mv search range on encoder side
-  // aom_extend_frame_inner_borders(cm->frame_to_show);
-  aom_extend_frame_borders(cm->frame_to_show);
+  // aom_extend_frame_inner_borders(cm->frame_to_show, num_planes);
+  aom_extend_frame_borders(cm->frame_to_show, num_planes);
 }
 
 static void encode_without_recode_loop(AV1_COMP *cpi) {
@@ -6715,6 +6730,7 @@ int av1_get_compressed_data(AV1_COMP *cpi, unsigned int *frame_flags,
                             int64_t *time_end, int flush) {
   const AV1EncoderConfig *const oxcf = &cpi->oxcf;
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   BufferPool *const pool = cm->buffer_pool;
   RATE_CONTROL *const rc = &cpi->rc;
   struct aom_usec_timer cmptimer;
@@ -6863,7 +6879,7 @@ int av1_get_compressed_data(AV1_COMP *cpi, unsigned int *frame_flags,
                               NULL, &cpi->alt_ref_buffer,
 #endif  // CONFIG_BGSPRITE
                               arf_src_index);
-        aom_extend_frame_borders(&cpi->alt_ref_buffer);
+        aom_extend_frame_borders(&cpi->alt_ref_buffer, num_planes);
         force_src_buffer = &cpi->alt_ref_buffer;
       }
 
@@ -6908,7 +6924,7 @@ int av1_get_compressed_data(AV1_COMP *cpi, unsigned int *frame_flags,
                             NULL, NULL,
 #endif  // CONFIG_BGSPRITE
                             arf_src_index);
-        aom_extend_frame_borders(&cpi->alt_ref_buffer);
+        aom_extend_frame_borders(&cpi->alt_ref_buffer, num_planes);
         force_src_buffer = &cpi->alt_ref_buffer;
       }
 
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index fe0509de87765656f20cb9911002bddc8bb80f52..1e47e946d2320798f7b8d38efa71b42a4c64e6ae 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -712,7 +712,8 @@ static INLINE int enc_is_ref_frame_buf(AV1_COMP *cpi, RefCntBuffer *frame_buf) {
 }
 
 static INLINE unsigned int get_token_alloc(int mb_rows, int mb_cols,
-                                           int sb_size_log2, int num_planes) {
+                                           int sb_size_log2,
+                                           const int num_planes) {
   // Calculate the maximum number of max superblocks in the image.
   const int shift = sb_size_log2 - 4;
   const int sb_size = 1 << sb_size_log2;
diff --git a/av1/encoder/encodetxb.c b/av1/encoder/encodetxb.c
index ef9fc0f52d5b1c74277226598e483af654930661..714c7a39a5676e58f092218fc15160e7b68047c3 100644
--- a/av1/encoder/encodetxb.c
+++ b/av1/encoder/encodetxb.c
@@ -58,13 +58,14 @@ typedef struct LevelDownStats {
 void av1_alloc_txb_buf(AV1_COMP *cpi) {
 #if 0
   AV1_COMMON *cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   int mi_block_size = 1 << MI_SIZE_LOG2;
   // TODO(angiebird): Make sure cm->subsampling_x/y is set correctly, and then
   // use precise buffer size according to cm->subsampling_x/y
   int pixel_stride = mi_block_size * cm->mi_cols;
   int pixel_height = mi_block_size * cm->mi_rows;
   int i;
-  for (i = 0; i < MAX_MB_PLANE; ++i) {
+  for (i = 0; i < num_planes; ++i) {
     CHECK_MEM_ERROR(
         cm, cpi->tcoeff_buf[i],
         aom_malloc(sizeof(*cpi->tcoeff_buf[i]) * pixel_stride * pixel_height));
@@ -84,7 +85,9 @@ void av1_alloc_txb_buf(AV1_COMP *cpi) {
 void av1_free_txb_buf(AV1_COMP *cpi) {
 #if 0
   int i;
-  for (i = 0; i < MAX_MB_PLANE; ++i) {
+  const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
+  for (i = 0; i < num_planes; ++i) {
     aom_free(cpi->tcoeff_buf[i]);
   }
 #else
@@ -94,12 +97,14 @@ void av1_free_txb_buf(AV1_COMP *cpi) {
 
 void av1_set_coeff_buffer(const AV1_COMP *const cpi, MACROBLOCK *const x,
                           int mi_row, int mi_col) {
-  int mib_size_log2 = cpi->common.mib_size_log2;
-  int stride = (cpi->common.mi_cols >> mib_size_log2) + 1;
+  const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
+  int mib_size_log2 = cm->mib_size_log2;
+  int stride = (cm->mi_cols >> mib_size_log2) + 1;
   int offset = (mi_row >> mib_size_log2) * stride + (mi_col >> mib_size_log2);
   CB_COEFF_BUFFER *coeff_buf = &cpi->coeff_buffer_base[offset];
   const int txb_offset = x->cb_offset / (TX_SIZE_W_MIN * TX_SIZE_H_MIN);
-  for (int plane = 0; plane < MAX_MB_PLANE; ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     x->mbmi_ext->tcoeff[plane] = coeff_buf->tcoeff[plane] + x->cb_offset;
     x->mbmi_ext->eobs[plane] = coeff_buf->eobs[plane] + txb_offset;
     x->mbmi_ext->txb_skip_ctx[plane] =
@@ -2335,6 +2340,8 @@ void av1_update_and_record_txb_context(int plane, int block, int blk_row,
 void av1_update_txb_context(const AV1_COMP *cpi, ThreadData *td,
                             RUN_TYPE dry_run, BLOCK_SIZE bsize, int *rate,
                             int mi_row, int mi_col, uint8_t allow_update_cdf) {
+  const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCK *const x = &td->mb;
   MACROBLOCKD *const xd = &x->e_mbd;
   MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
@@ -2343,16 +2350,17 @@ void av1_update_txb_context(const AV1_COMP *cpi, ThreadData *td,
   (void)mi_row;
   (void)mi_col;
   if (mbmi->skip) {
-    av1_reset_skip_context(xd, mi_row, mi_col, bsize);
+    av1_reset_skip_context(xd, mi_row, mi_col, bsize, num_planes);
     return;
   }
 
   if (!dry_run) {
     av1_foreach_transformed_block(xd, bsize, mi_row, mi_col,
-                                  av1_update_and_record_txb_context, &arg);
+                                  av1_update_and_record_txb_context, &arg,
+                                  num_planes);
   } else if (dry_run == DRY_RUN_NORMAL) {
     av1_foreach_transformed_block(xd, bsize, mi_row, mi_col,
-                                  av1_update_txb_context_b, &arg);
+                                  av1_update_txb_context_b, &arg, num_planes);
   } else {
     printf("DRY_RUN_COSTCOEFFS is not supported yet\n");
     assert(0);
diff --git a/av1/encoder/firstpass.c b/av1/encoder/firstpass.c
index 3af8c7a20af81777a145ee988bd166850afe57c1..8fdb0cbe813e10300bb369485cb1ac233e74b163 100644
--- a/av1/encoder/firstpass.c
+++ b/av1/encoder/firstpass.c
@@ -484,6 +484,7 @@ void av1_first_pass(AV1_COMP *cpi, const struct lookahead_entry *source) {
   int mb_row, mb_col;
   MACROBLOCK *const x = &cpi->td.mb;
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *const xd = &x->e_mbd;
   TileInfo tile;
   struct macroblock_plane *const p = x->plane;
@@ -552,13 +553,14 @@ void av1_first_pass(AV1_COMP *cpi, const struct lookahead_entry *source) {
   set_first_pass_params(cpi);
   av1_set_quantizer(cm, qindex);
 
-  av1_setup_block_planes(&x->e_mbd, cm->subsampling_x, cm->subsampling_y);
+  av1_setup_block_planes(&x->e_mbd, cm->subsampling_x, cm->subsampling_y,
+                         num_planes);
 
-  av1_setup_src_planes(x, cpi->source, 0, 0);
-  av1_setup_dst_planes(xd->plane, cm->sb_size, new_yv12, 0, 0);
+  av1_setup_src_planes(x, cpi->source, 0, 0, num_planes);
+  av1_setup_dst_planes(xd->plane, cm->sb_size, new_yv12, 0, 0, num_planes);
 
   if (!frame_is_intra_only(cm)) {
-    av1_setup_pre_planes(xd, 0, first_ref_buf, 0, 0, NULL);
+    av1_setup_pre_planes(xd, 0, first_ref_buf, 0, 0, NULL, num_planes);
   }
 
   xd->mi = cm->mi_grid_visible;
@@ -570,7 +572,7 @@ void av1_first_pass(AV1_COMP *cpi, const struct lookahead_entry *source) {
 #endif  // CONFIG_CFL
   av1_frame_init_quantizer(cpi);
 
-  for (i = 0; i < MAX_MB_PLANE; ++i) {
+  for (i = 0; i < num_planes; ++i) {
     p[i].coeff = ctx->coeff[i];
     p[i].qcoeff = ctx->qcoeff[i];
     pd[i].dqcoeff = ctx->dqcoeff[i];
@@ -633,7 +635,7 @@ void av1_first_pass(AV1_COMP *cpi, const struct lookahead_entry *source) {
 #endif  // CONFIG_DEPENDENT_HORZTILES
                      cm->mi_rows, cm->mi_cols);
 
-      set_plane_n4(xd, mi_size_wide[bsize], mi_size_high[bsize]);
+      set_plane_n4(xd, mi_size_wide[bsize], mi_size_high[bsize], num_planes);
 
       // Do intra 16x16 prediction.
       xd->mi[0]->mbmi.segment_id = 0;
@@ -1054,7 +1056,7 @@ void av1_first_pass(AV1_COMP *cpi, const struct lookahead_entry *source) {
     ++twopass->sr_update_lag;
   }
 
-  aom_extend_frame_borders(new_yv12);
+  aom_extend_frame_borders(new_yv12, num_planes);
 
   // The frame we just compressed now becomes the last frame.
   ref_cnt_fb(pool->frame_bufs,
diff --git a/av1/encoder/mcomp.c b/av1/encoder/mcomp.c
index 69a0d2171fa38e0e49467e6b4a2dcf2aa0fc54f3..649f354dfa4b4760116ba9e8f401890927d8df63 100644
--- a/av1/encoder/mcomp.c
+++ b/av1/encoder/mcomp.c
@@ -1921,6 +1921,8 @@ static const MV search_pos[4] = {
 unsigned int av1_int_pro_motion_estimation(const AV1_COMP *cpi, MACROBLOCK *x,
                                            BLOCK_SIZE bsize, int mi_row,
                                            int mi_col) {
+  const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *xd = &x->e_mbd;
   MB_MODE_INFO *mbmi = &xd->mi[0]->mbmi;
   struct buf_2d backup_yv12[MAX_MB_PLANE] = { { 0, 0, 0, 0, 0 } };
@@ -1943,8 +1945,9 @@ unsigned int av1_int_pro_motion_estimation(const AV1_COMP *cpi, MACROBLOCK *x,
     // Swap out the reference frame for a version that's been scaled to
     // match the resolution of the current frame, allowing the existing
     // motion search code to be used without additional modifications.
-    for (i = 0; i < MAX_MB_PLANE; i++) backup_yv12[i] = xd->plane[i].pre[0];
-    av1_setup_pre_planes(xd, 0, scaled_ref_frame, mi_row, mi_col, NULL);
+    for (i = 0; i < num_planes; i++) backup_yv12[i] = xd->plane[i].pre[0];
+    av1_setup_pre_planes(xd, 0, scaled_ref_frame, mi_row, mi_col, NULL,
+                         num_planes);
   }
 
   {
@@ -1956,7 +1959,7 @@ unsigned int av1_int_pro_motion_estimation(const AV1_COMP *cpi, MACROBLOCK *x,
 
     if (scaled_ref_frame) {
       int i;
-      for (i = 0; i < MAX_MB_PLANE; i++) xd->plane[i].pre[0] = backup_yv12[i];
+      for (i = 0; i < num_planes; i++) xd->plane[i].pre[0] = backup_yv12[i];
     }
     return this_sad;
   }
@@ -2040,7 +2043,7 @@ unsigned int av1_int_pro_motion_estimation(const AV1_COMP *cpi, MACROBLOCK *x,
 
   if (scaled_ref_frame) {
     int i;
-    for (i = 0; i < MAX_MB_PLANE; i++) xd->plane[i].pre[0] = backup_yv12[i];
+    for (i = 0; i < num_planes; i++) xd->plane[i].pre[0] = backup_yv12[i];
   }
 
   return best_sad;
diff --git a/av1/encoder/pickcdef.c b/av1/encoder/pickcdef.c
index 7821617da0bdb02e90f388888c16719939073a09..ab831ca88e9a18a85ae1b052209ee78400782c1d 100644
--- a/av1/encoder/pickcdef.c
+++ b/av1/encoder/pickcdef.c
@@ -316,7 +316,7 @@ void av1_cdef_search(YV12_BUFFER_CONFIG *frame, const YV12_BUFFER_CONFIG *ref,
   int nb_strength_bits;
   int quantizer;
   double lambda;
-  int nplanes = av1_num_planes(cm);
+  const int num_planes = av1_num_planes(cm);
   const int total_strengths = fast ? REDUCED_TOTAL_STRENGTHS : TOTAL_STRENGTHS;
   DECLARE_ALIGNED(32, uint16_t, inbuf[CDEF_INBUF_SIZE]);
   uint16_t *in;
@@ -325,10 +325,10 @@ void av1_cdef_search(YV12_BUFFER_CONFIG *frame, const YV12_BUFFER_CONFIG *ref,
       av1_ac_quant_Q3(cm->base_qindex, 0, cm->bit_depth) >> (cm->bit_depth - 8);
   lambda = .12 * quantizer * quantizer / 256.;
 
-  av1_setup_dst_planes(xd->plane, cm->sb_size, frame, 0, 0);
+  av1_setup_dst_planes(xd->plane, cm->sb_size, frame, 0, 0, num_planes);
   mse[0] = aom_malloc(sizeof(**mse) * nvfb * nhfb);
   mse[1] = aom_malloc(sizeof(**mse) * nvfb * nhfb);
-  for (pli = 0; pli < nplanes; pli++) {
+  for (pli = 0; pli < num_planes; pli++) {
     uint8_t *ref_buffer;
     int ref_stride;
     switch (pli) {
@@ -417,7 +417,7 @@ void av1_cdef_search(YV12_BUFFER_CONFIG *frame, const YV12_BUFFER_CONFIG *ref,
       cdef_count = sb_compute_cdef_list(cm, fbr * MI_SIZE_64X64,
                                         fbc * MI_SIZE_64X64, dlist);
 #endif
-      for (pli = 0; pli < nplanes; pli++) {
+      for (pli = 0; pli < num_planes; pli++) {
         for (i = 0; i < CDEF_INBUF_SIZE; i++) inbuf[i] = CDEF_VERY_LARGE;
         for (gi = 0; gi < total_strengths; gi++) {
           int threshold;
@@ -469,7 +469,7 @@ void av1_cdef_search(YV12_BUFFER_CONFIG *frame, const YV12_BUFFER_CONFIG *ref,
     int best_lev0[CDEF_MAX_STRENGTHS];
     int best_lev1[CDEF_MAX_STRENGTHS] = { 0 };
     nb_strengths = 1 << i;
-    if (nplanes >= 3)
+    if (num_planes >= 3)
       tot_mse = joint_strength_search_dual(best_lev0, best_lev1, nb_strengths,
                                            mse, sb_count, fast);
     else
@@ -499,7 +499,7 @@ void av1_cdef_search(YV12_BUFFER_CONFIG *frame, const YV12_BUFFER_CONFIG *ref,
     best_gi = 0;
     for (gi = 0; gi < cm->nb_cdef_strengths; gi++) {
       uint64_t curr = mse[0][i][cm->cdef_strengths[gi]];
-      if (nplanes >= 3) curr += mse[1][i][cm->cdef_uv_strengths[gi]];
+      if (num_planes >= 3) curr += mse[1][i][cm->cdef_uv_strengths[gi]];
       if (curr < best_mse) {
         best_gi = gi;
         best_mse = curr;
@@ -525,7 +525,7 @@ void av1_cdef_search(YV12_BUFFER_CONFIG *frame, const YV12_BUFFER_CONFIG *ref,
   cm->cdef_sec_damping = sec_damping;
   aom_free(mse[0]);
   aom_free(mse[1]);
-  for (pli = 0; pli < nplanes; pli++) {
+  for (pli = 0; pli < num_planes; pli++) {
     aom_free(src[pli]);
     aom_free(ref_coeff[pli]);
   }
diff --git a/av1/encoder/picklpf.c b/av1/encoder/picklpf.c
index 89c102b6f68e629909043ad1a1e921f9fac116e3..69d67b10c7f0132a1aa20e4172eb0a771f468d3a 100644
--- a/av1/encoder/picklpf.c
+++ b/av1/encoder/picklpf.c
@@ -212,6 +212,7 @@ static int search_filter_level(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi,
 void av1_pick_filter_level(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi,
                            LPF_PICK_METHOD method) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   struct loopfilter *const lf = &cm->lf;
   (void)sd;
 
@@ -274,10 +275,12 @@ void av1_pick_filter_level(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi,
     lf->filter_level[1] = search_filter_level(
         sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, NULL, 0, 1);
 
-    lf->filter_level_u = search_filter_level(
-        sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, NULL, 1, 0);
-    lf->filter_level_v = search_filter_level(
-        sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, NULL, 2, 0);
+    if (num_planes > 1) {
+      lf->filter_level_u = search_filter_level(
+          sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, NULL, 1, 0);
+      lf->filter_level_v = search_filter_level(
+          sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, NULL, 2, 0);
+    }
 #else
     lf->filter_level =
         search_filter_level(sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, NULL);
diff --git a/av1/encoder/pickrst.c b/av1/encoder/pickrst.c
index 87f1246aeeca1f77f9a00a4b7579974961760b65..1a1f81bd3591432a7e9b4605912ae2e3fbc961e8 100644
--- a/av1/encoder/pickrst.c
+++ b/av1/encoder/pickrst.c
@@ -1148,6 +1148,7 @@ static int rest_tiles_in_plane(const AV1_COMMON *cm, int plane) {
 
 void av1_pick_filter_restoration(const YV12_BUFFER_CONFIG *src, AV1_COMP *cpi) {
   AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
 
   int ntiles[2];
   for (int is_uv = 0; is_uv < 2; ++is_uv)
@@ -1165,7 +1166,9 @@ void av1_pick_filter_restoration(const YV12_BUFFER_CONFIG *src, AV1_COMP *cpi) {
   memset(rusi, 0, sizeof(*rusi) * ntiles[0]);
 
   RestSearchCtxt rsc;
-  for (int plane = AOM_PLANE_Y; plane <= AOM_PLANE_V; ++plane) {
+  const int plane_start = AOM_PLANE_Y;
+  const int plane_end = num_planes > 1 ? AOM_PLANE_V : AOM_PLANE_Y;
+  for (int plane = plane_start; plane <= plane_end; ++plane) {
     init_rsc(src, &cpi->common, &cpi->td.mb, plane, rusi, &cpi->trial_frame_rst,
              &rsc);
 
diff --git a/av1/encoder/rd.c b/av1/encoder/rd.c
index 58969f325546c43b13d833b4ce46dca8c4667bbb..8db6bb8c76b33fb307cf97e25b9bb4fd5e3e1976 100644
--- a/av1/encoder/rd.c
+++ b/av1/encoder/rd.c
@@ -488,9 +488,11 @@ void av1_set_mvcost(MACROBLOCK *x, MV_REFERENCE_FRAME ref_frame, int ref,
 }
 
 #if CONFIG_LV_MAP
-void av1_fill_coeff_costs(MACROBLOCK *x, FRAME_CONTEXT *fc) {
+void av1_fill_coeff_costs(MACROBLOCK *x, FRAME_CONTEXT *fc,
+                          const int num_planes) {
+  const int nplanes = AOMMIN(num_planes, PLANE_TYPES);
   for (int eob_multi_size = 0; eob_multi_size < 7; ++eob_multi_size) {
-    for (int plane = 0; plane < PLANE_TYPES; ++plane) {
+    for (int plane = 0; plane < nplanes; ++plane) {
       LV_MAP_EOB_COST *pcost = &x->eob_costs[eob_multi_size][plane];
 
       for (int ctx = 0; ctx < 2; ++ctx) {
@@ -517,7 +519,7 @@ void av1_fill_coeff_costs(MACROBLOCK *x, FRAME_CONTEXT *fc) {
     }
   }
   for (int tx_size = 0; tx_size < TX_SIZES; ++tx_size) {
-    for (int plane = 0; plane < PLANE_TYPES; ++plane) {
+    for (int plane = 0; plane < nplanes; ++plane) {
       LV_MAP_COEFF_COST *pcost = &x->coeff_costs[tx_size][plane];
 
       for (int ctx = 0; ctx < TXB_SKIP_CONTEXTS; ++ctx)
@@ -937,7 +939,8 @@ void av1_setup_pred_block(const MACROBLOCKD *xd,
                           struct buf_2d dst[MAX_MB_PLANE],
                           const YV12_BUFFER_CONFIG *src, int mi_row, int mi_col,
                           const struct scale_factors *scale,
-                          const struct scale_factors *scale_uv) {
+                          const struct scale_factors *scale_uv,
+                          const int num_planes) {
   int i;
 
   dst[0].buf = src->y_buffer;
@@ -946,7 +949,7 @@ void av1_setup_pred_block(const MACROBLOCKD *xd,
   dst[2].buf = src->v_buffer;
   dst[1].stride = dst[2].stride = src->uv_stride;
 
-  for (i = 0; i < MAX_MB_PLANE; ++i) {
+  for (i = 0; i < num_planes; ++i) {
     setup_pred_plane(dst + i, xd->mi[0]->mbmi.sb_type, dst[i].buf,
                      i ? src->uv_crop_width : src->y_crop_width,
                      i ? src->uv_crop_height : src->y_crop_height,
diff --git a/av1/encoder/rd.h b/av1/encoder/rd.h
index b192a4d877d8af9372df13980c6f1242ad6efde8..31057cd6070f734121a890f9ac1511c84d641815 100644
--- a/av1/encoder/rd.h
+++ b/av1/encoder/rd.h
@@ -304,6 +304,8 @@ static INLINE void av1_init_rd_stats(RD_STATS *rd_stats) {
   rd_stats->invalid_rate = 0;
   rd_stats->ref_rdcost = INT64_MAX;
 #if CONFIG_RD_DEBUG
+  // This may run into problems when monochrome video is
+  // encoded, as there will only be 1 plane
   for (plane = 0; plane < MAX_MB_PLANE; ++plane) {
     rd_stats->txb_coeff_cost[plane] = 0;
     {
@@ -329,6 +331,8 @@ static INLINE void av1_invalid_rd_stats(RD_STATS *rd_stats) {
   rd_stats->invalid_rate = 1;
   rd_stats->ref_rdcost = INT64_MAX;
 #if CONFIG_RD_DEBUG
+  // This may run into problems when monochrome video is
+  // encoded, as there will only be 1 plane
   for (plane = 0; plane < MAX_MB_PLANE; ++plane) {
     rd_stats->txb_coeff_cost[plane] = INT_MAX;
     {
@@ -354,6 +358,8 @@ static INLINE void av1_merge_rd_stats(RD_STATS *rd_stats_dst,
   rd_stats_dst->skip &= rd_stats_src->skip;
   rd_stats_dst->invalid_rate &= rd_stats_src->invalid_rate;
 #if CONFIG_RD_DEBUG
+  // This may run into problems when monochrome video is
+  // encoded, as there will only be 1 plane
   for (plane = 0; plane < MAX_MB_PLANE; ++plane) {
     rd_stats_dst->txb_coeff_cost[plane] += rd_stats_src->txb_coeff_cost[plane];
     {
@@ -446,7 +452,8 @@ void av1_setup_pred_block(const MACROBLOCKD *xd,
                           struct buf_2d dst[MAX_MB_PLANE],
                           const YV12_BUFFER_CONFIG *src, int mi_row, int mi_col,
                           const struct scale_factors *scale,
-                          const struct scale_factors *scale_uv);
+                          const struct scale_factors *scale_uv,
+                          const int num_planes);
 
 int av1_get_intra_cost_penalty(int qindex, int qdelta,
                                aom_bit_depth_t bit_depth);
@@ -455,7 +462,8 @@ void av1_fill_mode_rates(AV1_COMMON *const cm, MACROBLOCK *x,
                          FRAME_CONTEXT *fc);
 
 #if CONFIG_LV_MAP
-void av1_fill_coeff_costs(MACROBLOCK *x, FRAME_CONTEXT *fc);
+void av1_fill_coeff_costs(MACROBLOCK *x, FRAME_CONTEXT *fc,
+                          const int num_planes);
 #endif
 
 void av1_fill_token_costs_from_cdf(av1_coeff_cost *cost,
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index 572cea85e6df0d765d3d5cd84203cc2856cb1691..62911d08f27637903919e801d765873bb68f440c 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -5625,6 +5625,7 @@ static void joint_motion_search(const AV1_COMP *cpi, MACROBLOCK *x,
                                 const uint8_t *mask, int mask_stride,
                                 int *rate_mv, const int block) {
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   const int pw = block_size_wide[bsize];
   const int ph = block_size_high[bsize];
   MACROBLOCKD *xd = &x->e_mbd;
@@ -5668,10 +5669,10 @@ static void joint_motion_search(const AV1_COMP *cpi, MACROBLOCK *x,
       // Swap out the reference frame for a version that's been scaled to
       // match the resolution of the current frame, allowing the existing
       // motion search code to be used without additional modifications.
-      for (i = 0; i < MAX_MB_PLANE; i++)
+      for (i = 0; i < num_planes; i++)
         backup_yv12[ref][i] = xd->plane[i].pre[ref];
-      av1_setup_pre_planes(xd, ref, scaled_ref_frame[ref], mi_row, mi_col,
-                           NULL);
+      av1_setup_pre_planes(xd, ref, scaled_ref_frame[ref], mi_row, mi_col, NULL,
+                           num_planes);
     }
   }
 
@@ -5816,7 +5817,7 @@ static void joint_motion_search(const AV1_COMP *cpi, MACROBLOCK *x,
     if (scaled_ref_frame[ref]) {
       // Restore the prediction frame pointers to their unscaled versions.
       int i;
-      for (i = 0; i < MAX_MB_PLANE; i++)
+      for (i = 0; i < num_planes; i++)
         xd->plane[i].pre[ref] = backup_yv12[ref][i];
     }
 
@@ -6046,6 +6047,7 @@ static void setup_buffer_inter(
     int_mv frame_near_mv[TOTAL_REFS_PER_FRAME],
     struct buf_2d yv12_mb[TOTAL_REFS_PER_FRAME][MAX_MB_PLANE]) {
   const AV1_COMMON *cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   const YV12_BUFFER_CONFIG *yv12 = get_ref_frame_buffer(cpi, ref_frame);
   MACROBLOCKD *const xd = &x->e_mbd;
   MODE_INFO *const mi = xd->mi[0];
@@ -6057,7 +6059,8 @@ static void setup_buffer_inter(
 
   // TODO(jkoleszar): Is the UV buffer ever used here? If so, need to make this
   // use the UV scaling factors.
-  av1_setup_pred_block(xd, yv12_mb[ref_frame], yv12, mi_row, mi_col, sf, sf);
+  av1_setup_pred_block(xd, yv12_mb[ref_frame], yv12, mi_row, mi_col, sf, sf,
+                       num_planes);
 
   // Gets an initial list of candidate vectors from neighbours and orders them
   av1_find_mv_refs(cm, xd, mi, ref_frame, &mbmi_ext->ref_mv_count[ref_frame],
@@ -6087,6 +6090,7 @@ static void single_motion_search(const AV1_COMP *const cpi, MACROBLOCK *x,
                                  int ref_idx, int *rate_mv) {
   MACROBLOCKD *xd = &x->e_mbd;
   const AV1_COMMON *cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MB_MODE_INFO *mbmi = &xd->mi[0]->mbmi;
   struct buf_2d backup_yv12[MAX_MB_PLANE] = { { 0, 0, 0, 0, 0 } };
   int bestsme = INT_MAX;
@@ -6112,10 +6116,10 @@ static void single_motion_search(const AV1_COMP *const cpi, MACROBLOCK *x,
     // Swap out the reference frame for a version that's been scaled to
     // match the resolution of the current frame, allowing the existing
     // motion search code to be used without additional modifications.
-    for (i = 0; i < MAX_MB_PLANE; i++)
-      backup_yv12[i] = xd->plane[i].pre[ref_idx];
+    for (i = 0; i < num_planes; i++) backup_yv12[i] = xd->plane[i].pre[ref_idx];
 
-    av1_setup_pre_planes(xd, ref_idx, scaled_ref_frame, mi_row, mi_col, NULL);
+    av1_setup_pre_planes(xd, ref_idx, scaled_ref_frame, mi_row, mi_col, NULL,
+                         num_planes);
   }
 
   av1_set_mvcost(
@@ -6163,7 +6167,7 @@ static void single_motion_search(const AV1_COMP *const cpi, MACROBLOCK *x,
 
           if (scaled_ref_frame) {
             int j;
-            for (j = 0; j < MAX_MB_PLANE; ++j)
+            for (j = 0; j < num_planes; ++j)
               xd->plane[j].pre[ref_idx] = backup_yv12[j];
           }
           return;
@@ -6292,14 +6296,14 @@ static void single_motion_search(const AV1_COMP *const cpi, MACROBLOCK *x,
 
   if (scaled_ref_frame) {
     int i;
-    for (i = 0; i < MAX_MB_PLANE; i++)
-      xd->plane[i].pre[ref_idx] = backup_yv12[i];
+    for (i = 0; i < num_planes; i++) xd->plane[i].pre[ref_idx] = backup_yv12[i];
   }
 }
 
-static INLINE void restore_dst_buf(MACROBLOCKD *xd, BUFFER_SET dst) {
+static INLINE void restore_dst_buf(MACROBLOCKD *xd, BUFFER_SET dst,
+                                   const int num_planes) {
   int i;
-  for (i = 0; i < MAX_MB_PLANE; i++) {
+  for (i = 0; i < num_planes; i++) {
     xd->plane[i].dst.buf = dst.plane[i];
     xd->plane[i].dst.stride = dst.stride[i];
   }
@@ -6310,6 +6314,7 @@ static void build_second_inter_pred(const AV1_COMP *cpi, MACROBLOCK *x,
                                     int mi_row, int mi_col, const int block,
                                     int ref_idx, uint8_t *second_pred) {
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   const int pw = block_size_wide[bsize];
   const int ph = block_size_high[bsize];
   MACROBLOCKD *xd = &x->e_mbd;
@@ -6337,9 +6342,10 @@ static void build_second_inter_pred(const AV1_COMP *cpi, MACROBLOCK *x,
     // Swap out the reference frame for a version that's been scaled to
     // match the resolution of the current frame, allowing the existing
     // motion search code to be used without additional modifications.
-    for (i = 0; i < MAX_MB_PLANE; i++)
+    for (i = 0; i < num_planes; i++)
       backup_yv12[i] = xd->plane[i].pre[!ref_idx];
-    av1_setup_pre_planes(xd, !ref_idx, scaled_ref_frame, mi_row, mi_col, NULL);
+    av1_setup_pre_planes(xd, !ref_idx, scaled_ref_frame, mi_row, mi_col, NULL,
+                         num_planes);
   }
 
   // Since we have scaled the reference frames to match the size of the current
@@ -6380,7 +6386,7 @@ static void build_second_inter_pred(const AV1_COMP *cpi, MACROBLOCK *x,
   if (scaled_ref_frame) {
     // Restore the prediction frame pointers to their unscaled versions.
     int i;
-    for (i = 0; i < MAX_MB_PLANE; i++)
+    for (i = 0; i < num_planes; i++)
       xd->plane[i].pre[!ref_idx] = backup_yv12[i];
   }
 }
@@ -6393,6 +6399,8 @@ static void compound_single_motion_search(const AV1_COMP *cpi, MACROBLOCK *x,
                                           const uint8_t *second_pred,
                                           const uint8_t *mask, int mask_stride,
                                           int *rate_mv, int ref_idx) {
+  const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   const int pw = block_size_wide[bsize];
   const int ph = block_size_high[bsize];
   MACROBLOCKD *xd = &x->e_mbd;
@@ -6414,9 +6422,9 @@ static void compound_single_motion_search(const AV1_COMP *cpi, MACROBLOCK *x,
     // Swap out the reference frame for a version that's been scaled to
     // match the resolution of the current frame, allowing the existing
     // motion search code to be used without additional modifications.
-    for (i = 0; i < MAX_MB_PLANE; i++)
-      backup_yv12[i] = xd->plane[i].pre[ref_idx];
-    av1_setup_pre_planes(xd, ref_idx, scaled_ref_frame, mi_row, mi_col, NULL);
+    for (i = 0; i < num_planes; i++) backup_yv12[i] = xd->plane[i].pre[ref_idx];
+    av1_setup_pre_planes(xd, ref_idx, scaled_ref_frame, mi_row, mi_col, NULL,
+                         num_planes);
   }
 
   struct buf_2d orig_yv12;
@@ -6492,8 +6500,7 @@ static void compound_single_motion_search(const AV1_COMP *cpi, MACROBLOCK *x,
   if (scaled_ref_frame) {
     // Restore the prediction frame pointers to their unscaled versions.
     int i;
-    for (i = 0; i < MAX_MB_PLANE; i++)
-      xd->plane[i].pre[ref_idx] = backup_yv12[i];
+    for (i = 0; i < num_planes; i++) xd->plane[i].pre[ref_idx] = backup_yv12[i];
   }
 
   av1_set_mvcost(
@@ -7192,6 +7199,7 @@ static int64_t interpolation_filter_search(
     int64_t *const rd, int *const switchable_rate, int *const skip_txfm_sb,
     int64_t *const skip_sse_sb) {
   const AV1_COMMON *cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *const xd = &x->e_mbd;
   MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
   int i;
@@ -7217,7 +7225,7 @@ static int64_t interpolation_filter_search(
 
   *switchable_rate = av1_get_switchable_rate(cm, x, xd);
   av1_build_inter_predictors_sb(cm, xd, mi_row, mi_col, orig_dst, bsize);
-  model_rd_for_sb(cpi, bsize, x, xd, 0, MAX_MB_PLANE - 1, &tmp_rate, &tmp_dist,
+  model_rd_for_sb(cpi, bsize, x, xd, 0, num_planes - 1, &tmp_rate, &tmp_dist,
                   skip_txfm_sb, skip_sse_sb);
   *rd = RDCOST(x->rdmult, *switchable_rate + tmp_rate, tmp_dist);
 
@@ -7231,7 +7239,7 @@ static int64_t interpolation_filter_search(
 #endif  // CONFIG_DUAL_FILTER
       int best_in_temp = 0;
       InterpFilters best_filters = mbmi->interp_filters;
-      restore_dst_buf(xd, *tmp_dst);
+      restore_dst_buf(xd, *tmp_dst, num_planes);
 
 #if CONFIG_DUAL_FILTER  // Speed feature use_fast_interpolation_filter_search
       if (cpi->sf.use_fast_interpolation_filter_search) {
@@ -7254,7 +7262,7 @@ static int64_t interpolation_filter_search(
           tmp_rs = av1_get_switchable_rate(cm, x, xd);
           av1_build_inter_predictors_sb(cm, xd, mi_row, mi_col, orig_dst,
                                         bsize);
-          model_rd_for_sb(cpi, bsize, x, xd, 0, MAX_MB_PLANE - 1, &tmp_rate,
+          model_rd_for_sb(cpi, bsize, x, xd, 0, num_planes - 1, &tmp_rate,
                           &tmp_dist, &tmp_skip_sb, &tmp_skip_sse);
           tmp_rd = RDCOST(x->rdmult, tmp_rs + tmp_rate, tmp_dist);
 
@@ -7268,9 +7276,9 @@ static int64_t interpolation_filter_search(
             *skip_sse_sb = tmp_skip_sse;
             best_in_temp = !best_in_temp;
             if (best_in_temp) {
-              restore_dst_buf(xd, *orig_dst);
+              restore_dst_buf(xd, *orig_dst, num_planes);
             } else {
-              restore_dst_buf(xd, *tmp_dst);
+              restore_dst_buf(xd, *tmp_dst, num_planes);
             }
           }
         }
@@ -7287,7 +7295,7 @@ static int64_t interpolation_filter_search(
           tmp_rs = av1_get_switchable_rate(cm, x, xd);
           av1_build_inter_predictors_sb(cm, xd, mi_row, mi_col, orig_dst,
                                         bsize);
-          model_rd_for_sb(cpi, bsize, x, xd, 0, MAX_MB_PLANE - 1, &tmp_rate,
+          model_rd_for_sb(cpi, bsize, x, xd, 0, num_planes - 1, &tmp_rate,
                           &tmp_dist, &tmp_skip_sb, &tmp_skip_sse);
           tmp_rd = RDCOST(x->rdmult, tmp_rs + tmp_rate, tmp_dist);
 
@@ -7299,9 +7307,9 @@ static int64_t interpolation_filter_search(
             *skip_sse_sb = tmp_skip_sse;
             best_in_temp = !best_in_temp;
             if (best_in_temp) {
-              restore_dst_buf(xd, *orig_dst);
+              restore_dst_buf(xd, *orig_dst, num_planes);
             } else {
-              restore_dst_buf(xd, *tmp_dst);
+              restore_dst_buf(xd, *tmp_dst, num_planes);
             }
           }
         }
@@ -7322,7 +7330,7 @@ static int64_t interpolation_filter_search(
           tmp_rs = av1_get_switchable_rate(cm, x, xd);
           av1_build_inter_predictors_sb(cm, xd, mi_row, mi_col, orig_dst,
                                         bsize);
-          model_rd_for_sb(cpi, bsize, x, xd, 0, MAX_MB_PLANE - 1, &tmp_rate,
+          model_rd_for_sb(cpi, bsize, x, xd, 0, num_planes - 1, &tmp_rate,
                           &tmp_dist, &tmp_skip_sb, &tmp_skip_sse);
           tmp_rd = RDCOST(x->rdmult, tmp_rs + tmp_rate, tmp_dist);
 
@@ -7334,9 +7342,9 @@ static int64_t interpolation_filter_search(
             *skip_sse_sb = tmp_skip_sse;
             best_in_temp = !best_in_temp;
             if (best_in_temp) {
-              restore_dst_buf(xd, *orig_dst);
+              restore_dst_buf(xd, *orig_dst, num_planes);
             } else {
-              restore_dst_buf(xd, *tmp_dst);
+              restore_dst_buf(xd, *tmp_dst, num_planes);
             }
           }
         }
@@ -7345,9 +7353,9 @@ static int64_t interpolation_filter_search(
 #endif  // CONFIG_DUAL_FILTER Speed feature use_fast_interpolation_filter_search
 
       if (best_in_temp) {
-        restore_dst_buf(xd, *tmp_dst);
+        restore_dst_buf(xd, *tmp_dst, num_planes);
       } else {
-        restore_dst_buf(xd, *orig_dst);
+        restore_dst_buf(xd, *orig_dst, num_planes);
       }
       mbmi->interp_filters = best_filters;
     } else {
@@ -7384,6 +7392,7 @@ static int64_t motion_mode_rd(
     int rate2_bmc_nocoeff, MB_MODE_INFO *best_bmc_mbmi, int rate_mv_bmc, int rs,
     int *skip_txfm_sb, int64_t *skip_sse_sb, BUFFER_SET *orig_dst) {
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *xd = &x->e_mbd;
   MODE_INFO *mi = xd->mi[0];
   MB_MODE_INFO *mbmi = &mi->mbmi;
@@ -7468,7 +7477,7 @@ static int64_t motion_mode_rd(
       av1_build_obmc_inter_prediction(
           cm, xd, mi_row, mi_col, args->above_pred_buf, args->above_pred_stride,
           args->left_pred_buf, args->left_pred_stride);
-      model_rd_for_sb(cpi, bsize, x, xd, 0, MAX_MB_PLANE - 1, &tmp_rate,
+      model_rd_for_sb(cpi, bsize, x, xd, 0, num_planes - 1, &tmp_rate,
                       &tmp_dist, skip_txfm_sb, skip_sse_sb);
     }
 
@@ -7548,7 +7557,7 @@ static int64_t motion_mode_rd(
         }
 
         av1_build_inter_predictors_sb(cm, xd, mi_row, mi_col, NULL, bsize);
-        model_rd_for_sb(cpi, bsize, x, xd, 0, MAX_MB_PLANE - 1, &tmp_rate,
+        model_rd_for_sb(cpi, bsize, x, xd, 0, num_planes - 1, &tmp_rate,
                         &tmp_dist, skip_txfm_sb, skip_sse_sb);
       } else {
         continue;
@@ -7586,7 +7595,7 @@ static int64_t motion_mode_rd(
       xd->plane[0].dst.stride = bw;
       av1_build_inter_predictors_sby(cm, xd, mi_row, mi_col, NULL, bsize);
 
-      restore_dst_buf(xd, *orig_dst);
+      restore_dst_buf(xd, *orig_dst, num_planes);
       mbmi->ref_frame[1] = INTRA_FRAME;
       mbmi->use_wedge_interintra = 0;
       for (j = 0; j < INTERINTRA_MODES; ++j) {
@@ -7700,9 +7709,9 @@ static int64_t motion_mode_rd(
           mbmi->use_wedge_interintra = 0;
         }
       }  // if (is_interintra_wedge_used(bsize))
-      restore_dst_buf(xd, *orig_dst);
+      restore_dst_buf(xd, *orig_dst, num_planes);
       av1_build_inter_predictors_sb(cm, xd, mi_row, mi_col, orig_dst, bsize);
-      model_rd_for_sb(cpi, bsize, x, xd, 0, MAX_MB_PLANE - 1, &tmp_rate,
+      model_rd_for_sb(cpi, bsize, x, xd, 0, num_planes - 1, &tmp_rate,
                       &tmp_dist, skip_txfm_sb, skip_sse_sb);
     }
 
@@ -7765,7 +7774,7 @@ static int64_t motion_mode_rd(
             mbmi->ref_frame[1] == INTRA_FRAME) {
           continue;
         } else {
-          restore_dst_buf(xd, *orig_dst);
+          restore_dst_buf(xd, *orig_dst, num_planes);
           return INT64_MAX;
         }
       }
@@ -7774,15 +7783,19 @@ static int64_t motion_mode_rd(
 
       rdcosty = RDCOST(x->rdmult, rd_stats->rate, rd_stats->dist);
       rdcosty = AOMMIN(rdcosty, RDCOST(x->rdmult, 0, rd_stats->sse));
-      /* clang-format off */
-      is_cost_valid_uv =
-          inter_block_uvrd(cpi, x, rd_stats_uv, bsize, ref_best_rd - rdcosty,
-                           0);
-      if (!is_cost_valid_uv) {
-        continue;
+      if (num_planes > 1) {
+        /* clang-format off */
+        is_cost_valid_uv =
+            inter_block_uvrd(cpi, x, rd_stats_uv, bsize, ref_best_rd - rdcosty,
+                             0);
+        if (!is_cost_valid_uv) {
+          continue;
+        }
+        /* clang-format on */
+        av1_merge_rd_stats(rd_stats, rd_stats_uv);
+      } else {
+        av1_init_rd_stats(rd_stats_uv);
       }
-      /* clang-format on */
-      av1_merge_rd_stats(rd_stats, rd_stats_uv);
 #if CONFIG_RD_DEBUG
       // record transform block coefficient cost
       // TODO(angiebird): So far rd_debug tool only detects discrepancy of
@@ -7849,8 +7862,8 @@ static int64_t motion_mode_rd(
       best_rd = tmp_rd;
       best_rd_stats = *rd_stats;
       best_rd_stats_y = *rd_stats_y;
-      best_rd_stats_uv = *rd_stats_uv;
-      for (int i = 0; i < MAX_MB_PLANE; ++i)
+      if (num_planes > 1) best_rd_stats_uv = *rd_stats_uv;
+      for (int i = 0; i < num_planes; ++i)
         memcpy(best_blk_skip[i], x->blk_skip[i],
                sizeof(best_blk_skip[i][0]) * xd->n8_h * xd->n8_w * 4);
       best_xskip = x->skip;
@@ -7860,20 +7873,20 @@ static int64_t motion_mode_rd(
 
   if (best_rd == INT64_MAX) {
     av1_invalid_rd_stats(rd_stats);
-    restore_dst_buf(xd, *orig_dst);
+    restore_dst_buf(xd, *orig_dst, num_planes);
     return INT64_MAX;
   }
   *mbmi = best_mbmi;
   *rd_stats = best_rd_stats;
   *rd_stats_y = best_rd_stats_y;
-  *rd_stats_uv = best_rd_stats_uv;
-  for (int i = 0; i < MAX_MB_PLANE; ++i)
+  if (num_planes > 1) *rd_stats_uv = best_rd_stats_uv;
+  for (int i = 0; i < num_planes; ++i)
     memcpy(x->blk_skip[i], best_blk_skip[i],
            sizeof(x->blk_skip[i][0]) * xd->n8_h * xd->n8_w * 4);
   x->skip = best_xskip;
   *disable_skip = best_disable_skip;
 
-  restore_dst_buf(xd, *orig_dst);
+  restore_dst_buf(xd, *orig_dst, num_planes);
   return 0;
 }
 
@@ -7882,13 +7895,14 @@ static int64_t skip_mode_rd(const AV1_COMP *const cpi, MACROBLOCK *const x,
                             BLOCK_SIZE bsize, int mi_row, int mi_col,
                             BUFFER_SET *const orig_dst) {
   const AV1_COMMON *cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *const xd = &x->e_mbd;
   MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
 
   av1_build_inter_predictors_sb(cm, xd, mi_row, mi_col, orig_dst, bsize);
 
   int64_t total_sse = 0;
-  for (int plane = 0; plane < MAX_MB_PLANE; ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     const struct macroblock_plane *const p = &x->plane[plane];
     const struct macroblockd_plane *const pd = &xd->plane[plane];
     const BLOCK_SIZE plane_bsize = get_plane_block_size(bsize, pd);
@@ -7913,7 +7927,7 @@ static int64_t skip_mode_rd(const AV1_COMP *const cpi, MACROBLOCK *const x,
   // Save the mode index
   x->skip_mode_index = x->skip_mode_index_candidate;
 
-  restore_dst_buf(xd, *orig_dst);
+  restore_dst_buf(xd, *orig_dst, num_planes);
   return 0;
 }
 #endif  // CONFIG_EXT_SKIP
@@ -7924,6 +7938,7 @@ static int64_t handle_inter_mode(
     int *disable_skip, int_mv (*mode_mv)[TOTAL_REFS_PER_FRAME], int mi_row,
     int mi_col, HandleInterModeArgs *args, const int64_t ref_best_rd) {
   const AV1_COMMON *cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *xd = &x->e_mbd;
   MODE_INFO *mi = xd->mi[0];
   MB_MODE_INFO *mbmi = &mi->mbmi;
@@ -8164,16 +8179,23 @@ static int64_t handle_inter_mode(
       }
     }
 
+    // Initialise tmp_dst and orig_dst buffers to prevent "may be used
+    // uninitialized" warnings in GCC when the stream is monochrome.
+    memset(tmp_dst.plane, 0, sizeof(tmp_dst.plane));
+    memset(tmp_dst.stride, 0, sizeof(tmp_dst.stride));
+    memset(orig_dst.plane, 0, sizeof(tmp_dst.plane));
+    memset(orig_dst.stride, 0, sizeof(tmp_dst.stride));
+
     // do first prediction into the destination buffer. Do the next
     // prediction into a temporary buffer. Then keep track of which one
     // of these currently holds the best predictor, and use the other
     // one for future predictions. In the end, copy from tmp_buf to
     // dst if necessary.
-    for (i = 0; i < MAX_MB_PLANE; i++) {
+    for (i = 0; i < num_planes; i++) {
       tmp_dst.plane[i] = tmp_buf + i * MAX_SB_SQUARE;
       tmp_dst.stride[i] = MAX_SB_SIZE;
     }
-    for (i = 0; i < MAX_MB_PLANE; i++) {
+    for (i = 0; i < num_planes; i++) {
       orig_dst.plane[i] = xd->plane[i].dst.buf;
       orig_dst.stride[i] = xd->plane[i].dst.stride;
     }
@@ -8386,7 +8408,7 @@ static int64_t handle_inter_mode(
       }
 
       if (ref_best_rd < INT64_MAX && best_rd_compound / 3 > ref_best_rd) {
-        restore_dst_buf(xd, orig_dst);
+        restore_dst_buf(xd, orig_dst, num_planes);
 #if CONFIG_JNT_COMP
         early_terminate = INT64_MAX;
         continue;
@@ -8404,7 +8426,7 @@ static int64_t handle_inter_mode(
       int tmp_rate;
       int64_t tmp_dist;
       av1_build_inter_predictors_sb(cm, xd, mi_row, mi_col, &orig_dst, bsize);
-      model_rd_for_sb(cpi, bsize, x, xd, 0, MAX_MB_PLANE - 1, &tmp_rate,
+      model_rd_for_sb(cpi, bsize, x, xd, 0, num_planes - 1, &tmp_rate,
                       &tmp_dist, &skip_txfm_sb, &skip_sse_sb);
       rd = RDCOST(x->rdmult, rs + tmp_rate, tmp_dist);
     }
@@ -8420,7 +8442,7 @@ static int64_t handle_inter_mode(
         const int64_t mrd = AOMMIN(args->modelled_rd[mode0][refs[0]],
                                    args->modelled_rd[mode1][refs[1]]);
         if (rd / 4 * 3 > mrd && ref_best_rd < INT64_MAX) {
-          restore_dst_buf(xd, orig_dst);
+          restore_dst_buf(xd, orig_dst, num_planes);
 #if CONFIG_JNT_COMP
           early_terminate = INT64_MAX;
           continue;
@@ -8437,7 +8459,7 @@ static int64_t handle_inter_mode(
       // if current pred_error modeled rd is substantially more than the best
       // so far, do not bother doing full rd
       if (rd / 2 > ref_best_rd) {
-        restore_dst_buf(xd, orig_dst);
+        restore_dst_buf(xd, orig_dst, num_planes);
 #if CONFIG_JNT_COMP
         early_terminate = INT64_MAX;
         continue;
@@ -8476,7 +8498,7 @@ static int64_t handle_inter_mode(
         best_ret_val = ret_val;
         best_rd = tmp_rd;
         best_mbmi = *mbmi;
-        for (i = 0; i < MAX_MB_PLANE; ++i)
+        for (i = 0; i < num_planes; ++i)
           memcpy(best_blk_skip[i], x->blk_skip[i],
                  sizeof(uint8_t) * xd->n8_h * xd->n8_w * 4);
       }
@@ -8490,7 +8512,7 @@ static int64_t handle_inter_mode(
     mbmi->compound_idx = best_compound_idx;
     ret_val = best_ret_val;
     *mbmi = best_mbmi;
-    for (i = 0; i < MAX_MB_PLANE; ++i)
+    for (i = 0; i < num_planes; ++i)
       memcpy(x->blk_skip[i], best_blk_skip[i],
              sizeof(uint8_t) * xd->n8_h * xd->n8_w * 4);
   }
@@ -8507,6 +8529,7 @@ static int64_t rd_pick_intrabc_mode_sb(const AV1_COMP *cpi, MACROBLOCK *x,
                                        int64_t best_rd) {
   const AV1_COMMON *const cm = &cpi->common;
   if (!av1_allow_intrabc(cm)) return INT64_MAX;
+  const int num_planes = av1_num_planes(cm);
 
   MACROBLOCKD *const xd = &x->e_mbd;
   const TileInfo *tile = &xd->tile;
@@ -8542,8 +8565,9 @@ static int64_t rd_pick_intrabc_mode_sb(const AV1_COMP *cpi, MACROBLOCK *x,
   mbmi_ext->ref_mvs[INTRA_FRAME][0] = dv_ref;
 
   struct buf_2d yv12_mb[MAX_MB_PLANE];
-  av1_setup_pred_block(xd, yv12_mb, xd->cur_buf, mi_row, mi_col, NULL, NULL);
-  for (int i = 0; i < MAX_MB_PLANE; ++i) {
+  av1_setup_pred_block(xd, yv12_mb, xd->cur_buf, mi_row, mi_col, NULL, NULL,
+                       num_planes);
+  for (int i = 0; i < num_planes; ++i) {
     xd->plane[i].pre[0] = yv12_mb[i];
   }
 
@@ -8709,6 +8733,7 @@ void av1_rd_pick_intra_mode_sb(const AV1_COMP *cpi, MACROBLOCK *x, int mi_row,
   const AV1_COMMON *const cm = &cpi->common;
   MACROBLOCKD *const xd = &x->e_mbd;
   MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
+  const int num_planes = av1_num_planes(cm);
   int rate_y = 0, rate_uv = 0, rate_y_tokenonly = 0, rate_uv_tokenonly = 0;
   int y_skip = 0, uv_skip = 0;
   int64_t dist_y = 0, dist_uv = 0;
@@ -8745,11 +8770,13 @@ void av1_rd_pick_intra_mode_sb(const AV1_COMP *cpi, MACROBLOCK *x, int mi_row,
       xd->cfl.store_y = 0;
     }
 #endif  // CONFIG_CFL
-    max_uv_tx_size = av1_get_tx_size(AOM_PLANE_U, xd);
-    init_sbuv_mode(mbmi);
-    if (!x->skip_chroma_rd)
-      rd_pick_intra_sbuv_mode(cpi, x, &rate_uv, &rate_uv_tokenonly, &dist_uv,
-                              &uv_skip, bsize, max_uv_tx_size);
+    if (num_planes > 1) {
+      max_uv_tx_size = av1_get_tx_size(AOM_PLANE_U, xd);
+      init_sbuv_mode(mbmi);
+      if (!x->skip_chroma_rd)
+        rd_pick_intra_sbuv_mode(cpi, x, &rate_uv, &rate_uv_tokenonly, &dist_uv,
+                                &uv_skip, bsize, max_uv_tx_size);
+    }
 
     if (y_skip && (uv_skip || x->skip_chroma_rd)) {
       rd_cost->rate = rate_y + rate_uv - rate_y_tokenonly - rate_uv_tokenonly +
@@ -8906,6 +8933,7 @@ static void estimate_skip_mode_rdcost(
     int_mv frame_mv[MB_MODE_COUNT][TOTAL_REFS_PER_FRAME],
     struct buf_2d yv12_mb[TOTAL_REFS_PER_FRAME][MAX_MB_PLANE]) {
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCKD *const xd = &x->e_mbd;
   MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
   MB_MODE_INFO_EXT *const mbmi_ext = x->mbmi_ext;
@@ -9005,13 +9033,13 @@ static void estimate_skip_mode_rdcost(
     set_default_interp_filters(mbmi, cm->interp_filter);
 
     set_ref_ptrs(cm, xd, mbmi->ref_frame[0], mbmi->ref_frame[1]);
-    for (i = 0; i < MAX_MB_PLANE; i++) {
+    for (i = 0; i < num_planes; i++) {
       xd->plane[i].pre[0] = yv12_mb[mbmi->ref_frame[0]][i];
       xd->plane[i].pre[1] = yv12_mb[mbmi->ref_frame[1]][i];
     }
 
     BUFFER_SET orig_dst;
-    for (i = 0; i < MAX_MB_PLANE; i++) {
+    for (i = 0; i < num_planes; i++) {
       orig_dst.plane[i] = xd->plane[i].dst.buf;
       orig_dst.stride[i] = xd->plane[i].dst.stride;
     }
@@ -9028,6 +9056,7 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
                                RD_STATS *rd_cost, BLOCK_SIZE bsize,
                                PICK_MODE_CONTEXT *ctx, int64_t best_rd_so_far) {
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   const RD_OPT *const rd_opt = &cpi->rd;
   const SPEED_FEATURES *const sf = &cpi->sf;
   MACROBLOCKD *const xd = &x->e_mbd;
@@ -9222,7 +9251,7 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
                                        args.left_pred_buf, dst_width2,
                                        dst_height2, args.left_pred_stride);
     av1_setup_dst_planes(xd->plane, bsize, get_frame_new_buffer(cm), mi_row,
-                         mi_col);
+                         mi_col, num_planes);
     calc_target_weighted_pred(cm, x, xd, mi_row, mi_col, args.above_pred_buf[0],
                               args.above_pred_stride[0], args.left_pred_buf[0],
                               args.left_pred_stride[0]);
@@ -9542,7 +9571,7 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
     set_ref_ptrs(cm, xd, ref_frame, second_ref_frame);
 
     // Select prediction reference frames.
-    for (i = 0; i < MAX_MB_PLANE; i++) {
+    for (i = 0; i < num_planes; i++) {
       xd->plane[i].pre[0] = yv12_mb[ref_frame][i];
       if (comp_pred) xd->plane[i].pre[1] = yv12_mb[second_ref_frame][i];
     }
@@ -9697,26 +9726,28 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
 
       if (rate_y == INT_MAX) continue;
 
-      uv_tx = av1_get_tx_size(AOM_PLANE_U, xd);
-      if (rate_uv_intra[uv_tx] == INT_MAX) {
-        choose_intra_uv_mode(cpi, x, bsize, uv_tx, &rate_uv_intra[uv_tx],
-                             &rate_uv_tokenonly[uv_tx], &dist_uvs[uv_tx],
-                             &skip_uvs[uv_tx], &mode_uv[uv_tx]);
-        if (try_palette) pmi_uv[uv_tx] = *pmi;
-        uv_angle_delta[uv_tx] = mbmi->angle_delta[1];
-      }
+      if (num_planes > 1) {
+        uv_tx = av1_get_tx_size(AOM_PLANE_U, xd);
+        if (rate_uv_intra[uv_tx] == INT_MAX) {
+          choose_intra_uv_mode(cpi, x, bsize, uv_tx, &rate_uv_intra[uv_tx],
+                               &rate_uv_tokenonly[uv_tx], &dist_uvs[uv_tx],
+                               &skip_uvs[uv_tx], &mode_uv[uv_tx]);
+          if (try_palette) pmi_uv[uv_tx] = *pmi;
+          uv_angle_delta[uv_tx] = mbmi->angle_delta[1];
+        }
 
-      rate_uv = rate_uv_tokenonly[uv_tx];
-      distortion_uv = dist_uvs[uv_tx];
-      skippable = skippable && skip_uvs[uv_tx];
-      mbmi->uv_mode = mode_uv[uv_tx];
-      if (try_palette) {
-        pmi->palette_size[1] = pmi_uv[uv_tx].palette_size[1];
-        memcpy(pmi->palette_colors + PALETTE_MAX_SIZE,
-               pmi_uv[uv_tx].palette_colors + PALETTE_MAX_SIZE,
-               2 * PALETTE_MAX_SIZE * sizeof(pmi->palette_colors[0]));
+        rate_uv = rate_uv_tokenonly[uv_tx];
+        distortion_uv = dist_uvs[uv_tx];
+        skippable = skippable && skip_uvs[uv_tx];
+        mbmi->uv_mode = mode_uv[uv_tx];
+        if (try_palette) {
+          pmi->palette_size[1] = pmi_uv[uv_tx].palette_size[1];
+          memcpy(pmi->palette_colors + PALETTE_MAX_SIZE,
+                 pmi_uv[uv_tx].palette_colors + PALETTE_MAX_SIZE,
+                 2 * PALETTE_MAX_SIZE * sizeof(pmi->palette_colors[0]));
+        }
+        mbmi->angle_delta[1] = uv_angle_delta[uv_tx];
       }
-      mbmi->angle_delta[1] = uv_angle_delta[uv_tx];
 
       rate2 = rate_y + intra_mode_info_cost_y(cpi, x, mbmi, bsize,
                                               intra_mode_cost[mbmi->mode]);
@@ -9727,7 +9758,7 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
         // not the tokenonly rate.
         rate_y -= tx_size_cost(cm, x, bsize, mbmi->tx_size);
       }
-      if (!x->skip_chroma_rd) {
+      if (num_planes > 1 && !x->skip_chroma_rd) {
         const int uv_mode_cost =
 #if CONFIG_CFL
             x->intra_uv_mode_cost[is_cfl_allowed(mbmi)][mbmi->mode]
@@ -9855,7 +9886,7 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
                            rate_y - rate_uv,
                        total_sse);
         }
-        for (i = 0; i < MAX_MB_PLANE; ++i)
+        for (i = 0; i < num_planes; ++i)
           memcpy(x->blk_skip_drl[i], x->blk_skip[i],
                  sizeof(uint8_t) * ctx->num_4x4_blk);
 
@@ -9976,7 +10007,7 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
             tmp_ref_rd = tmp_alt_rd;
             backup_mbmi = *mbmi;
             backup_skip = x->skip;
-            for (i = 0; i < MAX_MB_PLANE; ++i)
+            for (i = 0; i < num_planes; ++i)
               memcpy(x->blk_skip_drl[i], x->blk_skip[i],
                      sizeof(uint8_t) * ctx->num_4x4_blk);
           } else {
@@ -9988,7 +10019,7 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
         frame_mv[NEARMV][ref_frame] = backup_mv;
         frame_mv[NEWMV][ref_frame] = backup_fmv[0];
         if (comp_pred) frame_mv[NEWMV][second_ref_frame] = backup_fmv[1];
-        for (i = 0; i < MAX_MB_PLANE; ++i)
+        for (i = 0; i < num_planes; ++i)
           memcpy(x->blk_skip[i], x->blk_skip_drl[i],
                  sizeof(uint8_t) * ctx->num_4x4_blk);
       }
@@ -10092,7 +10123,7 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
             rate_y +
             x->skip_cost[av1_get_skip_context(xd)][this_skip2 || skippable];
         best_rate_uv = rate_uv;
-        for (i = 0; i < MAX_MB_PLANE; ++i)
+        for (i = 0; i < num_planes; ++i)
           memcpy(ctx->blk_skip[i], x->blk_skip[i],
                  sizeof(uint8_t) * ctx->num_4x4_blk);
       }
@@ -10177,7 +10208,7 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
     set_ref_ptrs(cm, xd, mbmi->ref_frame[0], mbmi->ref_frame[1]);
 
     // Select prediction reference frames.
-    for (i = 0; i < MAX_MB_PLANE; i++) {
+    for (i = 0; i < num_planes; i++) {
       xd->plane[i].pre[0] = yv12_mb[mbmi->ref_frame[0]][i];
       if (has_second_ref(mbmi))
         xd->plane[i].pre[1] = yv12_mb[mbmi->ref_frame[1]][i];
@@ -10235,7 +10266,7 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
         for (idx = 0; idx < xd->n8_w; ++idx)
           best_mbmode.inter_tx_size[idy][idx] = mbmi->inter_tx_size[idy][idx];
 
-      for (i = 0; i < MAX_MB_PLANE; ++i)
+      for (i = 0; i < num_planes; ++i)
         memcpy(ctx->blk_skip[i], x->blk_skip[i],
                sizeof(uint8_t) * ctx->num_4x4_blk);
 
@@ -10317,7 +10348,7 @@ void av1_rd_pick_inter_mode_sb(const AV1_COMP *cpi, TileDataEnc *tile_data,
       best_mbmode = *mbmi;
       best_skip2 = 0;
       best_mode_skippable = skippable;
-      for (i = 0; i < MAX_MB_PLANE; ++i)
+      for (i = 0; i < num_planes; ++i)
         memcpy(ctx->blk_skip[i], x->blk_skip[i],
                sizeof(uint8_t) * ctx->num_4x4_blk);
     }
@@ -10414,7 +10445,7 @@ PALETTE_EXIT:
       x->skip = 1;
 #if 0
       // TODO(zoeliu): To investigate why following cause performance drop.
-      for (i = 0; i < MAX_MB_PLANE; ++i) {
+      for (i = 0; i < num_planes; ++i) {
         memset(x->blk_skip[i], x->skip, sizeof(uint8_t) * ctx->num_4x4_blk);
         memcpy(ctx->blk_skip[i], x->blk_skip[i],
                sizeof(uint8_t) * ctx->num_4x4_blk);
@@ -10768,12 +10799,11 @@ struct calc_target_weighted_pred_ctxt {
   int overlap;
 };
 
-static INLINE void calc_target_weighted_pred_above(MACROBLOCKD *xd,
-                                                   int rel_mi_col,
-                                                   uint8_t nb_mi_width,
-                                                   MODE_INFO *nb_mi,
-                                                   void *fun_ctxt) {
+static INLINE void calc_target_weighted_pred_above(
+    MACROBLOCKD *xd, int rel_mi_col, uint8_t nb_mi_width, MODE_INFO *nb_mi,
+    void *fun_ctxt, const int num_planes) {
   (void)nb_mi;
+  (void)num_planes;
 
   struct calc_target_weighted_pred_ctxt *ctxt =
       (struct calc_target_weighted_pred_ctxt *)fun_ctxt;
@@ -10816,12 +10846,11 @@ static INLINE void calc_target_weighted_pred_above(MACROBLOCKD *xd,
   }
 }
 
-static INLINE void calc_target_weighted_pred_left(MACROBLOCKD *xd,
-                                                  int rel_mi_row,
-                                                  uint8_t nb_mi_height,
-                                                  MODE_INFO *nb_mi,
-                                                  void *fun_ctxt) {
+static INLINE void calc_target_weighted_pred_left(
+    MACROBLOCKD *xd, int rel_mi_row, uint8_t nb_mi_height, MODE_INFO *nb_mi,
+    void *fun_ctxt, const int num_planes) {
   (void)nb_mi;
+  (void)num_planes;
 
   struct calc_target_weighted_pred_ctxt *ctxt =
       (struct calc_target_weighted_pred_ctxt *)fun_ctxt;
diff --git a/av1/encoder/temporal_filter.c b/av1/encoder/temporal_filter.c
index e47e3486eb278f4e57f80e7609eb5b24f77e84d0..987f34c495503b5051f9aa1a19469f62619019e2 100644
--- a/av1/encoder/temporal_filter.c
+++ b/av1/encoder/temporal_filter.c
@@ -296,6 +296,8 @@ static void temporal_filter_iterate_c(AV1_COMP *cpi,
                                       int frame_count, int alt_ref_index,
                                       int strength,
                                       struct scale_factors *scale) {
+  const AV1_COMMON *cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   int byte;
   int frame;
   int mb_col, mb_row;
@@ -324,7 +326,7 @@ static void temporal_filter_iterate_c(AV1_COMP *cpi,
     predictor = predictor8;
   }
 
-  for (i = 0; i < MAX_MB_PLANE; i++) input_buffer[i] = mbd->plane[i].pre[0].buf;
+  for (i = 0; i < num_planes; i++) input_buffer[i] = mbd->plane[i].pre[0].buf;
 
   for (mb_row = 0; mb_row < mb_rows; mb_row++) {
     // Source frames are extended to 16 pixels. This is different than
@@ -525,7 +527,7 @@ static void temporal_filter_iterate_c(AV1_COMP *cpi,
   }
 
   // Restore input state
-  for (i = 0; i < MAX_MB_PLANE; i++) mbd->plane[i].pre[0].buf = input_buffer[i];
+  for (i = 0; i < num_planes; i++) mbd->plane[i].pre[0].buf = input_buffer[i];
 }
 
 // Apply buffer limits and context specific adjustments to arnr filter.
diff --git a/av1/encoder/tokenize.c b/av1/encoder/tokenize.c
index e93e4d0f49771e512517fd58d3fa2f8b7935e563..d60f965b07aefca7e6a7599e4d4738df070b6489 100644
--- a/av1/encoder/tokenize.c
+++ b/av1/encoder/tokenize.c
@@ -546,6 +546,7 @@ void av1_tokenize_sb_vartx(const AV1_COMP *cpi, ThreadData *td, TOKENEXTRA **t,
                            BLOCK_SIZE bsize, int *rate,
                            uint8_t allow_update_cdf) {
   const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCK *const x = &td->mb;
   MACROBLOCKD *const xd = &x->e_mbd;
   MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
@@ -558,7 +559,7 @@ void av1_tokenize_sb_vartx(const AV1_COMP *cpi, ThreadData *td, TOKENEXTRA **t,
   if (mi_row >= cm->mi_rows || mi_col >= cm->mi_cols) return;
 
   if (mbmi->skip) {
-    av1_reset_skip_context(xd, mi_row, mi_col, bsize);
+    av1_reset_skip_context(xd, mi_row, mi_col, bsize, num_planes);
 #if !CONFIG_LV_MAP
     if (dry_run) *t = t_backup;
 #endif
@@ -570,7 +571,7 @@ void av1_tokenize_sb_vartx(const AV1_COMP *cpi, ThreadData *td, TOKENEXTRA **t,
     *t = t_backup;
 #endif
 
-  for (int plane = 0; plane < av1_num_planes(cm); ++plane) {
+  for (int plane = 0; plane < num_planes; ++plane) {
     if (!is_chroma_reference(mi_row, mi_col, bsize,
                              xd->plane[plane].subsampling_x,
                              xd->plane[plane].subsampling_y)) {
@@ -634,16 +635,17 @@ void av1_tokenize_sb(const AV1_COMP *cpi, ThreadData *td, TOKENEXTRA **t,
                      RUN_TYPE dry_run, BLOCK_SIZE bsize, int *rate,
                      const int mi_row, const int mi_col,
                      uint8_t allow_update_cdf) {
+  const AV1_COMMON *const cm = &cpi->common;
+  const int num_planes = av1_num_planes(cm);
   MACROBLOCK *const x = &td->mb;
   MACROBLOCKD *const xd = &x->e_mbd;
   MB_MODE_INFO *const mbmi = &xd->mi[0]->mbmi;
   struct tokenize_b_args arg = { cpi, td, t, 0, allow_update_cdf };
   if (mbmi->skip) {
-    av1_reset_skip_context(xd, mi_row, mi_col, bsize);
+    av1_reset_skip_context(xd, mi_row, mi_col, bsize, num_planes);
     return;
   }
 
-  const int num_planes = av1_num_planes(&cpi->common);
   if (!dry_run) {
     for (int plane = 0; plane < num_planes; ++plane) {
       if (!is_chroma_reference(mi_row, mi_col, bsize,