Don't calculate chroma data in monochrome mode

Encoder: Prior to this patch, some chroma data was calculated and
later discarded when in monochrome mode. This patch ensures that
the chroma planes are left uninitialised and that chroma
calculations are not performed.

Decoder: Prior to this patch, some chroma calculations were still
being performed in monochrome mode (e.g. loop filtering). This
patch ensures that calculations are only performed on the y
plane, with the chroma planes being set to a constant.

Change-Id: I394c0c9fc50f884e76a65e6131bd6598b8b21b10
diff --git a/aom_scale/aom_scale.h b/aom_scale/aom_scale.h
index 6e089f5..a4aef6c 100644
--- a/aom_scale/aom_scale.h
+++ b/aom_scale/aom_scale.h
@@ -18,6 +18,6 @@
                             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 62c6b3c..f6cfe13 100644
--- a/aom_scale/aom_scale_rtcd.pl
+++ b/aom_scale/aom_scale_rtcd.pl
@@ -26,9 +26,9 @@
   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_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 d124832..1e4adad 100644
--- a/aom_scale/generic/aom_scale.c
+++ b/aom_scale/generic/aom_scale.c
@@ -475,11 +475,11 @@
                      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 3cfed30..e06022f 100644
--- a/aom_scale/generic/yv12extend.c
+++ b/aom_scale/generic/yv12extend.c
@@ -98,7 +98,8 @@
   }
 }
 
-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 @@
   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 @@
     }
     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 @@
 }
 
 #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 @@
   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 @@
     }
     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 @@
   }
 }
 
-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 @@
 // 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 @@
          (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 @@
         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 @@
       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 51192f7..d038848 100644
--- a/aom_scale/mips/dspr2/yv12extend_dspr2.c
+++ b/aom_scale/mips/dspr2/yv12extend_dspr2.c
@@ -126,14 +126,16 @@
   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 4f9bdc1..cf3fb24 100644
--- a/aom_util/debug_util.c
+++ b/aom_util/debug_util.c
@@ -98,8 +98,8 @@
   }
 }
 
-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 11c1296..0b2ea05 100644
--- a/aom_util/debug_util.h
+++ b/aom_util/debug_util.h
@@ -46,7 +46,7 @@
 #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 ab0f98b..ccc7851 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -886,6 +886,7 @@
           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 @@
             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 @@
             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 5e072bc..5e3e681 100644
--- a/av1/common/alloccommon.c
+++ b/av1/common/alloccommon.c
@@ -109,7 +109,8 @@
 #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 @@
 #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_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 @@
 #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 @@
 #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 @@
   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 @@
         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 @@
         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 805133d..eef33db 100644
--- a/av1/common/av1_loopfilter.c
+++ b/av1/common/av1_loopfilter.c
@@ -2394,6 +2394,7 @@
 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 @@
   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 @@
   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 @@
   // 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 @@
   // 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 83a41d7..d6cc975 100644
--- a/av1/common/blockd.c
+++ b/av1/common/blockd.c
@@ -85,10 +85,8 @@
 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_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 @@
 }
 
 #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 3008958..5c5d91c 100644
--- a/av1/common/blockd.h
+++ b/av1/common/blockd.h
@@ -990,7 +990,8 @@
   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 @@
 }
 
 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(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 d82d060..bd37b2b 100644
--- a/av1/common/cdef.c
+++ b/av1/common/cdef.c
@@ -150,6 +150,7 @@
 
 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 @@
   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 @@
                    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 @@
       }
 
       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 @@
     }
   }
   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 5ad4d29..53301e7 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 @@
     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 @@
                                                 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 @@
     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 d8d3ed0..dbf0839 100644
--- a/av1/common/onyxc_int.h
+++ b/av1/common/onyxc_int.h
@@ -757,7 +757,8 @@
 
 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 @@
 #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 @@
   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 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 @@
   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 c924116..bb9997a 100644
--- a/av1/common/quant_common.c
+++ b/av1/common/quant_common.c
@@ -600,10 +600,11 @@
 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 e96abd4..b709793 100644
--- a/av1/common/reconinter.c
+++ b/av1/common/reconinter.c
@@ -1304,46 +1304,39 @@
 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 @@
 
 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 @@
 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 @@
   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_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 @@
       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 @@
   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 @@
   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 @@
       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 @@
 
   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 @@
   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 @@
   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 @@
       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 @@
 
   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_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 @@
   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 cd6e8bb..df92cfe 100644
--- a/av1/common/reconinter.h
+++ b/av1/common/reconinter.h
@@ -355,12 +355,12 @@
 }
 
 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 0bbecbc..5abdabb 100644
--- a/av1/common/resize.c
+++ b/av1/common/resize.c
@@ -1080,33 +1080,26 @@
 }
 
 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_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 @@
 // 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 @@
                        "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 7c7505e..e54eff8 100644
--- a/av1/common/resize.h
+++ b/av1/common/resize.h
@@ -61,7 +61,8 @@
                                 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 45a516c..946725a 100644
--- a/av1/common/restoration.c
+++ b/av1/common/restoration.c
@@ -1447,6 +1447,7 @@
 
 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 @@
   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 @@
                                    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 @@
 // 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 d515912..0f8a06e 100644
--- a/av1/common/thread_common.c
+++ b/av1/common/thread_common.c
@@ -161,7 +161,8 @@
       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 @@
       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 d2d5ae7..025afdf 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -282,6 +282,8 @@
 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 @@
     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 @@
                  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 @@
                                          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 @@
 #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 @@
       }
     }
   }
-  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 @@
           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 @@
     }
 
 #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 @@
 
       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 @@
             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 @@
                              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 @@
   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 @@
 #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 @@
   }
   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 @@
     }
   } 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 @@
 #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 @@
 #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_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 @@
   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 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 @@
                                    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 @@
       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 @@
         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 @@
                                        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 @@
   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 @@
                                     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 @@
 
 #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 326015b..1e38429 100644
--- a/av1/decoder/decoder.c
+++ b/av1/decoder/decoder.c
@@ -175,6 +175,7 @@
 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_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 @@
                        "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 @@
 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 @@
 #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 201691d..cb08ef5 100644
--- a/av1/encoder/aq_complexity.c
+++ b/av1/encoder/aq_complexity.c
@@ -122,6 +122,7 @@
 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 @@
                                                     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 0a79f33..36f31fe 100644
--- a/av1/encoder/bgsprite.c
+++ b/av1/encoder/bgsprite.c
@@ -943,6 +943,8 @@
                           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 @@
                          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 @@
       &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 @@
                          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 25eac0a..cbcb346 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -970,6 +970,7 @@
 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 @@
   }
 
   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 @@
                            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 @@
 #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 @@
                            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 @@
   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 @@
 #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 @@
     }
   }
 
-  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 @@
 #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 @@
 #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_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 @@
   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 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 @@
 #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 @@
         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 ec68cb1..c06df8b 100644
--- a/av1/encoder/context_tree.c
+++ b/av1/encoder/context_tree.c
@@ -21,11 +21,12 @@
 
 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 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 @@
 #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_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 @@
   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 3852251..d374ec8 100644
--- a/av1/encoder/context_tree.h
+++ b/av1/encoder/context_tree.h
@@ -81,7 +81,7 @@
 } 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 84ea271..81b3e29 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -253,13 +253,14 @@
                                            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 @@
 
   // 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 @@
   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 @@
                  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 @@
                          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 @@
     }
   }
 
-  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 @@
 }
 
 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 @@
                              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 @@
   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 @@
 
 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 @@
       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 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 @@
   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 @@
                              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 @@
       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 @@
         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 @@
     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 @@
       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 @@
 #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 @@
     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 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 @@
   // 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]);
+  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;
+      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 @@
                               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 @@
       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 @@
       }
     }
 
-    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 @@
   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 @@
       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 @@
       }
     }
 
-    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 @@
       }
     }
 
-    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 @@
                        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 @@
                        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 @@
                        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 @@
                        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 @@
         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 @@
         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 @@
                              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 @@
     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 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 @@
 
 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 @@
       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 @@
 
 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 @@
 #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 @@
                               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 @@
   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 @@
       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 @@
 
 #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 @@
                                   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 38a2fbb..e54b278 100644
--- a/av1/encoder/encodeframe.h
+++ b/av1/encoder/encodeframe.h
@@ -27,7 +27,7 @@
 
 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 1940bad..1d08265 100644
--- a/av1/encoder/encodemb.c
+++ b/av1/encoder/encodemb.c
@@ -765,6 +765,8 @@
 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 @@
 
   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 7a4e9b1..ed81931 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -481,6 +481,7 @@
 
 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 @@
   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_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 @@
   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 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 @@
     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 @@
   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 @@
       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 @@
 
 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_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 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 @@
                   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 @@
 // 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 @@
   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 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 @@
 #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 @@
       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 @@
 #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 @@
     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 @@
 #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 @@
   }
 #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 @@
                             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 @@
                               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 @@
                             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 fe0509d..1e47e94 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -712,7 +712,8 @@
 }
 
 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 ef9fc0f..714c7a3 100644
--- a/av1/encoder/encodetxb.c
+++ b/av1/encoder/encodetxb.c
@@ -58,13 +58,14 @@
 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_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_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_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)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 3af8c7a..8fdb0cb 100644
--- a/av1/encoder/firstpass.c
+++ b/av1/encoder/firstpass.c
@@ -484,6 +484,7 @@
   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 @@
   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 @@
 #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 @@
 #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 @@
     ++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 69a0d21..649f354 100644
--- a/av1/encoder/mcomp.c
+++ b/av1/encoder/mcomp.c
@@ -1921,6 +1921,8 @@
 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 @@
     // 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 @@
 
     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 @@
 
   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 7821617..ab831ca 100644
--- a/av1/encoder/pickcdef.c
+++ b/av1/encoder/pickcdef.c
@@ -316,7 +316,7 @@
   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 @@
       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 @@
       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 @@
     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 @@
     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 @@
   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 89c102b..69d67b1 100644
--- a/av1/encoder/picklpf.c
+++ b/av1/encoder/picklpf.c
@@ -212,6 +212,7 @@
 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 @@
     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 87f1246..1a1f81b 100644
--- a/av1/encoder/pickrst.c
+++ b/av1/encoder/pickrst.c
@@ -1148,6 +1148,7 @@
 
 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 @@
   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 58969f3..8db6bb8 100644
--- a/av1/encoder/rd.c
+++ b/av1/encoder/rd.c
@@ -488,9 +488,11 @@
 }
 
 #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 @@
     }
   }
   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 @@
                           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 @@
   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 b192a4d..31057cd 100644
--- a/av1/encoder/rd.h
+++ b/av1/encoder/rd.h
@@ -304,6 +304,8 @@
   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 @@
   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 @@
   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 @@
                           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 @@
                          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 572cea8..62911d0 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -5625,6 +5625,7 @@
                                 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 @@
       // 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 @@
     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 @@
     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 @@
 
   // 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 @@
                                  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 @@
     // 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 @@
 
           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 @@
 
   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 @@
                                     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 @@
     // 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 @@
   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 @@
                                           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 @@
     // 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 @@
   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 @@
     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 @@
 
   *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 @@
 #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 @@
           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 @@
             *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 @@
           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 @@
             *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 @@
           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 @@
             *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 @@
 #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 @@
     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 @@
       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 @@
         }
 
         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 @@
       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 @@
           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 @@
             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 @@
 
       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 @@
       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 @@
 
   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 @@
                             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 @@
   // 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 @@
     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 @@
       }
     }
 
+    // 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 @@
       }
 
       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 @@
       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 @@
         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 @@
       // 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 @@
         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 @@
     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 @@
                                        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 @@
   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 @@
   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 @@
       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 @@
     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 @@
     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 @@
                                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 @@
                                        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 @@
     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 @@
 
       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 @@
         // 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 @@
                            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 @@
             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 @@
         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 @@
             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 @@
     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 @@
         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 @@
       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 @@
       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 @@
   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_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 e47e348..987f34c 100644
--- a/av1/encoder/temporal_filter.c
+++ b/av1/encoder/temporal_filter.c
@@ -296,6 +296,8 @@
                                       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 @@
     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 @@
   }
 
   // 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 e93e4d0..d60f965 100644
--- a/av1/encoder/tokenize.c
+++ b/av1/encoder/tokenize.c
@@ -546,6 +546,7 @@
                            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 @@
   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 @@
     *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 @@
                      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,