Commit 493db4b2 authored by David Staessens's avatar David Staessens Committed by Commit Bot

media/gpu/test: Initial version of the Test Video player.

The test video player is created to improve the way VDA (video decode
accelerator) tests are created, and to allow more control of the test flow.

This is an early prototype, that only supports playing a video from start to
finish. More complex tests and their required functionality will be added soon.

TEST=ran new VDA tests on eve and kevin

BUG=879065

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: Ida7396e7294c0e367417c352284e4a601daa0cba
Reviewed-on: https://chromium-review.googlesource.com/c/1179484
Commit-Queue: David Staessens <dstaessens@chromium.org>
Reviewed-by: default avatarHirokazu Honda <hiroh@chromium.org>
Reviewed-by: default avatarTakuto Ikuta <tikuta@chromium.org>
Reviewed-by: default avatarAlexandre Courbot <acourbot@chromium.org>
Cr-Commit-Position: refs/heads/master@{#609622}
parent 12e13b30
...@@ -304,6 +304,10 @@ group("gn_all") { ...@@ -304,6 +304,10 @@ group("gn_all") {
"//content/public/android:content_junit_tests", "//content/public/android:content_junit_tests",
"//content/shell/android:content_shell_apk", "//content/shell/android:content_shell_apk",
"//device:device_junit_tests", "//device:device_junit_tests",
"//media/gpu:video_decode_accelerator_tests",
# TODO(https://crbug.com/879065): remove once tests have been migrated to
# the above target.
"//media/gpu:video_decode_accelerator_unittest", "//media/gpu:video_decode_accelerator_unittest",
"//net/android:net_junit_tests", "//net/android:net_junit_tests",
"//services:service_junit_tests", "//services:service_junit_tests",
...@@ -883,6 +887,10 @@ if (is_chromeos) { ...@@ -883,6 +887,10 @@ if (is_chromeos) {
if (use_v4l2_codec || use_vaapi) { if (use_v4l2_codec || use_vaapi) {
deps += [ deps += [
"//media/gpu:video_decode_accelerator_tests",
# TODO(https://crbug.com/879065): remove once tests have been migrated
# to the above target.
"//media/gpu:video_decode_accelerator_unittest", "//media/gpu:video_decode_accelerator_unittest",
"//media/gpu:video_encode_accelerator_unittest", "//media/gpu:video_encode_accelerator_unittest",
] ]
......
...@@ -74,6 +74,7 @@ component("gpu") { ...@@ -74,6 +74,7 @@ component("gpu") {
"//content/renderer:*", "//content/renderer:*",
"//media/gpu/ipc/*", "//media/gpu/ipc/*",
"//media/gpu/vaapi/*", "//media/gpu/vaapi/*",
"//media/gpu/test/*",
"//media/mojo/*", "//media/mojo/*",
"//remoting/codec:encoder", "//remoting/codec:encoder",
":*", ":*",
...@@ -391,23 +392,10 @@ if (is_win || is_android || use_v4l2_codec || use_vaapi) { ...@@ -391,23 +392,10 @@ if (is_win || is_android || use_v4l2_codec || use_vaapi) {
configs += [ "//third_party/khronos:khronos_headers" ] configs += [ "//third_party/khronos:khronos_headers" ]
if (is_win || is_chromeos || use_v4l2_codec) { if (is_win || is_chromeos || use_v4l2_codec) {
sources += [ sources += [ "video_decode_accelerator_unittest.cc" ]
"test/rendering_helper.cc",
"test/rendering_helper.h",
"test/texture_ref.cc",
"test/texture_ref.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 += [ deps += [
"test:helpers",
"//mojo/core/embedder", "//mojo/core/embedder",
"//third_party/libyuv",
"//ui/display", "//ui/display",
"//ui/display/types", "//ui/display/types",
"//ui/platform_window", "//ui/platform_window",
...@@ -432,19 +420,6 @@ if (is_win || is_android || use_v4l2_codec || use_vaapi) { ...@@ -432,19 +420,6 @@ 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_vaapi) {
sources += [
"test/vaapi_dmabuf_video_frame_mapper.cc",
"test/vaapi_dmabuf_video_frame_mapper.h",
]
}
}
if (use_x11) { if (use_x11) {
configs += [ "//build/config/linux:x11" ] configs += [ "//build/config/linux:x11" ]
deps += [ "//ui/gfx/x" ] deps += [ "//ui/gfx/x" ]
...@@ -502,6 +477,7 @@ source_set("android_video_decode_accelerator_unittests") { ...@@ -502,6 +477,7 @@ source_set("android_video_decode_accelerator_unittests") {
if (use_v4l2_codec || use_vaapi || is_mac || is_win) { if (use_v4l2_codec || use_vaapi || is_mac || is_win) {
test("video_encode_accelerator_unittest") { test("video_encode_accelerator_unittest") {
deps = [ deps = [
"test:helpers",
"//base", "//base",
"//base/test:test_support", "//base/test:test_support",
"//media:test_support", "//media:test_support",
...@@ -517,14 +493,6 @@ if (use_v4l2_codec || use_vaapi || is_mac || is_win) { ...@@ -517,14 +493,6 @@ if (use_v4l2_codec || use_vaapi || is_mac || is_win) {
] ]
configs += [ "//third_party/libyuv:libyuv_config" ] configs += [ "//third_party/libyuv:libyuv_config" ]
sources = [ sources = [
"test/texture_ref.cc",
"test/texture_ref.h",
"test/video_accelerator_unittest_helpers.h",
"test/video_encode_accelerator_unittest_helpers.cc",
"test/video_encode_accelerator_unittest_helpers.h",
"test/video_frame_mapper.h",
"test/video_frame_mapper_factory.cc",
"test/video_frame_mapper_factory.h",
"video_encode_accelerator_unittest.cc", "video_encode_accelerator_unittest.cc",
] ]
if (use_x11) { if (use_x11) {
...@@ -533,18 +501,6 @@ if (use_v4l2_codec || use_vaapi || is_mac || is_win) { ...@@ -533,18 +501,6 @@ if (use_v4l2_codec || use_vaapi || is_mac || is_win) {
if (use_ozone) { if (use_ozone) {
deps += [ "//ui/ozone" ] deps += [ "//ui/ozone" ]
} }
if (is_chromeos) {
sources += [
"test/generic_dmabuf_video_frame_mapper.cc",
"test/generic_dmabuf_video_frame_mapper.h",
]
if (use_vaapi) {
sources += [
"test/vaapi_dmabuf_video_frame_mapper.cc",
"test/vaapi_dmabuf_video_frame_mapper.h",
]
}
}
} }
} }
...@@ -673,3 +629,22 @@ source_set("unit_tests") { ...@@ -673,3 +629,22 @@ source_set("unit_tests") {
libs = [ "dxguid.lib" ] libs = [ "dxguid.lib" ]
} }
} }
test("video_decode_accelerator_tests") {
sources = [
"video_decode_accelerator_tests.cc",
]
data = [
"//media/test/data/",
]
deps = [
":buildflags",
"test:video_player",
"//base/test:test_support",
"//media:test_support",
"//testing/gtest",
]
if (use_vaapi) {
deps += [ "//media/gpu/vaapi" ]
}
}
# 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.
import("//build/config/ui.gni")
import("//media/gpu/args.gni")
# TODO(dstaessens@) Split up in encode/decode/render/common helpers
source_set("helpers") {
testonly = true
sources = [
"rendering_helper.cc",
"rendering_helper.h",
"texture_ref.cc",
"texture_ref.h",
"video_accelerator_unittest_helpers.h",
"video_decode_accelerator_unittest_helpers.cc",
"video_decode_accelerator_unittest_helpers.h",
"video_encode_accelerator_unittest_helpers.cc",
"video_encode_accelerator_unittest_helpers.h",
"video_frame_mapper.h",
"video_frame_mapper_factory.cc",
"video_frame_mapper_factory.h",
"video_frame_validator.cc",
"video_frame_validator.h",
]
if (is_chromeos) {
sources += [
"generic_dmabuf_video_frame_mapper.cc",
"generic_dmabuf_video_frame_mapper.h",
]
if (use_vaapi) {
sources += [
"vaapi_dmabuf_video_frame_mapper.cc",
"vaapi_dmabuf_video_frame_mapper.h",
]
}
}
deps = [
"//media/gpu",
"//testing/gtest",
"//third_party/libyuv",
"//ui/gl/init:init",
]
if (use_ozone) {
deps += [ "//ui/ozone" ]
}
}
static_library("video_player") {
testonly = true
sources = [
"video_player/frame_renderer.h",
"video_player/frame_renderer_dummy.cc",
"video_player/frame_renderer_dummy.h",
"video_player/video.cc",
"video_player/video.h",
"video_player/video_collection.cc",
"video_player/video_collection.h",
"video_player/video_decoder_client.cc",
"video_player/video_decoder_client.h",
"video_player/video_player.cc",
"video_player/video_player.h",
]
data = [
"//media/test/data/",
]
deps = [
":helpers",
"//media/gpu",
]
if (use_ozone) {
deps += [ "//ui/ozone" ]
}
}
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
#include "media/gpu/test/video_decode_accelerator_unittest_helpers.h" #include "media/gpu/test/video_decode_accelerator_unittest_helpers.h"
#include <utility>
#include "base/callback_helpers.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/files/scoped_file.h" #include "base/files/scoped_file.h"
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
...@@ -73,6 +76,13 @@ EncodedDataHelper::EncodedDataHelper(const std::string& data, ...@@ -73,6 +76,13 @@ EncodedDataHelper::EncodedDataHelper(const std::string& data,
VideoCodecProfile profile) VideoCodecProfile profile)
: data_(data), profile_(profile) {} : data_(data), profile_(profile) {}
EncodedDataHelper::EncodedDataHelper(const std::vector<uint8_t>& stream,
VideoCodecProfile profile)
: EncodedDataHelper(
std::string(reinterpret_cast<const char*>(stream.data()),
stream.size()),
profile) {}
EncodedDataHelper::~EncodedDataHelper() { EncodedDataHelper::~EncodedDataHelper() {
base::STLClearObject(&data_); base::STLClearObject(&data_);
} }
......
...@@ -57,7 +57,10 @@ class VideoDecodeAcceleratorTestEnvironment : public ::testing::Environment { ...@@ -57,7 +57,10 @@ class VideoDecodeAcceleratorTestEnvironment : public ::testing::Environment {
class EncodedDataHelper { class EncodedDataHelper {
public: public:
// TODO(dstaessens@) Remove this constructor once the VDA tests are migrated.
EncodedDataHelper(const std::string& encoded_data, VideoCodecProfile profile); EncodedDataHelper(const std::string& encoded_data, VideoCodecProfile profile);
EncodedDataHelper(const std::vector<uint8_t>& stream,
VideoCodecProfile profile);
~EncodedDataHelper(); ~EncodedDataHelper();
// Compute and return the next fragment to be sent to the decoder, starting // Compute and return the next fragment to be sent to the decoder, starting
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "media/gpu/test/video_frame_validator.h" #include "media/gpu/test/video_frame_validator.h"
// TODO(dstaessens@) Avoid depending on video_decode_accelerator here.
#include "media/gpu/test/video_decode_accelerator_unittest_helpers.h"
#include <libyuv.h> #include <libyuv.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.
#ifndef MEDIA_GPU_TEST_VIDEO_PLAYER_FRAME_RENDERER_H_
#define MEDIA_GPU_TEST_VIDEO_PLAYER_FRAME_RENDERER_H_
#include <vector>
#include "base/callback_forward.h"
#include "media/base/video_types.h"
#include "media/video/picture.h"
#include "ui/gfx/geometry/size.h"
namespace gl {
class GLContext;
} // namespace gl
namespace media {
namespace test {
// The frame renderer interface can be used to render decoded frames to screen,
// file,... It is responsible for creating picture buffers and maintaining a GL
// context.
class FrameRenderer {
public:
using PictureBuffersCreatedCB =
base::OnceCallback<void(const std::vector<PictureBuffer>)>;
using PictureRenderedCB = base::OnceClosure;
virtual ~FrameRenderer() = default;
// Acquire the GL context for the current thread. This is needed if the
// context is shared between multiple threads.
virtual void AcquireGLContext() = 0;
// Release the GL context on the current thread.
virtual void ReleaseGLContext() = 0;
// Get the current GL context.
virtual gl::GLContext* GetGLContext() = 0;
// Create a set of picture buffers, |cb| should be called upon completion.
virtual void CreatePictureBuffers(size_t requested_num_of_buffers,
VideoPixelFormat pixel_format,
const gfx::Size& size,
uint32_t texture_target,
PictureBuffersCreatedCB cb) = 0;
// Render the specified picture, |cb| should be called once rendering is done
// so the decoder can reuse the picture buffer.
virtual void RenderPicture(const Picture& picture, PictureRenderedCB cb) = 0;
};
} // namespace test
} // namespace media
#endif // MEDIA_GPU_TEST_VIDEO_PLAYER_FRAME_RENDERER_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_player/frame_renderer_dummy.h"
#include <utility>
#include <vector>
#include "base/memory/ptr_util.h"
#include "ui/ozone/public/ozone_gpu_test_helper.h"
#include "ui/ozone/public/ozone_platform.h"
#define VLOGF(level) VLOG(level) << __func__ << "(): "
namespace media {
namespace test {
FrameRendererDummy::FrameRendererDummy() {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
FrameRendererDummy::~FrameRendererDummy() {
Destroy();
}
// static
std::unique_ptr<FrameRendererDummy> FrameRendererDummy::Create() {
auto frame_renderer = base::WrapUnique(new FrameRendererDummy());
if (!frame_renderer->Initialize()) {
return nullptr;
}
return frame_renderer;
}
bool FrameRendererDummy::Initialize() {
#ifdef USE_OZONE
// Initialize Ozone. This is necessary even though we are not doing any actual
// rendering. If not initialized a crash will occur when assigning picture
// buffers, even when passing 0 as texture ID.
// TODO(@dstaessens):
// * Get rid of the Ozone dependency, as it forces us to call 'stop ui' when
// running tests.
LOG(INFO) << "Initializing Ozone Platform...\n"
"If this hangs indefinitely please call 'stop ui' first!";
ui::OzonePlatform::InitParams params = {.single_process = false};
ui::OzonePlatform::InitializeForUI(params);
ui::OzonePlatform::InitializeForGPU(params);
ui::OzonePlatform::GetInstance()->AfterSandboxEntry();
// Initialize the Ozone GPU helper. If this is not done an error will occur:
// "Check failed: drm. No devices available for buffer allocation."
// Note: If a task environment is not set up initialization will hang
// indefinitely here.
gpu_helper_.reset(new ui::OzoneGpuTestHelper());
gpu_helper_->Initialize(base::ThreadTaskRunnerHandle::Get());
#endif
return true;
}
void FrameRendererDummy::Destroy() {
#ifdef USE_OZONE
gpu_helper_.reset();
#endif
}
void FrameRendererDummy::AcquireGLContext() {
// As no actual rendering is done we don't have a GLContext to acquire.
}
void FrameRendererDummy::ReleaseGLContext() {
// As no actual rendering is done we don't have a GLContext to release.
}
gl::GLContext* FrameRendererDummy::GetGLContext() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// As no actual rendering is done we don't have a GLContext.
return nullptr;
}
void FrameRendererDummy::CreatePictureBuffers(size_t requested_num_of_buffers,
VideoPixelFormat pixel_format,
const gfx::Size& size,
uint32_t texture_target,
PictureBuffersCreatedCB cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<PictureBuffer> buffers;
for (size_t i = 0; i < requested_num_of_buffers; ++i) {
buffers.emplace_back(GetNextPictureBufferId(), size);
}
std::move(cb).Run(std::move(buffers));
}
void FrameRendererDummy::RenderPicture(const Picture& picture,
PictureRenderedCB cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(cb).Run();
}
int32_t FrameRendererDummy::GetNextPictureBufferId() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The picture buffer ID should always be positive, negative values are
// reserved for uninitialized buffers.
next_picture_buffer_id_ = (next_picture_buffer_id_ + 1) & 0x7FFFFFFF;
return next_picture_buffer_id_;
}
} // 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_PLAYER_FRAME_RENDERER_DUMMY_H_
#define MEDIA_GPU_TEST_VIDEO_PLAYER_FRAME_RENDERER_DUMMY_H_
#include <memory>
#include "base/macros.h"
#include "base/sequence_checker.h"
#include "media/gpu/test/video_player/frame_renderer.h"
#ifdef USE_OZONE
namespace ui {
class OzoneGpuTestHelper;
} // namespace ui
#endif
namespace media {
namespace test {
// The dummy frame renderer can be used when we're not interested in rendering
// the decoded frames to screen or file. No rate-limiting is done, and frames
// are consumed as fast as they are provided.
class FrameRendererDummy : public FrameRenderer {
public:
~FrameRendererDummy() override;
// Create an instance of the dummy frame renderer.
static std::unique_ptr<FrameRendererDummy> Create();
void AcquireGLContext() override;
void ReleaseGLContext() override;
gl::GLContext* GetGLContext() override;
void CreatePictureBuffers(size_t requested_num_of_buffers,
VideoPixelFormat pixel_format,
const gfx::Size& size,
uint32_t texture_target,
PictureBuffersCreatedCB cb) override;
void RenderPicture(const Picture& picture, PictureRenderedCB cb) override;
private:
FrameRendererDummy();
// Initialize the frame renderer, performs all rendering-related setup.
bool Initialize();
// Destroy the frame renderer.
void Destroy();
// Get the next picture buffer id to be used.
int32_t GetNextPictureBufferId();
#ifdef USE_OZONE
std::unique_ptr<ui::OzoneGpuTestHelper> gpu_helper_;
#endif
int32_t next_picture_buffer_id_ = 0;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(FrameRendererDummy);
};
} // namespace test
} // namespace media
#endif // MEDIA_GPU_TEST_VIDEO_PLAYER_FRAME_RENDERER_DUMMY_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_player/video.h"
#include <utility>
#include "base/files/file_util.h"
#include "base/numerics/safe_conversions.h"
#define VLOGF(level) VLOG(level) << __func__ << "(): "
namespace media {
namespace test {
base::FilePath Video::test_data_path_ = base::FilePath();
Video::Video(const base::FilePath& file_path) : file_path_(file_path) {}
Video::~Video() {}
bool Video::Load() {
// TODO(dstaessens@) Investigate reusing existing infrastructure such as
// DecoderBuffer.
DCHECK(!file_path_.empty());
DCHECK(data_.empty());
// The specified path can be either an absolute path, a path relative to the
// current directory, or relative to the test data path.
if (!file_path_.IsAbsolute()) {
if (!PathExists(file_path_))
file_path_ = test_data_path_.Append(file_path_);
file_path_ = base::MakeAbsoluteFilePath(file_path_);
}
VLOGF(2) << "File path: " << file_path_;
int64_t file_size;
if (!base::GetFileSize(file_path_, &file_size) || (file_size < 0)) {
VLOGF(1) << "Failed to read file size: " << file_path_;
return false;
}
std::vector<uint8_t> data(file_size);
if (base::ReadFile(file_path_, reinterpret_cast<char*>(data.data()),
base::checked_cast<int>(file_size)) != file_size) {
VLOGF(1) << "Failed to read file: " << file_path_;
return false;
}
data_ = std::move(data);
// TODO(keiitchiw@) Get this from the metadata accompanying each video file.
profile_ = H264PROFILE_BASELINE;
return true;
}
bool Video::IsLoaded() const {
return data_.size() > 0;
}
const std::vector<uint8_t>& Video::GetData() const {
return data_;
}
VideoCodecProfile Video::GetProfile() const {
return profile_;
}
// static
void Video::SetTestDataPath(const base::FilePath& test_data_path) {
test_data_path_ = test_data_path;
}
} // 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_PLAYER_VIDEO_H_
#define MEDIA_GPU_TEST_VIDEO_PLAYER_VIDEO_H_
#include <vector>
#include "base/files/file_path.h"
#include "base/macros.h"
#include "media/base/video_codecs.h"
namespace media {
namespace test {
// The video class provides functionality to load video files and manage their
// properties such as codec, number of frames,...
// TODO(@dstaessens):
// * Define and add functionality to load video properties. Each video should
// have an accompanying json file containing: codec, number of frames,
// MD5 checksums for the frame validator,... We could even go as far as
// having an offset for each individual frame in the video
// * Use a file stream rather than loading potentially huge files into memory.
class Video {
public:
explicit Video(const base::FilePath& file_path);
~Video();
// Load the video file from disk.
bool Load();
// Returns true if the video file was loaded.
bool IsLoaded() const;
// Get the video data, will be empty if the video hasn't been loaded yet.
const std::vector<uint8_t>& GetData() const;
// Get the video's codec.
VideoCodecProfile GetProfile() const;
// Set the default path to the test video data.
static void SetTestDataPath(const base::FilePath& test_data_path);
private:
// The path where all test video files are stored.
// TODO(dstaessens@) Avoid using a static data path here.
static base::FilePath test_data_path_;
// The video file's path, can be absolute or relative to the above path.
base::FilePath file_path_;
std::vector<uint8_t> data_;
VideoCodecProfile profile_ = VIDEO_CODEC_PROFILE_UNKNOWN;
DISALLOW_COPY_AND_ASSIGN(Video);
};
} // namespace test
} // namespace media
#endif // MEDIA_GPU_TEST_VIDEO_PLAYER_VIDEO_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_player/video_collection.h"
#include <utility>
#include "media/gpu/test/video_player/video.h"
namespace media {
namespace test {
const VideoCollection kDefaultTestVideoCollection = std::move(
VideoCollection()
.Add(std::make_unique<Video>(base::FilePath("test-25fps.h264")))
.Add(std::make_unique<Video>(base::FilePath("test-25fps.vp8")))
.Add(std::make_unique<Video>(base::FilePath("test-25fps.vp9"))));
VideoCollection::VideoCollection() {}
VideoCollection::~VideoCollection() {}
VideoCollection::VideoCollection(VideoCollection&& other) {
video_collection_ = std::move(other.video_collection_);
}
VideoCollection& VideoCollection::Add(std::unique_ptr<Video> video) {
video_collection_.push_back(std::move(video));
return *this;
}
const Video& VideoCollection::operator[](size_t index) const {
CHECK_LT(index, video_collection_.size());
Video* video = video_collection_.at(index).get();
// Only load video when actually requested, to avoid loading all videos in the
// collection, even when only a single video is needed.
if (!video->IsLoaded()) {
bool loaded = video->Load();
LOG_IF(FATAL, !loaded) << "Loading video failed";
}
return *video;
}
size_t VideoCollection::Size() const {
return video_collection_.size();
}
} // 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_PLAYER_VIDEO_COLLECTION_H_
#define MEDIA_GPU_TEST_VIDEO_PLAYER_VIDEO_COLLECTION_H_
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/macros.h"
namespace media {
namespace test {
class Video;
// The video collection class helps managing different sets of test videos.
// Multiple test video collections can be maintained:
// * A collection of lightweight videos for CQ testing.
// * A collection of large video files for performance testing.
// * A set of corrupt videos to test decoder stability.
// * A set of small generated video files with various properties.
// TODO(dstaessens@):
// * Add functionality to fetch video by codec/resolution/name/...
// * Add a collection of videos to test different codecs.
// * Add a collection of videos to test various resolutions.
// * Add a collection of lightweight videos (defined directly in code?).
class VideoCollection {
public:
VideoCollection();
~VideoCollection();
VideoCollection(VideoCollection&& other);
// Add a video to the collection, this will transfer ownership.
VideoCollection& Add(std::unique_ptr<Video> video);
// Get the video with specified index from the collection.
const Video& operator[](size_t index) const;
size_t Size() const;
private:
std::vector<std::unique_ptr<Video>> video_collection_;
DISALLOW_COPY_AND_ASSIGN(VideoCollection);
};
// The default video test file collection
extern const VideoCollection kDefaultTestVideoCollection;
} // namespace test
} // namespace media
#endif // MEDIA_GPU_TEST_VIDEO_PLAYER_VIDEO_COLLECTION_H_
This diff is collapsed.
// 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_PLAYER_VIDEO_DECODER_CLIENT_H_
#define MEDIA_GPU_TEST_VIDEO_PLAYER_VIDEO_DECODER_CLIENT_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <vector>
#include "base/macros.h"
#include "base/sequence_checker.h"
#include "base/threading/thread.h"
#include "media/gpu/test/video_player/video_player.h"
#include "media/video/video_decode_accelerator.h"
namespace media {
class GpuVideoDecodeAcceleratorFactory;
namespace test {
class EncodedDataHelper;
class FrameRenderer;
// The video decoder client is responsible for the communication between the
// video player and the video decoder. It also communicates with the frame
// renderer and other components. The video decoder client can only have one
// active decoder at any time. To decode a different stream the DestroyDecoder()
// and CreateDecoder() functions have to be called to destroy and re-create the
// decoder.
//
// All communication with the decoder is done on the |decoder_client_thread_|,
// so callbacks scheduled by the decoder can be executed asynchronously. This is
// necessary if we don't want to interrupt the test flow.
class VideoDecoderClient : public VideoDecodeAccelerator::Client {
public:
~VideoDecoderClient() override;
// Return an instance of the VideoDecoderClient. The |frame_renderer| will not
// be owned by the decoder client, the caller should guarantee it exists for
// the entire lifetime of the decoder client. The |event_cb| will be called
// whenever an event occurs (e.g. frame decoded) and should be thread-safe.
static std::unique_ptr<VideoDecoderClient> Create(
const VideoPlayer::EventCallback& event_cb,
FrameRenderer* frame_renderer);
// Create a decoder with specified |config|, video |stream| and video
// |stream_size|. The video stream will not be owned by the decoder client,
// the caller should guarantee it exists until DestroyDecoder() is called.
void CreateDecoder(const VideoDecodeAccelerator::Config& config,
const std::vector<uint8_t>& stream);
// Destroy the currently active decoder.
void DestroyDecoder();
// Queue the next video stream fragment to be decoded.
void DecodeNextFragment();
private:
VideoDecoderClient(const VideoPlayer::EventCallback& event_cb,
FrameRenderer* renderer);
bool Initialize();
void Destroy();
// VideoDecodeAccelerator::Client implementation
void ProvidePictureBuffers(uint32_t requested_num_of_buffers,
VideoPixelFormat pixel_format,
uint32_t textures_per_buffer,
const gfx::Size& size,
uint32_t texture_target) override;
void DismissPictureBuffer(int32_t picture_buffer_id) override;
void PictureReady(const Picture& picture) override;
void NotifyEndOfBitstreamBuffer(int32_t bitstream_buffer_id) override;
void NotifyFlushDone() override;
void NotifyResetDone() override;
void NotifyError(VideoDecodeAccelerator::Error error) override;
void CreateDecoderFactoryTask(base::WaitableEvent* done);
void CreateDecoderTask(VideoDecodeAccelerator::Config config,
const std::vector<uint8_t>* stream,
base::WaitableEvent* done);
void DestroyDecoderTask(base::WaitableEvent* done);
void DecodeNextFragmentTask();
// Called by the renderer in response to a CreatePictureBuffers request.
void OnPictureBuffersCreatedTask(std::vector<PictureBuffer> buffers);
// Called by the renderer in response to a RenderPicture request.
void OnPictureRenderedTask(int32_t picture_buffer_id);
// Get the next bitstream buffer id to be used.
int32_t GetNextBitstreamBufferId();
VideoPlayer::EventCallback event_cb_;
FrameRenderer* const frame_renderer_;
std::unique_ptr<GpuVideoDecodeAcceleratorFactory> decoder_factory_;
std::unique_ptr<VideoDecodeAccelerator> decoder_;
base::Thread decoder_client_thread_;
int32_t next_bitstream_buffer_id_ = 0;
// TODO(dstaessens@) Replace with StreamParser.
std::unique_ptr<media::test::EncodedDataHelper> encoded_data_helper_;
SEQUENCE_CHECKER(video_player_sequence_checker_);
SEQUENCE_CHECKER(decoder_client_sequence_checker_);
base::WeakPtr<VideoDecoderClient> weak_this_;
base::WeakPtrFactory<VideoDecoderClient> weak_this_factory_;
DISALLOW_COPY_AND_ASSIGN(VideoDecoderClient);
};
} // namespace test
} // namespace media
#endif // MEDIA_GPU_TEST_VIDEO_PLAYER_VIDEO_DECODER_CLIENT_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_player/video_player.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "media/gpu/test/video_player/video.h"
#include "media/gpu/test/video_player/video_decoder_client.h"
#define DVLOGF(level) DVLOG(level) << __func__ << "(): "
namespace media {
namespace test {
VideoPlayer::VideoPlayer(const Video* video)
: video_(video),
video_player_state_(VideoPlayerState::kUninitialized),
event_cv_(&event_lock_),
video_player_event_counts_{} {}
VideoPlayer::~VideoPlayer() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOGF(4);
Destroy();
}
// static
std::unique_ptr<VideoPlayer> VideoPlayer::Create(
const Video* video,
FrameRenderer* frame_renderer) {
DCHECK(video);
auto video_player = base::WrapUnique(new VideoPlayer(video));
if (!video_player->Initialize(frame_renderer)) {
return nullptr;
}
return video_player;
}
bool VideoPlayer::Initialize(FrameRenderer* frame_renderer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(video_player_state_, VideoPlayerState::kUninitialized);
DCHECK(frame_renderer);
DVLOGF(4);
EventCallback event_cb =
base::BindRepeating(&VideoPlayer::NotifyEvent, base::Unretained(this));
decoder_client_ = VideoDecoderClient::Create(event_cb, frame_renderer);
CHECK(decoder_client_) << "Failed to create decoder client";
video_player_state_ = VideoPlayerState::kIdle;
return true;
}
void VideoPlayer::Destroy() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_NE(video_player_state_, VideoPlayerState::kDestroyed);
DVLOGF(4);
decoder_client_.reset();
video_player_state_ = VideoPlayerState::kDestroyed;
}
void VideoPlayer::Play() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(video_player_state_, VideoPlayerState::kIdle);
DVLOGF(4);
// Create a decoder for the specified video.
VideoDecodeAccelerator::Config decoder_config(video_->GetProfile());
decoder_client_->CreateDecoder(decoder_config, video_->GetData());
// Start decoding the video.
video_player_state_ = VideoPlayerState::kDecoding;
decoder_client_->DecodeNextFragment();
}
void VideoPlayer::Stop() {
NOTIMPLEMENTED();
}
void VideoPlayer::Reset() {
NOTIMPLEMENTED();
}
void VideoPlayer::Flush() {
NOTIMPLEMENTED();
}
base::TimeDelta VideoPlayer::GetCurrentTime() const {
NOTIMPLEMENTED();
return base::TimeDelta();
}
size_t VideoPlayer::GetCurrentFrame() const {
NOTIMPLEMENTED();
return 0;
}
VideoPlayerState VideoPlayer::GetState() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::AutoLock auto_lock(event_lock_);
return video_player_state_;
}
size_t VideoPlayer::GetEventCount(VideoPlayerEvent event) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::AutoLock auto_lock(event_lock_);
return video_player_event_counts_[static_cast<size_t>(event)];
}
bool VideoPlayer::WaitForEvent(VideoPlayerEvent event,
base::TimeDelta max_wait) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOGF(4) << "Event ID: " << static_cast<size_t>(event);
static size_t event_id = 0;
base::TimeDelta time_waiting;
base::AutoLock auto_lock(event_lock_);
while (true) {
const base::TimeTicks start_time = base::TimeTicks::Now();
event_cv_.TimedWait(max_wait);
time_waiting += base::TimeTicks::Now() - start_time;
// Go through list of events since last wait, looking for the event we're
// interested in.
for (; event_id < video_player_events_.size(); ++event_id) {
if (video_player_events_[event_id] == event) {
event_id++;
return true;
}
}
// Check whether we've exceeded the maximum time we're allowed to wait.
if (time_waiting >= max_wait)
return false;
}
}
void VideoPlayer::NotifyEvent(VideoPlayerEvent event) {
base::AutoLock auto_lock(event_lock_);
if (event == VideoPlayerEvent::kFlushDone)
video_player_state_ = VideoPlayerState::kIdle;
video_player_events_.push_back(event);
video_player_event_counts_[static_cast<size_t>(event)]++;
event_cv_.Signal();
}
} // 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_PLAYER_VIDEO_PLAYER_H_
#define MEDIA_GPU_TEST_VIDEO_PLAYER_VIDEO_PLAYER_H_
#include <memory>
#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "base/sequence_checker.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
namespace media {
namespace test {
class FrameRenderer;
class VideoDecoderClient;
class Video;
enum class VideoPlayerState : size_t {
kUninitialized = 0,
kIdle,
kDecoding,
kDestroyed,
};
enum class VideoPlayerEvent : size_t {
kFlushDone,
kFrameDecoded,
kNumEvents,
};
// The video player provides a framework to build video decode accelerator tests
// upon. It provides methods to manipulate video playback, and wait for specific
// events to occur.
class VideoPlayer {
public:
using EventCallback = base::RepeatingCallback<void(VideoPlayerEvent)>;
~VideoPlayer();
// Return an instance of the video player. The |video| and |frame_renderer|
// will not be owned by the video player. The caller should guarantee they
// exist for the entire lifetime of the video player.
static std::unique_ptr<VideoPlayer> Create(const Video* video,
FrameRenderer* frame_renderer);
void Play();
void Stop();
void Reset();
void Flush();
// Get current media time.
base::TimeDelta GetCurrentTime() const;
// Get the current frame number.
size_t GetCurrentFrame() const;
// Get the current state of the video player.
VideoPlayerState GetState() const;
// Wait for the specified event to occur, will return immediately if the event
// already occurred. All events with different types that precede the
// specified event will be consumed. Will return false if the specified
// timeout is exceeded while waiting for the event.
bool WaitForEvent(
VideoPlayerEvent event,
base::TimeDelta max_wait = base::TimeDelta::FromSeconds(10));
// Get the number of times the specified event occurred.
size_t GetEventCount(VideoPlayerEvent event) const;
private:
explicit VideoPlayer(const Video* video);
bool Initialize(FrameRenderer* frame_renderer);
void Destroy();
// Notify the client an event has occurred (e.g. frame decoded).
void NotifyEvent(VideoPlayerEvent event);
const Video* video_;
VideoPlayerState video_player_state_;
std::unique_ptr<VideoDecoderClient> decoder_client_;
mutable base::Lock event_lock_;
base::ConditionVariable event_cv_;
std::vector<VideoPlayerEvent> video_player_events_ GUARDED_BY(event_lock_);
size_t video_player_event_counts_[static_cast<size_t>(
VideoPlayerEvent::kNumEvents)] GUARDED_BY(event_lock_);
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(VideoPlayer);
};
} // namespace test
} // namespace media
#endif // MEDIA_GPU_TEST_VIDEO_PLAYER_VIDEO_PLAYER_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 "base/at_exit.h"
#include "base/command_line.h"
#include "base/test/scoped_task_environment.h"
#include "media/base/test_data_util.h"
#include "media/gpu/buildflags.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_collection.h"
#include "media/gpu/test/video_player/video_player.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(USE_VAAPI)
#include "media/gpu/vaapi/vaapi_wrapper.h"
#endif
namespace media {
namespace test {
// TODO(dstaessens@)
// * Use a fixture here (TEST_F) with a testing environment that contains all
// required setup code.
// * Fetch the expected number of frames from the video file's metadata.
TEST(VideoDecodeAcceleratorTest, BasicPlayTest) {
auto renderer = FrameRendererDummy::Create();
CHECK(renderer) << "Failed to create renderer";
const Video* video = &kDefaultTestVideoCollection[0];
auto tvp = VideoPlayer::Create(video, renderer.get());
CHECK(tvp) << "Failed to create video player";
tvp->Play();
EXPECT_TRUE(tvp->WaitForEvent(VideoPlayerEvent::kFlushDone));
EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFlushDone), 1u);
EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kFrameDecoded), 250u);
}
} // namespace test
} // namespace media
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
base::CommandLine::Init(argc, argv);
base::AtExitManager exit_manager;
// Needed to enable DVLOG through --vmodule.
logging::LoggingSettings settings;
settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
LOG_ASSERT(logging::InitLogging(settings));
// Setting up a task environment will create a task runner for the current
// thread and allow posting tasks to other threads. This is required for the
// test video player to function.
base::test::ScopedTaskEnvironment scoped_task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::UI);
// Set the default test data path.
media::test::Video::SetTestDataPath(media::GetTestDataPath());
#if BUILDFLAG(USE_VAAPI)
media::VaapiWrapper::PreSandboxInitialization();
#endif
return RUN_ALL_TESTS();
}
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