Skip to content
This repository has been archived by the owner on Aug 20, 2020. It is now read-only.

Enable zero buffer copy method for wayland VDA. #260

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Alexis Menard <[email protected]>
Changbin Shao <[email protected]>
Daniel Narvaez <[email protected]>
Dongseong Hwang <[email protected]>
Eduardo Lima (Etrunko) <[email protected]>
Expand Down
3 changes: 2 additions & 1 deletion media/va_wayland.sigs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ VAStatus vaSetDisplayAttributes(VADisplay dpy, VADisplayAttribute *attr_list, in
VAStatus vaSyncSurface(VADisplay dpy, VASurfaceID render_target);
VAStatus vaTerminate(VADisplay dpy);
VAStatus vaUnmapBuffer(VADisplay dpy, VABufferID buf_id);

VAStatus vaLockBuffer(VADisplay dpy, VABufferID buf_id, VABufferInfo *buf_info_ptr);
VAStatus vaUnlockBuffer(VADisplay dpy, VABufferID buf_id, VABufferInfo *buf_info_ptr);

#------------------------------------------------
# Functions from libva-wayland used in chromium code.
Expand Down
161 changes: 143 additions & 18 deletions media/vaapi_video_decode_accelerator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include "ozone/media/vaapi_video_decode_accelerator.h"

#include <string>

#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
Expand All @@ -15,8 +17,9 @@
#include "content/common/gpu/gpu_channel.h"
#include "media/base/bind_to_current_loop.h"
#include "media/video/picture.h"
#include "ui/gl/scoped_binders.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_surface_egl.h"
#include "ui/gl/scoped_binders.h"

static void ReportToUMA(
media::VaapiH264Decoder::VAVDAH264DecoderFailure failure) {
Expand Down Expand Up @@ -88,9 +91,17 @@ class VaapiVideoDecodeAccelerator::TFPPicture : public base::NonThreadSafe {
return size_;
}

VAImage* va_image() {
return va_image_.get();
}

// Upload vaimage data to texture. Needs to be called every frame.
bool Upload(VASurfaceID id);

// Bind EGL image to texture. Needs to be called every frame.
bool Bind();
bool UpdateEGLImage(VASurfaceID id);

private:
TFPPicture(const base::Callback<bool(void)>& make_context_current, //NOLINT
VaapiWrapper* va_wrapper,
Expand All @@ -100,6 +111,11 @@ class VaapiVideoDecodeAccelerator::TFPPicture : public base::NonThreadSafe {

bool Initialize();

EGLImageKHR CreateEGLImage(EGLDisplay egl_display,
VASurfaceID surface,
VAImage* va_image);
bool DestroyEGLImage(EGLDisplay egl_display, EGLImageKHR egl_image);

base::Callback<bool(void)> make_context_current_; //NOLINT

VaapiWrapper* va_wrapper_;
Expand All @@ -109,7 +125,9 @@ class VaapiVideoDecodeAccelerator::TFPPicture : public base::NonThreadSafe {
uint32 texture_id_;

gfx::Size size_;
VAImage va_image_;
scoped_ptr<VAImage> va_image_;
EGLImageKHR egl_image_;
EGLDisplay egl_display_;

DISALLOW_COPY_AND_ASSIGN(TFPPicture);
};
Expand All @@ -124,9 +142,12 @@ VaapiVideoDecodeAccelerator::TFPPicture::TFPPicture(
va_wrapper_(va_wrapper),
picture_buffer_id_(picture_buffer_id),
texture_id_(texture_id),
size_(size) {
size_(size),
va_image_(new VAImage()),
egl_display_(gfx::GLSurfaceEGL::GetHardwareDisplay()) {
DCHECK(!make_context_current_.is_null());
};
DCHECK(va_image_);
}

linked_ptr<VaapiVideoDecodeAccelerator::TFPPicture>
VaapiVideoDecodeAccelerator::TFPPicture::Create(
Expand All @@ -150,7 +171,7 @@ bool VaapiVideoDecodeAccelerator::TFPPicture::Initialize() {
if (!make_context_current_.Run())
return false;

if (!va_wrapper_->CreateRGBImage(size_, &va_image_)) {
if (!va_wrapper_->CreateRGBImage(size_, va_image_.get())) {
DVLOG(1) << "Failed to create VAImage";
return false;
}
Expand All @@ -161,9 +182,31 @@ bool VaapiVideoDecodeAccelerator::TFPPicture::Initialize() {
VaapiVideoDecodeAccelerator::TFPPicture::~TFPPicture() {
DCHECK(CalledOnValidThread());

if (egl_image_ != EGL_NO_IMAGE_KHR)
DestroyEGLImage(egl_display_, egl_image_);

if (va_wrapper_) {
va_wrapper_->DestroyImage(&va_image_);
va_wrapper_->DestroyImage(va_image_.get());
}
}

bool VaapiVideoDecodeAccelerator::TFPPicture::UpdateEGLImage(
VASurfaceID surface) {
DCHECK(CalledOnValidThread());

if (!make_context_current_.Run())
return false;

if (egl_image_ != EGL_NO_IMAGE_KHR)
DestroyEGLImage(egl_display_, egl_image_);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why destroy egl_image_, if it's already null? What's the case that it falls in this conditional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EGL_NO_IMAGE_KHR equals NULL here. There is GL mapping of eglCreateImageKHR and eglDestroyImageKHR, so release an EGL image first before create a new one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fine. thanks.


egl_image_ = CreateEGLImage(egl_display_, surface, va_image_.get());
if (egl_image_ == EGL_NO_IMAGE_KHR) {
DVLOG(1) << "Failed to create EGL image";
return false;
}

return true;
}

bool VaapiVideoDecodeAccelerator::TFPPicture::Upload(VASurfaceID surface) {
Expand All @@ -172,13 +215,13 @@ bool VaapiVideoDecodeAccelerator::TFPPicture::Upload(VASurfaceID surface) {
if (!make_context_current_.Run())
return false;

if (!va_wrapper_->PutSurfaceIntoImage(surface, &va_image_)) {
if (!va_wrapper_->PutSurfaceIntoImage(surface, va_image_.get())) {
DVLOG(1) << "Failed to put va surface to image";
return false;
}

void* buffer = NULL;
if (!va_wrapper_->MapImage(&va_image_, &buffer)) {
if (!va_wrapper_->MapImage(va_image_.get(), &buffer)) {
DVLOG(1) << "Failed to map VAImage";
return false;
}
Expand All @@ -192,23 +235,84 @@ bool VaapiVideoDecodeAccelerator::TFPPicture::Upload(VASurfaceID surface) {
// texture output implementation. It can be removed when zero buffer copy
// is implemented.
unsigned int al = 4 * size_.width();
if (al != va_image_.pitches[0]) {
if (al != va_image_->pitches[0]) {
// Not aligned phenomenon occurs only in special size video in None-X11.
// So re-check RGBA data alignment and realign filled video frame in need.
unsigned char* bhandle = static_cast<unsigned char*>(buffer);
for (int i = 0; i < size_.height(); i++) {
memcpy(bhandle + (i * al), bhandle + (i * (va_image_.pitches[0])), al);
memcpy(bhandle + (i * al), bhandle + (i * (va_image_->pitches[0])), al);
}
}

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size_.width(), size_.height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_BGRA,
size_.width(),
size_.height(),
0,
GL_BGRA,
GL_UNSIGNED_BYTE,
buffer);

va_wrapper_->UnmapImage(&va_image_);
va_wrapper_->UnmapImage(va_image_.get());

return true;
}

bool VaapiVideoDecodeAccelerator::TFPPicture::Bind() {
DCHECK(CalledOnValidThread());
if (!make_context_current_.Run())
return false;

gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, texture_id_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
return true;
}

EGLImageKHR VaapiVideoDecodeAccelerator::TFPPicture::CreateEGLImage(
EGLDisplay egl_display, VASurfaceID va_surface, VAImage* va_image) {
DCHECK(CalledOnValidThread());
DCHECK(va_image);

VABufferInfo buffer_info;
if (!va_wrapper_->LockBuffer(va_surface, va_image->buf, &buffer_info)) {
DVLOG(1) << "Failed to lock Buffer";
return EGL_NO_IMAGE_KHR;
}

EGLint attribs[] = {
EGL_WIDTH, 0,
EGL_HEIGHT, 0,
EGL_DRM_BUFFER_STRIDE_MESA, 0,
EGL_DRM_BUFFER_FORMAT_MESA,
EGL_DRM_BUFFER_FORMAT_ARGB32_MESA,
EGL_DRM_BUFFER_USE_MESA,
EGL_DRM_BUFFER_USE_SHARE_MESA,
EGL_NONE };
attribs[1] = va_image->width;
attribs[3] = va_image->height;
attribs[5] = va_image->pitches[0] / 4;

EGLImageKHR egl_image = eglCreateImageKHR(egl_display,
EGL_NO_CONTEXT,
EGL_DRM_BUFFER_MESA,
(EGLClientBuffer)buffer_info.handle,
attribs);

if (va_wrapper_) {
va_wrapper_->UnlockBuffer(va_surface, va_image->buf, &buffer_info);
}

return egl_image;
}

bool VaapiVideoDecodeAccelerator::TFPPicture::DestroyEGLImage(
EGLDisplay egl_display, EGLImageKHR egl_image) {
return eglDestroyImageKHR(egl_display, egl_image);
}

VaapiVideoDecodeAccelerator::TFPPicture*
VaapiVideoDecodeAccelerator::TFPPictureById(int32 picture_buffer_id) {
TFPPictures::iterator it = tfp_pictures_.find(picture_buffer_id);
Expand Down Expand Up @@ -267,6 +371,11 @@ bool VaapiVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile,
return false;
}

supports_valockBuffer_apis_ = vaapi_wrapper_->SupportsVaLockBufferApis();
std::string query =
supports_valockBuffer_apis_ ? "supports" : "doesn't support";
LOG(INFO) << "VAAPI " << query << " vaLockBuffer apis";

decoder_.reset(
new VaapiH264Decoder(
vaapi_wrapper_.get(),
Expand Down Expand Up @@ -312,11 +421,27 @@ void VaapiVideoDecodeAccelerator::OutputPicture(
"output_id", output_id);

DVLOG(3) << "Outputting VASurface " << va_surface->id()
<< " into pixmap bound to picture buffer id " << output_id;

RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture->Upload(va_surface->id()),
"Failed to upload VASurface to texture",
PLATFORM_FAILURE, ); //NOLINT
<< " into texture bound to picture buffer id " << output_id;

if (supports_valockBuffer_apis_) {
RETURN_AND_NOTIFY_ON_FAILURE(vaapi_wrapper_->PutSurfaceIntoImage(
va_surface->id(),
tfp_picture->va_image()),
"Failed putting surface into vaimage",
PLATFORM_FAILURE, ); //NOLINT

RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture->UpdateEGLImage(va_surface->id()),
"Failed to update egl image per vaimage info",
PLATFORM_FAILURE, ); //NOLINT

RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture->Bind(),
"Failed to bind egl image to texture",
PLATFORM_FAILURE, ); //NOLINT
} else {
RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture->Upload(va_surface->id()),
"Failed to upload VASurface to texture",
PLATFORM_FAILURE, ); //NOLINT
}

// Notify the client a picture is ready to be displayed.
++num_frames_at_client_;
Expand Down
3 changes: 3 additions & 0 deletions media/vaapi_video_decode_accelerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ class VaapiVideoDecodeAccelerator
// The WeakPtrFactory for |weak_this_|.
base::WeakPtrFactory<VaapiVideoDecodeAccelerator> weak_this_factory_;

// Whether VaapiWrapper supports vaLockBuffer apis.
bool supports_valockBuffer_apis_;

DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAccelerator);
};

Expand Down
47 changes: 44 additions & 3 deletions media/vaapi_wrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "media/media/va_stubs.h"

#include "third_party/libva/va/wayland/va_wayland.h"
#include "third_party/libva/va/va_drmcommon.h"

using media_media::kModuleVa_wayland;
using media_media::InitializeStubs;
Expand All @@ -23,6 +24,9 @@ using media_media::StubPathMap;
static const base::FilePath::CharType kVaLib[] =
FILE_PATH_LITERAL("libva-wayland.so.1");

static const char kVaLockBufferSymbol[] = "vaLockBuffer";
static const char kVaUnlockBufferSymbol[] = "vaUnlockBuffer";

#define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \
do { \
DVLOG(1) << err_msg \
Expand Down Expand Up @@ -370,14 +374,15 @@ bool VaapiWrapper::CreateRGBImage(gfx::Size size, VAImage* image) {
base::AutoLock auto_lock(va_lock_);
VAStatus va_res;
VAImageFormat format;
format.fourcc = VA_FOURCC_RGBX;
format.fourcc = VA_FOURCC_BGRX;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this really needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previous solution use GL_RGBA format to create GL texture, so it declares VA_FOURCC_RGBX('X' takes 'A' - alpha position) here. The new solution uses format EGL_DRM_BUFFER_FORMAT_ARGB32_MESA to create EGL image, so need to use VA_FOURCC_BGRX instead. Otherwise, color is not correct when video playback.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

care to change the name of the function as well then?

format.byte_order = VA_LSB_FIRST;
format.bits_per_pixel = 32;
format.depth = 24;
format.red_mask = 0xff;
format.green_mask = 0xff00;
format.red_mask = 0x0000ff;
format.green_mask = 0x00ff00;
format.blue_mask = 0xff0000;
format.alpha_mask = 0;

va_res = vaCreateImage(va_display_,
&format,
size.width(),
Expand Down Expand Up @@ -421,6 +426,32 @@ bool VaapiWrapper::PutSurfaceIntoImage(VASurfaceID va_surface_id,
VA_SUCCESS_OR_RETURN(va_res, "Failed to put surface into image", false);
return true;
}

bool VaapiWrapper::LockBuffer(VASurfaceID va_surface_id,
VABufferID buf_id,
VABufferInfo* buf_info) {
DCHECK(buf_info);
base::AutoLock auto_lock(va_lock_);

buf_info->mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM;
VAStatus va_res = vaLockBuffer(va_display_, buf_id, buf_info);
VA_SUCCESS_OR_RETURN(va_res, "Failed to lock vabuffer", false);

return true;
}

bool VaapiWrapper::UnlockBuffer(VASurfaceID va_surface_id,
VABufferID buf_id,
VABufferInfo* buf_info) {
DCHECK(buf_info);
base::AutoLock auto_lock(va_lock_);
VAStatus va_res = vaUnlockBuffer(va_display_, buf_id, buf_info);
VA_SUCCESS_OR_RETURN(va_res, "Failed to unlock vabuffer", false);

return true;
}


bool VaapiWrapper::GetVaImageForTesting(VASurfaceID va_surface_id,
VAImage* image,
void** mem) {
Expand Down Expand Up @@ -462,4 +493,14 @@ bool VaapiWrapper::PostSandboxInitialization() {
return ret;
}

bool VaapiWrapper::SupportsVaLockBufferApis() {
void* handle = dlopen(kVaLib, RTLD_LAZY);
if (!handle) {
LOG(ERROR) << "Could not open " << kVaLib;
return false;
}
return dlsym(handle, kVaLockBufferSymbol) &&
dlsym(handle, kVaUnlockBufferSymbol);
}

} // namespace media
Loading