| /* |
| * 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 |