ref: 745979bc2913b4f26847084699d9fddd68b60410
parent: aae27c8f1e4ad38eff29ba9d35a772343c0ce5bf
author: Jerome Jiang <jianj@google.com>
date: Tue Mar 24 11:13:59 EDT 2020
vp9: add rate control interface for RTC 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
--- a/libs.mk
+++ b/libs.mk
@@ -397,6 +397,10 @@
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 > $@
@@ -486,6 +490,25 @@
-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
@@ -503,6 +526,21 @@
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)
@@ -527,6 +565,18 @@
-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
@@ -534,6 +584,7 @@
$(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)
@@ -574,6 +625,7 @@
## Update the global src list
SRCS += $(CODEC_SRCS) $(LIBVPX_TEST_SRCS) $(GTEST_SRCS)
+SRCS += $(RC_INTERFACE_TEST_SRCS)
##
## vpxdec/vpxenc tests.
--- /dev/null
+++ b/test/ratectrl_rtc_test.cc
@@ -1,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();
+}
--- a/test/test.mk
+++ b/test/test.mk
@@ -203,6 +203,8 @@
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
--- a/vp9/encoder/vp9_encoder.c
+++ b/vp9/encoder/vp9_encoder.c
@@ -1523,8 +1523,29 @@
vp9_noise_estimate_init(&cpi->noise_estimate, cm->width, cm->height);
}
-static void set_rc_buffer_sizes(RATE_CONTROL *rc,
- const VP9EncoderConfig *oxcf) {
+void vp9_check_reset_rc_flag(VP9_COMP *cpi) {
+ RATE_CONTROL *rc = &cpi->rc;
+
+ if (cpi->common.current_video_frame >
+ (unsigned int)cpi->svc.number_spatial_layers) {
+ if (cpi->use_svc) {
+ vp9_svc_check_reset_layer_rc_flag(cpi);
+ } else {
+ if (rc->avg_frame_bandwidth > (3 * rc->last_avg_frame_bandwidth >> 1) ||
+ rc->avg_frame_bandwidth < (rc->last_avg_frame_bandwidth >> 1)) {
+ rc->rc_1_frame = 0;
+ rc->rc_2_frame = 0;
+ rc->bits_off_target = rc->optimal_buffer_level;
+ rc->buffer_level = rc->optimal_buffer_level;
+ }
+ }
+ }
+}
+
+void vp9_set_rc_buffer_sizes(VP9_COMP *cpi) {
+ RATE_CONTROL *rc = &cpi->rc;
+ const VP9EncoderConfig *oxcf = &cpi->oxcf;
+
const int64_t bandwidth = oxcf->target_bandwidth;
const int64_t starting = oxcf->starting_buffer_level_ms;
const int64_t optimal = oxcf->optimal_buffer_level_ms;
@@ -1535,6 +1556,11 @@
(optimal == 0) ? bandwidth / 8 : optimal * bandwidth / 1000;
rc->maximum_buffer_size =
(maximum == 0) ? bandwidth / 8 : maximum * bandwidth / 1000;
+
+ // Under a configuration change, where maximum_buffer_size may change,
+ // keep buffer level clipped to the maximum allowed buffer size.
+ rc->bits_off_target = VPXMIN(rc->bits_off_target, rc->maximum_buffer_size);
+ rc->buffer_level = VPXMIN(rc->buffer_level, rc->maximum_buffer_size);
}
#if CONFIG_VP9_HIGHBITDEPTH
@@ -1991,13 +2017,8 @@
}
cpi->encode_breakout = cpi->oxcf.encode_breakout;
- set_rc_buffer_sizes(rc, &cpi->oxcf);
+ vp9_set_rc_buffer_sizes(cpi);
- // Under a configuration change, where maximum_buffer_size may change,
- // keep buffer level clipped to the maximum allowed buffer size.
- rc->bits_off_target = VPXMIN(rc->bits_off_target, rc->maximum_buffer_size);
- rc->buffer_level = VPXMIN(rc->buffer_level, rc->maximum_buffer_size);
-
// Set up frame rate and related parameters rate control values.
vp9_new_framerate(cpi, cpi->framerate);
@@ -2057,23 +2078,7 @@
(int)cpi->oxcf.target_bandwidth);
}
- // Check for resetting the rc flags (rc_1_frame, rc_2_frame) if the
- // configuration change has a large change in avg_frame_bandwidth.
- // For SVC check for resetting based on spatial layer average bandwidth.
- // Also reset buffer level to optimal level.
- if (cm->current_video_frame > (unsigned int)cpi->svc.number_spatial_layers) {
- if (cpi->use_svc) {
- vp9_svc_check_reset_layer_rc_flag(cpi);
- } else {
- if (rc->avg_frame_bandwidth > (3 * rc->last_avg_frame_bandwidth >> 1) ||
- rc->avg_frame_bandwidth < (rc->last_avg_frame_bandwidth >> 1)) {
- rc->rc_1_frame = 0;
- rc->rc_2_frame = 0;
- rc->bits_off_target = rc->optimal_buffer_level;
- rc->buffer_level = rc->optimal_buffer_level;
- }
- }
- }
+ vp9_check_reset_rc_flag(cpi);
cpi->alt_ref_source = NULL;
rc->is_src_frame_alt_ref = 0;
--- a/vp9/encoder/vp9_encoder.h
+++ b/vp9/encoder/vp9_encoder.h
@@ -1000,6 +1000,14 @@
void vp9_set_svc(VP9_COMP *cpi, int use_svc);
+// Check for resetting the rc flags (rc_1_frame, rc_2_frame) if the
+// configuration change has a large change in avg_frame_bandwidth.
+// For SVC check for resetting based on spatial layer average bandwidth.
+// Also reset buffer level to optimal level.
+void vp9_check_reset_rc_flag(VP9_COMP *cpi);
+
+void vp9_set_rc_buffer_sizes(VP9_COMP *cpi);
+
static INLINE int stack_pop(int *stack, int stack_size) {
int idx;
const int r = stack[0];
--- a/vp9/encoder/vp9_ratectrl.c
+++ b/vp9/encoder/vp9_ratectrl.c
@@ -249,7 +249,7 @@
// way for CBR mode, for the buffering updates below. Look into removing one
// of these (i.e., bits_off_target).
// Update the buffer level before encoding with the per-frame-bandwidth,
-static void update_buffer_level_preencode(VP9_COMP *cpi) {
+void vp9_update_buffer_level_preencode(VP9_COMP *cpi) {
RATE_CONTROL *const rc = &cpi->rc;
rc->bits_off_target += rc->avg_frame_bandwidth;
// Clip the buffer level to the maximum specified buffer size.
@@ -2098,7 +2098,7 @@
vp9_cyclic_refresh_update_parameters(cpi);
}
-static int calc_pframe_target_size_one_pass_cbr(const VP9_COMP *cpi) {
+int vp9_calc_pframe_target_size_one_pass_cbr(const VP9_COMP *cpi) {
const VP9EncoderConfig *oxcf = &cpi->oxcf;
const RATE_CONTROL *rc = &cpi->rc;
const SVC *const svc = &cpi->svc;
@@ -2147,7 +2147,7 @@
return VPXMAX(min_frame_target, target);
}
-static int calc_iframe_target_size_one_pass_cbr(const VP9_COMP *cpi) {
+int vp9_calc_iframe_target_size_one_pass_cbr(const VP9_COMP *cpi) {
const RATE_CONTROL *rc = &cpi->rc;
const VP9EncoderConfig *oxcf = &cpi->oxcf;
const SVC *const svc = &cpi->svc;
@@ -2253,7 +2253,7 @@
cpi->ref_frame_flags &= (~VP9_LAST_FLAG & ~VP9_GOLD_FLAG & ~VP9_ALT_FLAG);
// Assumption here is that LAST_FRAME is being updated for a keyframe.
// Thus no change in update flags.
- target = calc_iframe_target_size_one_pass_cbr(cpi);
+ target = vp9_calc_iframe_target_size_one_pass_cbr(cpi);
}
} else {
cm->frame_type = INTER_FRAME;
@@ -2266,7 +2266,7 @@
(svc->spatial_layer_id == 0 && cm->current_video_frame > 0)
? 0
: svc->layer_context[svc->temporal_layer_id].is_key_frame;
- target = calc_pframe_target_size_one_pass_cbr(cpi);
+ target = vp9_calc_pframe_target_size_one_pass_cbr(cpi);
}
}
@@ -2275,7 +2275,7 @@
svc->layer_context[layer].is_key_frame == 1) {
cm->frame_type = KEY_FRAME;
cpi->ref_frame_flags &= (~VP9_LAST_FLAG & ~VP9_GOLD_FLAG & ~VP9_ALT_FLAG);
- target = calc_iframe_target_size_one_pass_cbr(cpi);
+ target = vp9_calc_iframe_target_size_one_pass_cbr(cpi);
}
// Set the buffer idx and refresh flags for key frames in simulcast mode.
// Note the buffer slot for long-term reference is set below (line 2255),
@@ -2360,7 +2360,7 @@
}
if (svc->set_intra_only_frame) {
set_intra_only_frame(cpi);
- target = calc_iframe_target_size_one_pass_cbr(cpi);
+ target = vp9_calc_iframe_target_size_one_pass_cbr(cpi);
}
// Any update/change of global cyclic refresh parameters (amount/delta-qp)
// should be done here, before the frame qp is selected.
@@ -2433,13 +2433,13 @@
vp9_cyclic_refresh_update_parameters(cpi);
if (frame_is_intra_only(cm))
- target = calc_iframe_target_size_one_pass_cbr(cpi);
+ target = vp9_calc_iframe_target_size_one_pass_cbr(cpi);
else
- target = calc_pframe_target_size_one_pass_cbr(cpi);
+ target = vp9_calc_pframe_target_size_one_pass_cbr(cpi);
vp9_rc_set_frame_target(cpi, target);
- if (cm->show_frame) update_buffer_level_preencode(cpi);
+ if (cm->show_frame) vp9_update_buffer_level_preencode(cpi);
if (cpi->oxcf.resize_mode == RESIZE_DYNAMIC)
cpi->resize_pending = vp9_resize_one_pass_cbr(cpi);
@@ -2742,7 +2742,7 @@
// Reset buffer level to optimal, update target size.
rc->buffer_level = rc->optimal_buffer_level;
rc->bits_off_target = rc->optimal_buffer_level;
- rc->this_frame_target = calc_pframe_target_size_one_pass_cbr(cpi);
+ rc->this_frame_target = vp9_calc_pframe_target_size_one_pass_cbr(cpi);
// Get the projected qindex, based on the scaled target frame size (scaled
// so target_bits_per_mb in vp9_rc_regulate_q will be correct target).
target_bits_per_frame = (resize_action >= 0)
--- a/vp9/encoder/vp9_ratectrl.h
+++ b/vp9/encoder/vp9_ratectrl.h
@@ -252,6 +252,9 @@
// encode_frame_to_data_rate() function.
void vp9_rc_get_one_pass_vbr_params(struct VP9_COMP *cpi);
void vp9_rc_get_one_pass_cbr_params(struct VP9_COMP *cpi);
+int vp9_calc_pframe_target_size_one_pass_cbr(const struct VP9_COMP *cpi);
+int vp9_calc_iframe_target_size_one_pass_cbr(const struct VP9_COMP *cpi);
+void vp9_update_buffer_level_preencode(struct VP9_COMP *cpi);
void vp9_rc_get_svc_params(struct VP9_COMP *cpi);
// Post encode update of the rate control parameters based
--- /dev/null
+++ b/vp9/ratectrl_rtc.cc
@@ -1,0 +1,173 @@
+/*
+ * 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 <new>
+
+#include "vp9/encoder/vp9_encoder.h"
+#include "vp9/encoder/vp9_picklpf.h"
+#include "vpx/vp8cx.h"
+#include "vpx/vpx_codec.h"
+
+namespace libvpx {
+
+std::unique_ptr<VP9RateControlRTC> VP9RateControlRTC::Create(
+ const VP9RateControlRtcConfig &cfg) {
+ std::unique_ptr<VP9RateControlRTC> rc_api(new (std::nothrow)
+ VP9RateControlRTC());
+ if (!rc_api) return nullptr;
+ rc_api->cpi_ = static_cast<VP9_COMP *>(vpx_memalign(32, sizeof(*cpi_)));
+ if (rc_api->cpi_ == nullptr) {
+ return nullptr;
+ }
+ rc_api->InitRateControl(cfg);
+ return rc_api;
+}
+
+void VP9RateControlRTC::InitRateControl(const VP9RateControlRtcConfig &rc_cfg) {
+ VP9_COMMON *cm = &cpi_->common;
+ VP9EncoderConfig *oxcf = &cpi_->oxcf;
+ RATE_CONTROL *const rc = &cpi_->rc;
+ cm->profile = PROFILE_0;
+ cm->bit_depth = VPX_BITS_8;
+ cm->show_frame = 1;
+ oxcf->rc_mode = VPX_CBR;
+ oxcf->pass = 0;
+ oxcf->aq_mode = NO_AQ;
+ oxcf->content = VP9E_CONTENT_DEFAULT;
+ oxcf->drop_frames_water_mark = 0;
+
+ UpdateRateControl(rc_cfg);
+
+ cpi_->use_svc = (cpi_->svc.number_spatial_layers > 1 ||
+ cpi_->svc.number_temporal_layers > 1)
+ ? 1
+ : 0;
+
+ rc->rc_1_frame = 0;
+ rc->rc_2_frame = 0;
+ vp9_rc_init_minq_luts();
+ vp9_rc_init(oxcf, 0, rc);
+ cpi_->sf.use_nonrd_pick_mode = 1;
+ cm->current_video_frame = 0;
+}
+
+void VP9RateControlRTC::UpdateRateControl(
+ const VP9RateControlRtcConfig &rc_cfg) {
+ VP9_COMMON *cm = &cpi_->common;
+ VP9EncoderConfig *oxcf = &cpi_->oxcf;
+ RATE_CONTROL *const rc = &cpi_->rc;
+
+ cm->width = rc_cfg.width;
+ cm->height = rc_cfg.height;
+ oxcf->width = rc_cfg.width;
+ oxcf->height = rc_cfg.height;
+ oxcf->worst_allowed_q = vp9_quantizer_to_qindex(rc_cfg.max_quantizer);
+ oxcf->best_allowed_q = vp9_quantizer_to_qindex(rc_cfg.min_quantizer);
+ rc->worst_quality = oxcf->worst_allowed_q;
+ rc->best_quality = oxcf->best_allowed_q;
+ oxcf->target_bandwidth = 1000 * rc_cfg.target_bandwidth;
+ oxcf->starting_buffer_level_ms = rc_cfg.buf_initial_sz;
+ oxcf->optimal_buffer_level_ms = rc_cfg.buf_optimal_sz;
+ oxcf->maximum_buffer_size_ms = rc_cfg.buf_sz;
+ oxcf->under_shoot_pct = rc_cfg.undershoot_pct;
+ oxcf->over_shoot_pct = rc_cfg.overshoot_pct;
+ oxcf->ss_number_layers = rc_cfg.ss_number_layers;
+ oxcf->ts_number_layers = rc_cfg.ts_number_layers;
+ oxcf->temporal_layering_mode = (VP9E_TEMPORAL_LAYERING_MODE)(
+ (rc_cfg.ts_number_layers > 1) ? rc_cfg.ts_number_layers : 0);
+
+ cpi_->oxcf.rc_max_intra_bitrate_pct = rc_cfg.max_intra_bitrate_pct;
+ cpi_->framerate = rc_cfg.framerate;
+ cpi_->svc.number_spatial_layers = rc_cfg.ss_number_layers;
+ cpi_->svc.number_temporal_layers = rc_cfg.ts_number_layers;
+
+ for (int sl = 0; sl < cpi_->svc.number_spatial_layers; ++sl) {
+ for (int tl = 0; tl < cpi_->svc.number_temporal_layers; ++tl) {
+ const int layer =
+ LAYER_IDS_TO_IDX(sl, tl, cpi_->svc.number_temporal_layers);
+ LAYER_CONTEXT *lc = &cpi_->svc.layer_context[layer];
+ RATE_CONTROL *const lrc = &lc->rc;
+ oxcf->layer_target_bitrate[layer] =
+ 1000 * rc_cfg.layer_target_bitrate[layer];
+ lrc->worst_quality =
+ vp9_quantizer_to_qindex(rc_cfg.max_quantizers[layer]);
+ lrc->best_quality = vp9_quantizer_to_qindex(rc_cfg.min_quantizers[layer]);
+ lc->scaling_factor_num = rc_cfg.scaling_factor_num[sl];
+ lc->scaling_factor_den = rc_cfg.scaling_factor_den[sl];
+ oxcf->ts_rate_decimator[tl] = rc_cfg.ts_rate_decimator[tl];
+ }
+ }
+ vp9_set_rc_buffer_sizes(cpi_);
+ vp9_new_framerate(cpi_, cpi_->framerate);
+ if (cpi_->svc.number_temporal_layers > 1) {
+ if (cm->current_video_frame == 0) vp9_init_layer_context(cpi_);
+ vp9_update_layer_context_change_config(cpi_,
+ (int)cpi_->oxcf.target_bandwidth);
+ }
+ vp9_check_reset_rc_flag(cpi_);
+}
+
+void VP9RateControlRTC::ComputeQP(const VP9FrameParamsQpRTC &frame_params) {
+ VP9_COMMON *const cm = &cpi_->common;
+ int width, height;
+ cpi_->svc.spatial_layer_id = frame_params.spatial_layer_id;
+ cpi_->svc.temporal_layer_id = frame_params.temporal_layer_id;
+ if (cpi_->svc.number_spatial_layers > 1) {
+ const int layer = LAYER_IDS_TO_IDX(cpi_->svc.spatial_layer_id,
+ cpi_->svc.temporal_layer_id,
+ cpi_->svc.number_temporal_layers);
+ LAYER_CONTEXT *lc = &cpi_->svc.layer_context[layer];
+ get_layer_resolution(cpi_->oxcf.width, cpi_->oxcf.height,
+ lc->scaling_factor_num, lc->scaling_factor_den, &width,
+ &height);
+ cm->width = width;
+ cm->height = height;
+ }
+ vp9_set_mb_mi(cm, cm->width, cm->height);
+ cm->frame_type = frame_params.frame_type;
+ cpi_->refresh_golden_frame = (cm->frame_type == KEY_FRAME) ? 1 : 0;
+ cpi_->sf.use_nonrd_pick_mode = 1;
+ if (cpi_->svc.number_spatial_layers == 1 &&
+ cpi_->svc.number_temporal_layers == 1) {
+ int target;
+ if (frame_is_intra_only(cm))
+ target = vp9_calc_iframe_target_size_one_pass_cbr(cpi_);
+ else
+ target = vp9_calc_pframe_target_size_one_pass_cbr(cpi_);
+ vp9_rc_set_frame_target(cpi_, target);
+ vp9_update_buffer_level_preencode(cpi_);
+ } else {
+ vp9_update_temporal_layer_framerate(cpi_);
+ vp9_restore_layer_context(cpi_);
+ vp9_rc_get_svc_params(cpi_);
+ }
+ int bottom_index, top_index;
+ cpi_->common.base_qindex =
+ vp9_rc_pick_q_and_bounds(cpi_, &bottom_index, &top_index);
+}
+
+int VP9RateControlRTC::GetQP() const { return cpi_->common.base_qindex; }
+
+int VP9RateControlRTC::GetLoopfilterLevel() const {
+ struct loopfilter *const lf = &cpi_->common.lf;
+ vp9_pick_filter_level(NULL, cpi_, LPF_PICK_FROM_Q);
+ return lf->filter_level;
+}
+
+void VP9RateControlRTC::PostEncodeUpdate(uint64_t encoded_frame_size) {
+ vp9_rc_postencode_update(cpi_, encoded_frame_size);
+ if (cpi_->svc.number_spatial_layers > 1 ||
+ cpi_->svc.number_temporal_layers > 1)
+ vp9_save_layer_context(cpi_);
+ cpi_->common.current_video_frame++;
+}
+
+} // namespace libvpx
--- /dev/null
+++ b/vp9/ratectrl_rtc.h
@@ -1,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#ifndef VPX_VP9_RATECTRL_RTC_H_
+#define VPX_VP9_RATECTRL_RTC_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "vp9/common/vp9_entropymode.h"
+#include "vp9/common/vp9_enums.h"
+#include "vp9/common/vp9_onyxc_int.h"
+#include "vp9/vp9_iface_common.h"
+#include "vp9/encoder/vp9_encoder.h"
+#include "vp9/encoder/vp9_firstpass.h"
+#include "vp9/vp9_cx_iface.h"
+#include "vpx_mem/vpx_mem.h"
+
+namespace libvpx {
+
+struct VP9RateControlRtcConfig {
+ int width;
+ int height;
+ // 0-63
+ int max_quantizer;
+ int min_quantizer;
+ int64_t target_bandwidth;
+ int64_t buf_initial_sz;
+ int64_t buf_optimal_sz;
+ int64_t buf_sz;
+ int undershoot_pct;
+ int overshoot_pct;
+ int max_intra_bitrate_pct;
+ double framerate;
+ // Number of spatial layers
+ int ss_number_layers;
+ // Number of temporal layers
+ int ts_number_layers;
+ int max_quantizers[VPX_MAX_LAYERS];
+ int min_quantizers[VPX_MAX_LAYERS];
+ int scaling_factor_num[VPX_SS_MAX_LAYERS];
+ int scaling_factor_den[VPX_SS_MAX_LAYERS];
+ int layer_target_bitrate[VPX_MAX_LAYERS];
+ int ts_rate_decimator[VPX_TS_MAX_LAYERS];
+};
+
+struct VP9FrameParamsQpRTC {
+ FRAME_TYPE frame_type;
+ int spatial_layer_id;
+ int temporal_layer_id;
+};
+
+// This interface allows using VP9 real-time rate control without initializing
+// the encoder. To use this interface, you need to link with libvp9rc.a.
+//
+// #include "vp9/ratectrl_rtc.h"
+// VP9RateControlRTC rc_api;
+// VP9RateControlRtcConfig cfg;
+// VP9FrameParamsQpRTC frame_params;
+//
+// YourFunctionToInitializeConfig(cfg);
+// rc_api.InitRateControl(cfg);
+// // start encoding
+// while (frame_to_encode) {
+// if (config_changed)
+// rc_api.UpdateRateControl(cfg);
+// YourFunctionToFillFrameParams(frame_params);
+// rc_api.ComputeQP(frame_params);
+// YourFunctionToUseQP(rc_api.GetQP());
+// YourFunctionToUseLoopfilter(rc_api.GetLoopfilterLevel());
+// // After encoding
+// rc_api.PostEncode(encoded_frame_size);
+// }
+class VP9RateControlRTC {
+ public:
+ static std::unique_ptr<VP9RateControlRTC> Create(
+ 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);
+ }
+ }
+ vpx_free(cpi_);
+ }
+ }
+
+ void UpdateRateControl(const VP9RateControlRtcConfig &rc_cfg);
+ // GetQP() needs to be called after ComputeQP() to get the latest QP
+ int GetQP() const;
+ int GetLoopfilterLevel() const;
+ void ComputeQP(const VP9FrameParamsQpRTC &frame_params);
+ // Feedback to rate control with the size of current encoded frame
+ void PostEncodeUpdate(uint64_t encoded_frame_size);
+
+ private:
+ VP9RateControlRTC() {}
+ void InitRateControl(const VP9RateControlRtcConfig &cfg);
+ VP9_COMP *cpi_;
+};
+
+} // namespace libvpx
+
+#endif // VPX_VP9_RATECTRL_RTC_H_