Commit 02730a97 authored by David Staessens's avatar David Staessens Committed by Commit Bot

re-land "media/gpu/test: Add test helper class to write video frames to file."

> This helper provides more flexibility and improves code re-use. Currently only
> writing to png files is supported, but it's easy to expand the interface to
> writing raw files etc.
>
> TEST=ran new VDA tests on nocturne
>
> BUG=879065
>
> Change-Id: I08ea462f98c9aba993ec7ef0b64d74fef11360fa
> Reviewed-on: https://chromium-review.googlesource.com/c/1420490
> Commit-Queue: David Staessens <dstaessens@chromium.org>
> Reviewed-by: Hirokazu Honda <hiroh@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#626007}

BUG=879065

Change-Id: Idbdabf9aa91fb170f48b21fe05bd4fefe4a2ae6e
Reviewed-on: https://chromium-review.googlesource.com/c/1436473Reviewed-by: default avatarHirokazu Honda <hiroh@chromium.org>
Commit-Queue: David Staessens <dstaessens@chromium.org>
Cr-Commit-Position: refs/heads/master@{#626427}
parent 051719f9
...@@ -638,6 +638,7 @@ if (is_chromeos) { ...@@ -638,6 +638,7 @@ if (is_chromeos) {
] ]
deps = [ deps = [
":buildflags", ":buildflags",
"test:frame_file_writer",
"test:frame_validator", "test:frame_validator",
"test:video_player", "test:video_player",
"//base/test:test_support", "//base/test:test_support",
......
...@@ -76,6 +76,23 @@ source_set("frame_validator") { ...@@ -76,6 +76,23 @@ source_set("frame_validator") {
] ]
} }
# TODO(dstaessens@) Make this work on other platforms too.
if (is_chromeos) {
source_set("frame_file_writer") {
testonly = true
sources = [
"video_frame_file_writer.cc",
"video_frame_file_writer.h",
]
deps = [
":frame_mapper",
":render_helpers",
"//media/gpu",
"//ui/gfx/codec:codec",
]
}
}
source_set("decode_helpers") { source_set("decode_helpers") {
testonly = true testonly = true
sources = [ sources = [
......
// 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.
#include "media/gpu/test/video_frame_file_writer.h"
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "media/gpu/test/video_frame_mapper.h"
#include "media/gpu/test/video_frame_mapper_factory.h"
#include "ui/gfx/codec/png_codec.h"
// Default output folder used to store frames.
constexpr const base::FilePath::CharType* kDefaultOutputFolder =
FILE_PATH_LITERAL("video_frames");
namespace media {
namespace test {
VideoFrameFileWriter::VideoFrameFileWriter()
: num_frames_writing_(0),
frame_writer_thread_("FrameWriterThread"),
frame_writer_cv_(&frame_writer_lock_) {
DETACH_FROM_SEQUENCE(writer_sequence_checker_);
DETACH_FROM_SEQUENCE(writer_thread_sequence_checker_);
}
VideoFrameFileWriter::~VideoFrameFileWriter() {
Destroy();
}
// static
std::unique_ptr<VideoFrameFileWriter> VideoFrameFileWriter::Create() {
auto frame_file_writer = base::WrapUnique(new VideoFrameFileWriter());
if (!frame_file_writer->Initialize()) {
LOG(ERROR) << "Failed to initialize VideoFrameFileWriter";
return nullptr;
}
return frame_file_writer;
}
bool VideoFrameFileWriter::Initialize() {
video_frame_mapper_ = VideoFrameMapperFactory::CreateMapper();
if (!video_frame_mapper_) {
LOG(ERROR) << "Failed to create VideoFrameMapper";
return false;
}
if (!frame_writer_thread_.Start()) {
LOG(ERROR) << "Failed to start file writer thread";
return false;
}
SetOutputFolder(base::FilePath(kDefaultOutputFolder));
return true;
}
void VideoFrameFileWriter::Destroy() {
base::AutoLock auto_lock(frame_writer_lock_);
DCHECK_EQ(0u, num_frames_writing_);
frame_writer_thread_.Stop();
}
void VideoFrameFileWriter::SetOutputFolder(
const base::FilePath& output_folder) {
base::AutoLock auto_lock(frame_writer_lock_);
DCHECK_EQ(0u, num_frames_writing_);
output_folder_ = output_folder;
// If the directory is not absolute, consider it relative to our working dir.
if (!output_folder_.IsAbsolute()) {
output_folder_ = base::MakeAbsoluteFilePath(
base::FilePath(base::FilePath::kCurrentDirectory))
.Append(output_folder_);
}
// Create the directory tree if it doesn't exist yet.
if (!DirectoryExists(output_folder_)) {
base::CreateDirectory(output_folder_);
}
}
void VideoFrameFileWriter::WaitUntilDone() const {
base::AutoLock auto_lock(frame_writer_lock_);
while (num_frames_writing_ > 0) {
frame_writer_cv_.Wait();
}
}
void VideoFrameFileWriter::ProcessVideoFrame(
scoped_refptr<const VideoFrame> video_frame,
size_t frame_index) {
DCHECK_CALLED_ON_VALID_SEQUENCE(writer_sequence_checker_);
base::AutoLock auto_lock(frame_writer_lock_);
num_frames_writing_++;
// Unretained is safe here, as we should not destroy the writer while there
// are still frames being written.
frame_writer_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&VideoFrameFileWriter::ProcessVideoFrameTask,
base::Unretained(this), video_frame, frame_index));
}
void VideoFrameFileWriter::ProcessVideoFrameTask(
scoped_refptr<const VideoFrame> video_frame,
size_t frame_index) {
DCHECK_CALLED_ON_VALID_SEQUENCE(writer_thread_sequence_checker_);
auto mapped_frame = video_frame_mapper_->Map(std::move(video_frame));
if (!mapped_frame) {
LOG(ERROR) << "Failed to map video frame";
return;
}
auto argb_out_frame = ConvertVideoFrame(mapped_frame.get(),
VideoPixelFormat::PIXEL_FORMAT_ARGB);
// Convert the ARGB frame to PNG.
std::vector<uint8_t> png_output;
const bool png_encode_status = gfx::PNGCodec::Encode(
argb_out_frame->data(VideoFrame::kARGBPlane), gfx::PNGCodec::FORMAT_BGRA,
argb_out_frame->visible_rect().size(),
argb_out_frame->stride(VideoFrame::kARGBPlane),
true, /* discard_transparency */
std::vector<gfx::PNGCodec::Comment>(), &png_output);
LOG_ASSERT(png_encode_status);
base::FilePath filename;
{
base::AutoLock auto_lock(frame_writer_lock_);
filename = output_folder_.Append("frame_.png")
.InsertBeforeExtension(base::IntToString(frame_index));
}
// Write the PNG data to file.
const int size = base::checked_cast<int>(png_output.size());
const int bytes_written = base::WriteFile(
filename, reinterpret_cast<char*>(png_output.data()), size);
LOG_ASSERT(bytes_written == size);
base::AutoLock auto_lock(frame_writer_lock_);
num_frames_writing_--;
frame_writer_cv_.Signal();
}
} // namespace test
} // namespace media
// 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.
#ifndef MEDIA_GPU_TEST_VIDEO_FRAME_FILE_WRITER_H_
#define MEDIA_GPU_TEST_VIDEO_FRAME_FILE_WRITER_H_
#include <memory>
#include "base/files/file_path.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
#include "media/gpu/test/video_frame_helpers.h"
namespace media {
namespace test {
class VideoFrameMapper;
// The video frame file writer class implements functionality to write video
// frames to file. Currently only the PNG output format is supported.
class VideoFrameFileWriter : public VideoFrameProcessor {
public:
~VideoFrameFileWriter() override;
// Create an instance of the video frame file writer.
static std::unique_ptr<VideoFrameFileWriter> Create();
// Set the folder the video frame files will be written to.
void SetOutputFolder(const base::FilePath& output_folder);
// Wait until all currently scheduled frame write operations are done.
void WaitUntilDone() const;
// Interface VideoFrameProcessor
void ProcessVideoFrame(scoped_refptr<const VideoFrame> video_frame,
size_t frame_index) override;
private:
VideoFrameFileWriter();
// Initialize the video frame file writer.
bool Initialize();
// Destroy the video frame file writer.
void Destroy();
// Writes the specified video frame to file on the |file_writer_thread_|.
void ProcessVideoFrameTask(scoped_refptr<const VideoFrame> video_frame,
size_t frame_index);
// Output folder the frames will be written to.
base::FilePath output_folder_ GUARDED_BY(frame_writer_lock_);
// The video frame mapper used to gain access to the raw video frame memory.
std::unique_ptr<VideoFrameMapper> video_frame_mapper_;
// The number of frames currently queued for writing.
size_t num_frames_writing_ GUARDED_BY(frame_writer_lock_);
// Thread on which video frame writing is done.
base::Thread frame_writer_thread_;
mutable base::Lock frame_writer_lock_;
mutable base::ConditionVariable frame_writer_cv_;
SEQUENCE_CHECKER(writer_sequence_checker_);
SEQUENCE_CHECKER(writer_thread_sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(VideoFrameFileWriter);
};
} // namespace test
} // namespace media
#endif // MEDIA_GPU_TEST_VIDEO_FRAME_FILE_WRITER_H_
...@@ -221,6 +221,7 @@ std::string VideoFrameValidator::ComputeMD5FromVideoFrame( ...@@ -221,6 +221,7 @@ std::string VideoFrameValidator::ComputeMD5FromVideoFrame(
return MD5DigestToBase16(digest); return MD5DigestToBase16(digest);
} }
// TODO(dstaessens@) Move to video_frame_writer.h
bool VideoFrameValidator::WriteI420ToFile( bool VideoFrameValidator::WriteI420ToFile(
size_t frame_index, size_t frame_index,
const VideoFrame* const video_frame) const { const VideoFrame* const video_frame) const {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/test/test_timeouts.h" #include "base/test/test_timeouts.h"
#include "media/base/test_data_util.h" #include "media/base/test_data_util.h"
#include "media/gpu/buildflags.h" #include "media/gpu/buildflags.h"
#include "media/gpu/test/video_frame_file_writer.h"
#include "media/gpu/test/video_frame_validator.h" #include "media/gpu/test/video_frame_validator.h"
#include "media/gpu/test/video_player/frame_renderer_dummy.h" #include "media/gpu/test/video_player/frame_renderer_dummy.h"
#include "media/gpu/test/video_player/video.h" #include "media/gpu/test/video_player/video.h"
...@@ -39,6 +40,7 @@ class VideoDecoderTestEnvironment : public ::testing::Environment { ...@@ -39,6 +40,7 @@ class VideoDecoderTestEnvironment : public ::testing::Environment {
std::unique_ptr<base::test::ScopedTaskEnvironment> task_environment_; std::unique_ptr<base::test::ScopedTaskEnvironment> task_environment_;
std::unique_ptr<FrameRendererDummy> dummy_frame_renderer_; std::unique_ptr<FrameRendererDummy> dummy_frame_renderer_;
std::unique_ptr<VideoFrameFileWriter> frame_file_writer_;
const Video* const video_; const Video* const video_;
// An exit manager is required to run callbacks on shutdown. // An exit manager is required to run callbacks on shutdown.
...@@ -64,9 +66,12 @@ void VideoDecoderTestEnvironment::SetUp() { ...@@ -64,9 +66,12 @@ void VideoDecoderTestEnvironment::SetUp() {
dummy_frame_renderer_ = FrameRendererDummy::Create(); dummy_frame_renderer_ = FrameRendererDummy::Create();
ASSERT_NE(dummy_frame_renderer_, nullptr); ASSERT_NE(dummy_frame_renderer_, nullptr);
frame_file_writer_ = media::test::VideoFrameFileWriter::Create();
} }
void VideoDecoderTestEnvironment::TearDown() { void VideoDecoderTestEnvironment::TearDown() {
frame_file_writer_.reset();
dummy_frame_renderer_.reset(); dummy_frame_renderer_.reset();
task_environment_.reset(); task_environment_.reset();
} }
...@@ -81,10 +86,24 @@ class VideoDecoderTest : public ::testing::Test { ...@@ -81,10 +86,24 @@ class VideoDecoderTest : public ::testing::Test {
const VideoDecoderClientConfig& config = VideoDecoderClientConfig()) { const VideoDecoderClientConfig& config = VideoDecoderClientConfig()) {
frame_validator_ = frame_validator_ =
media::test::VideoFrameValidator::Create(video->FrameChecksums()); media::test::VideoFrameValidator::Create(video->FrameChecksums());
return VideoPlayer::Create(video, g_env->dummy_frame_renderer_.get(), // TODO(dstaessens@) allow enabling/disabling file writing
{frame_validator_.get()}, config); return VideoPlayer::Create(
video, g_env->dummy_frame_renderer_.get(),
{frame_validator_.get(), g_env->frame_file_writer_.get()}, config);
}
void SetUp() override {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
// Change the video frame output folder to 'video_frames/test_name/'.
base::FilePath output_folder =
base::FilePath("video_frames")
.Append(base::FilePath(test_info->name()));
g_env->frame_file_writer_->SetOutputFolder(output_folder);
} }
void TearDown() override { g_env->frame_file_writer_->WaitUntilDone(); }
protected: protected:
std::unique_ptr<VideoFrameValidator> frame_validator_; std::unique_ptr<VideoFrameValidator> frame_validator_;
}; };
......
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