blob: 7bbdc88df2f454cc27fdcbb9f730c1446fe71a91 [file] [log] [blame]
/*
* Copyright (c) 2022, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#include <array>
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <fstream>
#include <memory>
#include <numeric>
#include <string>
#include <vector>
#include "av1/encoder/encoder.h"
#include "av1/qmode_rc/ducky_encode.h"
#include "av1/qmode_rc/ratectrl_qmode.h"
#include "av1/qmode_rc/ratectrl_qmode_interface.h"
#include "test/video_source.h"
#include "third_party/googletest/src/googlemock/include/gmock/gmock.h"
#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
namespace aom {
constexpr int kMaxRefFrames = 7;
TEST(DuckyEncodeTest, ComputeFirstPassStats) {
aom_rational_t frame_rate = { 30, 1 };
VideoInfo video_info = { 352, 288,
frame_rate, AOM_IMG_FMT_I420,
1, "bus_352x288_420_f20_b8.yuv" };
video_info.file_path =
libaom_test::GetDataPath() + "/" + video_info.file_path;
DuckyEncode ducky_encode(video_info, BLOCK_64X64, kMaxRefFrames, 3, 128);
std::vector<FIRSTPASS_STATS> frame_stats =
ducky_encode.ComputeFirstPassStats();
EXPECT_EQ(frame_stats.size(), static_cast<size_t>(video_info.frame_count));
for (size_t i = 0; i < frame_stats.size(); ++i) {
// FIRSTPASS_STATS's first element is frame
EXPECT_EQ(frame_stats[i].frame, i);
}
}
TEST(DuckyEncodeTest, EncodeFrame) {
aom_rational_t frame_rate = { 30, 1 };
VideoInfo video_info = { 352, 288,
frame_rate, AOM_IMG_FMT_I420,
17, "bus_352x288_420_f20_b8.yuv" };
video_info.file_path =
libaom_test::GetDataPath() + "/" + video_info.file_path;
DuckyEncode ducky_encode(video_info, BLOCK_64X64, kMaxRefFrames, 3, 128);
std::vector<FIRSTPASS_STATS> frame_stats =
ducky_encode.ComputeFirstPassStats();
ducky_encode.StartEncode(frame_stats);
// We set coding_frame_count to a arbitrary number that smaller than
// 17 here.
// TODO(angiebird): Set coding_frame_count properly, once the DuckyEncode can
// provide proper information.
int coding_frame_count = 5;
EncodeFrameDecision decision = { aom::EncodeFrameMode::kNone,
aom::EncodeGopMode::kNone,
{} };
for (int i = 0; i < coding_frame_count; ++i) {
ducky_encode.AllocateBitstreamBuffer(video_info);
EncodeFrameResult encode_frame_result = ducky_encode.EncodeFrame(decision);
}
ducky_encode.EndEncode();
}
TEST(DuckyEncodeTest, EncodeFrameWithQindex) {
aom_rational_t frame_rate = { 30, 1 };
VideoInfo video_info = { 352, 288,
frame_rate, AOM_IMG_FMT_I420,
17, "bus_352x288_420_f20_b8.yuv" };
video_info.file_path =
libaom_test::GetDataPath() + "/" + video_info.file_path;
DuckyEncode ducky_encode(video_info, BLOCK_64X64, kMaxRefFrames, 3, 128);
std::vector<FIRSTPASS_STATS> frame_stats =
ducky_encode.ComputeFirstPassStats();
ducky_encode.StartEncode(frame_stats);
// We set coding_frame_count to a arbitrary number that smaller than
// 17 here.
// TODO(angiebird): Set coding_frame_count properly, once the DuckyEncode can
// provide proper information.
int coding_frame_count = 5;
int q_index = 0;
EncodeFrameDecision decision = { aom::EncodeFrameMode::kQindex,
aom::EncodeGopMode::kNone,
{ q_index, -1, {}, {} } };
for (int i = 0; i < coding_frame_count; ++i) {
ducky_encode.AllocateBitstreamBuffer(video_info);
EncodeFrameResult encode_frame_result = ducky_encode.EncodeFrame(decision);
EXPECT_EQ(encode_frame_result.dist, 0);
}
ducky_encode.EndEncode();
}
TEST(DuckyEncodeRCTest, EncodeVideoWithRC) {
aom_rational_t frame_rate = { 30, 1 };
const int frame_number = 35;
const int frame_width = 352;
const int frame_height = 288;
VideoInfo video_info = { frame_width, frame_height,
frame_rate, AOM_IMG_FMT_I420,
frame_number, "bus_352x288_420_f20_b8.yuv" };
video_info.file_path =
libaom_test::GetDataPath() + "/" + video_info.file_path;
DuckyEncode ducky_encode(video_info, BLOCK_64X64, kMaxRefFrames, 3, 128);
AV1RateControlQMode qmode_rc;
RateControlParam rc_param = {};
rc_param.max_gop_show_frame_count = 16;
rc_param.min_gop_show_frame_count = 4;
rc_param.ref_frame_table_size = 5;
rc_param.max_ref_frames = 3;
rc_param.base_q_index = 45;
rc_param.max_distinct_q_indices_per_frame = 8;
rc_param.max_distinct_lambda_scales_per_frame = 1;
rc_param.frame_width = frame_width;
rc_param.frame_height = frame_height;
rc_param.tpl_pass_count = TplPassCount::kOneTplPass;
rc_param.tpl_pass_index = 0;
const Status status = qmode_rc.SetRcParam(rc_param);
ASSERT_TRUE(status.ok());
FirstpassInfo firstpass_info;
firstpass_info.stats_list = ducky_encode.ComputeFirstPassStats();
constexpr int kBlockSize = 16;
firstpass_info.num_mbs_16x16 = ((frame_width + kBlockSize - 1) / kBlockSize) *
((frame_height + kBlockSize - 1) / kBlockSize);
const auto gop_info = qmode_rc.DetermineGopInfo(firstpass_info);
ASSERT_TRUE(gop_info.status().ok());
const GopStructList &gop_list = gop_info.value();
std::vector<aom::GopEncodeInfo> tpl_pass_gop_encode_info_list;
std::vector<aom::TplGopStats> tpl_gop_stats_list;
for (const auto &gop_struct : gop_list) {
const auto gop_encode_info =
qmode_rc.GetTplPassGopEncodeInfo(gop_struct, firstpass_info);
ASSERT_TRUE(gop_encode_info.status().ok());
tpl_pass_gop_encode_info_list.push_back(std::move(*gop_encode_info));
}
tpl_gop_stats_list = ducky_encode.ComputeTplStats(
firstpass_info.stats_list, gop_list, tpl_pass_gop_encode_info_list);
std::vector<aom::GopEncodeInfo> final_pass_gop_encode_info_list;
aom::RefFrameTable ref_frame_table;
for (size_t i = 0; i < gop_list.size(); ++i) {
const aom::GopStruct &gop_struct = gop_list[i];
const aom::TplGopStats &tpl_gop_stats = tpl_gop_stats_list[i];
std::vector<aom::LookaheadStats> lookahead_stats = {};
for (size_t lookahead_index = 1;
lookahead_index <= 1 && i + lookahead_index < gop_list.size();
++lookahead_index) {
lookahead_stats.push_back({ &gop_list[i + lookahead_index],
&tpl_gop_stats_list[i + lookahead_index] });
}
const auto gop_encode_info =
qmode_rc.GetGopEncodeInfo(gop_struct, tpl_gop_stats, lookahead_stats,
firstpass_info, ref_frame_table);
ASSERT_TRUE(gop_encode_info.status().ok());
ref_frame_table = gop_encode_info.value().final_snapshot;
final_pass_gop_encode_info_list.push_back(std::move(*gop_encode_info));
}
ducky_encode.StartEncode(firstpass_info.stats_list);
std::vector<aom::EncodeFrameResult> encoded_frames_list =
ducky_encode.EncodeVideo(gop_list, final_pass_gop_encode_info_list);
ducky_encode.EndEncode();
EXPECT_THAT(encoded_frames_list,
testing::Each(testing::Field(
"psnr", &aom::EncodeFrameResult::psnr, testing::Gt(37))));
}
TEST(DuckyEncodeTest, EncodeFrameMode) {
EXPECT_EQ(DUCKY_ENCODE_FRAME_MODE_NONE,
static_cast<DUCKY_ENCODE_FRAME_MODE>(EncodeFrameMode::kNone));
EXPECT_EQ(DUCKY_ENCODE_FRAME_MODE_QINDEX,
static_cast<DUCKY_ENCODE_FRAME_MODE>(EncodeFrameMode::kQindex));
EXPECT_EQ(
DUCKY_ENCODE_FRAME_MODE_QINDEX_RDMULT,
static_cast<DUCKY_ENCODE_FRAME_MODE>(EncodeFrameMode::kQindexRdmult));
}
} // namespace aom