Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / media / filters / skcanvas_video_renderer.cc
index fe1bfde..2243445 100644 (file)
@@ -9,6 +9,8 @@
 #include "media/base/yuv_convert.h"
 #include "third_party/libyuv/include/libyuv.h"
 #include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkImageGenerator.h"
+#include "third_party/skia/include/gpu/GrContext.h"
 #include "ui/gfx/skbitmap_operations.h"
 
 // Skia internal format depends on a platform. On Android it is ABGR, on others
 
 namespace media {
 
-static bool IsYUV(media::VideoFrame::Format format) {
-  return format == media::VideoFrame::YV12 ||
-         format == media::VideoFrame::YV16 ||
-         format == media::VideoFrame::I420 ||
-         format == media::VideoFrame::YV12A ||
-         format == media::VideoFrame::YV12J ||
-         format == media::VideoFrame::YV24;
+namespace {
+
+// This class keeps two temporary resources; software bitmap, hardware bitmap.
+// If both bitmap are created and then only software bitmap is updated every
+// frame, hardware bitmap outlives until the media player dies. So we delete
+// a temporary resource if it is not used for 3 sec.
+const int kTemporaryResourceDeletionDelay = 3;  // Seconds;
+
+bool IsYUV(media::VideoFrame::Format format) {
+  switch (format) {
+    case VideoFrame::YV12:
+    case VideoFrame::YV16:
+    case VideoFrame::I420:
+    case VideoFrame::YV12A:
+    case VideoFrame::YV12J:
+    case VideoFrame::YV24:
+    case VideoFrame::NV12:
+      return true;
+    case VideoFrame::UNKNOWN:
+    case VideoFrame::NATIVE_TEXTURE:
+#if defined(VIDEO_HOLE)
+    case VideoFrame::HOLE:
+#endif  // defined(VIDEO_HOLE)
+      return false;
+  }
+  NOTREACHED() << "Invalid videoframe format provided: " << format;
+  return false;
+}
+
+bool IsJPEGColorSpace(media::VideoFrame::Format format) {
+  switch (format) {
+    case VideoFrame::YV12J:
+      return true;
+    case VideoFrame::YV12:
+    case VideoFrame::YV16:
+    case VideoFrame::I420:
+    case VideoFrame::YV12A:
+    case VideoFrame::YV24:
+    case VideoFrame::NV12:
+    case VideoFrame::UNKNOWN:
+    case VideoFrame::NATIVE_TEXTURE:
+#if defined(VIDEO_HOLE)
+    case VideoFrame::HOLE:
+#endif  // defined(VIDEO_HOLE)
+      return false;
+  }
+  NOTREACHED() << "Invalid videoframe format provided: " << format;
+  return false;
 }
 
-static bool IsYUVOrNative(media::VideoFrame::Format format) {
+bool IsYUVOrNative(media::VideoFrame::Format format) {
   return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE;
 }
 
-// Converts a VideoFrame containing YUV data to a SkBitmap containing RGB data.
-//
-// |bitmap| will be (re)allocated to match the dimensions of |video_frame|.
-static void ConvertVideoFrameToBitmap(
+// Converts a |video_frame| to raw |rgb_pixels|.
+void ConvertVideoFrameToRGBPixels(
     const scoped_refptr<media::VideoFrame>& video_frame,
-    SkBitmap* bitmap) {
+    void* rgb_pixels,
+    size_t row_bytes) {
   DCHECK(IsYUVOrNative(video_frame->format()))
       << video_frame->format();
   if (IsYUV(video_frame->format())) {
@@ -53,17 +95,6 @@ static void ConvertVideoFrameToBitmap(
               video_frame->stride(media::VideoFrame::kVPlane));
   }
 
-  // Check if |bitmap| needs to be (re)allocated.
-  if (bitmap->isNull() ||
-      bitmap->width() != video_frame->visible_rect().width() ||
-      bitmap->height() != video_frame->visible_rect().height()) {
-    bitmap->allocN32Pixels(video_frame->visible_rect().width(),
-                           video_frame->visible_rect().height());
-    bitmap->setIsVolatile(true);
-  }
-
-  bitmap->lockPixels();
-
   size_t y_offset = 0;
   size_t uv_offset = 0;
   if (IsYUV(video_frame->format())) {
@@ -90,8 +121,8 @@ static void ConvertVideoFrameToBitmap(
           video_frame->stride(media::VideoFrame::kUPlane),
           video_frame->data(media::VideoFrame::kVPlane) + uv_offset,
           video_frame->stride(media::VideoFrame::kVPlane),
-          static_cast<uint8*>(bitmap->getPixels()),
-          bitmap->rowBytes(),
+          static_cast<uint8*>(rgb_pixels),
+          row_bytes,
           video_frame->visible_rect().width(),
           video_frame->visible_rect().height());
       break;
@@ -101,12 +132,12 @@ static void ConvertVideoFrameToBitmap(
           video_frame->data(media::VideoFrame::kYPlane) + y_offset,
           video_frame->data(media::VideoFrame::kUPlane) + uv_offset,
           video_frame->data(media::VideoFrame::kVPlane) + uv_offset,
-          static_cast<uint8*>(bitmap->getPixels()),
+          static_cast<uint8*>(rgb_pixels),
           video_frame->visible_rect().width(),
           video_frame->visible_rect().height(),
           video_frame->stride(media::VideoFrame::kYPlane),
           video_frame->stride(media::VideoFrame::kUPlane),
-          bitmap->rowBytes(),
+          row_bytes,
           media::YV12J);
       break;
 
@@ -118,8 +149,8 @@ static void ConvertVideoFrameToBitmap(
           video_frame->stride(media::VideoFrame::kUPlane),
           video_frame->data(media::VideoFrame::kVPlane) + uv_offset,
           video_frame->stride(media::VideoFrame::kVPlane),
-          static_cast<uint8*>(bitmap->getPixels()),
-          bitmap->rowBytes(),
+          static_cast<uint8*>(rgb_pixels),
+          row_bytes,
           video_frame->visible_rect().width(),
           video_frame->visible_rect().height());
       break;
@@ -133,13 +164,13 @@ static void ConvertVideoFrameToBitmap(
           video_frame->data(media::VideoFrame::kUPlane) + uv_offset,
           video_frame->data(media::VideoFrame::kVPlane) + uv_offset,
           video_frame->data(media::VideoFrame::kAPlane),
-          static_cast<uint8*>(bitmap->getPixels()),
+          static_cast<uint8*>(rgb_pixels),
           video_frame->visible_rect().width(),
           video_frame->visible_rect().height(),
           video_frame->stride(media::VideoFrame::kYPlane),
           video_frame->stride(media::VideoFrame::kUPlane),
           video_frame->stride(media::VideoFrame::kAPlane),
-          bitmap->rowBytes(),
+          row_bytes,
           media::YV12);
       break;
 
@@ -151,45 +182,147 @@ static void ConvertVideoFrameToBitmap(
           video_frame->stride(media::VideoFrame::kUPlane),
           video_frame->data(media::VideoFrame::kVPlane) + uv_offset,
           video_frame->stride(media::VideoFrame::kVPlane),
-          static_cast<uint8*>(bitmap->getPixels()),
-          bitmap->rowBytes(),
+          static_cast<uint8*>(rgb_pixels),
+          row_bytes,
           video_frame->visible_rect().width(),
           video_frame->visible_rect().height());
 #if SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \
     SK_A32_SHIFT == 24
-      libyuv::ARGBToABGR(
-          static_cast<uint8*>(bitmap->getPixels()),
-          bitmap->rowBytes(),
-          static_cast<uint8*>(bitmap->getPixels()),
-          bitmap->rowBytes(),
-          video_frame->visible_rect().width(),
-          video_frame->visible_rect().height());
+      libyuv::ARGBToABGR(static_cast<uint8*>(rgb_pixels),
+                         row_bytes,
+                         static_cast<uint8*>(rgb_pixels),
+                         row_bytes,
+                         video_frame->visible_rect().width(),
+                         video_frame->visible_rect().height());
 #endif
       break;
 
-    case media::VideoFrame::NATIVE_TEXTURE:
+    case media::VideoFrame::NATIVE_TEXTURE: {
       DCHECK_EQ(video_frame->format(), media::VideoFrame::NATIVE_TEXTURE);
-      video_frame->ReadPixelsFromNativeTexture(*bitmap);
+      SkBitmap tmp;
+      tmp.installPixels(
+          SkImageInfo::MakeN32Premul(video_frame->visible_rect().width(),
+                                     video_frame->visible_rect().height()),
+          rgb_pixels,
+          row_bytes);
+      video_frame->ReadPixelsFromNativeTexture(tmp);
       break;
-
+    }
     default:
       NOTREACHED();
       break;
   }
-  bitmap->notifyPixelsChanged();
-  bitmap->unlockPixels();
 }
 
+}  // anonymous namespace
+
+// Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU.
+class VideoImageGenerator : public SkImageGenerator {
+ public:
+  VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) {
+    DCHECK(frame_.get());
+  }
+  ~VideoImageGenerator() override {}
+
+  void set_frame(const scoped_refptr<VideoFrame>& frame) { frame_ = frame; }
+
+ protected:
+  bool onGetInfo(SkImageInfo* info) override {
+    info->fWidth = frame_->visible_rect().width();
+    info->fHeight = frame_->visible_rect().height();
+    info->fColorType = kN32_SkColorType;
+    info->fAlphaType = kPremul_SkAlphaType;
+    return true;
+  }
+
+  bool onGetPixels(const SkImageInfo& info,
+                   void* pixels,
+                   size_t row_bytes,
+                   SkPMColor ctable[],
+                   int* ctable_count) override {
+    if (!frame_.get())
+      return false;
+    if (!pixels)
+      return false;
+    // If skia couldn't do the YUV conversion on GPU, we will on CPU.
+    ConvertVideoFrameToRGBPixels(frame_, pixels, row_bytes);
+    return true;
+  }
+
+  bool onGetYUV8Planes(SkISize sizes[3],
+                       void* planes[3],
+                       size_t row_bytes[3],
+                       SkYUVColorSpace* color_space) override {
+    if (!frame_.get() || !IsYUV(frame_->format()))
+      return false;
+
+    if (color_space) {
+      if (IsJPEGColorSpace(frame_->format()))
+        *color_space = kJPEG_SkYUVColorSpace;
+      else
+        *color_space = kRec601_SkYUVColorSpace;
+    }
+
+    for (int plane = VideoFrame::kYPlane; plane <= VideoFrame::kVPlane;
+         ++plane) {
+      if (sizes) {
+        gfx::Size size;
+        size =
+            VideoFrame::PlaneSize(frame_->format(),
+                                  plane,
+                                  gfx::Size(frame_->visible_rect().width(),
+                                            frame_->visible_rect().height()));
+        sizes[plane].set(size.width(), size.height());
+      }
+      if (row_bytes && planes) {
+        size_t offset;
+        int y_shift = (frame_->format() == media::VideoFrame::YV16) ? 0 : 1;
+        if (plane == media::VideoFrame::kYPlane) {
+          offset = (frame_->stride(media::VideoFrame::kYPlane) *
+                    frame_->visible_rect().y()) +
+                   frame_->visible_rect().x();
+        } else {
+          offset = (frame_->stride(media::VideoFrame::kUPlane) *
+                    (frame_->visible_rect().y() >> y_shift)) +
+                   (frame_->visible_rect().x() >> 1);
+        }
+        row_bytes[plane] = static_cast<size_t>(frame_->stride(plane));
+        planes[plane] = frame_->data(plane) + offset;
+      }
+    }
+    return true;
+  }
+
+ private:
+  scoped_refptr<VideoFrame> frame_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator);
+};
+
 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
-    : last_frame_timestamp_(media::kNoTimestamp()) {
+    : last_frame_timestamp_(media::kNoTimestamp()),
+      frame_deleting_timer_(
+          FROM_HERE,
+          base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay),
+          this,
+          &SkCanvasVideoRenderer::ResetLastFrame),
+      accelerated_generator_(NULL),
+      accelerated_last_frame_timestamp_(media::kNoTimestamp()),
+      accelerated_frame_deleting_timer_(
+          FROM_HERE,
+          base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay),
+          this,
+          &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) {
+  last_frame_.setIsVolatile(true);
 }
 
 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
 
-void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame,
+void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
                                   SkCanvas* canvas,
                                   const gfx::RectF& dest_rect,
                                   uint8 alpha,
+                                  SkXfermode::Mode mode,
                                   VideoRotation video_rotation) {
   if (alpha == 0) {
     return;
@@ -203,43 +336,127 @@ void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame,
 
   // Paint black rectangle if there isn't a frame available or the
   // frame has an unexpected format.
-  if (!video_frame || !IsYUVOrNative(video_frame->format())) {
+  if (!video_frame.get() || video_frame->natural_size().IsEmpty() ||
+      !IsYUVOrNative(video_frame->format())) {
     canvas->drawRect(dest, paint);
+    canvas->flush();
     return;
   }
 
-  // Check if we should convert and update |last_frame_|.
-  if (last_frame_.isNull() ||
-      video_frame->timestamp() != last_frame_timestamp_) {
-    ConvertVideoFrameToBitmap(video_frame, &last_frame_);
+  SkBitmap* target_frame = NULL;
+  if (canvas->getGrContext()) {
+    if (accelerated_last_frame_.isNull() ||
+        video_frame->timestamp() != accelerated_last_frame_timestamp_) {
+      accelerated_generator_ = new VideoImageGenerator(video_frame);
+
+      // Note: This takes ownership of |accelerated_generator_|.
+      if (!SkInstallDiscardablePixelRef(accelerated_generator_,
+                                        &accelerated_last_frame_)) {
+        NOTREACHED();
+      }
+      DCHECK(video_frame->visible_rect().width() ==
+                 accelerated_last_frame_.width() &&
+             video_frame->visible_rect().height() ==
+                 accelerated_last_frame_.height());
 
+      accelerated_last_frame_timestamp_ = video_frame->timestamp();
+    } else {
+      accelerated_generator_->set_frame(video_frame);
+    }
+    target_frame = &accelerated_last_frame_;
+    accelerated_frame_deleting_timer_.Reset();
+  } else {
+    // Check if we should convert and update |last_frame_|.
+    if (last_frame_.isNull() ||
+        video_frame->timestamp() != last_frame_timestamp_) {
+      // Check if |bitmap| needs to be (re)allocated.
+      if (last_frame_.isNull() ||
+          last_frame_.width() != video_frame->visible_rect().width() ||
+          last_frame_.height() != video_frame->visible_rect().height()) {
+        last_frame_.allocN32Pixels(video_frame->visible_rect().width(),
+                                   video_frame->visible_rect().height());
+        last_frame_.setIsVolatile(true);
+      }
+      last_frame_.lockPixels();
+      ConvertVideoFrameToRGBPixels(
+          video_frame, last_frame_.getPixels(), last_frame_.rowBytes());
+      last_frame_.notifyPixelsChanged();
+      last_frame_.unlockPixels();
+      last_frame_timestamp_ = video_frame->timestamp();
+    }
+    target_frame = &last_frame_;
+    frame_deleting_timer_.Reset();
+  }
+
+  paint.setXfermodeMode(mode);
+  paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+
+  bool need_transform =
+      video_rotation != VIDEO_ROTATION_0 ||
+      dest_rect.size() != video_frame->visible_rect().size() ||
+      !dest_rect.origin().IsOrigin();
+  if (need_transform) {
+    canvas->save();
+    canvas->translate(
+        SkFloatToScalar(dest_rect.x() + (dest_rect.width() * 0.5f)),
+        SkFloatToScalar(dest_rect.y() + (dest_rect.height() * 0.5f)));
+    SkScalar angle = SkFloatToScalar(0.0f);
     switch (video_rotation) {
       case VIDEO_ROTATION_0:
         break;
       case VIDEO_ROTATION_90:
-        last_frame_ = SkBitmapOperations::Rotate(
-            last_frame_, SkBitmapOperations::ROTATION_90_CW);
+        angle = SkFloatToScalar(90.0f);
         break;
       case VIDEO_ROTATION_180:
-        last_frame_ = SkBitmapOperations::Rotate(
-            last_frame_, SkBitmapOperations::ROTATION_180_CW);
+        angle = SkFloatToScalar(180.0f);
         break;
       case VIDEO_ROTATION_270:
-        last_frame_ = SkBitmapOperations::Rotate(
-            last_frame_, SkBitmapOperations::ROTATION_270_CW);
+        angle = SkFloatToScalar(270.0f);
         break;
     }
+    canvas->rotate(angle);
 
-    last_frame_timestamp_ = video_frame->timestamp();
+    gfx::SizeF rotated_dest_size = dest_rect.size();
+    if (video_rotation == VIDEO_ROTATION_90 ||
+        video_rotation == VIDEO_ROTATION_270) {
+      rotated_dest_size =
+          gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width());
+    }
+    canvas->scale(
+        SkFloatToScalar(rotated_dest_size.width() / target_frame->width()),
+        SkFloatToScalar(rotated_dest_size.height() / target_frame->height()));
+    canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f),
+                      -SkFloatToScalar(target_frame->height() * 0.5f));
   }
+  canvas->drawBitmap(*target_frame, 0, 0, &paint);
+  if (need_transform)
+    canvas->restore();
+  canvas->flush();
+  // SkCanvas::flush() causes the generator to generate SkImage, so delete
+  // |video_frame| not to be outlived.
+  if (canvas->getGrContext())
+    accelerated_generator_->set_frame(NULL);
+}
 
-  // Use SRC mode so we completely overwrite the buffer (in case we have alpha)
-  // this means we don't need the extra cost of clearing the buffer first.
-  paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode));
+void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame,
+                                 SkCanvas* canvas) {
+  Paint(video_frame,
+        canvas,
+        video_frame->visible_rect(),
+        0xff,
+        SkXfermode::kSrc_Mode,
+        media::VIDEO_ROTATION_0);
+}
 
-  // Paint using |last_frame_|.
-  paint.setFilterLevel(SkPaint::kLow_FilterLevel);
-  canvas->drawBitmapRect(last_frame_, NULL, dest, &paint);
+void SkCanvasVideoRenderer::ResetLastFrame() {
+  last_frame_.reset();
+  last_frame_timestamp_ = media::kNoTimestamp();
+}
+
+void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() {
+  accelerated_last_frame_.reset();
+  accelerated_generator_ = nullptr;
+  accelerated_last_frame_timestamp_ = media::kNoTimestamp();
 }
 
 }  // namespace media