summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/vp9_ext_ratectrl_test.cc81
-rw-r--r--vp9/encoder/vp9_encoder.c26
-rw-r--r--vp9/encoder/vp9_ext_ratectrl.c29
-rw-r--r--vp9/encoder/vp9_ext_ratectrl.h7
-rw-r--r--vp9/encoder/vp9_rd.c11
-rw-r--r--vpx/vpx_ext_ratectrl.h34
6 files changed, 186 insertions, 2 deletions
diff --git a/test/vp9_ext_ratectrl_test.cc b/test/vp9_ext_ratectrl_test.cc
index 16e3248f7..2bfa6281d 100644
--- a/test/vp9_ext_ratectrl_test.cc
+++ b/test/vp9_ext_ratectrl_test.cc
@@ -41,6 +41,7 @@ constexpr int kDefaultMaxGfInterval = 16;
constexpr int kReadMinGfInterval = 5;
constexpr int kReadMaxGfInterval = 13;
const char kTestFileName[] = "bus_352x288_420_f20_b8.yuv";
+const double kPsnrThreshold = 30.50;
struct ToyRateCtrl {
int magic_number;
@@ -642,6 +643,19 @@ vpx_rc_status_t rc_update_encodeframe_result_gop_short(
return VPX_RC_OK;
}
+vpx_rc_status_t rc_get_default_frame_rdmult(
+ vpx_rc_model_t rate_ctrl_model,
+ const vpx_rc_encodeframe_info_t *encode_frame_info, int *rdmult) {
+ const ToyRateCtrl *toy_rate_ctrl =
+ static_cast<ToyRateCtrl *>(rate_ctrl_model);
+ EXPECT_EQ(toy_rate_ctrl->magic_number, kModelMagicNumber);
+ EXPECT_LT(encode_frame_info->show_index, kFrameNumGOPShort);
+ EXPECT_EQ(encode_frame_info->coding_index, toy_rate_ctrl->coding_index);
+
+ *rdmult = VPX_DEFAULT_RDMULT;
+ return VPX_RC_OK;
+}
+
vpx_rc_status_t rc_delete_model(vpx_rc_model_t rate_ctrl_model) {
ToyRateCtrl *toy_rate_ctrl = static_cast<ToyRateCtrl *>(rate_ctrl_model);
EXPECT_EQ(toy_rate_ctrl->magic_number, kModelMagicNumber);
@@ -880,4 +894,71 @@ TEST_F(ExtRateCtrlTestGOPShortNoARF, EncodeTest) {
ASSERT_NO_FATAL_FAILURE(RunLoop(video.get()));
}
+class ExtRateCtrlTestRdmult : public ::libvpx_test::EncoderTest,
+ public ::testing::Test {
+ protected:
+ ExtRateCtrlTestRdmult() : EncoderTest(&::libvpx_test::kVP9) {}
+
+ ~ExtRateCtrlTestRdmult() override = default;
+
+ void SetUp() override {
+ InitializeConfig();
+ SetMode(::libvpx_test::kTwoPassGood);
+ }
+
+ void BeginPassHook(unsigned int) override {
+ psnr_ = 0.0;
+ nframes_ = 0;
+ }
+
+ void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) override {
+ psnr_ += pkt->data.psnr.psnr[0];
+ nframes_++;
+ }
+
+ void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
+ ::libvpx_test::Encoder *encoder) override {
+ if (video->frame() == 0) {
+ vpx_rc_funcs_t rc_funcs;
+ rc_funcs.rc_type = VPX_RC_GOP_QP_RDMULT;
+ rc_funcs.create_model = rc_create_model_gop_short;
+ rc_funcs.send_firstpass_stats = rc_send_firstpass_stats_gop_short;
+ rc_funcs.get_encodeframe_decision = rc_get_encodeframe_decision_gop_short;
+ rc_funcs.get_gop_decision = rc_get_gop_decision_short;
+ rc_funcs.update_encodeframe_result =
+ rc_update_encodeframe_result_gop_short;
+ rc_funcs.get_frame_rdmult = rc_get_default_frame_rdmult;
+ rc_funcs.delete_model = rc_delete_model;
+ rc_funcs.priv = reinterpret_cast<void *>(PrivMagicNumber);
+ encoder->Control(VP9E_SET_EXTERNAL_RATE_CONTROL, &rc_funcs);
+ }
+ }
+
+ double GetAveragePsnr() const {
+ if (nframes_) return psnr_ / nframes_;
+ return 0.0;
+ }
+
+ private:
+ double psnr_;
+ unsigned int nframes_;
+};
+
+TEST_F(ExtRateCtrlTestRdmult, DefaultRdmult) {
+ cfg_.rc_target_bitrate = 500;
+ cfg_.g_lag_in_frames = kMaxLagInFrames - 1;
+ cfg_.rc_end_usage = VPX_VBR;
+ init_flags_ = VPX_CODEC_USE_PSNR;
+
+ std::unique_ptr<libvpx_test::VideoSource> video;
+ video.reset(new (std::nothrow) libvpx_test::YUVVideoSource(
+ kTestFileName, VPX_IMG_FMT_I420, 352, 288, 30, 1, 0, kFrameNumGOPShort));
+
+ ASSERT_NE(video, nullptr);
+ ASSERT_NO_FATAL_FAILURE(RunLoop(video.get()));
+
+ const double psnr = GetAveragePsnr();
+ EXPECT_GT(psnr, kPsnrThreshold);
+}
+
} // namespace
diff --git a/vp9/encoder/vp9_encoder.c b/vp9/encoder/vp9_encoder.c
index 87c5d7b67..5cfd846dd 100644
--- a/vp9/encoder/vp9_encoder.c
+++ b/vp9/encoder/vp9_encoder.c
@@ -5515,6 +5515,32 @@ static void encode_frame_to_data_rate(
save_encode_params(cpi);
}
#endif
+ if (cpi->ext_ratectrl.ready &&
+ (cpi->ext_ratectrl.funcs.rc_type & VPX_RC_RDMULT) != 0) {
+ vpx_codec_err_t codec_status;
+ const GF_GROUP *gf_group = &cpi->twopass.gf_group;
+ FRAME_UPDATE_TYPE update_type = gf_group->update_type[gf_group->index];
+ const int ref_frame_flags = get_ref_frame_flags(cpi);
+ RefCntBuffer *ref_frame_bufs[MAX_INTER_REF_FRAMES];
+ const RefCntBuffer *curr_frame_buf = get_ref_cnt_buffer(cm, cm->new_fb_idx);
+ // index 0 of a gf group is always KEY/OVERLAY/GOLDEN.
+ // index 1 refers to the first encoding frame in a gf group.
+ // Therefore if it is ARF_UPDATE, it means this gf group uses alt ref.
+ // See function define_gf_group_structure().
+ const int use_alt_ref = gf_group->update_type[1] == ARF_UPDATE;
+ int ext_rdmult = VPX_DEFAULT_RDMULT;
+ get_ref_frame_bufs(cpi, ref_frame_bufs);
+ codec_status = vp9_extrc_get_frame_rdmult(
+ &cpi->ext_ratectrl, curr_frame_buf->frame_index,
+ cm->current_frame_coding_index, gf_group->index, update_type,
+ gf_group->gf_group_size, use_alt_ref, ref_frame_bufs, ref_frame_flags,
+ &ext_rdmult);
+ if (codec_status != VPX_CODEC_OK) {
+ vpx_internal_error(&cm->error, codec_status,
+ "vp9_extrc_get_frame_rdmult() failed");
+ }
+ cpi->ext_ratectrl.ext_rdmult = ext_rdmult;
+ }
if (cpi->sf.recode_loop == DISALLOW_RECODE) {
if (!encode_without_recode_loop(cpi, size, dest)) return;
diff --git a/vp9/encoder/vp9_ext_ratectrl.c b/vp9/encoder/vp9_ext_ratectrl.c
index b4ee574ff..1d440442b 100644
--- a/vp9/encoder/vp9_ext_ratectrl.c
+++ b/vp9/encoder/vp9_ext_ratectrl.c
@@ -230,3 +230,32 @@ vpx_codec_err_t vp9_extrc_get_gop_decision(
}
return VPX_CODEC_OK;
}
+
+vpx_codec_err_t vp9_extrc_get_frame_rdmult(
+ EXT_RATECTRL *ext_ratectrl, int show_index, int coding_index, int gop_index,
+ FRAME_UPDATE_TYPE update_type, int gop_size, int use_alt_ref,
+ RefCntBuffer *ref_frame_bufs[MAX_INTER_REF_FRAMES], int ref_frame_flags,
+ int *rdmult) {
+ vpx_rc_status_t rc_status;
+ vpx_rc_encodeframe_info_t encode_frame_info;
+ if (ext_ratectrl == NULL || !ext_ratectrl->ready ||
+ (ext_ratectrl->funcs.rc_type & VPX_RC_RDMULT) == 0) {
+ return VPX_CODEC_INVALID_PARAM;
+ }
+ encode_frame_info.show_index = show_index;
+ encode_frame_info.coding_index = coding_index;
+ encode_frame_info.gop_index = gop_index;
+ encode_frame_info.frame_type = extrc_get_frame_type(update_type);
+ encode_frame_info.gop_size = gop_size;
+ encode_frame_info.use_alt_ref = use_alt_ref;
+
+ vp9_get_ref_frame_info(update_type, ref_frame_flags, ref_frame_bufs,
+ encode_frame_info.ref_frame_coding_indexes,
+ encode_frame_info.ref_frame_valid_list);
+ rc_status = ext_ratectrl->funcs.get_frame_rdmult(ext_ratectrl->model,
+ &encode_frame_info, rdmult);
+ if (rc_status == VPX_RC_ERROR) {
+ return VPX_CODEC_ERROR;
+ }
+ return VPX_CODEC_OK;
+}
diff --git a/vp9/encoder/vp9_ext_ratectrl.h b/vp9/encoder/vp9_ext_ratectrl.h
index b8f3d0c83..7c3875883 100644
--- a/vp9/encoder/vp9_ext_ratectrl.h
+++ b/vp9/encoder/vp9_ext_ratectrl.h
@@ -16,6 +16,7 @@
typedef struct EXT_RATECTRL {
int ready;
+ int ext_rdmult;
vpx_rc_model_t model;
vpx_rc_funcs_t funcs;
vpx_rc_config_t ratectrl_config;
@@ -49,4 +50,10 @@ vpx_codec_err_t vp9_extrc_get_gop_decision(
EXT_RATECTRL *ext_ratectrl, const vpx_rc_gop_info_t *const gop_info,
vpx_rc_gop_decision_t *gop_decision);
+vpx_codec_err_t vp9_extrc_get_frame_rdmult(
+ EXT_RATECTRL *ext_ratectrl, int show_index, int coding_index, int gop_index,
+ FRAME_UPDATE_TYPE update_type, int gop_size, int use_alt_ref,
+ RefCntBuffer *ref_frame_bufs[MAX_INTER_REF_FRAMES], int ref_frame_flags,
+ int *rdmult);
+
#endif // VPX_VP9_ENCODER_VP9_EXT_RATECTRL_H_
diff --git a/vp9/encoder/vp9_rd.c b/vp9/encoder/vp9_rd.c
index 28f992f4b..58dd75b44 100644
--- a/vp9/encoder/vp9_rd.c
+++ b/vp9/encoder/vp9_rd.c
@@ -244,6 +244,12 @@ int vp9_compute_rd_mult_based_on_qindex(const VP9_COMP *cpi, int qindex) {
// largest dc_quant is 21387, therefore rdmult should fit in int32_t
int rdmult = q * q;
+ if (cpi->ext_ratectrl.ready &&
+ (cpi->ext_ratectrl.funcs.rc_type & VPX_RC_RDMULT) != 0 &&
+ cpi->ext_ratectrl.ext_rdmult != VPX_DEFAULT_RDMULT) {
+ return cpi->ext_ratectrl.ext_rdmult;
+ }
+
// Make sure this function is floating point safe.
vpx_clear_system_state();
@@ -287,6 +293,11 @@ static int modulate_rdmult(const VP9_COMP *cpi, int rdmult) {
int vp9_compute_rd_mult(const VP9_COMP *cpi, int qindex) {
int rdmult = vp9_compute_rd_mult_based_on_qindex(cpi, qindex);
+ if (cpi->ext_ratectrl.ready &&
+ (cpi->ext_ratectrl.funcs.rc_type & VPX_RC_RDMULT) != 0 &&
+ cpi->ext_ratectrl.ext_rdmult != VPX_DEFAULT_RDMULT) {
+ return cpi->ext_ratectrl.ext_rdmult;
+ }
return modulate_rdmult(cpi, rdmult);
}
diff --git a/vpx/vpx_ext_ratectrl.h b/vpx/vpx_ext_ratectrl.h
index 95b883413..3c5fc8cfc 100644
--- a/vpx/vpx_ext_ratectrl.h
+++ b/vpx/vpx_ext_ratectrl.h
@@ -25,20 +25,26 @@ extern "C" {
* types, removing or reassigning enums, adding/removing/rearranging
* fields to structures.
*/
-#define VPX_EXT_RATECTRL_ABI_VERSION (5)
+#define VPX_EXT_RATECTRL_ABI_VERSION (6)
/*!\brief The control type of the inference API.
* In VPX_RC_QP mode, the external rate control model determines the
* quantization parameter (QP) for each frame.
* In VPX_RC_GOP mode, the external rate control model determines the
* group of picture (GOP) of the video sequence.
+ * In VPX_RC_RDMULT mode, the external rate control model determines the
+ * rate-distortion multiplier (rdmult) for the current frame.
* In VPX_RC_GOP_QP mode, the external rate control model determines
* both the QP and the GOP.
+ * In VPX_RC_GOP_QP_RDMULT mode, the external rate control model determines
+ * the QP, GOP and the rdmult.
*/
typedef enum vpx_rc_type {
VPX_RC_QP = 1 << 0,
VPX_RC_GOP = 1 << 1,
- VPX_RC_GOP_QP = VPX_RC_QP | VPX_RC_GOP
+ VPX_RC_RDMULT = 1 << 2,
+ VPX_RC_GOP_QP = VPX_RC_QP | VPX_RC_GOP,
+ VPX_RC_GOP_QP_RDMULT = VPX_RC_QP | VPX_RC_GOP | VPX_RC_RDMULT
} vpx_rc_type_t;
/*!\brief Abstract rate control model handler
@@ -55,6 +61,13 @@ typedef void *vpx_rc_model_t;
*/
#define VPX_DEFAULT_Q -1
+/*!\brief A reserved value for the rdmult.
+ * If the external rate control model returns this value,
+ * the encoder will use the default rdmult selected by libvpx's rate control
+ * system.
+ */
+#define VPX_DEFAULT_RDMULT -1
+
/*!\brief Encode frame decision made by the external rate control model
*
* The encoder will receive the decision from the external rate control model
@@ -432,6 +445,19 @@ typedef vpx_rc_status_t (*vpx_rc_get_gop_decision_cb_fn_t)(
vpx_rc_model_t rate_ctrl_model, const vpx_rc_gop_info_t *gop_info,
vpx_rc_gop_decision_t *gop_decision);
+/*!\brief Get the frame rdmult from the external rate control model.
+ *
+ * This callback is invoked by the encoder to get rdmult from
+ * the external rate control model.
+ *
+ * \param[in] rate_ctrl_model rate control model
+ * \param[in] frame_info information collected from the encoder
+ * \param[out] rdmult frame rate-distortion multiplier from the model
+ */
+typedef vpx_rc_status_t (*vpx_rc_get_frame_rdmult_cb_fn_t)(
+ vpx_rc_model_t rate_ctrl_model, const vpx_rc_encodeframe_info_t *frame_info,
+ int *rdmult);
+
/*!\brief Delete the external rate control model callback prototype
*
* This callback is invoked by the encoder to delete the external rate control
@@ -474,6 +500,10 @@ typedef struct vpx_rc_funcs {
*/
vpx_rc_get_gop_decision_cb_fn_t get_gop_decision;
/*!
+ * Get rdmult for the frame from the external rate control model.
+ */
+ vpx_rc_get_frame_rdmult_cb_fn_t get_frame_rdmult;
+ /*!
* Delete the external rate control model.
*/
vpx_rc_delete_model_cb_fn_t delete_model;