Commit 605b74ac authored by Jao-ke Chin-Lee's avatar Jao-ke Chin-Lee Committed by Commit Bot

[media/gpu/vaapi/test] Add VaapiDevice and SharedVASurface.

Add utilities to help handle initialization and teardown
of VA objects. Users of VaapiDevice may still manipulate the
VA objects directly with libva calls.

BUG=crbug:1062407

Change-Id: Id5f5233d1476f1abe006f04e3ad54fc6fb9b2486
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2296871
Commit-Queue: Jao-ke Chin-Lee <jchinlee@chromium.org>
Reviewed-by: default avatarAndres Calderon Jaramillo <andrescj@chromium.org>
Reviewed-by: default avatarMiguel Casas <mcasas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797848}
parent 92baa450
...@@ -321,12 +321,21 @@ group("vaapi_fuzzers") { ...@@ -321,12 +321,21 @@ group("vaapi_fuzzers") {
executable("decode_test") { executable("decode_test") {
testonly = true testonly = true
sources = [ "test/decode.cc" ] sources = [
"test/decode.cc",
"test/macros.h",
"test/shared_va_surface.cc",
"test/shared_va_surface.h",
"test/vaapi_device.cc",
"test/vaapi_device.h",
]
deps = [ deps = [
":libva_stubs", ":libva_stubs",
"//base", "//base",
"//media", "//media",
"//media:test_support", "//media:test_support",
"//third_party/libyuv",
"//ui/gfx/codec:codec",
"//ui/gfx/geometry", "//ui/gfx/geometry",
] ]
} }
...@@ -29,6 +29,7 @@ constexpr char kUsageMsg[] = ...@@ -29,6 +29,7 @@ constexpr char kUsageMsg[] =
" --profile=<profile of input video>\n" " --profile=<profile of input video>\n"
" [--frames=<number of frames to decode>]\n" " [--frames=<number of frames to decode>]\n"
" [--out-prefix=<path prefix of decoded frame PNGs>]\n" " [--out-prefix=<path prefix of decoded frame PNGs>]\n"
" [--v=<log verbosity>]"
" [--help]"; " [--help]";
constexpr char kHelpMsg[] = constexpr char kHelpMsg[] =
...@@ -122,7 +123,7 @@ int main(int argc, char** argv) { ...@@ -122,7 +123,7 @@ int main(int argc, char** argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
const gfx::Size size(file_header.width, file_header.height); const gfx::Size size(file_header.width, file_header.height);
LOG(INFO) << "video size: " << size.ToString(); VLOG(1) << "video size: " << size.ToString();
// Initialize VA stubs. // Initialize VA stubs.
StubPathMap paths; StubPathMap paths;
......
// Copyright 2020 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_VAAPI_TEST_MACROS_H_
#define MEDIA_GPU_VAAPI_TEST_MACROS_H_
#include "base/logging.h"
#define VA_LOG_ASSERT(va_error, name) \
LOG_ASSERT((va_error) == VA_STATUS_SUCCESS) \
<< name << " failed, VA error: " << vaErrorStr(va_error);
#endif // MEDIA_GPU_VAAPI_TEST_MACROS_H_
// Copyright 2020 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 <va/va.h>
#include "base/files/file_util.h"
#include "media/base/video_types.h"
#include "media/gpu/vaapi/test/macros.h"
#include "media/gpu/vaapi/test/shared_va_surface.h"
#include "third_party/libyuv/include/libyuv.h"
#include "ui/gfx/codec/png_codec.h"
namespace media {
namespace vaapi_test {
namespace {
// Derives the VAImage metadata and image data from |surface_id| in |display|,
// returning true on success.
bool DeriveImage(VADisplay display,
VASurfaceID surface_id,
VAImage* image,
uint8_t** image_data) {
VAStatus res = vaDeriveImage(display, surface_id, image);
VLOG_IF(2, (res != VA_STATUS_SUCCESS))
<< "vaDeriveImage failed, VA error: " << vaErrorStr(res);
if (image->format.fourcc != VA_FOURCC_NV12) {
VLOG(2) << "Test decoder binary does not support derived surface format "
<< "with fourcc " << media::FourccToString(image->format.fourcc);
res = vaDestroyImage(display, image->image_id);
VA_LOG_ASSERT(res, "vaDestroyImage");
return false;
}
res = vaMapBuffer(display, image->buf, reinterpret_cast<void**>(image_data));
VA_LOG_ASSERT(res, "vaMapBuffer");
return true;
}
// Maps the image data from |surface_id| in |display| with given |size| by
// attempting to derive into |image| and |image_data|, or creating an NV12
// VAImage to use with vaGetImage as fallback and setting |image| and
// |image_data| accordingly.
void GetSurfaceImage(VADisplay display,
VASurfaceID surface_id,
const gfx::Size size,
VAImage* image,
uint8_t** image_data) {
// First attempt to derive the image from the surface.
if (DeriveImage(display, surface_id, image, image_data))
return;
// Fall back to getting the image as NV12.
VAImageFormat format{
.fourcc = VA_FOURCC_NV12,
.byte_order = VA_LSB_FIRST,
.bits_per_pixel = 12,
};
VAStatus res =
vaCreateImage(display, &format, size.width(), size.height(), image);
VA_LOG_ASSERT(res, "vaCreateImage");
res = vaGetImage(display, surface_id, 0, 0, size.width(), size.height(),
image->image_id);
VA_LOG_ASSERT(res, "vaGetImage");
res = vaMapBuffer(display, image->buf, reinterpret_cast<void**>(image_data));
VA_LOG_ASSERT(res, "vaMapBuffer");
}
} // namespace
SharedVASurface::SharedVASurface(const VaapiDevice& va,
VASurfaceID id,
const gfx::Size& size,
unsigned int format)
: va_(va), id_(id), size_(size), internal_va_format_(format) {}
// static
scoped_refptr<SharedVASurface> SharedVASurface::Create(const VaapiDevice& va,
const gfx::Size& size,
VASurfaceAttrib attrib) {
VASurfaceID surface_id;
VAStatus res =
vaCreateSurfaces(va.display(), va.internal_va_format(),
base::checked_cast<unsigned int>(size.width()),
base::checked_cast<unsigned int>(size.height()),
&surface_id, 1u, &attrib, 1u);
VA_LOG_ASSERT(res, "vaCreateSurfaces");
VLOG(1) << "created surface: " << surface_id;
return base::WrapRefCounted(
new SharedVASurface(va, surface_id, size, va.internal_va_format()));
}
SharedVASurface::~SharedVASurface() {
VAStatus res =
vaDestroySurfaces(va_.display(), const_cast<VASurfaceID*>(&id_), 1u);
VA_LOG_ASSERT(res, "vaDestroySurfaces");
VLOG(1) << "destroyed surface " << id_;
}
void SharedVASurface::SaveAsPNG(const std::string& path) {
VAImage image;
uint8_t* image_data;
GetSurfaceImage(va_.display(), id_, size_, &image, &image_data);
// Convert the NV12 image data to ARGB and write to |path|.
const size_t argb_stride = image.width * 4;
auto argb_data = std::make_unique<uint8_t[]>(argb_stride * image.height);
const int convert_res = libyuv::NV12ToARGB(
(uint8_t*)(image_data + image.offsets[0]), image.pitches[0],
(uint8_t*)(image_data + image.offsets[1]), image.pitches[1],
argb_data.get(), argb_stride, image.width, image.height);
DCHECK(convert_res == 0);
std::vector<unsigned char> image_buffer;
const bool result = gfx::PNGCodec::Encode(
argb_data.get(), gfx::PNGCodec::FORMAT_BGRA, size_, argb_stride,
true /* discard_transparency */, std::vector<gfx::PNGCodec::Comment>(),
&image_buffer);
DCHECK(result);
LOG_ASSERT(base::WriteFile(base::FilePath(path), image_buffer));
// Clean up VA handles.
VAStatus res = vaUnmapBuffer(va_.display(), image.buf);
VA_LOG_ASSERT(res, "vaUnmapBuffer");
res = vaDestroyImage(va_.display(), image.image_id);
VA_LOG_ASSERT(res, "vaDestroyImage");
}
} // namespace vaapi_test
} // namespace media
// Copyright 2020 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_VAAPI_TEST_SHARED_VA_SURFACE_H_
#define MEDIA_GPU_VAAPI_TEST_SHARED_VA_SURFACE_H_
#include "base/memory/ref_counted.h"
#include "media/gpu/vaapi/test/vaapi_device.h"
#include "ui/gfx/geometry/size.h"
namespace media {
namespace vaapi_test {
constexpr unsigned int kInvalidVaRtFormat = 0u;
// Provides a wrapper around VASurfaces that properly handles creation and
// destruction.
class SharedVASurface : public base::RefCounted<SharedVASurface> {
public:
// Constructs a VASurface with given |size| and |attribute|.
static scoped_refptr<SharedVASurface> Create(const VaapiDevice& va,
const gfx::Size& size,
VASurfaceAttrib attribute);
// Saves this surface into a png at the given |path|. The image data is
// retrieved by first attempting to call vaDeriveImage on the surface;
// if that fails or returns an unsupported format, fall back to
// vaCreateImage + vaGetImage with NV12.
// NB: vaDeriveImage may succeed but fetch garbage output in AMD.
void SaveAsPNG(const std::string& path);
VASurfaceID id() const { return id_; }
const gfx::Size& size() const { return size_; }
unsigned int internal_va_format() const { return internal_va_format_; }
private:
friend class base::RefCounted<SharedVASurface>;
SharedVASurface(const VaapiDevice& va,
VASurfaceID id,
const gfx::Size& size,
unsigned int format);
SharedVASurface(const SharedVASurface&) = delete;
SharedVASurface& operator=(const SharedVASurface&) = delete;
~SharedVASurface();
const VaapiDevice& va_;
const VASurfaceID id_;
const gfx::Size size_;
const unsigned int internal_va_format_;
};
} // namespace vaapi_test
} // namespace media
#endif // MEDIA_GPU_VAAPI_TEST_SHARED_VA_SURFACE_H_
// Copyright 2020 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 <fcntl.h>
#include <va/va.h>
#include <va/va_drm.h>
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "media/gpu/vaapi/test/macros.h"
#include "media/gpu/vaapi/test/vaapi_device.h"
namespace media {
namespace vaapi_test {
// Returns the preferred VA_RT_FORMAT for the given |profile|.
unsigned int GetFormatForProfile(VAProfile profile) {
if (profile == VAProfileVP9Profile2)
return VA_RT_FORMAT_YUV420_10BPP;
return VA_RT_FORMAT_YUV420;
}
VaapiDevice::VaapiDevice(VAProfile profile)
: display_(nullptr),
config_id_(VA_INVALID_ID),
profile_(profile),
internal_va_format_(GetFormatForProfile(profile_)) {
Initialize();
}
VaapiDevice::~VaapiDevice() {
VLOG(1) << "Tearing down...";
VAStatus res;
if (config_id_ != VA_INVALID_ID) {
res = vaDestroyConfig(display_, config_id_);
VA_LOG_ASSERT(res, "vaDestroyConfig");
}
if (display_ != nullptr) {
res = vaTerminate(display_);
VA_LOG_ASSERT(res, "vaTerminate");
}
VLOG(1) << "Teardown done.";
}
void VaapiDevice::Initialize() {
constexpr char kDriRenderNode0Path[] = "/dev/dri/renderD128";
display_file_ = base::File(
base::FilePath::FromUTF8Unsafe(kDriRenderNode0Path),
base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE);
LOG_ASSERT(display_file_.IsValid())
<< "Failed to open " << kDriRenderNode0Path;
display_ = vaGetDisplayDRM(display_file_.GetPlatformFile());
LOG_ASSERT(display_ != nullptr) << "vaGetDisplayDRM failed";
int major, minor;
VAStatus res = vaInitialize(display_, &major, &minor);
VA_LOG_ASSERT(res, "vaInitialize");
VLOG(1) << "VA major version: " << major << ", minor version: " << minor;
// Create config.
// We rely on vaCreateConfig to specify the error mode if decode is not
// supported for the given profile.
// TODO(jchinlee): Refactor configuration management to be owned by decoders
// (this will also allow decoders to adjust the VAConfig as needed, e.g. if
// the profile changes part-way).
std::vector<VAConfigAttrib> attribs;
attribs.push_back({VAConfigAttribRTFormat, internal_va_format_});
res = vaCreateConfig(display_, profile_, VAEntrypointVLD, attribs.data(),
attribs.size(), &config_id_);
VA_LOG_ASSERT(res, "vaCreateConfig");
}
} // namespace vaapi_test
} // namespace media
// Copyright 2020 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_VAAPI_TEST_VAAPI_DEVICE_H_
#define MEDIA_GPU_VAAPI_TEST_VAAPI_DEVICE_H_
#include "base/files/file.h"
namespace media {
namespace vaapi_test {
// This class holds shared VA handles used by the various test decoders.
// The decoders themselves may still make direct libva calls.
class VaapiDevice {
public:
// Initializes a VaapiDevice for |profile|. Success is ASSERTed.
explicit VaapiDevice(VAProfile profile);
VaapiDevice(const VaapiDevice&) = delete;
VaapiDevice& operator=(const VaapiDevice&) = delete;
~VaapiDevice();
VADisplay display() const { return display_; }
VAConfigID config_id() const { return config_id_; }
VAProfile profile() const { return profile_; }
unsigned int internal_va_format() const { return internal_va_format_; }
private:
// Initializes VA handles and display descriptors, checking that HW decode
// with the expected profile is supported. Success is ASSERTed.
void Initialize();
base::File display_file_;
// VA info and handles
// Populated on Initialize().
VADisplay display_;
VAConfigID config_id_;
const VAProfile profile_;
const unsigned int internal_va_format_;
};
} // namespace vaapi_test
} // namespace media
#endif // MEDIA_GPU_VAAPI_TEST_VAAPI_DEVICE_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