shithub: libvpx

Download patch

ref: f9b565f7ecebb5f76c8d406e35dba1bd25a6398d
parent: 76ad30b6fb85f1462b28323220960d165d167e78
author: Jerome Jiang <jianj@google.com>
date: Mon Jul 12 10:04:12 EDT 2021

Refactor rtc rate control test

Remove golden files. Run actual encoding as the ground truth.

Change-Id: I1cea001278c1e9409bb02d33823cf69192c790a4

--- a/libs.mk
+++ b/libs.mk
@@ -493,10 +493,12 @@
                            $(call enabled,TEST_INTRA_PRED_SPEED_SRCS))
 TEST_INTRA_PRED_SPEED_OBJS := $(sort $(call objs,$(TEST_INTRA_PRED_SPEED_SRCS)))
 
+ifeq ($(CONFIG_VP9_ENCODER),yes)
 RC_INTERFACE_TEST_BIN=./test_rc_interface$(EXE_SFX)
 RC_INTERFACE_TEST_SRCS=$(call addprefix_clean,test/,\
                        $(call enabled,RC_INTERFACE_TEST_SRCS))
 RC_INTERFACE_TEST_OBJS := $(sort $(call objs,$(RC_INTERFACE_TEST_SRCS)))
+endif
 
 SIMPLE_ENCODE_TEST_BIN=./test_simple_encode$(EXE_SFX)
 SIMPLE_ENCODE_TEST_SRCS=$(call addprefix_clean,test/,\
@@ -597,6 +599,7 @@
             -L. -l$(CODEC_LIB) -l$(GTEST_LIB) $^
 endif  # TEST_INTRA_PRED_SPEED
 
+ifeq ($(CONFIG_VP9_ENCODER),yes)
 ifneq ($(strip $(RC_INTERFACE_TEST_OBJS)),)
 PROJECTS-$(CONFIG_MSVS) += test_rc_interface.$(VCPROJ_SFX)
 test_rc_interface.$(VCPROJ_SFX): $(RC_INTERFACE_TEST_SRCS) vpx.$(VCPROJ_SFX) \
@@ -616,6 +619,7 @@
             -I. -I"$(SRC_PATH_BARE)/third_party/googletest/src/include" \
             -L. -l$(CODEC_LIB) -l$(RC_RTC_LIB) -l$(GTEST_LIB) $^
 endif  # RC_INTERFACE_TEST
+endif  # CONFIG_VP9_ENCODER
 endif
 else
 
@@ -657,6 +661,7 @@
               -L. -lvpx -lgtest $(extralibs) -lm))
 endif  # TEST_INTRA_PRED_SPEED
 
+ifeq ($(CONFIG_VP9_ENCODER),yes)
 ifneq ($(strip $(RC_INTERFACE_TEST_OBJS)),)
 $(RC_INTERFACE_TEST_OBJS) $(RC_INTERFACE_TEST_OBJS:.o=.d): \
   CXXFLAGS += $(GTEST_INCLUDES)
@@ -668,6 +673,7 @@
               $(RC_INTERFACE_TEST_OBJS) \
               -L. -lvpx -lgtest -lvp9rc $(extralibs) -lm))
 endif  # RC_INTERFACE_TEST
+endif  # CONFIG_VP9_ENCODER
 
 ifneq ($(strip $(SIMPLE_ENCODE_TEST_OBJS)),)
 $(SIMPLE_ENCODE_TEST_OBJS) $(SIMPLE_ENCODE_TEST_OBJS:.o=.d): \
--- a/test/ratectrl_rtc_test.cc
+++ b/test/ratectrl_rtc_test.cc
@@ -16,6 +16,7 @@
 #include "third_party/googletest/src/include/gtest/gtest.h"
 #include "test/codec_factory.h"
 #include "test/encode_test_driver.h"
+#include "test/i420_video_source.h"
 #include "test/util.h"
 #include "test/video_source.h"
 #include "vpx/vpx_codec.h"
@@ -23,188 +24,89 @@
 
 namespace {
 
-const size_t kNumFrame = 850;
+const size_t kNumFrames = 300;
 
-struct FrameInfo {
-  friend std::istream &operator>>(std::istream &is, FrameInfo &info) {
-    is >> info.frame_id >> info.spatial_id >> info.temporal_id >> info.base_q >>
-        info.target_bandwidth >> info.buffer_level >> info.filter_level_ >>
-        info.bytes_used;
-    return is;
-  }
+const int kTemporalId[4] = { 0, 2, 1, 2 };
 
-  int frame_id;
-  int spatial_id;
-  int temporal_id;
-  // Base QP
-  int base_q;
-  size_t target_bandwidth;
-  size_t buffer_level;
-  // Loopfilter level
-  int filter_level_;
-  // Frame size for current frame, used for pose encode update
-  size_t bytes_used;
-};
-
-// This test runs the rate control interface and compare against ground truth
-// generated by encoders.
-// Settings for the encoder:
-// For 1 layer CBR:
-// - AQ_Mode 0
-// - Disable golden refresh
-// - Bitrate x 2 at frame/superframe 200
-// - Bitrate / 4 at frame/superframe 400
-// examples/vpx_temporal_svc_encoder gipsrec_motion1.1280_720.yuv out vp9
-//    1280 720 1 30 7 0 0 1 0 1000
-//
-// For 1 layer VBR:
-//  - Set rc_end_usage to VPX_VBR
-//  - AQ Mode 0
-//  - Disable vp9_compute_frame_low_motion in vp9_encoder.c
-//  - Set rc->constrain_gf_key_freq_onepass_vbr = 0 in vp9_rc_init
-// examples/vpx_temporal_svc_encoder gipsrec_motion1.1280_720.yuv out vp9
-//    1280 720 1 30 7 0 0 1 0 1000
-//
-// For SVC (3 temporal layers, 3 spatial layers):
-// - AQ_Mode 0
-// - Disable golden refresh
-// - Bitrate x 2 at frame/superframe 200
-// - Bitrate / 4 at frame/superframe 400
-// examples/vp9_spatial_svc_encoder -f 10000 -w 1280 -h 720 -t 1/30 -sl 3
-// -k 10000 -bl 100,140,200,250,350,500,450,630,900 -b 1600 --rc-end-usage=1
-// --lag-in-frames=0 --passes=1 --speed=7 --threads=1
-// --temporal-layering-mode=3 -aq 1 -rcstat 1
-// gipsrec_motion1.1280_720.yuv -o out.webm
-//
-// The generated file includes:
-// frame number, spatial layer ID, temporal layer ID, base QP, target
-// bandwidth, buffer level, loopfilter level, encoded frame size
-// TODO(jianj): Remove golden files, and run actual encoding in this test.
-class RcInterfaceTest : public ::testing::Test {
+class RcInterfaceTest
+    : public ::libvpx_test::EncoderTest,
+      public ::libvpx_test::CodecTestWith2Params<int, vpx_rc_mode> {
  public:
-  explicit RcInterfaceTest() {}
+  RcInterfaceTest()
+      : EncoderTest(GET_PARAM(0)), aq_mode_(GET_PARAM(1)), key_interval_(3000),
+        encoder_exit_(false) {}
 
   virtual ~RcInterfaceTest() {}
 
  protected:
-  void RunOneLayerCBR() {
-    SetConfigOneLayerCBR();
-    rc_api_ = libvpx::VP9RateControlRTC::Create(rc_cfg_);
-    FrameInfo frame_info;
-    libvpx::VP9FrameParamsQpRTC frame_params;
-    frame_params.frame_type = KEY_FRAME;
-    frame_params.spatial_layer_id = 0;
-    frame_params.temporal_layer_id = 0;
-    std::ifstream one_layer_file;
-    one_layer_file.open(libvpx_test::GetDataPath() +
-                        "/rc_interface_test_one_layer");
-    ASSERT_TRUE(one_layer_file.good());
-    for (size_t i = 0; i < kNumFrame; i++) {
-      one_layer_file >> frame_info;
-      if (frame_info.frame_id > 0) frame_params.frame_type = INTER_FRAME;
-      if (frame_info.frame_id == 200) {
-        rc_cfg_.target_bandwidth = rc_cfg_.target_bandwidth * 2;
-        rc_api_->UpdateRateControl(rc_cfg_);
-      } else if (frame_info.frame_id == 400) {
-        rc_cfg_.target_bandwidth = rc_cfg_.target_bandwidth / 4;
-        rc_api_->UpdateRateControl(rc_cfg_);
-      }
-      ASSERT_EQ(frame_info.spatial_id, 0);
-      ASSERT_EQ(frame_info.temporal_id, 0);
-      rc_api_->ComputeQP(frame_params);
-      ASSERT_EQ(rc_api_->GetQP(), frame_info.base_q);
-      ASSERT_EQ(rc_api_->GetLoopfilterLevel(), frame_info.filter_level_);
-      rc_api_->PostEncodeUpdate(frame_info.bytes_used);
+  virtual void SetUp() {
+    InitializeConfig();
+    SetMode(::libvpx_test::kRealTime);
+  }
+
+  virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
+                                  libvpx_test::Encoder *encoder) {
+    if (video->frame() == 0) {
+      encoder->Control(VP8E_SET_CPUUSED, 7);
+      encoder->Control(VP9E_SET_AQ_MODE, aq_mode_);
+      encoder->Control(VP9E_SET_TUNE_CONTENT, 0);
+      encoder->Control(VP8E_SET_MAX_INTRA_BITRATE_PCT, 1000);
+      encoder->Control(VP9E_SET_RTC_EXTERNAL_RATECTRL, 1);
     }
+    frame_params_.frame_type =
+        video->frame() % key_interval_ == 0 ? KEY_FRAME : INTER_FRAME;
+    if (rc_cfg_.rc_mode == VPX_CBR && frame_params_.frame_type == INTER_FRAME) {
+      // Disable golden frame update.
+      frame_flags_ |= VP8_EFLAG_NO_UPD_GF;
+      frame_flags_ |= VP8_EFLAG_NO_UPD_ARF;
+    }
+    encoder_exit_ = video->frame() == kNumFrames;
   }
 
-  void RunSVC() {
-    SetConfigSVC();
-    rc_api_ = libvpx::VP9RateControlRTC::Create(rc_cfg_);
-    FrameInfo frame_info;
-    libvpx::VP9FrameParamsQpRTC frame_params;
-    frame_params.frame_type = KEY_FRAME;
-    std::ifstream svc_file;
-    svc_file.open(libvpx_test::GetDataPath() + "/rc_interface_test_svc");
-    ASSERT_TRUE(svc_file.good());
-    for (size_t i = 0; i < kNumFrame * rc_cfg_.ss_number_layers; i++) {
-      svc_file >> frame_info;
-      if (frame_info.frame_id > 0) frame_params.frame_type = INTER_FRAME;
-      if (frame_info.frame_id == 200 * rc_cfg_.ss_number_layers) {
-        for (int layer = 0;
-             layer < rc_cfg_.ss_number_layers * rc_cfg_.ts_number_layers;
-             layer++)
-          rc_cfg_.layer_target_bitrate[layer] *= 2;
-        rc_cfg_.target_bandwidth *= 2;
-        rc_api_->UpdateRateControl(rc_cfg_);
-      } else if (frame_info.frame_id == 400 * rc_cfg_.ss_number_layers) {
-        for (int layer = 0;
-             layer < rc_cfg_.ss_number_layers * rc_cfg_.ts_number_layers;
-             layer++)
-          rc_cfg_.layer_target_bitrate[layer] /= 4;
-        rc_cfg_.target_bandwidth /= 4;
-        rc_api_->UpdateRateControl(rc_cfg_);
-      }
-      frame_params.spatial_layer_id = frame_info.spatial_id;
-      frame_params.temporal_layer_id = frame_info.temporal_id;
-      rc_api_->ComputeQP(frame_params);
-      ASSERT_EQ(rc_api_->GetQP(), frame_info.base_q);
-      ASSERT_EQ(rc_api_->GetLoopfilterLevel(), frame_info.filter_level_);
-      rc_api_->PostEncodeUpdate(frame_info.bytes_used);
+  virtual void PostEncodeFrameHook(::libvpx_test::Encoder *encoder) {
+    if (encoder_exit_) {
+      return;
     }
+    int loopfilter_level, qp;
+    encoder->Control(VP9E_GET_LOOPFILTER_LEVEL, &loopfilter_level);
+    encoder->Control(VP8E_GET_LAST_QUANTIZER, &qp);
+    rc_api_->ComputeQP(frame_params_);
+    ASSERT_EQ(rc_api_->GetQP(), qp);
+    ASSERT_EQ(rc_api_->GetLoopfilterLevel(), loopfilter_level);
   }
 
-  void RunOneLayerVBR() {
-    SetConfigOneLayerVBR();
+  virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
+    rc_api_->PostEncodeUpdate(pkt->data.frame.sz);
+  }
+
+  void RunOneLayer() {
+    SetConfig(GET_PARAM(2));
     rc_api_ = libvpx::VP9RateControlRTC::Create(rc_cfg_);
-    FrameInfo frame_info;
-    libvpx::VP9FrameParamsQpRTC frame_params;
-    frame_params.frame_type = KEY_FRAME;
-    frame_params.spatial_layer_id = 0;
-    frame_params.temporal_layer_id = 0;
-    std::ifstream one_layer_file;
-    one_layer_file.open(libvpx_test::GetDataPath() +
-                        "/rc_interface_test_one_layer_vbr");
-    ASSERT_TRUE(one_layer_file.good());
-    for (size_t i = 0; i < kNumFrame; i++) {
-      one_layer_file >> frame_info;
-      if (frame_info.frame_id > 0) frame_params.frame_type = INTER_FRAME;
-      ASSERT_EQ(frame_info.spatial_id, 0);
-      ASSERT_EQ(frame_info.temporal_id, 0);
-      rc_api_->ComputeQP(frame_params);
-      ASSERT_EQ(rc_api_->GetQP(), frame_info.base_q);
-      ASSERT_EQ(rc_api_->GetLoopfilterLevel(), frame_info.filter_level_);
-      rc_api_->PostEncodeUpdate(frame_info.bytes_used);
-    }
+    frame_params_.spatial_layer_id = 0;
+    frame_params_.temporal_layer_id = 0;
+
+    ::libvpx_test::I420VideoSource video("desktop_office1.1280_720-020.yuv",
+                                         1280, 720, 30, 1, 0, kNumFrames);
+
+    ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
   }
 
   void RunOneLayerVBRPeriodicKey() {
-    SetConfigOneLayerVBRPeriodicKey();
+    if (GET_PARAM(2) != VPX_VBR) return;
+    key_interval_ = 100;
+    SetConfig(VPX_VBR);
     rc_api_ = libvpx::VP9RateControlRTC::Create(rc_cfg_);
-    FrameInfo frame_info;
-    libvpx::VP9FrameParamsQpRTC frame_params;
-    frame_params.frame_type = KEY_FRAME;
-    frame_params.spatial_layer_id = 0;
-    frame_params.temporal_layer_id = 0;
-    std::ifstream one_layer_file;
-    one_layer_file.open(libvpx_test::GetDataPath() +
-                        "/rc_interface_test_one_layer_vbr_periodic_key");
-    ASSERT_TRUE(one_layer_file.good());
-    for (size_t i = 0; i < kNumFrame; i++) {
-      one_layer_file >> frame_info;
-      if (frame_info.frame_id > 0) frame_params.frame_type = INTER_FRAME;
-      if (frame_info.frame_id % 300 == 0) frame_params.frame_type = KEY_FRAME;
-      ASSERT_EQ(frame_info.spatial_id, 0);
-      ASSERT_EQ(frame_info.temporal_id, 0);
-      rc_api_->ComputeQP(frame_params);
-      ASSERT_EQ(rc_api_->GetQP(), frame_info.base_q);
-      ASSERT_EQ(rc_api_->GetLoopfilterLevel(), frame_info.filter_level_);
-      rc_api_->PostEncodeUpdate(frame_info.bytes_used);
-    }
+    frame_params_.spatial_layer_id = 0;
+    frame_params_.temporal_layer_id = 0;
+
+    ::libvpx_test::I420VideoSource video("desktop_office1.1280_720-020.yuv",
+                                         1280, 720, 30, 1, 0, kNumFrames);
+
+    ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
   }
 
  private:
-  void SetConfig() {
+  void SetConfig(vpx_rc_mode rc_mode) {
     rc_cfg_.width = 1280;
     rc_cfg_.height = 720;
     rc_cfg_.max_quantizer = 52;
@@ -224,24 +126,182 @@
     rc_cfg_.layer_target_bitrate[0] = 1000;
     rc_cfg_.max_quantizers[0] = 52;
     rc_cfg_.min_quantizers[0] = 2;
+    rc_cfg_.rc_mode = rc_mode;
+
+    // Encoder settings for ground truth.
+    cfg_.g_w = 1280;
+    cfg_.g_h = 720;
+    cfg_.rc_undershoot_pct = 50;
+    cfg_.rc_overshoot_pct = 50;
+    cfg_.rc_buf_initial_sz = 600;
+    cfg_.rc_buf_optimal_sz = 600;
+    cfg_.rc_buf_sz = 1000;
+    cfg_.rc_dropframe_thresh = 0;
+    cfg_.rc_min_quantizer = 2;
+    cfg_.rc_max_quantizer = 52;
+    cfg_.rc_end_usage = rc_mode;
+    cfg_.g_lag_in_frames = 0;
+    cfg_.g_error_resilient = 0;
+    cfg_.rc_target_bitrate = 1000;
+    cfg_.kf_min_dist = key_interval_;
+    cfg_.kf_max_dist = key_interval_;
   }
 
-  void SetConfigOneLayerCBR() {
-    SetConfig();
-    rc_cfg_.rc_mode = VPX_CBR;
+  std::unique_ptr<libvpx::VP9RateControlRTC> rc_api_;
+  libvpx::VP9RateControlRtcConfig rc_cfg_;
+  int aq_mode_;
+  int key_interval_;
+  libvpx::VP9FrameParamsQpRTC frame_params_;
+  bool encoder_exit_;
+};
+
+class RcInterfaceSvcTest : public ::libvpx_test::EncoderTest,
+                           public ::libvpx_test::CodecTestWithParam<int> {
+ public:
+  RcInterfaceSvcTest() : EncoderTest(GET_PARAM(0)), aq_mode_(GET_PARAM(1)) {}
+  virtual ~RcInterfaceSvcTest() {}
+
+ protected:
+  virtual void SetUp() {
+    InitializeConfig();
+    SetMode(::libvpx_test::kRealTime);
   }
 
-  void SetConfigOneLayerVBR() {
-    SetConfig();
-    rc_cfg_.rc_mode = VPX_VBR;
+  virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
+                                  ::libvpx_test::Encoder *encoder) {
+    if (video->frame() == 0) {
+      encoder->Control(VP8E_SET_CPUUSED, 7);
+      encoder->Control(VP9E_SET_AQ_MODE, aq_mode_);
+      encoder->Control(VP9E_SET_TUNE_CONTENT, 0);
+      encoder->Control(VP8E_SET_MAX_INTRA_BITRATE_PCT, 900);
+      encoder->Control(VP9E_SET_RTC_EXTERNAL_RATECTRL, 1);
+      encoder->Control(VP9E_SET_SVC, 1);
+      encoder->Control(VP9E_SET_SVC_PARAMETERS, &svc_params_);
+    }
+
+    frame_params_.frame_type = video->frame() == 0 ? KEY_FRAME : INTER_FRAME;
+    if (rc_cfg_.rc_mode == VPX_CBR && frame_params_.frame_type == INTER_FRAME) {
+      // Disable golden frame update.
+      frame_flags_ |= VP8_EFLAG_NO_UPD_GF;
+      frame_flags_ |= VP8_EFLAG_NO_UPD_ARF;
+    }
+    encoder_exit_ = video->frame() == kNumFrames;
+    current_superframe_ = video->frame();
   }
 
-  void SetConfigOneLayerVBRPeriodicKey() {
-    SetConfig();
-    rc_cfg_.rc_mode = VPX_VBR;
+  virtual void PostEncodeFrameHook(::libvpx_test::Encoder *encoder) {
+    ::libvpx_test::CxDataIterator iter = encoder->GetCxData();
+    while (const vpx_codec_cx_pkt_t *pkt = iter.Next()) {
+      ParseSuperframeSizes(static_cast<const uint8_t *>(pkt->data.frame.buf),
+                           pkt->data.frame.sz);
+      for (int sl = 0; sl < rc_cfg_.ss_number_layers; sl++) {
+        frame_params_.spatial_layer_id = sl;
+        frame_params_.temporal_layer_id = kTemporalId[current_superframe_ % 4];
+        rc_api_->ComputeQP(frame_params_);
+        frame_params_.frame_type = INTER_FRAME;
+        rc_api_->PostEncodeUpdate(sizes_[sl]);
+      }
+    }
+    if (!encoder_exit_) {
+      int loopfilter_level, qp;
+      encoder->Control(VP9E_GET_LOOPFILTER_LEVEL, &loopfilter_level);
+      encoder->Control(VP8E_GET_LAST_QUANTIZER, &qp);
+      ASSERT_EQ(rc_api_->GetQP(), qp);
+      ASSERT_EQ(rc_api_->GetLoopfilterLevel(), loopfilter_level);
+    }
   }
+  // This method needs to be overridden because non-reference frames are
+  // expected to be mismatched frames as the encoder will avoid loopfilter on
+  // these frames.
+  virtual void MismatchHook(const vpx_image_t * /*img1*/,
+                            const vpx_image_t * /*img2*/) {}
 
-  void SetConfigSVC() {
+  void RunSvc() {
+    SetConfigSvc();
+    rc_api_ = libvpx::VP9RateControlRTC::Create(rc_cfg_);
+    SetEncoderSvc();
+
+    ::libvpx_test::I420VideoSource video("desktop_office1.1280_720-020.yuv",
+                                         1280, 720, 30, 1, 0, kNumFrames);
+
+    ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+  }
+
+ private:
+  vpx_codec_err_t ParseSuperframeSizes(const uint8_t *data, size_t data_sz) {
+    uint8_t marker = *(data + data_sz - 1);
+    if ((marker & 0xe0) == 0xc0) {
+      const uint32_t frames = (marker & 0x7) + 1;
+      const uint32_t mag = ((marker >> 3) & 0x3) + 1;
+      const size_t index_sz = 2 + mag * frames;
+      // This chunk is marked as having a superframe index but doesn't have
+      // enough data for it, thus it's an invalid superframe index.
+      if (data_sz < index_sz) return VPX_CODEC_CORRUPT_FRAME;
+      {
+        const uint8_t marker2 = *(data + data_sz - index_sz);
+        // This chunk is marked as having a superframe index but doesn't have
+        // the matching marker byte at the front of the index therefore it's an
+        // invalid chunk.
+        if (marker != marker2) return VPX_CODEC_CORRUPT_FRAME;
+      }
+      const uint8_t *x = &data[data_sz - index_sz + 1];
+      for (uint32_t i = 0; i < frames; ++i) {
+        uint32_t this_sz = 0;
+
+        for (uint32_t j = 0; j < mag; ++j) this_sz |= (*x++) << (j * 8);
+        sizes_[i] = this_sz;
+      }
+    }
+    return VPX_CODEC_OK;
+  }
+
+  void SetEncoderSvc() {
+    cfg_.ss_number_layers = 3;
+    cfg_.ts_number_layers = 3;
+    cfg_.g_timebase.num = 1;
+    cfg_.g_timebase.den = 30;
+    svc_params_.scaling_factor_num[0] = 72;
+    svc_params_.scaling_factor_den[0] = 288;
+    svc_params_.scaling_factor_num[1] = 144;
+    svc_params_.scaling_factor_den[1] = 288;
+    svc_params_.scaling_factor_num[2] = 288;
+    svc_params_.scaling_factor_den[2] = 288;
+    for (int i = 0; i < VPX_MAX_LAYERS; ++i) {
+      svc_params_.max_quantizers[i] = 56;
+      svc_params_.min_quantizers[i] = 2;
+    }
+    cfg_.rc_end_usage = VPX_CBR;
+    cfg_.g_lag_in_frames = 0;
+    cfg_.g_error_resilient = 0;
+    // 3 temporal layers
+    cfg_.ts_rate_decimator[0] = 4;
+    cfg_.ts_rate_decimator[1] = 2;
+    cfg_.ts_rate_decimator[2] = 1;
+    cfg_.temporal_layering_mode = 3;
+
+    cfg_.rc_buf_initial_sz = 500;
+    cfg_.rc_buf_optimal_sz = 600;
+    cfg_.rc_buf_sz = 1000;
+    cfg_.rc_min_quantizer = 2;
+    cfg_.rc_max_quantizer = 56;
+    cfg_.g_threads = 1;
+    cfg_.kf_max_dist = 9999;
+    cfg_.rc_target_bitrate = 1600;
+    cfg_.rc_overshoot_pct = 50;
+    cfg_.rc_undershoot_pct = 50;
+
+    cfg_.layer_target_bitrate[0] = 100;
+    cfg_.layer_target_bitrate[1] = 140;
+    cfg_.layer_target_bitrate[2] = 200;
+    cfg_.layer_target_bitrate[3] = 250;
+    cfg_.layer_target_bitrate[4] = 350;
+    cfg_.layer_target_bitrate[5] = 500;
+    cfg_.layer_target_bitrate[6] = 450;
+    cfg_.layer_target_bitrate[7] = 630;
+    cfg_.layer_target_bitrate[8] = 900;
+  }
+
+  void SetConfigSvc() {
     rc_cfg_.width = 1280;
     rc_cfg_.height = 720;
     rc_cfg_.max_quantizer = 56;
@@ -288,17 +348,25 @@
     }
   }
 
+  int aq_mode_;
   std::unique_ptr<libvpx::VP9RateControlRTC> rc_api_;
   libvpx::VP9RateControlRtcConfig rc_cfg_;
+  vpx_svc_extra_cfg_t svc_params_;
+  libvpx::VP9FrameParamsQpRTC frame_params_;
+  bool encoder_exit_;
+  int current_superframe_;
+  uint32_t sizes_[8];
 };
 
-TEST_F(RcInterfaceTest, OneLayerCBR) { RunOneLayerCBR(); }
+TEST_P(RcInterfaceTest, OneLayer) { RunOneLayer(); }
 
-TEST_F(RcInterfaceTest, OneLayerVBR) { RunOneLayerVBR(); }
+TEST_P(RcInterfaceTest, OneLayerVBRPeriodicKey) { RunOneLayerVBRPeriodicKey(); }
 
-TEST_F(RcInterfaceTest, OneLayerVBRPeriodicKey) { RunOneLayerVBRPeriodicKey(); }
+TEST_P(RcInterfaceSvcTest, Svc) { RunSvc(); }
 
-TEST_F(RcInterfaceTest, SVC) { RunSVC(); }
+VP9_INSTANTIATE_TEST_SUITE(RcInterfaceTest, ::testing::Values(0),
+                           ::testing::Values(VPX_CBR, VPX_VBR));
+VP9_INSTANTIATE_TEST_SUITE(RcInterfaceSvcTest, ::testing::Values(0));
 }  // namespace
 
 int main(int argc, char **argv) {
--- a/test/test-data.mk
+++ b/test/test-data.mk
@@ -27,10 +27,6 @@
 LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += rush_hour_444.y4m
 LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += screendata.y4m
 LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += niklas_640_480_30.yuv
-LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += rc_interface_test_one_layer
-LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += rc_interface_test_one_layer_vbr
-LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += rc_interface_test_one_layer_vbr_periodic_key
-LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += rc_interface_test_svc
 LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += bus_352x288_420_f20_b8.yuv
 
 # Test vectors
--- a/test/test-data.sha1
+++ b/test/test-data.sha1
@@ -869,7 +869,3 @@
 518a0be998afece76d3df76047d51e256c591ff2 *invalid-bug-148271109.ivf
 d3964f9dad9f60363c81b688324d95b4ec7c8038 *invalid-bug-148271109.ivf.res
 ad18ca16f0a249fb3b7c38de0d9b327fed273f96 *hantro_collage_w352h288_nv12.yuv
-03f827c0e36ff9a6e23c5cc11936924e4f1827ab *rc_interface_test_one_layer
-99e4f4c2961d46dc286db230090a39d78460b25d *rc_interface_test_svc
-9dcaafd91bc61ed360c23616b4788437b9f9b96b *rc_interface_test_one_layer_vbr
-babd17cca2e93cc74753c6ed80de87457bc3a5f3 *rc_interface_test_one_layer_vbr_periodic_key
--- a/test/test.mk
+++ b/test/test.mk
@@ -214,6 +214,11 @@
 TEST_INTRA_PRED_SPEED_SRCS-yes += ../md5_utils.h ../md5_utils.c
 
 RC_INTERFACE_TEST_SRCS-$(CONFIG_VP9_ENCODER) := ratectrl_rtc_test.cc
+RC_INTERFACE_TEST_SRCS-$(CONFIG_VP9_ENCODER) += encode_test_driver.cc
+RC_INTERFACE_TEST_SRCS-$(CONFIG_VP9_ENCODER) += encode_test_driver.h
+RC_INTERFACE_TEST_SRCS-yes += decode_test_driver.cc
+RC_INTERFACE_TEST_SRCS-yes += decode_test_driver.h
+RC_INTERFACE_TEST_SRCS-yes += codec_factory.h
 
 endif # CONFIG_SHARED
 
--- a/vp9/ratectrl_rtc.h
+++ b/vp9/ratectrl_rtc.h
@@ -117,13 +117,16 @@
       const VP9RateControlRtcConfig &cfg);
   ~VP9RateControlRTC() {
     if (cpi_) {
-      for (int sl = 0; sl < cpi_->svc.number_spatial_layers; sl++) {
-        for (int tl = 0; tl < cpi_->svc.number_temporal_layers; tl++) {
-          int layer = LAYER_IDS_TO_IDX(sl, tl, cpi_->oxcf.ts_number_layers);
-          LAYER_CONTEXT *const lc = &cpi_->svc.layer_context[layer];
-          vpx_free(lc->map);
-          vpx_free(lc->last_coded_q_map);
-          vpx_free(lc->consec_zero_mv);
+      if (cpi_->svc.number_spatial_layers > 1 ||
+          cpi_->svc.number_temporal_layers > 1) {
+        for (int sl = 0; sl < cpi_->svc.number_spatial_layers; sl++) {
+          for (int tl = 0; tl < cpi_->svc.number_temporal_layers; tl++) {
+            int layer = LAYER_IDS_TO_IDX(sl, tl, cpi_->oxcf.ts_number_layers);
+            LAYER_CONTEXT *const lc = &cpi_->svc.layer_context[layer];
+            vpx_free(lc->map);
+            vpx_free(lc->last_coded_q_map);
+            vpx_free(lc->consec_zero_mv);
+          }
         }
       }
       vpx_free(cpi_);