/* * 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 #include #include #include #include #include #include #include #include #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 frame_stats = ducky_encode.ComputeFirstPassStats(); EXPECT_EQ(frame_stats.size(), static_cast(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 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 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 tpl_pass_gop_encode_info_list; std::vector 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 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 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 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(EncodeFrameMode::kNone)); EXPECT_EQ(DUCKY_ENCODE_FRAME_MODE_QINDEX, static_cast(EncodeFrameMode::kQindex)); EXPECT_EQ( DUCKY_ENCODE_FRAME_MODE_QINDEX_RDMULT, static_cast(EncodeFrameMode::kQindexRdmult)); } } // namespace aom