1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // This has to be included first.
6 // See http://code.google.com/p/googletest/issues/detail?id=371
7 #include "testing/gtest/include/gtest/gtest.h"
8
9 #include <unistd.h>
10 #include <map>
11 #include <vector>
12
13 #include <va/va.h>
14 #include <va/va_str.h>
15
16 #include "base/files/file.h"
17 #include "base/files/scoped_file.h"
18 #include "base/logging.h"
19 #include "base/optional.h"
20 #include "base/process/launch.h"
21 #include "base/stl_util.h"
22 #include "base/strings/string_split.h"
23 #include "base/test/launcher/unit_test_launcher.h"
24 #include "base/test/test_suite.h"
25 #include "build/chromeos_buildflags.h"
26 #include "gpu/config/gpu_driver_bug_workarounds.h"
27 #include "media/gpu/vaapi/vaapi_wrapper.h"
28 #include "media/media_buildflags.h"
29
30 namespace media {
31 namespace {
32
ConvertToVAProfile(VideoCodecProfile profile)33 base::Optional<VAProfile> ConvertToVAProfile(VideoCodecProfile profile) {
34 // A map between VideoCodecProfile and VAProfile.
35 const std::map<VideoCodecProfile, VAProfile> kProfileMap = {
36 // VAProfileH264Baseline is deprecated in <va/va.h> from libva 2.0.0.
37 {H264PROFILE_BASELINE, VAProfileH264ConstrainedBaseline},
38 {H264PROFILE_MAIN, VAProfileH264Main},
39 {H264PROFILE_HIGH, VAProfileH264High},
40 {VP8PROFILE_ANY, VAProfileVP8Version0_3},
41 {VP9PROFILE_PROFILE0, VAProfileVP9Profile0},
42 {VP9PROFILE_PROFILE2, VAProfileVP9Profile2},
43 #if BUILDFLAG(IS_ASH)
44 // TODO(hiroh): Remove if-macro once libva for linux-chrome is upreved to
45 // 2.9.0 or newer.
46 // https://source.chromium.org/chromium/chromium/src/+/master:build/linux/sysroot_scripts/generated_package_lists/sid.amd64
47 {AV1PROFILE_PROFILE_MAIN, VAProfileAV1Profile0},
48 #endif
49 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
50 {HEVCPROFILE_MAIN, VAProfileHEVCMain},
51 #endif
52 };
53 auto it = kProfileMap.find(profile);
54 return it != kProfileMap.end() ? base::make_optional<VAProfile>(it->second)
55 : base::nullopt;
56 }
57
58 // Converts the given string to VAProfile
StringToVAProfile(const std::string & va_profile)59 base::Optional<VAProfile> StringToVAProfile(const std::string& va_profile) {
60 const std::map<std::string, VAProfile> kStringToVAProfile = {
61 {"VAProfileNone", VAProfileNone},
62 {"VAProfileH264ConstrainedBaseline", VAProfileH264ConstrainedBaseline},
63 // Even though it's deprecated, we leave VAProfileH264Baseline's
64 // translation here to assert we never encounter it.
65 {"VAProfileH264Baseline", VAProfileH264Baseline},
66 {"VAProfileH264Main", VAProfileH264Main},
67 {"VAProfileH264High", VAProfileH264High},
68 {"VAProfileJPEGBaseline", VAProfileJPEGBaseline},
69 {"VAProfileVP8Version0_3", VAProfileVP8Version0_3},
70 {"VAProfileVP9Profile0", VAProfileVP9Profile0},
71 {"VAProfileVP9Profile2", VAProfileVP9Profile2},
72 #if BUILDFLAG(IS_ASH)
73 // TODO(hiroh): Remove if-macro once libva for linux-chrome is upreved to
74 // 2.9.0 or newer.
75 // https://source.chromium.org/chromium/chromium/src/+/master:build/linux/sysroot_scripts/generated_package_lists/sid.amd64
76 {"VAProfileAV1Profile0", VAProfileAV1Profile0},
77 #endif
78 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
79 {"VAProfileHEVCMain", VAProfileHEVCMain},
80 #endif
81 };
82
83 auto it = kStringToVAProfile.find(va_profile);
84 return it != kStringToVAProfile.end()
85 ? base::make_optional<VAProfile>(it->second)
86 : base::nullopt;
87 }
88
89 // Converts the given string to VAProfile
StringToVAEntrypoint(const std::string & va_entrypoint)90 base::Optional<VAEntrypoint> StringToVAEntrypoint(
91 const std::string& va_entrypoint) {
92 const std::map<std::string, VAEntrypoint> kStringToVAEntrypoint = {
93 {"VAEntrypointVLD", VAEntrypointVLD},
94 {"VAEntrypointEncSlice", VAEntrypointEncSlice},
95 {"VAEntrypointEncPicture", VAEntrypointEncPicture},
96 {"VAEntrypointEncSliceLP", VAEntrypointEncSliceLP},
97 {"VAEntrypointVideoProc", VAEntrypointVideoProc}};
98
99 auto it = kStringToVAEntrypoint.find(va_entrypoint);
100 return it != kStringToVAEntrypoint.end()
101 ? base::make_optional<VAEntrypoint>(it->second)
102 : base::nullopt;
103 }
104 } // namespace
105
106 class VaapiTest : public testing::Test {
107 public:
108 VaapiTest() = default;
109 ~VaapiTest() override = default;
110 };
111
ParseVainfo(const std::string & output)112 std::map<VAProfile, std::vector<VAEntrypoint>> ParseVainfo(
113 const std::string& output) {
114 const std::vector<std::string> lines =
115 base::SplitString(output, "\n", base::WhitespaceHandling::TRIM_WHITESPACE,
116 base::SplitResult::SPLIT_WANT_ALL);
117 std::map<VAProfile, std::vector<VAEntrypoint>> info;
118 for (const std::string& line : lines) {
119 if (!base::StartsWith(line, "VAProfile",
120 base::CompareCase::INSENSITIVE_ASCII)) {
121 continue;
122 }
123 std::vector<std::string> res =
124 base::SplitString(line, ":", base::WhitespaceHandling::TRIM_WHITESPACE,
125 base::SplitResult::SPLIT_WANT_ALL);
126 if (res.size() != 2) {
127 LOG(ERROR) << "Unexpected line: " << line;
128 continue;
129 }
130
131 auto va_profile = StringToVAProfile(res[0]);
132 if (!va_profile)
133 continue;
134 auto va_entrypoint = StringToVAEntrypoint(res[1]);
135 if (!va_entrypoint)
136 continue;
137 info[*va_profile].push_back(*va_entrypoint);
138 DVLOG(3) << line;
139 }
140 return info;
141 }
142
RetrieveVAInfoOutput()143 std::map<VAProfile, std::vector<VAEntrypoint>> RetrieveVAInfoOutput() {
144 int fds[2];
145 PCHECK(pipe(fds) == 0);
146 base::File read_pipe(fds[0]);
147 base::ScopedFD write_pipe_fd(fds[1]);
148
149 base::LaunchOptions options;
150 options.fds_to_remap.emplace_back(write_pipe_fd.get(), STDOUT_FILENO);
151 std::vector<std::string> argv = {"vainfo"};
152 EXPECT_TRUE(LaunchProcess(argv, options).IsValid());
153 write_pipe_fd.reset();
154
155 char buf[4096] = {};
156 int n = read_pipe.ReadAtCurrentPos(buf, sizeof(buf));
157 PCHECK(n >= 0);
158 EXPECT_LT(n, 4096);
159 std::string output(buf, n);
160 DVLOG(4) << output;
161 return ParseVainfo(output);
162 }
163
TEST_F(VaapiTest,VaapiSandboxInitialization)164 TEST_F(VaapiTest, VaapiSandboxInitialization) {
165 // Here we just test that the PreSandboxInitialization() in SetUp() worked
166 // fine. Said initialization is buried in internal singletons, but we can
167 // verify that at least the implementation type has been filled in.
168 EXPECT_NE(VaapiWrapper::GetImplementationType(), VAImplementation::kInvalid);
169 }
170
171 // Commit [1] deprecated VAProfileH264Baseline from libva in 2017 (release
172 // 2.0.0). This test verifies that such profile is never seen in the lab.
173 // [1] https://github.com/intel/libva/commit/6f69256f8ccc9a73c0b196ab77ac69ab1f4f33c2
TEST_F(VaapiTest,VerifyNoVAProfileH264Baseline)174 TEST_F(VaapiTest, VerifyNoVAProfileH264Baseline) {
175 const auto va_info = RetrieveVAInfoOutput();
176 EXPECT_FALSE(base::Contains(va_info, VAProfileH264Baseline));
177 }
178
179 // Verifies that every VAProfile from VaapiWrapper::GetSupportedDecodeProfiles()
180 // is indeed supported by the command line vainfo utility and by
181 // VaapiWrapper::IsDecodeSupported().
TEST_F(VaapiTest,GetSupportedDecodeProfiles)182 TEST_F(VaapiTest, GetSupportedDecodeProfiles) {
183 const auto va_info = RetrieveVAInfoOutput();
184
185 for (const auto& profile : VaapiWrapper::GetSupportedDecodeProfiles(
186 gpu::GpuDriverBugWorkarounds())) {
187 const auto va_profile = ConvertToVAProfile(profile.profile);
188 ASSERT_TRUE(va_profile.has_value());
189
190 EXPECT_TRUE(base::Contains(va_info.at(*va_profile), VAEntrypointVLD))
191 << " profile: " << GetProfileName(profile.profile)
192 << ", va profile: " << vaProfileStr(*va_profile);
193 EXPECT_TRUE(VaapiWrapper::IsDecodeSupported(*va_profile))
194 << " profile: " << GetProfileName(profile.profile)
195 << ", va profile: " << vaProfileStr(*va_profile);
196 }
197 }
198
199 // Verifies that every VAProfile from VaapiWrapper::GetSupportedEncodeProfiles()
200 // is indeed supported by the command line vainfo utility.
TEST_F(VaapiTest,GetSupportedEncodeProfiles)201 TEST_F(VaapiTest, GetSupportedEncodeProfiles) {
202 const auto va_info = RetrieveVAInfoOutput();
203
204 for (const auto& profile : VaapiWrapper::GetSupportedEncodeProfiles()) {
205 const auto va_profile = ConvertToVAProfile(profile.profile);
206 ASSERT_TRUE(va_profile.has_value());
207
208 EXPECT_TRUE(base::Contains(va_info.at(*va_profile), VAEntrypointEncSlice) ||
209 base::Contains(va_info.at(*va_profile), VAEntrypointEncSliceLP))
210 << " profile: " << GetProfileName(profile.profile)
211 << ", va profile: " << vaProfileStr(*va_profile);
212 }
213 }
214
215 // Verifies that if JPEG decoding and encoding are supported by VaapiWrapper,
216 // they are also supported by by the command line vainfo utility.
TEST_F(VaapiTest,VaapiProfilesJPEG)217 TEST_F(VaapiTest, VaapiProfilesJPEG) {
218 const auto va_info = RetrieveVAInfoOutput();
219
220 EXPECT_EQ(VaapiWrapper::IsDecodeSupported(VAProfileJPEGBaseline),
221 base::Contains(va_info.at(VAProfileJPEGBaseline), VAEntrypointVLD));
222 EXPECT_EQ(VaapiWrapper::IsJpegEncodeSupported(),
223 base::Contains(va_info.at(VAProfileJPEGBaseline),
224 VAEntrypointEncPicture));
225 }
226
227 // Verifies that the default VAEntrypoint as per VaapiWrapper is indeed among
228 // the supported ones.
TEST_F(VaapiTest,DefaultEntrypointIsSupported)229 TEST_F(VaapiTest, DefaultEntrypointIsSupported) {
230 for (size_t i = 0; i < VaapiWrapper::kCodecModeMax; ++i) {
231 const auto wrapper_mode = static_cast<VaapiWrapper::CodecMode>(i);
232 std::map<VAProfile, std::vector<VAEntrypoint>> configurations =
233 VaapiWrapper::GetSupportedConfigurationsForCodecModeForTesting(
234 wrapper_mode);
235 for (const auto& profile_and_entrypoints : configurations) {
236 const VAEntrypoint default_entrypoint =
237 VaapiWrapper::GetDefaultVaEntryPoint(wrapper_mode,
238 profile_and_entrypoints.first);
239 const auto& supported_entrypoints = profile_and_entrypoints.second;
240 EXPECT_TRUE(base::Contains(supported_entrypoints, default_entrypoint))
241 << "Default VAEntrypoint " << vaEntrypointStr(default_entrypoint)
242 << " (VaapiWrapper mode = " << wrapper_mode
243 << ") is not supported for "
244 << vaProfileStr(profile_and_entrypoints.first);
245 }
246 }
247 }
248 } // namespace media
249
main(int argc,char ** argv)250 int main(int argc, char** argv) {
251 base::TestSuite test_suite(argc, argv);
252
253 // PreSandboxInitialization() loads and opens the driver, queries its
254 // capabilities and fills in the VASupportedProfiles.
255 media::VaapiWrapper::PreSandboxInitialization();
256
257 return base::LaunchUnitTests(
258 argc, argv,
259 base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
260 }
261