Commit 676790d0 authored by Hirokazu Honda's avatar Hirokazu Honda Committed by Commit Bot

media/gpu/VDA unittest: Add command line option for VideoFrameValidator flags

This adds video frame flags, --frame_validator=,
check : compares md5 values computed from video frame with the expected md5
        values. Test fails if there is an unexpected video frame.
dump  : outputs video frames to files

BUG=chromium:856562
TEST=VDA unittest with --test_import
TEST=VDA unittest with --test_import --frame_validator=check
TEST=VDA unittest with --frame_validator=check
TEST=VDA unittest with --frame_validator=check,dump
TEST=VDA unittest with --frame_validator=dump
TEST=VDA unittest with --gtest_filter=VideoDecodeAcceleratorTest.DISABLED_GenMD5 --gtest_also_run_disabled_tests

Change-Id: I1511ef07a2bd01231e9daf1038776324c22e3da5
Reviewed-on: https://chromium-review.googlesource.com/c/1309390Reviewed-by: default avatarAlexandre Courbot <acourbot@chromium.org>
Commit-Queue: Hirokazu Honda <hiroh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605598}
parent 565a5aaf
......@@ -18,29 +18,59 @@ namespace test {
// static
std::unique_ptr<VideoFrameValidator>
VideoFrameValidator::CreateVideoFrameValidator(
uint32_t flags,
const base::FilePath& prefix_output_yuv,
const base::FilePath& md5_file_path) {
if ((flags & VideoFrameValidator::OUTPUTYUV) && prefix_output_yuv.empty()) {
LOG(ERROR) << "Prefix of yuv files isn't specified with dump flags.";
return nullptr;
}
if ((flags & VideoFrameValidator::GENMD5) &&
(flags & VideoFrameValidator::CHECK)) {
LOG(ERROR) << "Generating and checking MD5 values at the same time is not "
<< "supported.";
}
auto video_frame_mapper = VideoFrameMapperFactory::CreateMapper();
if (!video_frame_mapper) {
LOG(ERROR) << "Failed to create VideoFrameMapper.";
return nullptr;
}
auto md5_of_frames = ReadGoldenThumbnailMD5s(md5_file_path);
if (md5_of_frames.empty()) {
LOG(ERROR) << "Failed to read md5 values in " << md5_file_path;
return nullptr;
std::vector<std::string> md5_of_frames;
base::File md5_file;
if (flags & VideoFrameValidator::GENMD5) {
// Writes out computed md5 values to md5_file_path.
md5_file = base::File(md5_file_path, base::File::FLAG_CREATE_ALWAYS |
base::File::FLAG_WRITE |
base::File::FLAG_APPEND);
if (!md5_file.IsValid()) {
LOG(ERROR) << "Failed to create md5 file to write " << md5_file_path;
return nullptr;
}
} else if (flags & VideoFrameValidator::CHECK) {
md5_of_frames = ReadGoldenThumbnailMD5s(md5_file_path);
if (md5_of_frames.empty()) {
LOG(ERROR) << "Failed to read md5 values in " << md5_file_path;
return nullptr;
}
}
return base::WrapUnique(
new VideoFrameValidator(prefix_output_yuv, std::move(md5_of_frames),
std::move(video_frame_mapper)));
return base::WrapUnique(new VideoFrameValidator(
flags, prefix_output_yuv, std::move(md5_of_frames), std::move(md5_file),
std::move(video_frame_mapper)));
}
VideoFrameValidator::VideoFrameValidator(
uint32_t flags,
const base::FilePath& prefix_output_yuv,
std::vector<std::string> md5_of_frames,
base::File md5_file,
std::unique_ptr<VideoFrameMapper> video_frame_mapper)
: prefix_output_yuv_(prefix_output_yuv),
: flags_(flags),
prefix_output_yuv_(prefix_output_yuv),
md5_of_frames_(std::move(md5_of_frames)),
md5_file_(std::move(md5_file)),
video_frame_mapper_(std::move(video_frame_mapper)) {
DETACH_FROM_THREAD(thread_checker_);
}
......@@ -51,23 +81,28 @@ void VideoFrameValidator::EvaluateVideoFrame(
scoped_refptr<VideoFrame> video_frame,
size_t frame_index) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
std::string expected_md5;
LOG_IF(FATAL, frame_index >= md5_of_frames_.size())
<< "Frame number is over than the number of read md5 values in file.";
expected_md5 = md5_of_frames_[frame_index];
auto standard_frame = CreateStandardizedFrame(video_frame);
if (!standard_frame) {
LOG(ERROR) << "Failed to create standardized frame.";
return;
}
std::string computed_md5 = ComputeMD5FromVideoFrame(standard_frame);
if (computed_md5 != expected_md5) {
mismatched_frames_.push_back(
MismatchedFrameInfo{frame_index, computed_md5, expected_md5});
if (flags_ & Flags::GENMD5) {
md5_file_.Write(0, computed_md5.data(), computed_md5.size());
md5_file_.Write(0, "\n", 1);
}
if (!prefix_output_yuv_.empty()) {
// Output yuv.
if (flags_ & Flags::CHECK) {
LOG_IF(FATAL, frame_index >= md5_of_frames_.size())
<< "Frame number is over than the number of read md5 values in file.";
const auto& expected_md5 = md5_of_frames_[frame_index];
if (computed_md5 != expected_md5) {
mismatched_frames_.push_back(
MismatchedFrameInfo{frame_index, computed_md5, expected_md5});
}
}
if (flags_ & Flags::OUTPUTYUV) {
LOG_IF(WARNING, !WriteI420ToFile(frame_index, standard_frame.get()))
<< "Failed to write yuv into file.";
}
......
......@@ -34,12 +34,23 @@ namespace test {
// performance measurements.
class VideoFrameValidator {
public:
enum Flags : uint32_t {
// Checks soundness of video frames.
CHECK = 1 << 0,
// Writes out video frames to files.
OUTPUTYUV = 1 << 1,
// Writes out md5 values to a file.
GENMD5 = 1 << 2,
};
struct MismatchedFrameInfo {
size_t frame_index;
std::string computed_md5;
std::string expected_md5;
};
// |flags| decides the behavior of created video frame validator. See the
// detail in Flags.
// |prefix_output_yuv| is the prefix name of saved yuv files.
// VideoFrameValidator saves all I420 video frames.
// If |prefix_output_yuv_| is not specified, no yuv file will be saved.
......@@ -47,6 +58,7 @@ class VideoFrameValidator {
// The file contains one md5 value per line, listed in display order.
// Returns nullptr on failure.
static std::unique_ptr<VideoFrameValidator> CreateVideoFrameValidator(
uint32_t flags,
const base::FilePath& prefix_output_yuv,
const base::FilePath& md5_file_path);
......@@ -64,8 +76,10 @@ class VideoFrameValidator {
std::vector<MismatchedFrameInfo> GetMismatchedFramesInfo() const;
private:
VideoFrameValidator(const base::FilePath& prefix_output_yuv,
VideoFrameValidator(uint32_t flags,
const base::FilePath& prefix_output_yuv,
std::vector<std::string> md5_of_frames,
base::File md5_file,
std::unique_ptr<VideoFrameMapper> video_frame_mapper);
// This maps |video_frame|, converts it to I420 format.
......@@ -89,12 +103,17 @@ class VideoFrameValidator {
// The results of invalid frame data.
std::vector<MismatchedFrameInfo> mismatched_frames_;
const uint32_t flags_;
// Prefix of saved yuv files.
const base::FilePath prefix_output_yuv_;
// Golden MD5 values.
const std::vector<std::string> md5_of_frames_;
// File to write md5 values if flags includes GENMD5.
base::File md5_file_;
const std::unique_ptr<VideoFrameMapper> video_frame_mapper_;
THREAD_CHECKER(thread_checker_);
......
......@@ -122,11 +122,6 @@ double g_rendering_fps = 60;
bool g_use_gl_renderer = true;
// Validate each decoded frame on thumbnail test case.
// TODO(crbug.com/856562): Enable VideoFrameValidator by default if
// |g_test_import| is true.
bool g_frame_validator = false;
// The value is set by the switch "--num_play_throughs". The video will play
// the specified number of times. In different test cases, we have different
// values for |num_play_throughs|. This setting will override the value. A
......@@ -140,6 +135,10 @@ bool g_fake_decoder = 0;
// requesting the VDA itself to allocate buffers.
bool g_test_import = false;
// VideoFrameValidator flags.
// If this is set to non-zero, g_test_import becomes true.
uint32_t g_frame_validator_flags = 0;
// This is the location of the test files. If empty, they're in the current
// working directory.
base::FilePath g_test_file_path;
......@@ -1150,15 +1149,10 @@ static void AssertWaitForStateOrDeleted(
std::unique_ptr<media::test::VideoFrameValidator>
CreateAndInitializeVideoFrameValidator(
const base::FilePath::StringType& video_file) {
// TODO(crbug.com/856562): Add a command line option to stand for outputting
// decoded yuv.
// Currently decoded yuv is not output.
constexpr bool output_yuv = false;
// Initialize prefix of yuv files.
base::FilePath prefix_output_yuv;
base::FilePath filepath(video_file);
if (output_yuv) {
if (g_frame_validator_flags & test::VideoFrameValidator::OUTPUTYUV) {
if (!g_thumbnail_output_dir.empty() &&
base::DirectoryExists(g_thumbnail_output_dir)) {
prefix_output_yuv = g_thumbnail_output_dir.Append(filepath.BaseName());
......@@ -1167,7 +1161,7 @@ CreateAndInitializeVideoFrameValidator(
}
}
return media::test::VideoFrameValidator::CreateVideoFrameValidator(
prefix_output_yuv,
g_frame_validator_flags, prefix_output_yuv,
filepath.AddExtension(FILE_PATH_LITERAL(".frames.md5")));
}
......@@ -1207,7 +1201,7 @@ TEST_P(VideoDecodeAcceleratorParamTest, MAYBE_TestSimpleDecode) {
notes_.resize(num_concurrent_decoders);
clients_.resize(num_concurrent_decoders);
bool use_video_frame_validator = g_frame_validator && g_test_import;
bool use_video_frame_validator = g_frame_validator_flags != 0;
if (use_video_frame_validator) {
LOG(INFO) << "Using Frame Validator..";
#if !defined(OS_CHROMEOS)
......@@ -1653,6 +1647,40 @@ TEST_F(VideoDecodeAcceleratorTest, NoCrash) {
WaitUntilDecodeFinish(notes_[0].get());
}
#if defined(OS_CHROMEOS)
// This is the case only for generating md5 values of video frames on stream.
// This is disabled by default. To run this, you should run this test with
// --gtest_filter=VideoDecodeAcceleratorTest.DISABLED_GenMD5 and
// --gtest_also_run_disabled_tests
TEST_F(VideoDecodeAcceleratorTest, DISABLED_GenMD5) {
g_frame_validator_flags = test::VideoFrameValidator::GENMD5;
g_test_import = true;
ASSERT_EQ(test_video_files_.size(), 1u);
notes_.push_back(std::make_unique<ClientStateNotification<ClientState>>());
const TestVideoFile* video_file = test_video_files_[0].get();
GLRenderingVDAClient::Config config;
config.frame_size = gfx::Size(video_file->width, video_file->height);
config.profile = video_file->profile;
config.fake_decoder = g_fake_decoder;
config.num_frames = video_file->num_frames;
auto video_frame_validator =
CreateAndInitializeVideoFrameValidator(video_file->file_name);
clients_.push_back(std::make_unique<GLRenderingVDAClient>(
std::move(config), video_file->data_str, &rendering_helper_,
std::move(video_frame_validator), notes_[0].get()));
RenderingHelperParams helper_params;
helper_params.num_windows = 1;
InitializeRenderingHelper(helper_params);
CreateAndStartDecoder(clients_[0].get(), notes_[0].get());
ClientState last_state = WaitUntilDecodeFinish(notes_[0].get());
EXPECT_NE(CS_ERROR, last_state);
g_test_import = false;
g_frame_validator_flags = 0;
}
#endif
// TODO(fischman, vrk): add more tests! In particular:
// - Test life-cycle: Seek/Stop/Pause/Play for a single decoder.
// - Test alternate configurations
......@@ -1753,7 +1781,23 @@ int main(int argc, char** argv) {
continue;
}
if (it->first == "frame_validator") {
media::g_frame_validator = true;
#if defined(OS_CHROMEOS)
auto flags = base::SplitString(it->second, ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
for (auto& f : flags) {
if (f == "check") {
media::g_frame_validator_flags |=
media::test::VideoFrameValidator::CHECK;
} else if (f == "dump") {
media::g_frame_validator_flags |=
media::test::VideoFrameValidator::OUTPUTYUV;
} else {
LOG(FATAL) << "Unknown flag: " << f;
}
}
media::g_test_import = true;
#endif
continue;
}
if (it->first == "use-test-data-path") {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment