rtc: Spatial layers for RTC

Make spatial layers work for RTC mode.
Added unittest and updated sample encoder.

Change-Id: I221b5f421d6c6eaf0e2c8df2a3c0faade2b0c9fc
diff --git a/av1/encoder/encode_strategy.c b/av1/encoder/encode_strategy.c
index 2e56077..3874904 100644
--- a/av1/encoder/encode_strategy.c
+++ b/av1/encoder/encode_strategy.c
@@ -393,6 +393,12 @@
   // Clear down mmx registers
   aom_clear_system_state();
 
+  if (cpi->use_svc && cpi->svc.spatial_layer_id > 0) {
+    cpi->framerate = cpi->svc.base_framerate;
+    av1_rc_update_framerate(cpi, cpi->common.width, cpi->common.height);
+    return;
+  }
+
   if (source->ts_start == cpi->first_time_stamp_ever) {
     this_duration = source->ts_end - source->ts_start;
     step = 1;
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index c564e2e..37fff27 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -765,6 +765,8 @@
   for (int i = 0; i < MAX_NUM_OPERATING_POINTS; ++i) {
     aom_free(cpi->level_info[i]);
   }
+
+  if (cpi->use_svc) av1_free_svc_cyclic_refresh(cpi);
 }
 
 static void save_coding_context(AV1_COMP *cpi) {
@@ -4070,7 +4072,7 @@
 }
 
 // Returns 1 if the assigned width or height was <= 0.
-static int set_size_literal(AV1_COMP *cpi, int width, int height) {
+int av1_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->seq_params.use_highbitdepth,
@@ -4104,7 +4106,7 @@
 
   if (width != cm->width || height != cm->height) {
     // There has been a change in the encoded frame size
-    set_size_literal(cpi, width, height);
+    av1_set_size_literal(cpi, width, height);
     set_mv_search_params(cpi);
     // Recalculate 'all_lossless' in case super-resolution was (un)selected.
     cm->all_lossless = cm->coded_lossless && !av1_superres_scaled(cm);
@@ -4415,6 +4417,12 @@
   const AV1EncoderConfig *oxcf = &cpi->oxcf;
   size_params_type rsz = { oxcf->width, oxcf->height, SCALE_NUMERATOR };
   int resize_denom = SCALE_NUMERATOR;
+  if (oxcf->pass == 0 && cpi->use_svc &&
+      cpi->svc.spatial_layer_id < cpi->svc.number_spatial_layers - 1) {
+    rsz.resize_width = cpi->common.width;
+    rsz.resize_height = cpi->common.height;
+    return rsz;
+  }
   if (oxcf->pass == 1) return rsz;
   if (cpi->resize_pending_width && cpi->resize_pending_height) {
     rsz.resize_width = cpi->resize_pending_width;
@@ -6282,6 +6290,9 @@
   aom_bitstream_queue_set_frame_write(cm->current_frame.frame_number * 2 +
                                       cm->show_frame);
 #endif
+  if (cpi->use_svc && cm->number_spatial_layers > 1) {
+    av1_one_pass_cbr_svc_start_layer(cpi);
+  }
 
   // Indicates whether or not to use an adaptive quantize b rather than
   // the traditional version
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index fe91842..5d4343d 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -1152,6 +1152,8 @@
 
 int av1_set_reference_enc(AV1_COMP *cpi, int idx, YV12_BUFFER_CONFIG *sd);
 
+int av1_set_size_literal(AV1_COMP *cpi, int width, int height);
+
 void av1_set_frame_size(AV1_COMP *cpi, int width, int height);
 
 int av1_update_entropy(AV1_COMP *cpi, int update);
diff --git a/av1/encoder/svc_layercontext.c b/av1/encoder/svc_layercontext.c
index 3c34335..59a4f3d 100644
--- a/av1/encoder/svc_layercontext.c
+++ b/av1/encoder/svc_layercontext.c
@@ -26,6 +26,7 @@
   SVC *const svc = &cpi->svc;
   int mi_rows = cpi->common.mi_rows;
   int mi_cols = cpi->common.mi_cols;
+  svc->base_framerate = 30.0;
 
   for (int sl = 0; sl < svc->number_spatial_layers; ++sl) {
     for (int tl = 0; tl < svc->number_temporal_layers; ++tl) {
@@ -178,6 +179,7 @@
   lc->rc = cpi->rc;
   lc->target_bandwidth = (int)cpi->oxcf.target_bandwidth;
   lc->group_index = gf_group->index;
+  if (cpi->svc.spatial_layer_id == 0) cpi->svc.base_framerate = cpi->framerate;
   // For spatial-svc, allow cyclic-refresh to be applied on the spatial layers,
   // for the base temporal layer.
   if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ &&
@@ -220,3 +222,29 @@
   av1_update_temporal_layer_framerate(cpi);
   av1_restore_layer_context(cpi);
 }
+
+static void get_layer_resolution(const int width_org, const int height_org,
+                                 const int num, const int den, int *width_out,
+                                 int *height_out) {
+  int w, h;
+  if (width_out == NULL || height_out == NULL || den == 0) return;
+  w = width_org * num / den;
+  h = height_org * num / den;
+  // Make height and width even.
+  w += w % 2;
+  h += h % 2;
+  *width_out = w;
+  *height_out = h;
+}
+
+void av1_one_pass_cbr_svc_start_layer(AV1_COMP *const cpi) {
+  SVC *const svc = &cpi->svc;
+  LAYER_CONTEXT *lc = NULL;
+  int width = 0, height = 0;
+  lc = &svc->layer_context[svc->spatial_layer_id * svc->number_temporal_layers +
+                           svc->temporal_layer_id];
+  get_layer_resolution(cpi->oxcf.width, cpi->oxcf.height,
+                       lc->scaling_factor_num, lc->scaling_factor_den, &width,
+                       &height);
+  av1_set_size_literal(cpi, width, height);
+}
diff --git a/av1/encoder/svc_layercontext.h b/av1/encoder/svc_layercontext.h
index d52677f..b018f48 100644
--- a/av1/encoder/svc_layercontext.h
+++ b/av1/encoder/svc_layercontext.h
@@ -52,6 +52,7 @@
   int non_reference_frame;
   int ref_idx[INTER_REFS_PER_FRAME];
   int refresh[REF_FRAMES];
+  double base_framerate;
   // Layer context used for rate control in one pass temporal CBR mode or
   // two pass spatial mode.
   LAYER_CONTEXT layer_context[AOM_MAX_LAYERS];
@@ -81,6 +82,8 @@
 
 void av1_svc_reset_temporal_layers(struct AV1_COMP *const cpi, int is_key);
 
+void av1_one_pass_cbr_svc_start_layer(struct AV1_COMP *const cpi);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/examples/svc_encoder_rtc.c b/examples/svc_encoder_rtc.c
index 2e9d5fc..a5ab076 100644
--- a/examples/svc_encoder_rtc.c
+++ b/examples/svc_encoder_rtc.c
@@ -30,26 +30,26 @@
 
 void usage_exit(void) { exit(EXIT_FAILURE); }
 
-static int mode_to_num_layers[5] = { 1, 2, 3, 3, 2 };
+static int mode_to_num_temporal_layers[6] = { 1, 2, 3, 3, 2, 1 };
+static int mode_to_num_spatial_layers[6] = { 1, 1, 1, 1, 1, 2 };
+static int mode_to_num_layers[6] = { 1, 2, 3, 3, 2, 2 };
 
 // For rate control encoding stats.
 struct RateControlMetrics {
   // Number of input frames per layer.
   int layer_input_frames[AOM_MAX_TS_LAYERS];
-  // Total (cumulative) number of encoded frames per layer.
-  int layer_tot_enc_frames[AOM_MAX_TS_LAYERS];
   // Number of encoded non-key frames per layer.
   int layer_enc_frames[AOM_MAX_TS_LAYERS];
   // Framerate per layer layer (cumulative).
   double layer_framerate[AOM_MAX_TS_LAYERS];
   // Target average frame size per layer (per-frame-bandwidth per layer).
-  double layer_pfb[AOM_MAX_TS_LAYERS];
+  double layer_pfb[AOM_MAX_LAYERS];
   // Actual average frame size per layer.
-  double layer_avg_frame_size[AOM_MAX_TS_LAYERS];
+  double layer_avg_frame_size[AOM_MAX_LAYERS];
   // Average rate mismatch per layer (|target - actual| / target).
-  double layer_avg_rate_mismatch[AOM_MAX_TS_LAYERS];
-  // Actual encoding bitrate per layer (cumulative).
-  double layer_encoding_bitrate[AOM_MAX_TS_LAYERS];
+  double layer_avg_rate_mismatch[AOM_MAX_LAYERS];
+  // Actual encoding bitrate per layer (cumulative across temporal layers).
+  double layer_encoding_bitrate[AOM_MAX_LAYERS];
   // Average of the short-time encoder actual bitrate.
   // TODO(marpan): Should we add these short-time stats for each layer?
   double avg_st_encoding_bitrate;
@@ -59,7 +59,7 @@
   int window_size;
   // Number of window measurements.
   int window_count;
-  int layer_target_bitrate[AOM_MAX_TS_LAYERS];
+  int layer_target_bitrate[AOM_MAX_LAYERS];
 };
 
 static int read_frame(struct AvxInputContext *input_ctx, aom_image_t *img) {
@@ -151,6 +151,7 @@
 // in the stream.
 static void set_rate_control_metrics(struct RateControlMetrics *rc,
                                      double framerate,
+                                     unsigned int ss_number_layers,
                                      unsigned int ts_number_layers) {
   int ts_rate_decimator[AOM_MAX_TS_LAYERS] = { 1 };
   ts_rate_decimator[0] = 1;
@@ -165,23 +166,26 @@
   }
   // Set the layer (cumulative) framerate and the target layer (non-cumulative)
   // per-frame-bandwidth, for the rate control encoding stats below.
-  rc->layer_framerate[0] = framerate / ts_rate_decimator[0];
-  rc->layer_pfb[0] =
-      1000.0 * rc->layer_target_bitrate[0] / rc->layer_framerate[0];
-  for (unsigned int i = 0; i < ts_number_layers; ++i) {
-    if (i > 0) {
-      rc->layer_framerate[i] = framerate / ts_rate_decimator[i];
-      rc->layer_pfb[i] =
-          1000.0 *
-          (rc->layer_target_bitrate[i] - rc->layer_target_bitrate[i - 1]) /
-          (rc->layer_framerate[i] - rc->layer_framerate[i - 1]);
+  for (unsigned int sl = 0; sl < ss_number_layers; ++sl) {
+    unsigned int i = sl * ts_number_layers;
+    rc->layer_framerate[0] = framerate / ts_rate_decimator[0];
+    rc->layer_pfb[i] =
+        1000.0 * rc->layer_target_bitrate[i] / rc->layer_framerate[0];
+    for (unsigned int tl = 0; tl < ts_number_layers; ++tl) {
+      i = sl * ts_number_layers + tl;
+      if (tl > 0) {
+        rc->layer_framerate[tl] = framerate / ts_rate_decimator[tl];
+        rc->layer_pfb[i] =
+            1000.0 *
+            (rc->layer_target_bitrate[i] - rc->layer_target_bitrate[i - 1]) /
+            (rc->layer_framerate[tl] - rc->layer_framerate[tl - 1]);
+      }
+      rc->layer_input_frames[tl] = 0;
+      rc->layer_enc_frames[tl] = 0;
+      rc->layer_encoding_bitrate[i] = 0.0;
+      rc->layer_avg_frame_size[i] = 0.0;
+      rc->layer_avg_rate_mismatch[i] = 0.0;
     }
-    rc->layer_input_frames[i] = 0;
-    rc->layer_enc_frames[i] = 0;
-    rc->layer_tot_enc_frames[i] = 0;
-    rc->layer_encoding_bitrate[i] = 0.0;
-    rc->layer_avg_frame_size[i] = 0.0;
-    rc->layer_avg_rate_mismatch[i] = 0.0;
   }
   rc->window_count = 0;
   rc->window_size = 15;
@@ -191,35 +195,40 @@
 
 static void printout_rate_control_summary(struct RateControlMetrics *rc,
                                           int frame_cnt,
+                                          unsigned int ss_number_layers,
                                           unsigned int ts_number_layers) {
   int tot_num_frames = 0;
   double perc_fluctuation = 0.0;
   printf("Total number of processed frames: %d\n\n", frame_cnt - 1);
   printf("Rate control layer stats for %d layer(s):\n\n", ts_number_layers);
-  for (unsigned int i = 0; i < ts_number_layers; ++i) {
-    const int num_dropped =
-        i > 0 ? rc->layer_input_frames[i] - rc->layer_enc_frames[i]
-              : rc->layer_input_frames[i] - rc->layer_enc_frames[i] - 1;
-    tot_num_frames += rc->layer_input_frames[i];
-    rc->layer_encoding_bitrate[i] = 0.001 * rc->layer_framerate[i] *
-                                    rc->layer_encoding_bitrate[i] /
-                                    tot_num_frames;
-    rc->layer_avg_frame_size[i] =
-        rc->layer_avg_frame_size[i] / rc->layer_enc_frames[i];
-    rc->layer_avg_rate_mismatch[i] =
-        100.0 * rc->layer_avg_rate_mismatch[i] / rc->layer_enc_frames[i];
-    printf("For layer#: %d\n", i);
-    printf("Bitrate (target vs actual): %d %f\n", rc->layer_target_bitrate[i],
-           rc->layer_encoding_bitrate[i]);
-    printf("Average frame size (target vs actual): %f %f\n", rc->layer_pfb[i],
-           rc->layer_avg_frame_size[i]);
-    printf("Average rate_mismatch: %f\n", rc->layer_avg_rate_mismatch[i]);
-    printf(
-        "Number of input frames, encoded (non-key) frames, "
-        "and perc dropped frames: %d %d %f\n",
-        rc->layer_input_frames[i], rc->layer_enc_frames[i],
-        100.0 * num_dropped / rc->layer_input_frames[i]);
-    printf("\n");
+  for (unsigned int sl = 0; sl < ss_number_layers; ++sl) {
+    tot_num_frames = 0;
+    for (unsigned int tl = 0; tl < ts_number_layers; ++tl) {
+      unsigned int i = sl * ts_number_layers + tl;
+      const int num_dropped =
+          tl > 0 ? rc->layer_input_frames[tl] - rc->layer_enc_frames[tl]
+                 : rc->layer_input_frames[tl] - rc->layer_enc_frames[tl] - 1;
+      tot_num_frames += rc->layer_input_frames[tl];
+      rc->layer_encoding_bitrate[i] = 0.001 * rc->layer_framerate[tl] *
+                                      rc->layer_encoding_bitrate[i] /
+                                      tot_num_frames;
+      rc->layer_avg_frame_size[i] =
+          rc->layer_avg_frame_size[i] / rc->layer_enc_frames[tl];
+      rc->layer_avg_rate_mismatch[i] =
+          100.0 * rc->layer_avg_rate_mismatch[i] / rc->layer_enc_frames[tl];
+      printf("For layer#: %d %d \n", sl, tl);
+      printf("Bitrate (target vs actual): %d %f\n", rc->layer_target_bitrate[i],
+             rc->layer_encoding_bitrate[i]);
+      printf("Average frame size (target vs actual): %f %f\n", rc->layer_pfb[i],
+             rc->layer_avg_frame_size[i]);
+      printf("Average rate_mismatch: %f\n", rc->layer_avg_rate_mismatch[i]);
+      printf(
+          "Number of input frames, encoded (non-key) frames, "
+          "and perc dropped frames: %d %d %f\n",
+          rc->layer_input_frames[tl], rc->layer_enc_frames[tl],
+          100.0 * num_dropped / rc->layer_input_frames[tl]);
+      printf("\n");
+    }
   }
   rc->avg_st_encoding_bitrate = rc->avg_st_encoding_bitrate / rc->window_count;
   rc->variance_st_encoding_bitrate =
@@ -239,11 +248,10 @@
 static int set_layer_pattern(int layering_mode, int frame_cnt,
                              aom_svc_layer_id_t *layer_id,
                              aom_svc_ref_frame_config_t *ref_frame_config,
-                             int *use_svc_control) {
+                             int *use_svc_control, int spatial_layer_id) {
   int i;
   *use_svc_control = 1;
-  // No spatial layers in this test.
-  layer_id->spatial_layer_id = 0;
+  layer_id->spatial_layer_id = spatial_layer_id;
   // Set the referende map buffer idx for the 7 references:
   // LAST_FRAME (0), LAST2_FRAME(1), LAST3_FRAME(2), GOLDEN_FRAME(3),
   // BWDREF_FRAME(4), ALTREF2_FRAME(5), ALTREF_FRAME(6).
@@ -345,13 +353,29 @@
                        AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_REF_GF;
       }
       break;
+    case 5:
+      layer_id->temporal_layer_id = 0;
+      if (layer_id->spatial_layer_id == 0) {
+        // Reference LAST, update LAST. Keep LAST and GOLDEN in slots 0 and 3.
+        ref_frame_config->ref_idx[0] = 0;
+        ref_frame_config->ref_idx[3] = 3;
+        ref_frame_config->refresh[0] = 1;
+        layer_flags |= AOM_EFLAG_NO_REF_GF;
+      } else if (layer_id->spatial_layer_id == 1) {
+        // Reference LAST and GOLDEN. Set buffer_idx for LAST to slot 3
+        // and GOLDEN to slot 0. Update slot 3 (LAST).
+        ref_frame_config->ref_idx[0] = 3;
+        ref_frame_config->ref_idx[3] = 0;
+        ref_frame_config->refresh[3] = 1;
+      }
+      break;
     default: assert(0); die("Error: Unsupported temporal layering mode!\n");
   }
   return layer_flags;
 }
 
 int main(int argc, char **argv) {
-  AvxVideoWriter *outfile[AOM_MAX_TS_LAYERS] = { NULL };
+  AvxVideoWriter *outfile[AOM_MAX_LAYERS] = { NULL };
   aom_codec_ctx_t codec;
   aom_codec_enc_cfg_t cfg;
   int frame_cnt = 0;
@@ -362,9 +386,9 @@
   uint32_t error_resilient = 0;
   int speed;
   int frame_avail;
-  int got_data;
+  int got_data = 0;
   int flags = 0;
-  unsigned int i;
+  unsigned i;
   int pts = 0;             // PTS starts at 0.
   int frame_duration = 1;  // 1 timebase tick per frame.
   int layering_mode = 0;
@@ -420,7 +444,8 @@
     die("Invalid number of arguments");
   }
 
-  ts_number_layers = mode_to_num_layers[layering_mode];
+  ts_number_layers = mode_to_num_temporal_layers[layering_mode];
+  ss_number_layers = mode_to_num_spatial_layers[layering_mode];
 
   input_ctx.filename = argv[1];
   open_input_file(&input_ctx, 0);
@@ -458,7 +483,8 @@
     svc_params.layer_target_bitrate[i - 13] = rc.layer_target_bitrate[i - 13];
   }
 
-  cfg.rc_target_bitrate = svc_params.layer_target_bitrate[ts_number_layers - 1];
+  cfg.rc_target_bitrate =
+      svc_params.layer_target_bitrate[ss_number_layers * ts_number_layers - 1];
 
   svc_params.framerate_factor[0] = 1;
   if (ts_number_layers == 2) {
@@ -499,7 +525,7 @@
   cfg.kf_min_dist = cfg.kf_max_dist = 3000;
 
   framerate = cfg.g_timebase.den / cfg.g_timebase.num;
-  set_rate_control_metrics(&rc, framerate, ts_number_layers);
+  set_rate_control_metrics(&rc, framerate, ss_number_layers, ts_number_layers);
 
   if (input_ctx.file_type == FILE_TYPE_Y4M) {
     if (input_ctx.width != cfg.g_w || input_ctx.height != cfg.g_h) {
@@ -513,20 +539,22 @@
   }
 
   // Open an output file for each stream.
-  for (i = 0; i < ts_number_layers; ++i) {
-    char file_name[PATH_MAX];
-    AvxVideoInfo info;
-    info.codec_fourcc = encoder->fourcc;
-    info.frame_width = cfg.g_w;
-    info.frame_height = cfg.g_h;
-    info.time_base.numerator = cfg.g_timebase.num;
-    info.time_base.denominator = cfg.g_timebase.den;
+  for (unsigned int sl = 0; sl < ss_number_layers; ++sl) {
+    for (unsigned tl = 0; tl < ts_number_layers; ++tl) {
+      i = sl * ts_number_layers + tl;
+      char file_name[PATH_MAX];
+      AvxVideoInfo info;
+      info.codec_fourcc = encoder->fourcc;
+      info.frame_width = cfg.g_w;
+      info.frame_height = cfg.g_h;
+      info.time_base.numerator = cfg.g_timebase.num;
+      info.time_base.denominator = cfg.g_timebase.den;
 
-    snprintf(file_name, sizeof(file_name), "%s_%d.av1", argv[2], i);
-    outfile[i] = aom_video_writer_open(file_name, kContainerIVF, &info);
-    if (!outfile[i]) die("Failed to open %s for writing", file_name);
-
-    assert(outfile[i] != NULL);
+      snprintf(file_name, sizeof(file_name), "%s_%d.av1", argv[2], i);
+      outfile[i] = aom_video_writer_open(file_name, kContainerIVF, &info);
+      if (!outfile[i]) die("Failed to open %s for writing", file_name);
+      assert(outfile[i] != NULL);
+    }
   }
 
   // Initialize codec.
@@ -540,7 +568,7 @@
 
   svc_params.number_spatial_layers = ss_number_layers;
   svc_params.number_temporal_layers = ts_number_layers;
-  for (i = 0; i < ts_number_layers; ++i) {
+  for (i = 0; i < ss_number_layers * ts_number_layers; ++i) {
     svc_params.max_quantizers[i] = cfg.rc_max_quantizer;
     svc_params.min_quantizers[i] = cfg.rc_min_quantizer;
   }
@@ -548,6 +576,16 @@
     svc_params.scaling_factor_num[i] = 1;
     svc_params.scaling_factor_den[i] = 1;
   }
+  if (ss_number_layers == 2) {
+    svc_params.scaling_factor_num[0] = 1;
+    svc_params.scaling_factor_den[0] = 2;
+  } else if (ss_number_layers == 3) {
+    svc_params.scaling_factor_num[0] = 1;
+    svc_params.scaling_factor_den[0] = 4;
+    svc_params.scaling_factor_num[1] = 1;
+    svc_params.scaling_factor_den[1] = 2;
+  }
+
   aom_codec_control(&codec, AV1E_SET_SVC_PARAMS, &svc_params);
 
   // This controls the maximum target size of the key frame.
@@ -562,83 +600,101 @@
   frame_avail = 1;
   while (frame_avail || got_data) {
     struct aom_usec_timer timer;
-    aom_codec_iter_t iter = NULL;
-    const aom_codec_cx_pkt_t *pkt;
-
-    // Set the reference/update flags, layer_id, and reference_map
-    // buffer index.
-    flags = set_layer_pattern(layering_mode, frame_cnt, &layer_id,
-                              &ref_frame_config, &use_svc_control);
-    aom_codec_control(&codec, AV1E_SET_SVC_LAYER_ID, &layer_id);
-    if (use_svc_control)
-      aom_codec_control(&codec, AV1E_SET_SVC_REF_FRAME_CONFIG,
-                        &ref_frame_config);
-
     frame_avail = read_frame(&input_ctx, &raw);
-    if (frame_avail) ++rc.layer_input_frames[layer_id.temporal_layer_id];
-    aom_usec_timer_start(&timer);
-    if (aom_codec_encode(&codec, frame_avail ? &raw : NULL, pts, 1, flags)) {
-      die_codec(&codec, "Failed to encode frame");
-    }
-    aom_usec_timer_mark(&timer);
-    cx_time += aom_usec_timer_elapsed(&timer);
-    got_data = 0;
-    while ((pkt = aom_codec_get_cx_data(&codec, &iter))) {
-      got_data = 1;
-      switch (pkt->kind) {
-        case AOM_CODEC_CX_FRAME_PKT:
-          for (i = layer_id.temporal_layer_id; i < ts_number_layers; ++i) {
-            aom_video_writer_write_frame(outfile[i], pkt->data.frame.buf,
-                                         pkt->data.frame.sz, pts);
-            ++rc.layer_tot_enc_frames[i];
-            rc.layer_encoding_bitrate[i] += 8.0 * pkt->data.frame.sz;
-            // Keep count of rate control stats per layer (for non-key frames).
-            if (i == (unsigned int)layer_id.temporal_layer_id &&
-                !(pkt->data.frame.flags & AOM_FRAME_IS_KEY)) {
-              rc.layer_avg_frame_size[i] += 8.0 * pkt->data.frame.sz;
-              rc.layer_avg_rate_mismatch[i] +=
-                  fabs(8.0 * pkt->data.frame.sz - rc.layer_pfb[i]) /
-                  rc.layer_pfb[i];
-              ++rc.layer_enc_frames[i];
+    // Loop over spatial layers.
+    for (unsigned int slx = 0; slx < ss_number_layers; slx++) {
+      aom_codec_iter_t iter = NULL;
+      const aom_codec_cx_pkt_t *pkt;
+      int layer = 0;
+
+      // Set the reference/update flags, layer_id, and reference_map
+      // buffer index.
+      flags = set_layer_pattern(layering_mode, frame_cnt, &layer_id,
+                                &ref_frame_config, &use_svc_control, slx);
+      aom_codec_control(&codec, AV1E_SET_SVC_LAYER_ID, &layer_id);
+      if (use_svc_control)
+        aom_codec_control(&codec, AV1E_SET_SVC_REF_FRAME_CONFIG,
+                          &ref_frame_config);
+
+      layer = slx * ts_number_layers + layer_id.temporal_layer_id;
+      if (frame_avail && slx == 0) ++rc.layer_input_frames[layer];
+
+      // Do the layer encode.
+      aom_usec_timer_start(&timer);
+      if (aom_codec_encode(&codec, frame_avail ? &raw : NULL, pts, 1, flags))
+        die_codec(&codec, "Failed to encode frame");
+      aom_usec_timer_mark(&timer);
+      cx_time += aom_usec_timer_elapsed(&timer);
+
+      got_data = 0;
+      while ((pkt = aom_codec_get_cx_data(&codec, &iter))) {
+        got_data = 1;
+        switch (pkt->kind) {
+          case AOM_CODEC_CX_FRAME_PKT:
+            for (unsigned int sl = layer_id.spatial_layer_id;
+                 sl < ss_number_layers; ++sl) {
+              for (unsigned tl = layer_id.temporal_layer_id;
+                   tl < ts_number_layers; ++tl) {
+                unsigned int j = sl * ts_number_layers + tl;
+                aom_video_writer_write_frame(outfile[j], pkt->data.frame.buf,
+                                             pkt->data.frame.sz, pts);
+
+                if (sl == (unsigned int)layer_id.spatial_layer_id)
+                  rc.layer_encoding_bitrate[j] += 8.0 * pkt->data.frame.sz;
+                // Keep count of rate control stats per layer (for non-key).
+                if (tl == (unsigned int)layer_id.temporal_layer_id &&
+                    sl == (unsigned int)layer_id.spatial_layer_id &&
+                    !(pkt->data.frame.flags & AOM_FRAME_IS_KEY)) {
+                  rc.layer_avg_frame_size[j] += 8.0 * pkt->data.frame.sz;
+                  rc.layer_avg_rate_mismatch[j] +=
+                      fabs(8.0 * pkt->data.frame.sz - rc.layer_pfb[j]) /
+                      rc.layer_pfb[j];
+                  if (slx == 0) ++rc.layer_enc_frames[tl];
+                }
+              }
             }
-          }
-          // Update for short-time encoding bitrate states, for moving window
-          // of size rc->window, shifted by rc->window / 2.
-          // Ignore first window segment, due to key frame.
-          if (frame_cnt > rc.window_size) {
-            sum_bitrate += 0.001 * 8.0 * pkt->data.frame.sz * framerate;
-            rc.window_size = (rc.window_size <= 0) ? 1 : rc.window_size;
-            if (frame_cnt % rc.window_size == 0) {
-              rc.window_count += 1;
-              rc.avg_st_encoding_bitrate += sum_bitrate / rc.window_size;
-              rc.variance_st_encoding_bitrate +=
-                  (sum_bitrate / rc.window_size) *
-                  (sum_bitrate / rc.window_size);
-              sum_bitrate = 0.0;
+
+            // Update for short-time encoding bitrate states, for moving window
+            // of size rc->window, shifted by rc->window / 2.
+            // Ignore first window segment, due to key frame.
+            // For spatial layers: only do this for top/highest SL.
+            if (frame_cnt > rc.window_size && slx == ss_number_layers - 1) {
+              sum_bitrate += 0.001 * 8.0 * pkt->data.frame.sz * framerate;
+              rc.window_size = (rc.window_size <= 0) ? 1 : rc.window_size;
+              if (frame_cnt % rc.window_size == 0) {
+                rc.window_count += 1;
+                rc.avg_st_encoding_bitrate += sum_bitrate / rc.window_size;
+                rc.variance_st_encoding_bitrate +=
+                    (sum_bitrate / rc.window_size) *
+                    (sum_bitrate / rc.window_size);
+                sum_bitrate = 0.0;
+              }
             }
-          }
-          // Second shifted window.
-          if (frame_cnt > rc.window_size + rc.window_size / 2) {
-            sum_bitrate2 += 0.001 * 8.0 * pkt->data.frame.sz * framerate;
-            if (frame_cnt > 2 * rc.window_size &&
-                frame_cnt % rc.window_size == 0) {
-              rc.window_count += 1;
-              rc.avg_st_encoding_bitrate += sum_bitrate2 / rc.window_size;
-              rc.variance_st_encoding_bitrate +=
-                  (sum_bitrate2 / rc.window_size) *
-                  (sum_bitrate2 / rc.window_size);
-              sum_bitrate2 = 0.0;
+            // Second shifted window.
+            if (frame_cnt > rc.window_size + rc.window_size / 2 &&
+                slx == ss_number_layers - 1) {
+              sum_bitrate2 += 0.001 * 8.0 * pkt->data.frame.sz * framerate;
+              if (frame_cnt > 2 * rc.window_size &&
+                  frame_cnt % rc.window_size == 0) {
+                rc.window_count += 1;
+                rc.avg_st_encoding_bitrate += sum_bitrate2 / rc.window_size;
+                rc.variance_st_encoding_bitrate +=
+                    (sum_bitrate2 / rc.window_size) *
+                    (sum_bitrate2 / rc.window_size);
+                sum_bitrate2 = 0.0;
+              }
             }
-          }
-          break;
-        default: break;
+            break;
+          default: break;
+        }
       }
-    }
+    }  // loop over spatial layers
     ++frame_cnt;
     pts += frame_duration;
   }
   close_input_file(&input_ctx);
-  printout_rate_control_summary(&rc, frame_cnt, ts_number_layers);
+  printout_rate_control_summary(&rc, frame_cnt, ss_number_layers,
+                                ts_number_layers);
   printf("\n");
   printf("Frame cnt and encoding time/FPS stats for encoding: %d %f %f\n",
          frame_cnt, 1000 * (float)cx_time / (double)(frame_cnt * 1000000),
@@ -647,7 +703,8 @@
   if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec");
 
   // Try to rewrite the output file headers with the actual frame count.
-  for (i = 0; i < ts_number_layers; ++i) aom_video_writer_close(outfile[i]);
+  for (i = 0; i < ss_number_layers * ts_number_layers; ++i)
+    aom_video_writer_close(outfile[i]);
 
   if (input_ctx.file_type != FILE_TYPE_Y4M) {
     aom_img_free(&raw);
diff --git a/test/encode_test_driver.cc b/test/encode_test_driver.cc
index d06168f..226248b 100644
--- a/test/encode_test_driver.cc
+++ b/test/encode_test_driver.cc
@@ -219,65 +219,69 @@
     }
 #endif
 
+    number_spatial_layers_ = GetNumSpatialLayers();
+
     bool again;
     for (again = true; again; video->Next()) {
       again = (video->img() != NULL);
 
-      PreEncodeFrameHook(video);
-      PreEncodeFrameHook(video, encoder.get());
-      encoder->EncodeFrame(video, frame_flags_);
+      for (int sl = 0; sl < number_spatial_layers_; sl++) {
+        PreEncodeFrameHook(video);
+        PreEncodeFrameHook(video, encoder.get());
+        encoder->EncodeFrame(video, frame_flags_);
 
-      CxDataIterator iter = encoder->GetCxData();
+        CxDataIterator iter = encoder->GetCxData();
 
-      bool has_cxdata = false;
-      bool has_dxdata = false;
-      while (const aom_codec_cx_pkt_t *pkt = iter.Next()) {
-        pkt = MutateEncoderOutputHook(pkt);
-        again = true;
-        switch (pkt->kind) {
-          case AOM_CODEC_CX_FRAME_PKT:
-            has_cxdata = true;
-            if (decoder.get() != NULL && DoDecode()) {
-              aom_codec_err_t res_dec;
-              if (DoDecodeInvisible()) {
-                res_dec = decoder->DecodeFrame(
-                    (const uint8_t *)pkt->data.frame.buf, pkt->data.frame.sz);
-              } else {
-                res_dec = decoder->DecodeFrame(
-                    (const uint8_t *)pkt->data.frame.buf +
-                        (pkt->data.frame.sz - pkt->data.frame.vis_frame_size),
-                    pkt->data.frame.vis_frame_size);
+        bool has_cxdata = false;
+        bool has_dxdata = false;
+        while (const aom_codec_cx_pkt_t *pkt = iter.Next()) {
+          pkt = MutateEncoderOutputHook(pkt);
+          again = true;
+          switch (pkt->kind) {
+            case AOM_CODEC_CX_FRAME_PKT:
+              has_cxdata = true;
+              if (decoder.get() != NULL && DoDecode()) {
+                aom_codec_err_t res_dec;
+                if (DoDecodeInvisible()) {
+                  res_dec = decoder->DecodeFrame(
+                      (const uint8_t *)pkt->data.frame.buf, pkt->data.frame.sz);
+                } else {
+                  res_dec = decoder->DecodeFrame(
+                      (const uint8_t *)pkt->data.frame.buf +
+                          (pkt->data.frame.sz - pkt->data.frame.vis_frame_size),
+                      pkt->data.frame.vis_frame_size);
+                }
+
+                if (!HandleDecodeResult(res_dec, decoder.get())) break;
+
+                has_dxdata = true;
               }
+              ASSERT_GE(pkt->data.frame.pts, last_pts_);
+              if (sl == number_spatial_layers_) last_pts_ = pkt->data.frame.pts;
+              FramePktHook(pkt);
+              break;
 
-              if (!HandleDecodeResult(res_dec, decoder.get())) break;
+            case AOM_CODEC_PSNR_PKT: PSNRPktHook(pkt); break;
 
-              has_dxdata = true;
-            }
-            ASSERT_GE(pkt->data.frame.pts, last_pts_);
-            last_pts_ = pkt->data.frame.pts;
-            FramePktHook(pkt);
-            break;
-
-          case AOM_CODEC_PSNR_PKT: PSNRPktHook(pkt); break;
-
-          default: break;
-        }
-      }
-
-      if (has_dxdata && has_cxdata) {
-        const aom_image_t *img_enc = encoder->GetPreviewFrame();
-        DxDataIterator dec_iter = decoder->GetDxData();
-        const aom_image_t *img_dec = dec_iter.Next();
-        if (img_enc && img_dec) {
-          const bool res =
-              compare_img(img_enc, img_dec, NULL, NULL, NULL, NULL, NULL);
-          if (!res) {  // Mismatch
-            MismatchHook(img_enc, img_dec);
+            default: break;
           }
         }
-        if (img_dec) DecompressedFrameHook(*img_dec, video->pts());
-      }
-      if (!Continue()) break;
+
+        if (has_dxdata && has_cxdata) {
+          const aom_image_t *img_enc = encoder->GetPreviewFrame();
+          DxDataIterator dec_iter = decoder->GetDxData();
+          const aom_image_t *img_dec = dec_iter.Next();
+          if (img_enc && img_dec) {
+            const bool res =
+                compare_img(img_enc, img_dec, NULL, NULL, NULL, NULL, NULL);
+            if (!res) {  // Mismatch
+              MismatchHook(img_enc, img_dec);
+            }
+          }
+          if (img_dec) DecompressedFrameHook(*img_dec, video->pts());
+        }
+        if (!Continue()) break;
+      }  // Loop over spatial layers
     }
 
     EndPassHook();
diff --git a/test/encode_test_driver.h b/test/encode_test_driver.h
index 65f8944..19448c8 100644
--- a/test/encode_test_driver.h
+++ b/test/encode_test_driver.h
@@ -179,7 +179,7 @@
  protected:
   explicit EncoderTest(const CodecFactory *codec)
       : codec_(codec), abort_(false), init_flags_(0), frame_flags_(0),
-        last_pts_(0), mode_(kRealTime) {
+        last_pts_(0), mode_(kRealTime), number_spatial_layers_(1) {
     // Default to 1 thread.
     cfg_.g_threads = 1;
   }
@@ -242,6 +242,8 @@
     return AOM_CODEC_OK == res_dec;
   }
 
+  virtual int GetNumSpatialLayers() { return 1; }
+
   // Hook that can modify the encoder's output data
   virtual const aom_codec_cx_pkt_t *MutateEncoderOutputHook(
       const aom_codec_cx_pkt_t *pkt) {
@@ -257,6 +259,7 @@
   unsigned long frame_flags_;
   aom_codec_pts_t last_pts_;
   TestMode mode_;
+  int number_spatial_layers_;
 };
 
 }  // namespace libaom_test
diff --git a/test/svc_datarate_test.cc b/test/svc_datarate_test.cc
index 3d2c206..1a50f16 100644
--- a/test/svc_datarate_test.cc
+++ b/test/svc_datarate_test.cc
@@ -40,10 +40,14 @@
     ResetModel();
   }
 
+  virtual int GetNumSpatialLayers() { return number_spatial_layers_; }
+
   virtual void ResetModel() {
     DatarateTest::ResetModel();
+    layer_frame_cnt_ = 0;
     number_temporal_layers_ = 1;
-    for (int i = 0; i < 3; i++) {
+    number_spatial_layers_ = 1;
+    for (int i = 0; i < AOM_MAX_LAYERS; i++) {
       target_layer_bitrate_[i] = 0;
       effective_datarate_tl[i] = 0.0;
     }
@@ -54,48 +58,48 @@
 
   virtual void PreEncodeFrameHook(::libaom_test::VideoSource *video,
                                   ::libaom_test::Encoder *encoder) {
+    int spatial_layer_id = 0;
     if (video->frame() == 0) {
-      if (number_temporal_layers_ > 1) {
-        initialize_svc(number_temporal_layers_, &svc_params_);
-        encoder->Control(AV1E_SET_SVC_PARAMS, &svc_params_);
-      }
+      initialize_svc(number_temporal_layers_, number_spatial_layers_,
+                     &svc_params_);
+      encoder->Control(AV1E_SET_SVC_PARAMS, &svc_params_);
     }
-    if (number_temporal_layers_ > 1) {
-      // Set the reference/update flags, layer_id, and reference_map
-      // buffer index.
-      frame_flags_ =
-          set_layer_pattern(video->frame(), &layer_id_, &ref_frame_config_);
-      encoder->Control(AV1E_SET_SVC_LAYER_ID, &layer_id_);
-      encoder->Control(AV1E_SET_SVC_REF_FRAME_CONFIG, &ref_frame_config_);
-    }
+    if (number_spatial_layers_ == 2)
+      spatial_layer_id = (layer_frame_cnt_ % 2 == 0) ? 0 : 1;
+    // Set the reference/update flags, layer_id, and reference_map
+    // buffer index.
+    frame_flags_ = set_layer_pattern(video->frame(), &layer_id_,
+                                     &ref_frame_config_, spatial_layer_id);
+    encoder->Control(AV1E_SET_SVC_LAYER_ID, &layer_id_);
+    encoder->Control(AV1E_SET_SVC_REF_FRAME_CONFIG, &ref_frame_config_);
+    layer_frame_cnt_++;
     DatarateTest::PreEncodeFrameHook(video, encoder);
   }
 
   virtual void FramePktHook(const aom_codec_cx_pkt_t *pkt) {
-    DatarateTest::FramePktHook(pkt);
     const size_t frame_size_in_bits = pkt->data.frame.sz * 8;
-    if (number_temporal_layers_ > 1) {
-      // Update the layer cumulative  bitrate.
-      for (int i = layer_id_.temporal_layer_id; i < number_temporal_layers_;
-           i++)
-        effective_datarate_tl[i] += 1.0 * frame_size_in_bits;
+    // Update the layer cumulative  bitrate.
+    for (int i = layer_id_.temporal_layer_id; i < number_temporal_layers_;
+         i++) {
+      int layer = layer_id_.spatial_layer_id * number_temporal_layers_ + i;
+      effective_datarate_tl[layer] += 1.0 * frame_size_in_bits;
     }
+    if (layer_id_.spatial_layer_id == number_spatial_layers_ - 1)
+      last_pts_ = pkt->data.frame.pts;
   }
 
   virtual void EndPassHook(void) {
-    DatarateTest::EndPassHook();
-    if (number_temporal_layers_ > 1) {
-      for (int i = 0; i < number_temporal_layers_; i++)
-        effective_datarate_tl[i] =
-            (effective_datarate_tl[i] / 1000) / duration_;
+    duration_ = ((last_pts_ + 1) * timebase_);
+    for (int i = 0; i < number_temporal_layers_ * number_spatial_layers_; i++) {
+      effective_datarate_tl[i] = (effective_datarate_tl[i] / 1000) / duration_;
     }
   }
 
   // Layer pattern configuration.
   virtual int set_layer_pattern(int frame_cnt, aom_svc_layer_id_t *layer_id,
-                                aom_svc_ref_frame_config_t *ref_frame_config) {
-    // No spatial layers in this test.
-    layer_id->spatial_layer_id = 0;
+                                aom_svc_ref_frame_config_t *ref_frame_config,
+                                int spatial_layer) {
+    layer_id->spatial_layer_id = spatial_layer;
     // Set the referende map buffer idx for the 7 references:
     // LAST_FRAME (0), LAST2_FRAME(1), LAST3_FRAME(2), GOLDEN_FRAME(3),
     // BWDREF_FRAME(4), ALTREF2_FRAME(5), ALTREF_FRAME(6).
@@ -105,47 +109,64 @@
     int layer_flags = AOM_EFLAG_NO_REF_LAST2 | AOM_EFLAG_NO_REF_LAST3 |
                       AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_REF_BWD |
                       AOM_EFLAG_NO_REF_ARF2;
-    // 3-layer:
-    //   1    3   5    7
-    //     2        6
-    // 0        4        8
-    if (frame_cnt % 4 == 0) {
-      // Base layer.
+    if (number_temporal_layers_ == 3 && number_spatial_layers_ == 1) {
+      // 3-layer:
+      //   1    3   5    7
+      //     2        6
+      // 0        4        8
+      if (frame_cnt % 4 == 0) {
+        // Base layer.
+        layer_id->temporal_layer_id = 0;
+        // Update LAST on layer 0, reference LAST and GF.
+        ref_frame_config->refresh[0] = 1;
+      } else if ((frame_cnt - 1) % 4 == 0) {
+        layer_id->temporal_layer_id = 2;
+        // First top layer: no updates, only reference LAST (TL0).
+        layer_flags |= AOM_EFLAG_NO_REF_GF;
+      } else if ((frame_cnt - 2) % 4 == 0) {
+        layer_id->temporal_layer_id = 1;
+        // Middle layer (TL1): update LAST2, only reference LAST (TL0).
+        ref_frame_config->refresh[1] = 1;
+        layer_flags |= AOM_EFLAG_NO_REF_GF;
+      } else if ((frame_cnt - 3) % 4 == 0) {
+        layer_id->temporal_layer_id = 2;
+        // Second top layer: no updates, only reference LAST.
+        // Set buffer idx for LAST to slot 1, since that was the slot
+        // updated in previous frame. So LAST is TL1 frame.
+        ref_frame_config->ref_idx[0] = 1;
+        ref_frame_config->ref_idx[1] = 0;
+        layer_flags |= AOM_EFLAG_NO_REF_GF;
+      }
+    } else if (number_temporal_layers_ == 1 && number_spatial_layers_ == 2) {
       layer_id->temporal_layer_id = 0;
-      // Update LAST on layer 0, reference LAST and GF.
-      ref_frame_config->refresh[0] = 1;
-    } else if ((frame_cnt - 1) % 4 == 0) {
-      layer_id->temporal_layer_id = 2;
-      // First top layer: no updates, only reference LAST (TL0).
-      layer_flags |= AOM_EFLAG_NO_REF_GF;
-    } else if ((frame_cnt - 2) % 4 == 0) {
-      layer_id->temporal_layer_id = 1;
-      // Middle layer (TL1): update LAST2, only reference LAST (TL0).
-      ref_frame_config->refresh[1] = 1;
-      layer_flags |= AOM_EFLAG_NO_REF_GF;
-    } else if ((frame_cnt - 3) % 4 == 0) {
-      layer_id->temporal_layer_id = 2;
-      // Second top layer: no updates, only reference LAST.
-      // Set buffer idx for LAST to slot 1, since that was the slot
-      // updated in previous frame. So LAST is TL1 frame.
-      ref_frame_config->ref_idx[0] = 1;
-      ref_frame_config->ref_idx[1] = 0;
-      layer_flags |= AOM_EFLAG_NO_REF_GF;
+      if (layer_id->spatial_layer_id == 0) {
+        // Reference LAST, update LAST. Keep LAST and GOLDEN in slots 0 and 3.
+        ref_frame_config->ref_idx[0] = 0;
+        ref_frame_config->ref_idx[3] = 3;
+        ref_frame_config->refresh[0] = 1;
+        layer_flags |= AOM_EFLAG_NO_REF_GF;
+      } else if (layer_id->spatial_layer_id == 1) {
+        // Reference LAST and GOLDEN. Set buffer_idx for LAST to slot 3
+        // and GOLDEN to slot 0. Update slot 3 (LAST).
+        ref_frame_config->ref_idx[0] = 3;
+        ref_frame_config->ref_idx[3] = 0;
+        ref_frame_config->refresh[3] = 1;
+      }
     }
     return layer_flags;
   }
 
   virtual void initialize_svc(int number_temporal_layers,
+                              int number_spatial_layers,
                               aom_svc_params *svc_params) {
-    svc_params->number_spatial_layers = 1;
-    svc_params->scaling_factor_num[0] = 1;
-    svc_params->scaling_factor_den[0] = 1;
+    svc_params->number_spatial_layers = number_spatial_layers;
     svc_params->number_temporal_layers = number_temporal_layers;
-    for (int i = 0; i < number_temporal_layers; ++i) {
-      svc_params->max_quantizers[i] = 56;
+    for (int i = 0; i < number_temporal_layers * number_spatial_layers; ++i) {
+      svc_params->max_quantizers[i] = 60;
       svc_params->min_quantizers[i] = 2;
       svc_params->layer_target_bitrate[i] = target_layer_bitrate_[i];
     }
+    // Do at most 3 spatial or temporal layers here.
     svc_params->framerate_factor[0] = 1;
     if (number_temporal_layers == 2) {
       svc_params->framerate_factor[0] = 2;
@@ -155,6 +176,21 @@
       svc_params->framerate_factor[1] = 2;
       svc_params->framerate_factor[2] = 1;
     }
+    svc_params->scaling_factor_num[0] = 1;
+    svc_params->scaling_factor_den[0] = 1;
+    if (number_spatial_layers == 2) {
+      svc_params->scaling_factor_num[0] = 1;
+      svc_params->scaling_factor_den[0] = 2;
+      svc_params->scaling_factor_num[1] = 1;
+      svc_params->scaling_factor_den[1] = 1;
+    } else if (number_spatial_layers == 3) {
+      svc_params->scaling_factor_num[0] = 1;
+      svc_params->scaling_factor_den[0] = 4;
+      svc_params->scaling_factor_num[1] = 1;
+      svc_params->scaling_factor_den[1] = 2;
+      svc_params->scaling_factor_num[2] = 1;
+      svc_params->scaling_factor_den[2] = 1;
+    }
   }
 
   virtual void BasicRateTargetingSVC3TL1SLTest() {
@@ -178,11 +214,8 @@
     target_layer_bitrate_[0] = 50 * cfg_.rc_target_bitrate / 100;
     target_layer_bitrate_[1] = 70 * cfg_.rc_target_bitrate / 100;
     target_layer_bitrate_[2] = cfg_.rc_target_bitrate;
-    framerate_factor_[0] = 4;
-    framerate_factor_[1] = 2;
-    framerate_factor_[2] = 1;
     ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
-    for (int i = 0; i < number_temporal_layers_; i++) {
+    for (int i = 0; i < number_temporal_layers_ * number_spatial_layers_; i++) {
       ASSERT_GE(effective_datarate_tl[i], target_layer_bitrate_[i] * 0.80)
           << " The datarate for the file is lower than target by too much!";
       ASSERT_LE(effective_datarate_tl[i], target_layer_bitrate_[i] * 1.30)
@@ -190,14 +223,45 @@
     }
   }
 
+  virtual void BasicRateTargetingSVC1TL2SLTest() {
+    cfg_.rc_buf_initial_sz = 500;
+    cfg_.rc_buf_optimal_sz = 500;
+    cfg_.rc_buf_sz = 1000;
+    cfg_.rc_dropframe_thresh = 0;
+    cfg_.rc_min_quantizer = 0;
+    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_end_usage = AOM_CBR;
+    cfg_.g_lag_in_frames = 0;
+    cfg_.g_usage = AOM_USAGE_REALTIME;
+    cfg_.g_error_resilient = 1;
+
+    ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352,
+                                         288, 30, 1, 0, 300);
+    const int bitrate_array[2] = { 300, 600 };
+    cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)];
+    ResetModel();
+    number_temporal_layers_ = 1;
+    number_spatial_layers_ = 2;
+    target_layer_bitrate_[0] = 2 * cfg_.rc_target_bitrate / 4;
+    target_layer_bitrate_[1] = 2 * cfg_.rc_target_bitrate / 4;
+    ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+    for (int i = 0; i < number_temporal_layers_ * number_spatial_layers_; i++) {
+      ASSERT_GE(effective_datarate_tl[i], target_layer_bitrate_[i] * 0.80)
+          << " The datarate for the file is lower than target by too much!";
+      ASSERT_LE(effective_datarate_tl[i], target_layer_bitrate_[i] * 1.35)
+          << " The datarate for the file is greater than target by too much!";
+    }
+  }
+
+  int layer_frame_cnt_;
   int number_temporal_layers_;
+  int number_spatial_layers_;
   // Allow for up to 3 temporal layers.
-  int target_layer_bitrate_[3];
+  int target_layer_bitrate_[AOM_MAX_LAYERS];
   aom_svc_params_t svc_params_;
   aom_svc_ref_frame_config_t ref_frame_config_;
   aom_svc_layer_id_t layer_id_;
-  double effective_datarate_tl[3];
-  int framerate_factor_[3];
+  double effective_datarate_tl[AOM_MAX_LAYERS];
 };
 
 // Check basic rate targeting for CBR, for 3 temporal layers.
@@ -205,6 +269,11 @@
   BasicRateTargetingSVC3TL1SLTest();
 }
 
+// Check basic rate targeting for CBR, for 2 spatial layers.
+TEST_P(DatarateTestSVC, BasicRateTargetingSVC1TL2SL) {
+  BasicRateTargetingSVC1TL2SLTest();
+}
+
 AV1_INSTANTIATE_TEST_CASE(DatarateTestSVC,
                           ::testing::Values(::libaom_test::kRealTime),
                           ::testing::Range(7, 9),