blob: abb662d777af0c90c3f0fad3a1877238bcb0b97f [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This has to be included first.
// See http://code.google.com/p/googletest/issues/detail?id=371
#include "testing/gtest/include/gtest/gtest.h"
#include <unistd.h>
#include <map>
#include <vector>
#include <va/va.h>
#include <va/va_str.h>
#include "base/files/file.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/optional.h"
#include "base/process/launch.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h"
#include "media/gpu/vaapi/vaapi_wrapper.h"
namespace media {
namespace {
base::Optional<VAProfile> ConvertToVAProfile(VideoCodecProfile profile) {
// A map between VideoCodecProfile and VAProfile.
const std::map<VideoCodecProfile, VAProfile> kProfileMap = {
{H264PROFILE_BASELINE, VAProfileH264Baseline},
{H264PROFILE_MAIN, VAProfileH264Main},
{H264PROFILE_HIGH, VAProfileH264High},
{VP8PROFILE_ANY, VAProfileVP8Version0_3},
{VP9PROFILE_PROFILE0, VAProfileVP9Profile0},
{VP9PROFILE_PROFILE1, VAProfileVP9Profile1},
{VP9PROFILE_PROFILE2, VAProfileVP9Profile2},
{VP9PROFILE_PROFILE3, VAProfileVP9Profile3},
};
auto it = kProfileMap.find(profile);
return it != kProfileMap.end() ? base::make_optional<VAProfile>(it->second)
: base::nullopt;
}
// Converts the given string to VAProfile
base::Optional<VAProfile> StringToVAProfile(const std::string& va_profile) {
const std::map<std::string, VAProfile> kStringToVAProfile = {
{"VAProfileNone", VAProfileNone},
{"VAProfileH264ConstrainedBaseline", VAProfileH264ConstrainedBaseline},
{"VAProfileH264Baseline", VAProfileH264Baseline},
{"VAProfileH264Main", VAProfileH264Main},
{"VAProfileH264High", VAProfileH264High},
{"VAProfileJPEGBaseline", VAProfileJPEGBaseline},
{"VAProfileVP8Version0_3", VAProfileVP8Version0_3},
{"VAProfileVP9Profile0", VAProfileVP9Profile0},
{"VAProfileVP9Profile1", VAProfileVP9Profile1},
{"VAProfileVP9Profile2", VAProfileVP9Profile2},
{"VAProfileVP9Profile3", VAProfileVP9Profile3},
};
auto it = kStringToVAProfile.find(va_profile);
return it != kStringToVAProfile.end()
? base::make_optional<VAProfile>(it->second)
: base::nullopt;
}
// Converts the given string to VAProfile
base::Optional<VAEntrypoint> StringToVAEntrypoint(
const std::string& va_entrypoint) {
const std::map<std::string, VAEntrypoint> kStringToVAEntrypoint = {
{"VAEntrypointVLD", VAEntrypointVLD},
{"VAEntrypointEncSlice", VAEntrypointEncSlice},
{"VAEntrypointEncPicture", VAEntrypointEncPicture},
{"VAEntrypointEncSliceLP", VAEntrypointEncSliceLP},
{"VAEntrypointVideoProc", VAEntrypointVideoProc}};
auto it = kStringToVAEntrypoint.find(va_entrypoint);
return it != kStringToVAEntrypoint.end()
? base::make_optional<VAEntrypoint>(it->second)
: base::nullopt;
}
} // namespace
class VaapiTest : public testing::Test {
public:
VaapiTest() = default;
~VaapiTest() override = default;
};
std::map<VAProfile, std::vector<VAEntrypoint>> ParseVainfo(
const std::string& output) {
const std::vector<std::string> lines =
base::SplitString(output, "\n", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_ALL);
std::map<VAProfile, std::vector<VAEntrypoint>> info;
for (const std::string& line : lines) {
if (!base::StartsWith(line, "VAProfile",
base::CompareCase::INSENSITIVE_ASCII)) {
continue;
}
std::vector<std::string> res =
base::SplitString(line, ":", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_ALL);
if (res.size() != 2) {
LOG(ERROR) << "Unexpected line: " << line;
continue;
}
auto va_profile = StringToVAProfile(res[0]);
if (!va_profile)
continue;
auto va_entrypoint = StringToVAEntrypoint(res[1]);
if (!va_entrypoint)
continue;
info[*va_profile].push_back(*va_entrypoint);
DVLOG(3) << line;
}
return info;
}
TEST_F(VaapiTest, VaapiSandboxInitialization) {
// VASupportedProfiles::Get() is called in PreSandboxInitialization().
// It queries VA-API driver their capabilities.
VaapiWrapper::PreSandboxInitialization();
}
TEST_F(VaapiTest, VaapiProfiles) {
// VASupportedProfiles::Get() is called in PreSandboxInitialization().
// It queries VA-API driver their capabilities.
VaapiWrapper::PreSandboxInitialization();
int fds[2];
PCHECK(pipe(fds) == 0);
base::File read_pipe(fds[0]);
base::ScopedFD write_pipe_fd(fds[1]);
base::LaunchOptions options;
options.fds_to_remap.emplace_back(write_pipe_fd.get(), STDOUT_FILENO);
std::vector<std::string> argv = {"vainfo"};
EXPECT_TRUE(LaunchProcess(argv, options).IsValid());
write_pipe_fd.reset();
char buf[4096] = {};
int n = read_pipe.ReadAtCurrentPos(buf, sizeof(buf));
PCHECK(n >= 0);
EXPECT_LT(n, 4096);
std::string output(buf, n);
DVLOG(4) << output;
auto va_info = ParseVainfo(output);
for (const auto& profile : VaapiWrapper::GetSupportedDecodeProfiles()) {
auto va_profile = ConvertToVAProfile(profile.profile);
ASSERT_TRUE(va_profile.has_value());
bool is_profile_supported =
base::Contains(va_info[*va_profile], VAEntrypointVLD);
if (profile.profile == H264PROFILE_BASELINE) {
// ConstrainedBaseline is the fallback profile for H264PROFILE_BASELINE.
// This is the same logic as in vaapi_wrapper.cc.
is_profile_supported |= base::Contains(
va_info[VAProfileH264ConstrainedBaseline], VAEntrypointVLD);
}
EXPECT_TRUE(is_profile_supported)
<< " profile: " << GetProfileName(profile.profile);
}
for (const auto& profile : VaapiWrapper::GetSupportedEncodeProfiles()) {
auto va_profile = ConvertToVAProfile(profile.profile);
ASSERT_TRUE(va_profile.has_value());
bool is_profile_supported =
base::Contains(va_info[*va_profile], VAEntrypointEncSlice) ||
base::Contains(va_info[*va_profile], VAEntrypointEncSliceLP);
if (profile.profile == H264PROFILE_BASELINE) {
// ConstrainedBaseline is the fallback profile for H264PROFILE_BASELINE.
// This is the same logic as in vaapi_wrapper.cc.
is_profile_supported |=
base::Contains(va_info[VAProfileH264ConstrainedBaseline],
VAEntrypointEncSlice) ||
base::Contains(va_info[VAProfileH264ConstrainedBaseline],
VAEntrypointEncSliceLP);
}
EXPECT_TRUE(is_profile_supported)
<< " profile: " << GetProfileName(profile.profile);
}
EXPECT_EQ(VaapiWrapper::IsDecodeSupported(VAProfileJPEGBaseline),
base::Contains(va_info[VAProfileJPEGBaseline], VAEntrypointVLD));
EXPECT_EQ(
VaapiWrapper::IsJpegEncodeSupported(),
base::Contains(va_info[VAProfileJPEGBaseline], VAEntrypointEncPicture));
}
// Verifies that the default VAEntrypoint as per VaapiWrapper is indeed among
// the supported ones.
TEST_F(VaapiTest, DefaultEntrypointIsSupported) {
for (size_t i = 0; i < VaapiWrapper::kCodecModeMax; ++i) {
const auto wrapper_mode = static_cast<VaapiWrapper::CodecMode>(i);
std::map<VAProfile, std::vector<VAEntrypoint>> configurations =
VaapiWrapper::GetSupportedConfigurationsForCodecModeForTesting(
wrapper_mode);
for (const auto& profile_and_entrypoints : configurations) {
const VAEntrypoint default_entrypoint =
VaapiWrapper::GetDefaultVaEntryPoint(wrapper_mode,
profile_and_entrypoints.first);
const auto& supported_entrypoints = profile_and_entrypoints.second;
EXPECT_TRUE(base::Contains(supported_entrypoints, default_entrypoint))
<< "Default VAEntrypoint " << vaEntrypointStr(default_entrypoint)
<< " (VaapiWrapper mode = " << wrapper_mode
<< ") is not supported for "
<< vaProfileStr(profile_and_entrypoints.first);
}
}
}
} // namespace media
int main(int argc, char** argv) {
base::TestSuite test_suite(argc, argv);
media::VaapiWrapper::PreSandboxInitialization();
return base::LaunchUnitTests(
argc, argv,
base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
}