Files
android_external_libvpx/test/vp9_ext_ratectrl_test.cc
Cheng Chen 557368a8fa L2E: let external rate control pass in a max frame size
And allow the frame to recode when the frame size is larger
than the input max frame size.

If the max frame size is not specified, let vp9 decide whether
to recode.  The recode follows the vp9's current recoding mechanism.

The rate control api will return the new qindex back to the
external model.

Change-Id: I796fbf713ad50a5b413b0e2501583b565ed2343f
2021-02-03 11:29:06 -08:00

202 lines
7.4 KiB
C++

/*
* 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 <new>
#include "test/codec_factory.h"
#include "test/encode_test_driver.h"
#include "test/util.h"
#include "test/yuv_video_source.h"
#include "third_party/googletest/src/include/gtest/gtest.h"
#include "vpx/vpx_ext_ratectrl.h"
namespace {
constexpr int kModelMagicNumber = 51396;
constexpr unsigned int PrivMagicNumber = 5566;
constexpr int kFrameNum = 5;
constexpr int kLosslessCodingIndex = 2;
struct ToyRateCtrl {
int magic_number;
int coding_index;
};
vpx_rc_status_t rc_create_model(void *priv,
const vpx_rc_config_t *ratectrl_config,
vpx_rc_model_t *rate_ctrl_model_pt) {
ToyRateCtrl *toy_rate_ctrl = new (std::nothrow) ToyRateCtrl;
EXPECT_NE(toy_rate_ctrl, nullptr);
toy_rate_ctrl->magic_number = kModelMagicNumber;
toy_rate_ctrl->coding_index = -1;
*rate_ctrl_model_pt = toy_rate_ctrl;
EXPECT_EQ(priv, reinterpret_cast<void *>(PrivMagicNumber));
EXPECT_EQ(ratectrl_config->frame_width, 352);
EXPECT_EQ(ratectrl_config->frame_height, 288);
EXPECT_EQ(ratectrl_config->show_frame_count, kFrameNum);
EXPECT_EQ(ratectrl_config->target_bitrate_kbps, 24000);
EXPECT_EQ(ratectrl_config->frame_rate_num, 30);
EXPECT_EQ(ratectrl_config->frame_rate_den, 1);
return VPX_RC_OK;
}
vpx_rc_status_t rc_send_firstpass_stats(
vpx_rc_model_t rate_ctrl_model,
const vpx_rc_firstpass_stats_t *first_pass_stats) {
const ToyRateCtrl *toy_rate_ctrl =
static_cast<ToyRateCtrl *>(rate_ctrl_model);
EXPECT_EQ(toy_rate_ctrl->magic_number, kModelMagicNumber);
EXPECT_EQ(first_pass_stats->num_frames, kFrameNum);
for (int i = 0; i < first_pass_stats->num_frames; ++i) {
EXPECT_DOUBLE_EQ(first_pass_stats->frame_stats[i].frame, i);
}
return VPX_RC_OK;
}
vpx_rc_status_t rc_get_encodeframe_decision(
vpx_rc_model_t rate_ctrl_model,
const vpx_rc_encodeframe_info_t *encode_frame_info,
vpx_rc_encodeframe_decision_t *frame_decision) {
ToyRateCtrl *toy_rate_ctrl = static_cast<ToyRateCtrl *>(rate_ctrl_model);
toy_rate_ctrl->coding_index += 1;
EXPECT_EQ(toy_rate_ctrl->magic_number, kModelMagicNumber);
EXPECT_LT(encode_frame_info->show_index, kFrameNum);
EXPECT_EQ(encode_frame_info->coding_index, toy_rate_ctrl->coding_index);
if (encode_frame_info->coding_index == 0) {
EXPECT_EQ(encode_frame_info->show_index, 0);
EXPECT_EQ(encode_frame_info->gop_index, 0);
EXPECT_EQ(encode_frame_info->frame_type, 0 /*kFrameTypeKey*/);
EXPECT_EQ(encode_frame_info->ref_frame_valid_list[0],
0); // kRefFrameTypeLast
EXPECT_EQ(encode_frame_info->ref_frame_valid_list[1],
0); // kRefFrameTypePast
EXPECT_EQ(encode_frame_info->ref_frame_valid_list[2],
0); // kRefFrameTypeFuture
}
if (encode_frame_info->coding_index == 1) {
EXPECT_EQ(encode_frame_info->show_index, 4);
EXPECT_EQ(encode_frame_info->gop_index, 1);
EXPECT_EQ(encode_frame_info->frame_type, 2 /*kFrameTypeAltRef*/);
EXPECT_EQ(encode_frame_info->ref_frame_valid_list[0],
1); // kRefFrameTypeLast
EXPECT_EQ(encode_frame_info->ref_frame_valid_list[1],
0); // kRefFrameTypePast
EXPECT_EQ(encode_frame_info->ref_frame_valid_list[2],
0); // kRefFrameTypeFuture
EXPECT_EQ(encode_frame_info->ref_frame_coding_indexes[0],
0); // kRefFrameTypeLast
}
if (encode_frame_info->coding_index >= 2 &&
encode_frame_info->coding_index < 5) {
// In the first group of pictures, coding_index and gop_index are equal.
EXPECT_EQ(encode_frame_info->gop_index, encode_frame_info->coding_index);
EXPECT_EQ(encode_frame_info->frame_type, 1 /*kFrameTypeInter*/);
}
if (encode_frame_info->coding_index == 5) {
EXPECT_EQ(encode_frame_info->show_index, 4);
EXPECT_EQ(encode_frame_info->gop_index, 0);
EXPECT_EQ(encode_frame_info->frame_type, 3 /*kFrameTypeOverlay*/);
EXPECT_EQ(encode_frame_info->ref_frame_valid_list[0],
1); // kRefFrameTypeLast
EXPECT_EQ(encode_frame_info->ref_frame_valid_list[1],
1); // kRefFrameTypePast
EXPECT_EQ(encode_frame_info->ref_frame_valid_list[2],
1); // kRefFrameTypeFuture
EXPECT_EQ(encode_frame_info->ref_frame_coding_indexes[0],
4); // kRefFrameTypeLast
EXPECT_EQ(encode_frame_info->ref_frame_coding_indexes[1],
0); // kRefFrameTypePast
EXPECT_EQ(encode_frame_info->ref_frame_coding_indexes[2],
1); // kRefFrameTypeFuture
}
if (encode_frame_info->coding_index == kLosslessCodingIndex) {
// We should get sse == 0 at rc_update_encodeframe_result()
frame_decision->q_index = 0;
} else {
frame_decision->q_index = 100;
}
frame_decision->max_frame_size = 0;
return VPX_RC_OK;
}
vpx_rc_status_t rc_update_encodeframe_result(
vpx_rc_model_t rate_ctrl_model,
const vpx_rc_encodeframe_result_t *encode_frame_result) {
const ToyRateCtrl *toy_rate_ctrl =
static_cast<ToyRateCtrl *>(rate_ctrl_model);
EXPECT_EQ(toy_rate_ctrl->magic_number, kModelMagicNumber);
const int64_t ref_pixel_count = 352 * 288 * 3 / 2;
EXPECT_EQ(encode_frame_result->pixel_count, ref_pixel_count);
if (toy_rate_ctrl->coding_index == kLosslessCodingIndex) {
EXPECT_EQ(encode_frame_result->sse, 0);
}
if (toy_rate_ctrl->coding_index == kLosslessCodingIndex) {
EXPECT_EQ(encode_frame_result->actual_encoding_qindex, 0);
} else {
EXPECT_EQ(encode_frame_result->actual_encoding_qindex, 100);
}
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);
delete toy_rate_ctrl;
return VPX_RC_OK;
}
class ExtRateCtrlTest : public ::libvpx_test::EncoderTest,
public ::testing::Test {
protected:
ExtRateCtrlTest() : EncoderTest(&::libvpx_test::kVP9) {}
~ExtRateCtrlTest() override = default;
void SetUp() override {
InitializeConfig();
SetMode(::libvpx_test::kTwoPassGood);
}
void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
::libvpx_test::Encoder *encoder) override {
if (video->frame() == 0) {
vpx_rc_funcs_t rc_funcs;
rc_funcs.create_model = rc_create_model;
rc_funcs.send_firstpass_stats = rc_send_firstpass_stats;
rc_funcs.get_encodeframe_decision = rc_get_encodeframe_decision;
rc_funcs.update_encodeframe_result = rc_update_encodeframe_result;
rc_funcs.delete_model = rc_delete_model;
rc_funcs.priv = reinterpret_cast<void *>(PrivMagicNumber);
encoder->Control(VP9E_SET_EXTERNAL_RATE_CONTROL, &rc_funcs);
}
}
};
TEST_F(ExtRateCtrlTest, EncodeTest) {
cfg_.rc_target_bitrate = 24000;
std::unique_ptr<libvpx_test::VideoSource> video;
video.reset(new (std::nothrow) libvpx_test::YUVVideoSource(
"bus_352x288_420_f20_b8.yuv", VPX_IMG_FMT_I420, 352, 288, 30, 1, 0,
kFrameNum));
ASSERT_NE(video.get(), nullptr);
ASSERT_NO_FATAL_FAILURE(RunLoop(video.get()));
}
} // namespace