Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / ozone / media / vaapi_video_decode_accelerator.cc
index 3c5ac59..f2d542b 100644 (file)
@@ -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"
@@ -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) {
@@ -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,
@@ -100,6 +111,10 @@ 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_;
@@ -109,7 +124,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);
 };
@@ -124,9 +141,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(
@@ -150,7 +170,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;
   }
@@ -161,9 +181,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_);
+
+  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) {
@@ -172,13 +214,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;
   }
@@ -192,23 +234,85 @@ 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_.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);
@@ -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(),
@@ -312,19 +421,41 @@ void VaapiVideoDecodeAccelerator::OutputPicture(
                "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() {