1 /*
2 * Copyright (c) 2018, Alliance for Open Media. All rights reserved.
3 *
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10 */
11
12 #include <cstdio>
13 #include <cstdlib>
14 #include <string>
15
16 #include "aom_mem/aom_mem.h"
17 #include "gtest/gtest.h"
18 #include "test/codec_factory.h"
19 #include "test/encode_test_driver.h"
20 #include "test/i420_video_source.h"
21 #include "test/md5_helper.h"
22 #include "test/util.h"
23
24 namespace {
25
26 static const int kNumMultiThreadDecoders = 3;
27
28 class AV1DecodeMultiThreadedTest
29 : public ::libaom_test::CodecTestWith5Params<int, int, int, int, int>,
30 public ::libaom_test::EncoderTest {
31 protected:
AV1DecodeMultiThreadedTest()32 AV1DecodeMultiThreadedTest()
33 : EncoderTest(GET_PARAM(0)), md5_single_thread_(), md5_multi_thread_(),
34 n_tile_cols_(GET_PARAM(1)), n_tile_rows_(GET_PARAM(2)),
35 n_tile_groups_(GET_PARAM(3)), set_cpu_used_(GET_PARAM(4)),
36 row_mt_(GET_PARAM(5)) {
37 init_flags_ = AOM_CODEC_USE_PSNR;
38 aom_codec_dec_cfg_t cfg = aom_codec_dec_cfg_t();
39 cfg.w = 704;
40 cfg.h = 576;
41 cfg.threads = 1;
42 cfg.allow_lowbitdepth = 1;
43 single_thread_dec_ = codec_->CreateDecoder(cfg, 0);
44
45 // Test cfg.threads == powers of 2.
46 for (int i = 0; i < kNumMultiThreadDecoders; ++i) {
47 cfg.threads <<= 1;
48 multi_thread_dec_[i] = codec_->CreateDecoder(cfg, 0);
49 multi_thread_dec_[i]->Control(AV1D_SET_ROW_MT, row_mt_);
50 }
51
52 if (single_thread_dec_->IsAV1()) {
53 single_thread_dec_->Control(AV1D_EXT_TILE_DEBUG, 1);
54 single_thread_dec_->Control(AV1_SET_DECODE_TILE_ROW, -1);
55 single_thread_dec_->Control(AV1_SET_DECODE_TILE_COL, -1);
56 }
57 for (int i = 0; i < kNumMultiThreadDecoders; ++i) {
58 if (multi_thread_dec_[i]->IsAV1()) {
59 multi_thread_dec_[i]->Control(AV1D_EXT_TILE_DEBUG, 1);
60 multi_thread_dec_[i]->Control(AV1_SET_DECODE_TILE_ROW, -1);
61 multi_thread_dec_[i]->Control(AV1_SET_DECODE_TILE_COL, -1);
62 }
63 }
64 }
65
~AV1DecodeMultiThreadedTest()66 ~AV1DecodeMultiThreadedTest() override {
67 delete single_thread_dec_;
68 for (int i = 0; i < kNumMultiThreadDecoders; ++i)
69 delete multi_thread_dec_[i];
70 }
71
SetUp()72 void SetUp() override { InitializeConfig(libaom_test::kTwoPassGood); }
73
PreEncodeFrameHook(libaom_test::VideoSource * video,libaom_test::Encoder * encoder)74 void PreEncodeFrameHook(libaom_test::VideoSource *video,
75 libaom_test::Encoder *encoder) override {
76 if (video->frame() == 0) {
77 encoder->Control(AV1E_SET_TILE_COLUMNS, n_tile_cols_);
78 encoder->Control(AV1E_SET_TILE_ROWS, n_tile_rows_);
79 encoder->Control(AV1E_SET_NUM_TG, n_tile_groups_);
80 encoder->Control(AOME_SET_CPUUSED, set_cpu_used_);
81 }
82 }
83
UpdateMD5(::libaom_test::Decoder * dec,const aom_codec_cx_pkt_t * pkt,::libaom_test::MD5 * md5)84 void UpdateMD5(::libaom_test::Decoder *dec, const aom_codec_cx_pkt_t *pkt,
85 ::libaom_test::MD5 *md5) {
86 const aom_codec_err_t res = dec->DecodeFrame(
87 reinterpret_cast<uint8_t *>(pkt->data.frame.buf), pkt->data.frame.sz);
88 if (res != AOM_CODEC_OK) {
89 abort_ = true;
90 ASSERT_EQ(AOM_CODEC_OK, res);
91 }
92 const aom_image_t *img = dec->GetDxData().Next();
93 md5->Add(img);
94 }
95
FramePktHook(const aom_codec_cx_pkt_t * pkt)96 void FramePktHook(const aom_codec_cx_pkt_t *pkt) override {
97 UpdateMD5(single_thread_dec_, pkt, &md5_single_thread_);
98
99 for (int i = 0; i < kNumMultiThreadDecoders; ++i)
100 UpdateMD5(multi_thread_dec_[i], pkt, &md5_multi_thread_[i]);
101 }
102
DoTest()103 void DoTest() {
104 const aom_rational timebase = { 33333333, 1000000000 };
105 cfg_.g_timebase = timebase;
106 cfg_.rc_target_bitrate = 500;
107 cfg_.g_lag_in_frames = 12;
108 cfg_.rc_end_usage = AOM_VBR;
109
110 libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 704, 576,
111 timebase.den, timebase.num, 0, 2);
112 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
113
114 const char *md5_single_thread_str = md5_single_thread_.Get();
115
116 for (int i = 0; i < kNumMultiThreadDecoders; ++i) {
117 const char *md5_multi_thread_str = md5_multi_thread_[i].Get();
118 ASSERT_STREQ(md5_single_thread_str, md5_multi_thread_str);
119 }
120 }
121
122 ::libaom_test::MD5 md5_single_thread_;
123 ::libaom_test::MD5 md5_multi_thread_[kNumMultiThreadDecoders];
124 ::libaom_test::Decoder *single_thread_dec_;
125 ::libaom_test::Decoder *multi_thread_dec_[kNumMultiThreadDecoders];
126
127 private:
128 int n_tile_cols_;
129 int n_tile_rows_;
130 int n_tile_groups_;
131 int set_cpu_used_;
132 int row_mt_;
133 };
134
135 // run an encode and do the decode both in single thread
136 // and multi thread. Ensure that the MD5 of the output in both cases
137 // is identical. If so, the test passes.
TEST_P(AV1DecodeMultiThreadedTest,MD5Match)138 TEST_P(AV1DecodeMultiThreadedTest, MD5Match) {
139 cfg_.large_scale_tile = 0;
140 single_thread_dec_->Control(AV1_SET_TILE_MODE, 0);
141 for (int i = 0; i < kNumMultiThreadDecoders; ++i)
142 multi_thread_dec_[i]->Control(AV1_SET_TILE_MODE, 0);
143 DoTest();
144 }
145
146 class AV1DecodeMultiThreadedTestLarge : public AV1DecodeMultiThreadedTest {};
147
TEST_P(AV1DecodeMultiThreadedTestLarge,MD5Match)148 TEST_P(AV1DecodeMultiThreadedTestLarge, MD5Match) {
149 cfg_.large_scale_tile = 0;
150 single_thread_dec_->Control(AV1_SET_TILE_MODE, 0);
151 for (int i = 0; i < kNumMultiThreadDecoders; ++i)
152 multi_thread_dec_[i]->Control(AV1_SET_TILE_MODE, 0);
153 DoTest();
154 }
155
156 // TODO(ranjit): More tests have to be added using pre-generated MD5.
157 AV1_INSTANTIATE_TEST_SUITE(AV1DecodeMultiThreadedTest, ::testing::Values(1, 2),
158 ::testing::Values(1, 2), ::testing::Values(1),
159 ::testing::Values(3), ::testing::Values(0, 1));
160 AV1_INSTANTIATE_TEST_SUITE(AV1DecodeMultiThreadedTestLarge,
161 ::testing::Values(0, 1, 2, 6),
162 ::testing::Values(0, 1, 2, 6),
163 ::testing::Values(1, 4), ::testing::Values(0),
164 ::testing::Values(0, 1));
165
166 class AV1DecodeMultiThreadedLSTestLarge
167 : public AV1DecodeMultiThreadedTestLarge {};
168
TEST_P(AV1DecodeMultiThreadedLSTestLarge,MD5Match)169 TEST_P(AV1DecodeMultiThreadedLSTestLarge, MD5Match) {
170 cfg_.large_scale_tile = 1;
171 single_thread_dec_->Control(AV1_SET_TILE_MODE, 1);
172 for (int i = 0; i < kNumMultiThreadDecoders; ++i)
173 multi_thread_dec_[i]->Control(AV1_SET_TILE_MODE, 1);
174 DoTest();
175 }
176
177 AV1_INSTANTIATE_TEST_SUITE(AV1DecodeMultiThreadedLSTestLarge,
178 ::testing::Values(6), ::testing::Values(6),
179 ::testing::Values(1), ::testing::Values(0, 3),
180 ::testing::Values(0, 1));
181
182 } // namespace
183