rtc: Add frame-dropper support to external RTC RC
frame_drop_thresh and max_consec_drop are input
to the frame dropper.
Test coverage added for single layer.
Move the reset of gf_frame_index to where the index
is updated, this resolves an out of array bounds for
external rc with frame droppiing.
Support for the full superframe_drop mode for
spatial SVC to be added.
Change-Id: I08c2d7da20e1b260fd3413d10d548340cd681b3a
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index ddb8054..689af2a 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -3173,12 +3173,6 @@
ppi->num_fp_contexts = av1_compute_num_fp_contexts(ppi, &cpi->oxcf);
}
- // Reset gf_frame_index in case it reaches MAX_STATIC_GF_GROUP_LENGTH for
- // real time encoding.
- if (is_one_pass_rt_params(cpi) &&
- cpi->gf_frame_index == MAX_STATIC_GF_GROUP_LENGTH)
- cpi->gf_frame_index = 0;
-
// Get the next visible frame. Invisible frames get packed with the next
// visible frame.
while (cpi_data.cx_data_sz >= ctx->cx_data_sz / 2 && !is_frame_visible) {
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 7846b7f..fed15eb 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -4431,7 +4431,16 @@
static AOM_INLINE void update_gf_group_index(AV1_COMP *cpi) {
// Increment the gf group index ready for the next frame.
- ++cpi->gf_frame_index;
+ if (is_one_pass_rt_params(cpi) &&
+ cpi->svc.spatial_layer_id == cpi->svc.number_spatial_layers - 1) {
+ ++cpi->gf_frame_index;
+ // Reset gf_frame_index in case it reaches MAX_STATIC_GF_GROUP_LENGTH
+ // for real time encoding.
+ if (cpi->gf_frame_index == MAX_STATIC_GF_GROUP_LENGTH)
+ cpi->gf_frame_index = 0;
+ } else {
+ ++cpi->gf_frame_index;
+ }
}
static void update_fb_of_context_type(const AV1_COMP *const cpi,
diff --git a/av1/ratectrl_rtc.cc b/av1/ratectrl_rtc.cc
index cfcea24..cd9597b 100644
--- a/av1/ratectrl_rtc.cc
+++ b/av1/ratectrl_rtc.cc
@@ -39,6 +39,8 @@
undershoot_pct = overshoot_pct = 50;
max_intra_bitrate_pct = 50;
max_inter_bitrate_pct = 0;
+ frame_drop_thresh = 0;
+ max_consec_drop = 0;
framerate = 30.0;
ss_number_layers = 1;
ts_number_layers = 1;
@@ -124,7 +126,8 @@
oxcf->pass = AOM_RC_ONE_PASS;
oxcf->q_cfg.aq_mode = rc_cfg.aq_mode ? CYCLIC_REFRESH_AQ : NO_AQ;
oxcf->tune_cfg.content = AOM_CONTENT_DEFAULT;
- oxcf->rc_cfg.drop_frames_water_mark = 0;
+ oxcf->rc_cfg.drop_frames_water_mark = rc_cfg.frame_drop_thresh;
+ rc->max_consec_drop = rc_cfg.max_consec_drop;
oxcf->tool_cfg.bit_depth = AOM_BITS_8;
oxcf->tool_cfg.superblock_size = AOM_SUPERBLOCK_SIZE_DYNAMIC;
oxcf->algo_cfg.loopfilter_control = LOOPFILTER_ALL;
@@ -185,6 +188,8 @@
oxcf->rc_cfg.maximum_buffer_size_ms = rc_cfg.buf_sz;
oxcf->rc_cfg.under_shoot_pct = rc_cfg.undershoot_pct;
oxcf->rc_cfg.over_shoot_pct = rc_cfg.overshoot_pct;
+ oxcf->rc_cfg.drop_frames_water_mark = rc_cfg.frame_drop_thresh;
+ rc->max_consec_drop = rc_cfg.max_consec_drop;
oxcf->rc_cfg.max_intra_bitrate_pct = rc_cfg.max_intra_bitrate_pct;
oxcf->rc_cfg.max_inter_bitrate_pct = rc_cfg.max_inter_bitrate_pct;
cpi_->framerate = rc_cfg.framerate;
@@ -226,7 +231,8 @@
return true;
}
-void AV1RateControlRTC::ComputeQP(const AV1FrameParamsRTC &frame_params) {
+FrameDropDecision AV1RateControlRTC::ComputeQP(
+ const AV1FrameParamsRTC &frame_params) {
AV1_COMMON *const cm = &cpi_->common;
int width, height;
GF_GROUP *const gf_group = &cpi_->ppi->gf_group;
@@ -292,14 +298,25 @@
}
}
av1_rc_set_frame_target(cpi_, target, cm->width, cm->height);
-
+ // Always drop for spatial enhancement layer if layer bandwidth is 0.
+ // Otherwise check for frame-dropping based on buffer level in
+ // av1_rc_drop_frame().
+ if ((cpi_->svc.spatial_layer_id > 0 &&
+ cpi_->oxcf.rc_cfg.target_bandwidth == 0) ||
+ av1_rc_drop_frame(cpi_)) {
+ cpi_->is_dropped_frame = true;
+ av1_rc_postencode_update_drop_frame(cpi_);
+ cpi_->frame_index_set.show_frame_count++;
+ cpi_->common.current_frame.frame_number++;
+ return FrameDropDecision::kDrop;
+ }
int bottom_index, top_index;
cpi_->common.quant_params.base_qindex =
av1_rc_pick_q_and_bounds(cpi_, cm->width, cm->height,
cpi_->gf_frame_index, &bottom_index, &top_index);
-
if (cpi_->oxcf.q_cfg.aq_mode == CYCLIC_REFRESH_AQ)
av1_cyclic_refresh_setup(cpi_);
+ return FrameDropDecision::kOk;
}
int AV1RateControlRTC::GetQP() const {
diff --git a/av1/ratectrl_rtc.h b/av1/ratectrl_rtc.h
index a8b2deb..48ed9aa 100644
--- a/av1/ratectrl_rtc.h
+++ b/av1/ratectrl_rtc.h
@@ -45,6 +45,8 @@
int overshoot_pct;
int max_intra_bitrate_pct;
int max_inter_bitrate_pct;
+ int frame_drop_thresh;
+ int max_consec_drop;
double framerate;
int layer_target_bitrate[kAV1MaxLayers];
int ts_rate_decimator[kAV1MaxTemporalLayers];
@@ -84,6 +86,11 @@
size_t delta_q_size;
};
+enum class FrameDropDecision {
+ kOk, // Frame is encoded.
+ kDrop, // Frame is dropped.
+};
+
class AV1RateControlRTC {
public:
static std::unique_ptr<AV1RateControlRTC> Create(
@@ -99,7 +106,11 @@
AV1CdefInfo GetCdefInfo() const;
// Returns the segmentation map used for cyclic refresh, based on 4x4 blocks.
bool GetSegmentationData(AV1SegmentationData *segmentation_data) const;
- void ComputeQP(const AV1FrameParamsRTC &frame_params);
+ // ComputeQP returns the QP if the frame is not dropped (kOk return),
+ // otherwise it returns kDrop and subsequent GetQP and PostEncodeUpdate
+ // are not to be called (av1_rc_postencode_update_drop_frame is already
+ // called via ComputeQP if drop is decided).
+ FrameDropDecision ComputeQP(const AV1FrameParamsRTC &frame_params);
// Feedback to rate control with the size of current encoded frame
void PostEncodeUpdate(uint64_t encoded_frame_size);
diff --git a/test/ratectrl_rtc_test.cc b/test/ratectrl_rtc_test.cc
index 04e90c8..92b7d39 100644
--- a/test/ratectrl_rtc_test.cc
+++ b/test/ratectrl_rtc_test.cc
@@ -36,7 +36,8 @@
RcInterfaceTest()
: EncoderTest(GET_PARAM(0)), aq_mode_(GET_PARAM(1)), key_interval_(3000),
encoder_exit_(false), layer_frame_cnt_(0), superframe_cnt_(0),
- dynamic_temporal_layers_(false), dynamic_spatial_layers_(false) {
+ dynamic_temporal_layers_(false), dynamic_spatial_layers_(false),
+ num_drops_(0), max_consec_drop_(0), frame_drop_thresh_(0) {
memset(&svc_params_, 0, sizeof(svc_params_));
memset(&layer_id_, 0, sizeof(layer_id_));
}
@@ -61,6 +62,7 @@
encoder->Control(AOME_SET_MAX_INTRA_BITRATE_PCT,
rc_cfg_.max_intra_bitrate_pct);
if (use_svc) encoder->Control(AV1E_SET_SVC_PARAMS, &svc_params_);
+ encoder->Control(AV1E_SET_MAX_CONSEC_FRAME_DROP_CBR, max_consec_drop_);
}
// SVC specific settings
if (use_svc) {
@@ -144,16 +146,19 @@
superframe_cnt_++;
int qp;
encoder->Control(AOME_GET_LAST_QUANTIZER, &qp);
- rc_api_->ComputeQP(frame_params_);
- ASSERT_EQ(rc_api_->GetQP(), qp);
- int encoder_lpf_level;
- encoder->Control(AOME_GET_LOOPFILTER_LEVEL, &encoder_lpf_level);
- aom::AV1LoopfilterLevel loopfilter_level = rc_api_->GetLoopfilterLevel();
- ASSERT_EQ(loopfilter_level.filter_level[0], encoder_lpf_level);
- aom::AV1CdefInfo cdef_level = rc_api_->GetCdefInfo();
- int cdef_y_strengths[16];
- encoder->Control(AV1E_GET_LUMA_CDEF_STRENGTH, cdef_y_strengths);
- ASSERT_EQ(cdef_level.cdef_strength_y, cdef_y_strengths[0]);
+ if (rc_api_->ComputeQP(frame_params_) == aom::FrameDropDecision::kOk) {
+ ASSERT_EQ(rc_api_->GetQP(), qp);
+ int encoder_lpf_level;
+ encoder->Control(AOME_GET_LOOPFILTER_LEVEL, &encoder_lpf_level);
+ aom::AV1LoopfilterLevel loopfilter_level = rc_api_->GetLoopfilterLevel();
+ ASSERT_EQ(loopfilter_level.filter_level[0], encoder_lpf_level);
+ aom::AV1CdefInfo cdef_level = rc_api_->GetCdefInfo();
+ int cdef_y_strengths[16];
+ encoder->Control(AV1E_GET_LUMA_CDEF_STRENGTH, cdef_y_strengths);
+ ASSERT_EQ(cdef_level.cdef_strength_y, cdef_y_strengths[0]);
+ } else {
+ num_drops_++;
+ }
}
void FramePktHook(const aom_codec_cx_pkt_t *pkt) override {
@@ -181,6 +186,27 @@
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
}
+ void RunOneLayerDropFramesCBR() {
+ key_interval_ = 10000;
+ max_consec_drop_ = 8;
+ frame_drop_thresh_ = 30;
+ SetConfig();
+ rc_cfg_.target_bandwidth = 100;
+ cfg_.rc_target_bitrate = 100;
+ rc_cfg_.max_quantizer = 50;
+ cfg_.rc_max_quantizer = 50;
+ rc_api_ = aom::AV1RateControlRTC::Create(rc_cfg_);
+ frame_params_.spatial_layer_id = 0;
+ frame_params_.temporal_layer_id = 0;
+
+ ::libaom_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30,
+ 1, 0, kNumFrames);
+
+ ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+ // Check that some frames were dropped, otherwise test has no value.
+ ASSERT_GE(num_drops_, 1);
+ }
+
void RunOneLayerPeriodicKey() {
key_interval_ = 100;
SetConfig();
@@ -270,6 +296,8 @@
rc_cfg_.max_quantizers[0] = 52;
rc_cfg_.min_quantizers[0] = 2;
rc_cfg_.aq_mode = aq_mode_;
+ rc_cfg_.frame_drop_thresh = frame_drop_thresh_;
+ rc_cfg_.max_consec_drop = max_consec_drop_;
// Encoder settings for ground truth.
cfg_.g_w = 640;
@@ -288,6 +316,7 @@
cfg_.rc_target_bitrate = 1000;
cfg_.kf_min_dist = key_interval_;
cfg_.kf_max_dist = key_interval_;
+ cfg_.rc_dropframe_thresh = frame_drop_thresh_;
}
void SetConfigSvc(int number_spatial_layers, int number_temporal_layers) {
@@ -427,10 +456,15 @@
int superframe_cnt_;
bool dynamic_temporal_layers_;
bool dynamic_spatial_layers_;
+ int num_drops_;
+ int max_consec_drop_;
+ int frame_drop_thresh_;
};
TEST_P(RcInterfaceTest, OneLayer) { RunOneLayer(); }
+TEST_P(RcInterfaceTest, OneLayerDropFramesCBR) { RunOneLayerDropFramesCBR(); }
+
TEST_P(RcInterfaceTest, OneLayerPeriodicKey) { RunOneLayerPeriodicKey(); }
TEST_P(RcInterfaceTest, Svc) { RunSvc(); }