#include "ozone/media/vaapi_video_decode_accelerator.h"
+#include <string>
+
#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#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) {
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,
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_;
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);
};
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(
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;
}
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_);
+
+ 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) {
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;
}
// 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_.get());
+
+ return true;
+}
- va_wrapper_->UnmapImage(&va_image_);
+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);
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(),
"output_id", output_id);
DVLOG(3) << "Outputting VASurface " << va_surface->id()
- << " into pixmap bound to picture buffer id " << output_id;
+ << " into texture 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
+ 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_;
TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_);
DVLOG(4) << "Notifying output picture id " << output_id
<< " for input "<< input_id << " is ready";
+ // TODO(posciak): Use visible size from decoder here instead
+ // (crbug.com/402760).
if (client_)
- client_->PictureReady(media::Picture(output_id, input_id));
+ client_->PictureReady(media::Picture(output_id, input_id, gfx::Rect(tfp_picture->size())));
}
void VaapiVideoDecodeAccelerator::TryOutputSurface() {