Skip to content

Commit

Permalink
vp9: add rate control interface for RTC
Browse files Browse the repository at this point in the history
Add vp9 RTC rate control without creating encoder,
to allow external codecs to use vp9 rate control.

A new library (libvp9rc.a) will be built. Applications using this
interface must be linked with the library.

BUG=1060775

Change-Id: Ib3e597256725a37d2d104e1e1a1733c469991b03
  • Loading branch information
jeromejj committed Apr 6, 2020
1 parent aae27c8 commit 745979b
Show file tree
Hide file tree
Showing 9 changed files with 624 additions and 36 deletions.
52 changes: 52 additions & 0 deletions libs.mk
Expand Up @@ -397,6 +397,10 @@ TEST_INTRA_PRED_SPEED_BIN=./test_intra_pred_speed$(EXE_SFX)
TEST_INTRA_PRED_SPEED_SRCS=$(addprefix test/,$(call enabled,TEST_INTRA_PRED_SPEED_SRCS))
TEST_INTRA_PRED_SPEED_OBJS := $(sort $(call objs,$(TEST_INTRA_PRED_SPEED_SRCS)))

RC_INTERFACE_TEST_BIN=./test_rc_interface$(EXE_SFX)
RC_INTERFACE_TEST_SRCS=$(addprefix test/,$(call enabled,RC_INTERFACE_TEST_SRCS))
RC_INTERFACE_TEST_OBJS := $(sort $(call objs,$(RC_INTERFACE_TEST_SRCS)))

libvpx_test_srcs.txt:
@echo " [CREATE] $@"
@echo $(LIBVPX_TEST_SRCS) | xargs -n1 echo | LC_ALL=C sort -u > $@
Expand Down Expand Up @@ -486,6 +490,25 @@ test_intra_pred_speed.$(VCPROJ_SFX): $(TEST_INTRA_PRED_SPEED_SRCS) vpx.$(VCPROJ_
-I. -I"$(SRC_PATH_BARE)/third_party/googletest/src/include" \
-L. -l$(CODEC_LIB) -l$(GTEST_LIB) $^
endif # TEST_INTRA_PRED_SPEED

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) gtest.$(VCPROJ_SFX)
@echo " [CREATE] $@"
$(qexec)$(GEN_VCPROJ) \
--exe \
--target=$(TOOLCHAIN) \
--name=test_rc_interface \
-D_VARIADIC_MAX=10 \
--proj-guid=CD837F5F-52D8-4314-A370-895D614166A7 \
--ver=$(CONFIG_VS_VERSION) \
--src-path-bare="$(SRC_PATH_BARE)" \
$(if $(CONFIG_STATIC_MSVCRT),--static-crt) \
--out=$@ $(INTERNAL_CFLAGS) $(CFLAGS) \
-I.-I"$(SRC_PATH_BARE)/third_party/googletest/src/include" \
-L. -l$(CODEC_LIB) -l$(GTEST_LIB) -lvp9rc$^
endif # RC_INTERFACE_TEST
endif
else

Expand All @@ -503,6 +526,21 @@ OBJS-yes += $(GTEST_OBJS)
LIBS-yes += $(BUILD_PFX)libgtest.a $(BUILD_PFX)libgtest_g.a
$(BUILD_PFX)libgtest_g.a: $(GTEST_OBJS)

ifeq ($(CONFIG_VP9_ENCODER),yes)
VP9_PREFIX=vp9/
include $(SRC_PATH_BARE)/$(VP9_PREFIX)vp9cx.mk
RC_RTC_SRCS := $(addprefix $(VP9_PREFIX),$(call enabled,VP9_CX_SRCS))
RC_RTC_SRCS += $(VP9_PREFIX)vp9cx.mk vpx/vp8.h vpx/vp8cx.h
RC_RTC_SRCS += $(VP9_PREFIX)ratectrl_rtc.cc
RC_RTC_SRCS += $(VP9_PREFIX)ratectrl_rtc.h
VP9_CX_SRCS-$(CONFIG_VP9_ENCODER) += ratectrl_rtc.cc
VP9_CX_SRCS-$(CONFIG_VP9_ENCODER) += ratectrl_rtc.h
RC_RTC_OBJS=$(call objs,$(RC_RTC_SRCS))
OBJS-yes += $(RC_RTC_OBJS)
LIBS-yes += $(BUILD_PFX)libvp9rc.a $(BUILD_PFX)libvp9rc_g.a
$(BUILD_PFX)libvp9rc_g.a: $(RC_RTC_OBJS)
endif

LIBVPX_TEST_OBJS=$(sort $(call objs,$(LIBVPX_TEST_SRCS)))
$(LIBVPX_TEST_OBJS) $(LIBVPX_TEST_OBJS:.o=.d): CXXFLAGS += $(GTEST_INCLUDES)
OBJS-yes += $(LIBVPX_TEST_OBJS)
Expand All @@ -527,13 +565,26 @@ $(eval $(call linkerxx_template,$(TEST_INTRA_PRED_SPEED_BIN), \
-L. -lvpx -lgtest $(extralibs) -lm))
endif # TEST_INTRA_PRED_SPEED

ifneq ($(strip $(RC_INTERFACE_TEST_OBJS)),)
$(RC_INTERFACE_TEST_OBJS) $(RC_INTERFACE_TEST_OBJS:.o=.d): \
CXXFLAGS += $(GTEST_INCLUDES)
OBJS-yes += $(RC_INTERFACE_TEST_OBJS)
BINS-yes += $(RC_INTERFACE_TEST_BIN)

$(RC_INTERFACE_TEST_BIN): $(TEST_LIBS) libvp9rc.a
$(eval $(call linkerxx_template,$(RC_INTERFACE_TEST_BIN), \
$(RC_INTERFACE_TEST_OBJS) \
-L. -lvpx -lgtest -lvp9rc $(extralibs) -lm))
endif # RC_INTERFACE_TEST

endif # CONFIG_UNIT_TESTS

# Install test sources only if codec source is included
INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(patsubst $(SRC_PATH_BARE)/%,%,\
$(shell find $(SRC_PATH_BARE)/third_party/googletest -type f))
INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(LIBVPX_TEST_SRCS)
INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(TEST_INTRA_PRED_SPEED_SRCS)
INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(RC_INTERFACE_TEST_SRCS)

define test_shard_template
test:: test_shard.$(1)
Expand Down Expand Up @@ -574,6 +625,7 @@ endif

## Update the global src list
SRCS += $(CODEC_SRCS) $(LIBVPX_TEST_SRCS) $(GTEST_SRCS)
SRCS += $(RC_INTERFACE_TEST_SRCS)

##
## vpxdec/vpxenc tests.
Expand Down
229 changes: 229 additions & 0 deletions test/ratectrl_rtc_test.cc
@@ -0,0 +1,229 @@
/*
* Copyright (c) 2020 The WebM project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "vp9/ratectrl_rtc.h"

#include <fstream> // NOLINT
#include <string>

#include "./vpx_config.h"
#include "third_party/googletest/src/include/gtest/gtest.h"
#include "test/codec_factory.h"
#include "test/encode_test_driver.h"
#include "test/util.h"
#include "test/video_source.h"
#include "vpx/vpx_codec.h"
#include "vpx_ports/bitops.h"

namespace {

const size_t kNumFrame = 850;

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;
}
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:
//
// 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):
//
// 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
//
// - AQ_Mode 0
// - Disable golden refresh
// - Bitrate x 2 at frame/superframe 200
// - Bitrate / 4 at frame/superframe 400
//
// 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 {
public:
explicit RcInterfaceTest() {}

virtual ~RcInterfaceTest() {}

protected:
void RunOneLayer() {
SetConfigOneLayer();
rc_api_->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_EQ(one_layer_file.rdstate() & std::ifstream::failbit, 0);
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);
}
}

void RunSVC() {
SetConfigSVC();
rc_api_->Create(rc_cfg_);
FrameInfo frame_info;
libvpx::VP9FrameParamsQpRTC frame_params;
frame_params.frame_type = KEY_FRAME;
std::ifstream svc_file;
svc_file.open(std::string(std::getenv("LIBVPX_TEST_DATA_PATH")) +
"/rc_interface_test_svc");
ASSERT_EQ(svc_file.rdstate() & std::ifstream::failbit, 0);
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);
}
}

private:
void SetConfigOneLayer() {
rc_cfg_.width = 1280;
rc_cfg_.height = 720;
rc_cfg_.max_quantizer = 52;
rc_cfg_.min_quantizer = 2;
rc_cfg_.target_bandwidth = 1000;
rc_cfg_.buf_initial_sz = 600;
rc_cfg_.buf_optimal_sz = 600;
rc_cfg_.buf_sz = 1000;
rc_cfg_.undershoot_pct = 50;
rc_cfg_.overshoot_pct = 50;
rc_cfg_.max_intra_bitrate_pct = 1000;
rc_cfg_.framerate = 30.0;
rc_cfg_.ss_number_layers = 1;
rc_cfg_.ts_number_layers = 1;
rc_cfg_.scaling_factor_num[0] = 1;
rc_cfg_.scaling_factor_den[0] = 1;
rc_cfg_.layer_target_bitrate[0] = 1000;
rc_cfg_.max_quantizers[0] = 52;
rc_cfg_.min_quantizers[0] = 2;
}

void SetConfigSVC() {
rc_cfg_.width = 1280;
rc_cfg_.height = 720;
rc_cfg_.max_quantizer = 56;
rc_cfg_.min_quantizer = 2;
rc_cfg_.target_bandwidth = 1600;
rc_cfg_.buf_initial_sz = 500;
rc_cfg_.buf_optimal_sz = 600;
rc_cfg_.buf_sz = 1000;
rc_cfg_.undershoot_pct = 50;
rc_cfg_.overshoot_pct = 50;
rc_cfg_.max_intra_bitrate_pct = 900;
rc_cfg_.framerate = 30.0;
rc_cfg_.ss_number_layers = 3;
rc_cfg_.ts_number_layers = 3;

rc_cfg_.scaling_factor_num[0] = 1;
rc_cfg_.scaling_factor_den[0] = 4;
rc_cfg_.scaling_factor_num[1] = 2;
rc_cfg_.scaling_factor_den[1] = 4;
rc_cfg_.scaling_factor_num[2] = 4;
rc_cfg_.scaling_factor_den[2] = 4;

rc_cfg_.ts_rate_decimator[0] = 4;
rc_cfg_.ts_rate_decimator[1] = 2;
rc_cfg_.ts_rate_decimator[2] = 1;

rc_cfg_.layer_target_bitrate[0] = 100;
rc_cfg_.layer_target_bitrate[1] = 140;
rc_cfg_.layer_target_bitrate[2] = 200;
rc_cfg_.layer_target_bitrate[3] = 250;
rc_cfg_.layer_target_bitrate[4] = 350;
rc_cfg_.layer_target_bitrate[5] = 500;
rc_cfg_.layer_target_bitrate[6] = 450;
rc_cfg_.layer_target_bitrate[7] = 630;
rc_cfg_.layer_target_bitrate[8] = 900;

for (int sl = 0; sl < rc_cfg_.ss_number_layers; ++sl) {
for (int tl = 0; tl < rc_cfg_.ts_number_layers; ++tl) {
const int i = sl * rc_cfg_.ts_number_layers + tl;
rc_cfg_.max_quantizers[i] = 56;
rc_cfg_.min_quantizers[i] = 2;
}
}
}

std::unique_ptr<libvpx::VP9RateControlRTC> rc_api_;
libvpx::VP9RateControlRtcConfig rc_cfg_;
};

TEST_F(RcInterfaceTest, OneLayer) { RunOneLayer(); }

TEST_F(RcInterfaceTest, SVC) { RunSVC(); }
} // namespace

int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
2 changes: 2 additions & 0 deletions test/test.mk
Expand Up @@ -203,6 +203,8 @@ LIBVPX_TEST_SRCS-$(CONFIG_ENCODERS) += sum_squares_test.cc
TEST_INTRA_PRED_SPEED_SRCS-yes := test_intra_pred_speed.cc
TEST_INTRA_PRED_SPEED_SRCS-yes += ../md5_utils.h ../md5_utils.c

RC_INTERFACE_TEST_SRCS-$(CONFIG_VP9_ENCODER) := ratectrl_rtc_test.cc

endif # CONFIG_SHARED

include $(SRC_PATH_BARE)/test/test-data.mk

0 comments on commit 745979b

Please sign in to comment.