Commit adc481c9 authored by Hirokazu Honda's avatar Hirokazu Honda Committed by Commit Bot

media/gpu/test VideoFrameValidator: Compare each decoded picture (yuv) with golden md5

Decoded pictures (i.e. yuv format) on h264, vp8 and vp9 must be identical on any
platforms, except its pixel format and device alignment.
VideoFrameValidator checks soundness of each decoded picture comparing its
md5sum value with golden md5sum.
VideoFrameMapper used by VideoFrameValidator map decoded pictures and converts
them, by libyuv, to I420 picture to resolve pixel format and device alignment
differences.

This change is the first step for it. VideoFrameValidator would support non
tiled buffers.

BUG=chromium:856562
TEST=VDA unittest on h264, vp8 and vp9 with --frame_validator at veyron_minnie and kevin,
$ ./video_decode_accelerator_unittest --test_video_data=test-25fps.vp8:320:240:250:250:35:150:11 --ozone-platform=gbm --gtest_filter=Thumbnail/VideoDecodeAcceleratorParamTest.TestSimpleDecode/0 --test_import --frame_validator

Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: Iddd284bd47b2a5bb9921dc8d54f9d77ed694f5ad
Reviewed-on: https://chromium-review.googlesource.com/c/1172266
Commit-Queue: Hirokazu Honda <hiroh@chromium.org>
Reviewed-by: default avatarAlexandre Courbot <acourbot@chromium.org>
Cr-Commit-Position: refs/heads/master@{#601216}
parent d7070cb7
......@@ -396,10 +396,16 @@ if (is_win || is_android || use_v4l2_codec || use_vaapi) {
"test/rendering_helper.h",
"test/video_decode_accelerator_unittest_helpers.cc",
"test/video_decode_accelerator_unittest_helpers.h",
"test/video_frame_mapper.h",
"test/video_frame_mapper_factory.cc",
"test/video_frame_mapper_factory.h",
"test/video_frame_validator.cc",
"test/video_frame_validator.h",
"video_decode_accelerator_unittest.cc",
]
deps += [
"//mojo/core/embedder",
"//third_party/libyuv",
"//ui/display",
"//ui/display/types",
"//ui/platform_window",
......@@ -424,6 +430,13 @@ if (is_win || is_android || use_v4l2_codec || use_vaapi) {
]
}
if (is_chromeos) {
sources += [
"test/generic_dmabuf_video_frame_mapper.cc",
"test/generic_dmabuf_video_frame_mapper.h",
]
}
if (use_x11) {
configs += [ "//build/config/linux:x11" ]
deps += [ "//ui/gfx/x" ]
......
// Copyright 2018 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/generic_dmabuf_video_frame_mapper.h"
#include <sys/mman.h>
#include <algorithm>
#include <utility>
#include <vector>
#include "base/bind.h"
#define VLOGF(level) VLOG(level) << __func__ << "(): "
namespace media {
namespace test {
namespace {
constexpr size_t kNumOfYUVPlanes = 3;
uint8_t* Mmap(const size_t length, const int fd) {
void* addr = mmap(nullptr, length, PROT_READ, MAP_PRIVATE, fd, 0u);
if (addr == MAP_FAILED) {
VLOGF(1) << "Failed to mmap.";
return nullptr;
}
return static_cast<uint8_t*>(addr);
}
void MunmapBuffers(const std::vector<std::pair<uint8_t*, size_t>>& chunks,
scoped_refptr<VideoFrame> video_frame) {
for (const auto& chunk : chunks) {
DLOG_IF(ERROR, !chunk.first) << "Pointer to be released is nullptr.";
munmap(chunk.first, chunk.second);
}
}
// Create VideoFrame whose dtor deallocates memory in mapped planes referred
// by |plane_addrs|. |plane_addrs| are addresses to (Y, U, V) in this order.
// |chunks| is the vector of pair of (address, size) to be called in munmap().
// |src_video_frame| is the video frame that owns dmabufs to the mapped planes.
scoped_refptr<VideoFrame> CreateMappedVideoFrame(
const VideoFrameLayout& layout,
const gfx::Rect& visible_rect,
uint8_t* plane_addrs[kNumOfYUVPlanes],
const std::vector<std::pair<uint8_t*, size_t>>& chunks,
scoped_refptr<VideoFrame> src_video_frame) {
int32_t strides[kNumOfYUVPlanes] = {};
for (size_t i = 0; i < layout.num_planes(); i++) {
strides[i] = layout.planes()[i].stride;
}
if (layout.format() == PIXEL_FORMAT_YV12) {
// Swap the address of U and V planes, because the order U and V planes is
// reversed in YV12.
std::swap(plane_addrs[1], plane_addrs[2]);
}
auto video_frame = VideoFrame::WrapExternalYuvData(
layout.format(), layout.coded_size(), visible_rect, visible_rect.size(),
strides[0], strides[1], strides[2], plane_addrs[0], plane_addrs[1],
plane_addrs[2], base::TimeDelta());
if (!video_frame) {
return nullptr;
}
// Pass org_video_frame so that it outlives video_frame.
video_frame->AddDestructionObserver(
base::BindOnce(MunmapBuffers, chunks, std::move(src_video_frame)));
return video_frame;
}
} // namespace
scoped_refptr<VideoFrame> GenericDmaBufVideoFrameMapper::Map(
scoped_refptr<VideoFrame> video_frame) const {
if (video_frame->storage_type() != VideoFrame::StorageType::STORAGE_DMABUFS) {
VLOGF(1) << "VideoFrame's storage type is not DMABUF: "
<< video_frame->storage_type();
return nullptr;
}
const VideoFrameLayout& layout = video_frame->layout();
const VideoPixelFormat pixel_format = layout.format();
// GenericDmaBufVideoFrameMapper only supports NV12 and YV12 for output
// picture format.
if (pixel_format != PIXEL_FORMAT_NV12 && pixel_format != PIXEL_FORMAT_YV12) {
NOTIMPLEMENTED() << " Unsupported PixelFormat: " << pixel_format;
return nullptr;
}
// Map all buffers from their start address.
const auto& dmabuf_fds = video_frame->DmabufFds();
std::vector<std::pair<uint8_t*, size_t>> chunks;
const auto& buffer_sizes = layout.buffer_sizes();
std::vector<uint8_t*> buffer_addrs(buffer_sizes.size(), nullptr);
DCHECK_EQ(buffer_addrs.size(), dmabuf_fds.size());
DCHECK_LE(buffer_addrs.size(), kNumOfYUVPlanes);
for (size_t i = 0; i < dmabuf_fds.size(); i++) {
buffer_addrs[i] = Mmap(buffer_sizes[i], dmabuf_fds[i].get());
if (!buffer_addrs[i]) {
MunmapBuffers(chunks, std::move(video_frame));
return nullptr;
}
chunks.emplace_back(buffer_addrs[i], buffer_sizes[i]);
}
// Always prepare 3 addresses for planes initialized by nullptr.
// This enables to specify nullptr to redundant plane, for pixel format whose
// number of planes are less than 3.
const auto& planes = layout.planes();
const size_t num_of_planes = layout.num_planes();
uint8_t* plane_addrs[kNumOfYUVPlanes] = {};
if (dmabuf_fds.size() == 1) {
for (size_t i = 0; i < num_of_planes; i++) {
plane_addrs[i] = buffer_addrs[0] + planes[i].offset;
}
} else {
for (size_t i = 0; i < num_of_planes; i++) {
plane_addrs[i] = buffer_addrs[i] + planes[i].offset;
}
}
return CreateMappedVideoFrame(layout, video_frame->visible_rect(),
plane_addrs, chunks, std::move(video_frame));
}
} // namespace test
} // namespace media
// Copyright 2018 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_GENERIC_DMABUF_VIDEO_FRAME_MAPPER_H_
#define MEDIA_GPU_TEST_GENERIC_DMABUF_VIDEO_FRAME_MAPPER_H_
#include "media/gpu/test/video_frame_mapper.h"
namespace media {
namespace test {
// VideoFrameMapper for DMAbuf-backed VideoFrame.
class GenericDmaBufVideoFrameMapper : public VideoFrameMapper {
public:
GenericDmaBufVideoFrameMapper() = default;
~GenericDmaBufVideoFrameMapper() override = default;
// VideoFrameMapper implementation.
scoped_refptr<VideoFrame> Map(
scoped_refptr<VideoFrame> video_frame) const override;
DISALLOW_COPY_AND_ASSIGN(GenericDmaBufVideoFrameMapper);
};
} // namespace test
} // namespace media
#endif // MEDIA_GPU_TEST_GENERIC_DMABUF_VIDEO_FRAME_MAPPER_H_
......@@ -4,8 +4,11 @@
#include "media/gpu/test/video_decode_accelerator_unittest_helpers.h"
#include <memory>
#include "base/callback_helpers.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/strings/string_split.h"
#include "base/threading/thread_task_runner_handle.h"
#include "media/base/video_decoder_config.h"
......@@ -111,6 +114,7 @@ scoped_refptr<TextureRef> TextureRef::CreatePreallocated(
buffer_format,
gfx::BufferUsage::SCANOUT_VDA_WRITE);
LOG_ASSERT(texture_ref->pixmap_);
texture_ref->coded_size_ = size;
#endif
return texture_ref;
......@@ -142,6 +146,53 @@ gfx::GpuMemoryBufferHandle TextureRef::ExportGpuMemoryBufferHandle() const {
return handle;
}
scoped_refptr<VideoFrame> TextureRef::CreateVideoFrame(
const gfx::Rect& visible_rect) const {
scoped_refptr<VideoFrame> video_frame;
#if defined(OS_CHROMEOS)
VideoPixelFormat pixel_format =
GfxBufferFormatToVideoPixelFormat(pixmap_->GetBufferFormat());
CHECK_NE(pixel_format, PIXEL_FORMAT_UNKNOWN);
size_t num_planes = VideoFrame::NumPlanes(pixel_format);
std::vector<VideoFrameLayout::Plane> planes(num_planes);
std::vector<int> plane_height(num_planes, 0u);
for (size_t i = 0; i < num_planes; ++i) {
planes[i].stride = pixmap_->GetDmaBufPitch(i);
planes[i].offset = pixmap_->GetDmaBufOffset(i);
plane_height[i] = VideoFrame::Rows(i, pixel_format, coded_size_.height());
}
std::vector<base::ScopedFD> dmabuf_fds;
size_t num_fds = pixmap_->GetDmaBufFdCount();
LOG_ASSERT(num_fds <= num_planes);
for (size_t i = 0; i < num_fds; ++i) {
int duped_fd = HANDLE_EINTR(dup(pixmap_->GetDmaBufFd(i)));
LOG_ASSERT(duped_fd != -1) << "Failed duplicating dmabuf fd";
dmabuf_fds.emplace_back(duped_fd);
}
std::vector<size_t> buffer_sizes(num_fds, 0u);
for (size_t plane = 0, i = 0; plane < num_planes; ++plane) {
if (plane + 1 < buffer_sizes.size()) {
buffer_sizes[i] =
planes[plane].offset + planes[plane].stride * plane_height[plane];
++i;
} else {
buffer_sizes[i] = std::max(
buffer_sizes[i],
planes[plane].offset + planes[plane].stride * plane_height[plane]);
}
}
auto layout = VideoFrameLayout::CreateWithPlanes(
pixel_format, coded_size_, std::move(planes), std::move(buffer_sizes));
LOG_ASSERT(layout.has_value() == true);
video_frame = VideoFrame::WrapExternalDmabufs(
*layout, visible_rect, visible_rect.size(), std::move(dmabuf_fds),
base::TimeDelta());
#endif
return video_frame;
}
EncodedDataHelper::EncodedDataHelper(const std::string& data,
VideoCodecProfile profile)
: data_(data), profile_(profile) {}
......
......@@ -17,6 +17,7 @@
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "media/base/video_codecs.h"
#include "media/base/video_frame.h"
#include "media/base/video_types.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/size.h"
......@@ -69,6 +70,8 @@ class TextureRef : public base::RefCounted<TextureRef> {
const gfx::Size& size);
gfx::GpuMemoryBufferHandle ExportGpuMemoryBufferHandle() const;
scoped_refptr<VideoFrame> CreateVideoFrame(
const gfx::Rect& visible_rect) const;
int32_t texture_id() const { return texture_id_; }
......@@ -82,6 +85,7 @@ class TextureRef : public base::RefCounted<TextureRef> {
base::OnceClosure no_longer_needed_cb_;
#if defined(OS_CHROMEOS)
scoped_refptr<gfx::NativePixmap> pixmap_;
gfx::Size coded_size_;
#endif
THREAD_CHECKER(thread_checker_);
};
......
// Copyright 2018 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_MAPPER_H_
#define MEDIA_GPU_TEST_VIDEO_FRAME_MAPPER_H_
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "media/base/video_frame.h"
namespace media {
namespace test {
// VideoFrameMapper is a class for mapping a video frame referred by VideoFrame.
// VideoFrameMapper should be created by using VideoFrameMapperFactory.
class VideoFrameMapper {
public:
virtual ~VideoFrameMapper() = default;
// Maps data referred by |video_frame| and creates a VideoFrame whose dtor
// unmap the mapped memory.
virtual scoped_refptr<VideoFrame> Map(
scoped_refptr<VideoFrame> video_frame) const = 0;
protected:
VideoFrameMapper() = default;
DISALLOW_COPY_AND_ASSIGN(VideoFrameMapper);
};
} // namespace test
} // namespace media
#endif // MEDIA_GPU_TEST_VIDEO_FRAME_MAPPER_H_
// Copyright 2018 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_mapper_factory.h"
#if defined(OS_CHROMEOS)
#include "media/gpu/test/generic_dmabuf_video_frame_mapper.h"
#endif
namespace media {
namespace test {
// static
std::unique_ptr<VideoFrameMapper> VideoFrameMapperFactory::CreateMapper() {
#if defined(OS_CHROMEOS)
return std::make_unique<GenericDmaBufVideoFrameMapper>();
#endif // defined(OS_CHROMEOS)
NOTREACHED();
return nullptr;
}
} // namespace test
} // namespace media
// Copyright 2018 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_MAPPER_FACTORY_H_
#define MEDIA_GPU_TEST_VIDEO_FRAME_MAPPER_FACTORY_H_
#include <memory>
#include "media/gpu/test/video_frame_mapper.h"
namespace media {
namespace test {
// A factory function for VideoFrameMapper.
// The appropriate VideoFrameMapper is a platform-dependent.
class VideoFrameMapperFactory {
public:
// Create an appropriate mapper on a platform.
static std::unique_ptr<VideoFrameMapper> CreateMapper();
};
} // namespace test
} // namespace media
#endif // MEDIA_GPU_TEST_GENERIC_VIDEO_FRAME_MAPPER_FACTORY_H_
// Copyright 2018 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_validator.h"
#include <libyuv.h>
#include "base/files/file.h"
#include "base/md5.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
namespace media {
namespace test {
// static
std::unique_ptr<VideoFrameValidator>
VideoFrameValidator::CreateVideoFrameValidator(
const base::FilePath& prefix_output_yuv,
const base::FilePath& md5_file_path) {
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;
}
return base::WrapUnique(
new VideoFrameValidator(prefix_output_yuv, std::move(md5_of_frames),
std::move(video_frame_mapper)));
}
VideoFrameValidator::VideoFrameValidator(
const base::FilePath& prefix_output_yuv,
std::vector<std::string> md5_of_frames,
std::unique_ptr<VideoFrameMapper> video_frame_mapper)
: prefix_output_yuv_(prefix_output_yuv),
md5_of_frames_(std::move(md5_of_frames)),
video_frame_mapper_(std::move(video_frame_mapper)) {
DETACH_FROM_THREAD(thread_checker_);
}
VideoFrameValidator::~VideoFrameValidator() {}
void VideoFrameValidator::EvaluateVideoFrame(
scoped_refptr<VideoFrame> video_frame) {
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_];
std::string computed_md5 = ComputeMD5FromVideoFrame(std::move(video_frame));
if (computed_md5 != expected_md5) {
mismatched_frames_.push_back(
MismatchedFrameInfo{frame_index_, computed_md5, expected_md5});
}
frame_index_++;
}
std::vector<VideoFrameValidator::MismatchedFrameInfo>
VideoFrameValidator::GetMismatchedFramesInfo() const {
return mismatched_frames_;
}
std::string VideoFrameValidator::ComputeMD5FromVideoFrame(
scoped_refptr<VideoFrame> video_frame) const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto mapped_frame = video_frame_mapper_->Map(std::move(video_frame));
if (!mapped_frame) {
LOG(FATAL) << "Failed to map decoded picture.";
return std::string();
}
auto i420_frame = CreateI420Frame(mapped_frame.get());
if (!i420_frame) {
LOG(ERROR) << "Failed to convert to I420";
return std::string();
}
base::MD5Context context;
base::MD5Init(&context);
VideoFrame::HashFrameForTesting(&context, i420_frame);
base::MD5Digest digest;
base::MD5Final(&digest, &context);
auto md5_string = MD5DigestToBase16(digest);
if (!prefix_output_yuv_.empty()) {
// Output yuv.
LOG_IF(WARNING, !WriteI420ToFile(frame_index_, i420_frame.get()))
<< "Failed to write yuv into file.";
}
return md5_string;
}
scoped_refptr<VideoFrame> VideoFrameValidator::CreateI420Frame(
const VideoFrame* const src_frame) const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
const auto& visible_rect = src_frame->visible_rect();
const int width = visible_rect.width();
const int height = visible_rect.height();
auto dst_frame = VideoFrame::CreateFrame(
PIXEL_FORMAT_I420, visible_rect.size(), visible_rect, visible_rect.size(),
base::TimeDelta());
uint8_t* const dst_y = dst_frame->data(VideoFrame::kYPlane);
uint8_t* const dst_u = dst_frame->data(VideoFrame::kUPlane);
uint8_t* const dst_v = dst_frame->data(VideoFrame::kVPlane);
const int dst_stride_y = dst_frame->stride(VideoFrame::kYPlane);
const int dst_stride_u = dst_frame->stride(VideoFrame::kUPlane);
const int dst_stride_v = dst_frame->stride(VideoFrame::kVPlane);
switch (src_frame->format()) {
case PIXEL_FORMAT_NV12:
libyuv::NV12ToI420(src_frame->data(VideoFrame::kYPlane),
src_frame->stride(VideoFrame::kYPlane),
src_frame->data(VideoFrame::kUVPlane),
src_frame->stride(VideoFrame::kUVPlane), dst_y,
dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v,
width, height);
break;
case PIXEL_FORMAT_YV12:
case PIXEL_FORMAT_I420:
libyuv::I420Copy(src_frame->data(VideoFrame::kYPlane),
src_frame->stride(VideoFrame::kYPlane),
src_frame->data(VideoFrame::kUPlane),
src_frame->stride(VideoFrame::kUPlane),
src_frame->data(VideoFrame::kVPlane),
src_frame->stride(VideoFrame::kVPlane), dst_y,
dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v,
width, height);
break;
default:
LOG(FATAL) << "Unsupported format: " << src_frame->format();
return nullptr;
}
return dst_frame;
}
bool VideoFrameValidator::WriteI420ToFile(
size_t frame_index,
const VideoFrame* const video_frame) const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (video_frame->format() != PIXEL_FORMAT_I420) {
LOG(ERROR) << "No I420 format frame.";
return false;
}
if (video_frame->storage_type() !=
VideoFrame::StorageType::STORAGE_OWNED_MEMORY) {
LOG(ERROR) << "Video frame doesn't own memory.";
return false;
}
const int width = video_frame->visible_rect().width();
const int height = video_frame->visible_rect().height();
base::FilePath::StringType output_yuv_fname;
base::SStringPrintf(&output_yuv_fname,
FILE_PATH_LITERAL("%04zu_%dx%d_I420.yuv"), frame_index,
width, height);
base::File yuv_file(prefix_output_yuv_.AddExtension(output_yuv_fname),
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_APPEND);
const size_t num_planes = VideoFrame::NumPlanes(video_frame->format());
for (size_t i = 0; i < num_planes; i++) {
size_t plane_w = VideoFrame::Rows(i, video_frame->format(), width);
size_t plane_h = VideoFrame::Rows(i, video_frame->format(), height);
int data_size = base::checked_cast<int>(plane_w * plane_h);
const uint8_t* data = video_frame->data(i);
if (yuv_file.Write(0, reinterpret_cast<const char*>(data), data_size) !=
data_size) {
LOG(ERROR) << "Fail to write file in plane #" << i;
return false;
}
}
return true;
}
} // namespace test
} // namespace media
// Copyright 2018 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_VALIDATOR_H_
#define MEDIA_GPU_TEST_VIDEO_FRAME_VALIDATOR_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/files/file_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/threading/thread_checker.h"
#include "media/base/video_frame.h"
#include "media/base/video_types.h"
#include "media/gpu/test/video_decode_accelerator_unittest_helpers.h"
#include "media/gpu/test/video_frame_mapper.h"
#include "media/gpu/test/video_frame_mapper_factory.h"
#include "ui/gfx/geometry/rect.h"
namespace media {
namespace test {
// VideoFrameValidator validates the pixel content of each video frame.
// It maps a video frame by using VideoFrameMapper, and converts the mapped
// frame to I420 format to resolve layout differences due to different pixel
// layouts/alignments on different platforms.
// Thereafter, it compares md5 values of the mapped and converted buffer with
// golden md5 values. The golden values are prepared in advance and must be
// identical on all platforms.
// Mapping and verification of a frame is a costly operation and will influence
// performance measurements.
class VideoFrameValidator {
public:
struct MismatchedFrameInfo {
size_t frame_index;
std::string computed_md5;
std::string expected_md5;
};
// |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.
// |md5_file_path| is the path to the file that contains golden md5 values.
// The file contains one md5 value per line, listed in display order.
// Returns nullptr on failure.
static std::unique_ptr<VideoFrameValidator> CreateVideoFrameValidator(
const base::FilePath& prefix_output_yuv,
const base::FilePath& md5_file_path);
~VideoFrameValidator();
// This checks if |video_frame|'s pixel content is as expected.
// A client of VideoFrameValidator would call this function on each frame in
// PictureReady().
// The client MUST pass video frames in display order.
// TODO(crbug.com/856562): Specify frame index and compare the index-th video
// frame, so that a client can call this in any order.
void EvaluateVideoFrame(scoped_refptr<VideoFrame> video_frame);
// Returns information of frames that don't match golden md5 values.
// If there is no mismatched frame, returns an empty vector.
std::vector<MismatchedFrameInfo> GetMismatchedFramesInfo() const;
private:
VideoFrameValidator(const base::FilePath& prefix_output_yuv,
std::vector<std::string> md5_of_frames,
std::unique_ptr<VideoFrameMapper> video_frame_mapper);
// This maps |video_frame|, converts it to I420 format and computes the MD5
// value of the converted I420 video frame.
// |video_frame| is unchanged in this method.
std::string ComputeMD5FromVideoFrame(
scoped_refptr<VideoFrame> video_frame) const;
// Create VideoFrame with I420 format from |src_frame|.
scoped_refptr<VideoFrame> CreateI420Frame(
const VideoFrame* const src_frame) const;
// Helper function to save I420 yuv image.
bool WriteI420ToFile(size_t frame_index,
const VideoFrame* const video_frame) const;
// The results of invalid frame data.
std::vector<MismatchedFrameInfo> mismatched_frames_;
// Current frame index to be evaluated.
size_t frame_index_ = 0;
// Prefix of saved yuv files.
const base::FilePath prefix_output_yuv_;
// Golden MD5 values.
const std::vector<std::string> md5_of_frames_;
const std::unique_ptr<VideoFrameMapper> video_frame_mapper_;
THREAD_CHECKER(thread_checker_);
DISALLOW_COPY_AND_ASSIGN(VideoFrameValidator);
};
} // namespace test
} // namespace media
#endif // MEDIA_GPU_TEST_VIDEO_FRAME_VALIDATOR_H_
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