Home | History | Annotate | Download | only in filters
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "media/filters/skcanvas_video_renderer.h"
      6 
      7 #include "base/logging.h"
      8 #include "media/base/video_frame.h"
      9 #include "media/base/yuv_convert.h"
     10 #include "third_party/skia/include/core/SkCanvas.h"
     11 #include "third_party/skia/include/core/SkDevice.h"
     12 
     13 namespace media {
     14 
     15 static bool IsEitherYV12OrYV16(media::VideoFrame::Format format) {
     16   return format == media::VideoFrame::YV12 || format == media::VideoFrame::YV16;
     17 }
     18 
     19 static bool IsEitherYV12OrYV16OrNative(media::VideoFrame::Format format) {
     20   return IsEitherYV12OrYV16(format) ||
     21       format == media::VideoFrame::NATIVE_TEXTURE;
     22 }
     23 
     24 static bool IsEitherYV12OrYV12AOrYV16(media::VideoFrame::Format format) {
     25   return IsEitherYV12OrYV16(format) ||
     26       format == media::VideoFrame::YV12A;
     27 }
     28 
     29 static bool IsEitherYV12OrYV12AOrYV16OrNative(
     30     media::VideoFrame::Format format) {
     31   return IsEitherYV12OrYV16OrNative(format) ||
     32       format == media::VideoFrame::YV12A;
     33 }
     34 
     35 // CanFastPaint is a helper method to determine the conditions for fast
     36 // painting. The conditions are:
     37 // 1. No skew in canvas matrix.
     38 // 2. No flipping nor mirroring.
     39 // 3. Canvas has pixel format ARGB8888.
     40 // 4. Canvas is opaque.
     41 // 5. Frame format is YV12 or YV16.
     42 //
     43 // TODO(hclam): The fast paint method should support flipping and mirroring.
     44 // Disable the flipping and mirroring checks once we have it.
     45 static bool CanFastPaint(SkCanvas* canvas, uint8 alpha,
     46                          media::VideoFrame::Format format) {
     47   if (alpha != 0xFF || !IsEitherYV12OrYV16(format))
     48     return false;
     49 
     50   const SkMatrix& total_matrix = canvas->getTotalMatrix();
     51   // Perform the following checks here:
     52   // 1. Check for skewing factors of the transformation matrix. They should be
     53   //    zero.
     54   // 2. Check for mirroring and flipping. Make sure they are greater than zero.
     55   if (SkScalarNearlyZero(total_matrix.getSkewX()) &&
     56       SkScalarNearlyZero(total_matrix.getSkewY()) &&
     57       total_matrix.getScaleX() > 0 &&
     58       total_matrix.getScaleY() > 0) {
     59     SkDevice* device = canvas->getDevice();
     60     const SkBitmap::Config config = device->config();
     61 
     62     if (config == SkBitmap::kARGB_8888_Config && device->isOpaque()) {
     63       return true;
     64     }
     65   }
     66 
     67   return false;
     68 }
     69 
     70 // Fast paint does YUV => RGB, scaling, blitting all in one step into the
     71 // canvas. It's not always safe and appropriate to perform fast paint.
     72 // CanFastPaint() is used to determine the conditions.
     73 static void FastPaint(
     74     const scoped_refptr<media::VideoFrame>& video_frame,
     75     SkCanvas* canvas,
     76     const SkRect& dest_rect) {
     77   DCHECK(IsEitherYV12OrYV16(video_frame->format())) << video_frame->format();
     78   DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane),
     79             video_frame->stride(media::VideoFrame::kVPlane));
     80 
     81   const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(true);
     82   media::YUVType yuv_type = media::YV16;
     83   int y_shift = 0;
     84   if (video_frame->format() == media::VideoFrame::YV12 ||
     85       video_frame->format() == media::VideoFrame::YV12A) {
     86     yuv_type = media::YV12;
     87     y_shift = 1;
     88   }
     89 
     90   // Transform the destination rectangle to local coordinates.
     91   const SkMatrix& local_matrix = canvas->getTotalMatrix();
     92   SkRect local_dest_rect;
     93   local_matrix.mapRect(&local_dest_rect, dest_rect);
     94 
     95   // After projecting the destination rectangle to local coordinates, round
     96   // the projected rectangle to integer values, this will give us pixel values
     97   // of the rectangle.
     98   SkIRect local_dest_irect, local_dest_irect_saved;
     99   local_dest_rect.round(&local_dest_irect);
    100   local_dest_rect.round(&local_dest_irect_saved);
    101 
    102   // No point painting if the destination rect doesn't intersect with the
    103   // clip rect.
    104   if (!local_dest_irect.intersect(canvas->getTotalClip().getBounds()))
    105     return;
    106 
    107   // At this point |local_dest_irect| contains the rect that we should draw
    108   // to within the clipping rect.
    109 
    110   // Calculate the address for the top left corner of destination rect in
    111   // the canvas that we will draw to. The address is obtained by the base
    112   // address of the canvas shifted by "left" and "top" of the rect.
    113   uint8* dest_rect_pointer = static_cast<uint8*>(bitmap.getPixels()) +
    114       local_dest_irect.fTop * bitmap.rowBytes() +
    115       local_dest_irect.fLeft * 4;
    116 
    117   // Project the clip rect to the original video frame, obtains the
    118   // dimensions of the projected clip rect, "left" and "top" of the rect.
    119   // The math here are all integer math so we won't have rounding error and
    120   // write outside of the canvas.
    121   // We have the assumptions of dest_rect.width() and dest_rect.height()
    122   // being non-zero, these are valid assumptions since finding intersection
    123   // above rejects empty rectangle so we just do a DCHECK here.
    124   DCHECK_NE(0, dest_rect.width());
    125   DCHECK_NE(0, dest_rect.height());
    126   size_t frame_clip_width = local_dest_irect.width() *
    127       video_frame->visible_rect().width() / local_dest_irect_saved.width();
    128   size_t frame_clip_height = local_dest_irect.height() *
    129       video_frame->visible_rect().height() / local_dest_irect_saved.height();
    130 
    131   // Project the "left" and "top" of the final destination rect to local
    132   // coordinates of the video frame, use these values to find the offsets
    133   // in the video frame to start reading.
    134   size_t frame_clip_left =
    135       video_frame->visible_rect().x() +
    136       (local_dest_irect.fLeft - local_dest_irect_saved.fLeft) *
    137       video_frame->visible_rect().width() / local_dest_irect_saved.width();
    138   size_t frame_clip_top =
    139       video_frame->visible_rect().y() +
    140       (local_dest_irect.fTop - local_dest_irect_saved.fTop) *
    141       video_frame->visible_rect().height() / local_dest_irect_saved.height();
    142 
    143   // Use the "left" and "top" of the destination rect to locate the offset
    144   // in Y, U and V planes.
    145   size_t y_offset = (video_frame->stride(media::VideoFrame::kYPlane) *
    146                      frame_clip_top) + frame_clip_left;
    147 
    148   // For format YV12, there is one U, V value per 2x2 block.
    149   // For format YV16, there is one U, V value per 2x1 block.
    150   size_t uv_offset = (video_frame->stride(media::VideoFrame::kUPlane) *
    151                       (frame_clip_top >> y_shift)) + (frame_clip_left >> 1);
    152   uint8* frame_clip_y =
    153       video_frame->data(media::VideoFrame::kYPlane) + y_offset;
    154   uint8* frame_clip_u =
    155       video_frame->data(media::VideoFrame::kUPlane) + uv_offset;
    156   uint8* frame_clip_v =
    157       video_frame->data(media::VideoFrame::kVPlane) + uv_offset;
    158 
    159   // TODO(hclam): do rotation and mirroring here.
    160   // TODO(fbarchard): switch filtering based on performance.
    161   bitmap.lockPixels();
    162   media::ScaleYUVToRGB32(frame_clip_y,
    163                          frame_clip_u,
    164                          frame_clip_v,
    165                          dest_rect_pointer,
    166                          frame_clip_width,
    167                          frame_clip_height,
    168                          local_dest_irect.width(),
    169                          local_dest_irect.height(),
    170                          video_frame->stride(media::VideoFrame::kYPlane),
    171                          video_frame->stride(media::VideoFrame::kUPlane),
    172                          bitmap.rowBytes(),
    173                          yuv_type,
    174                          media::ROTATE_0,
    175                          media::FILTER_BILINEAR);
    176   bitmap.unlockPixels();
    177 }
    178 
    179 // Converts a VideoFrame containing YUV data to a SkBitmap containing RGB data.
    180 //
    181 // |bitmap| will be (re)allocated to match the dimensions of |video_frame|.
    182 static void ConvertVideoFrameToBitmap(
    183     const scoped_refptr<media::VideoFrame>& video_frame,
    184     SkBitmap* bitmap) {
    185   DCHECK(IsEitherYV12OrYV12AOrYV16OrNative(video_frame->format()))
    186       << video_frame->format();
    187   if (IsEitherYV12OrYV12AOrYV16(video_frame->format())) {
    188     DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane),
    189               video_frame->stride(media::VideoFrame::kVPlane));
    190   }
    191 
    192   // Check if |bitmap| needs to be (re)allocated.
    193   if (bitmap->isNull() ||
    194       bitmap->width() != video_frame->visible_rect().width() ||
    195       bitmap->height() != video_frame->visible_rect().height()) {
    196     bitmap->setConfig(SkBitmap::kARGB_8888_Config,
    197                       video_frame->visible_rect().width(),
    198                       video_frame->visible_rect().height());
    199     bitmap->allocPixels();
    200     bitmap->setIsVolatile(true);
    201   }
    202 
    203   bitmap->lockPixels();
    204 
    205   size_t y_offset = 0;
    206   size_t uv_offset = 0;
    207   if (IsEitherYV12OrYV12AOrYV16(video_frame->format())) {
    208     int y_shift = (video_frame->format() == media::VideoFrame::YV16) ? 0 : 1;
    209     // Use the "left" and "top" of the destination rect to locate the offset
    210     // in Y, U and V planes.
    211     y_offset = (video_frame->stride(media::VideoFrame::kYPlane) *
    212                 video_frame->visible_rect().y()) +
    213                 video_frame->visible_rect().x();
    214     // For format YV12, there is one U, V value per 2x2 block.
    215     // For format YV16, there is one U, V value per 2x1 block.
    216     uv_offset = (video_frame->stride(media::VideoFrame::kUPlane) *
    217                 (video_frame->visible_rect().y() >> y_shift)) +
    218                 (video_frame->visible_rect().x() >> 1);
    219   }
    220   switch (video_frame->format()) {
    221     case media::VideoFrame::YV12:
    222       media::ConvertYUVToRGB32(
    223           video_frame->data(media::VideoFrame::kYPlane) + y_offset,
    224           video_frame->data(media::VideoFrame::kUPlane) + uv_offset,
    225           video_frame->data(media::VideoFrame::kVPlane) + uv_offset,
    226           static_cast<uint8*>(bitmap->getPixels()),
    227           video_frame->visible_rect().width(),
    228           video_frame->visible_rect().height(),
    229           video_frame->stride(media::VideoFrame::kYPlane),
    230           video_frame->stride(media::VideoFrame::kUPlane),
    231           bitmap->rowBytes(),
    232           media::YV12);
    233       break;
    234 
    235     case media::VideoFrame::YV16:
    236       media::ConvertYUVToRGB32(
    237           video_frame->data(media::VideoFrame::kYPlane) + y_offset,
    238           video_frame->data(media::VideoFrame::kUPlane) + uv_offset,
    239           video_frame->data(media::VideoFrame::kVPlane) + uv_offset,
    240           static_cast<uint8*>(bitmap->getPixels()),
    241           video_frame->visible_rect().width(),
    242           video_frame->visible_rect().height(),
    243           video_frame->stride(media::VideoFrame::kYPlane),
    244           video_frame->stride(media::VideoFrame::kUPlane),
    245           bitmap->rowBytes(),
    246           media::YV16);
    247       break;
    248 
    249     case media::VideoFrame::YV12A:
    250       media::ConvertYUVAToARGB(
    251           video_frame->data(media::VideoFrame::kYPlane) + y_offset,
    252           video_frame->data(media::VideoFrame::kUPlane) + uv_offset,
    253           video_frame->data(media::VideoFrame::kVPlane) + uv_offset,
    254           video_frame->data(media::VideoFrame::kAPlane),
    255           static_cast<uint8*>(bitmap->getPixels()),
    256           video_frame->visible_rect().width(),
    257           video_frame->visible_rect().height(),
    258           video_frame->stride(media::VideoFrame::kYPlane),
    259           video_frame->stride(media::VideoFrame::kUPlane),
    260           video_frame->stride(media::VideoFrame::kAPlane),
    261           bitmap->rowBytes(),
    262           media::YV12);
    263       break;
    264 
    265     case media::VideoFrame::NATIVE_TEXTURE:
    266       DCHECK_EQ(video_frame->format(), media::VideoFrame::NATIVE_TEXTURE);
    267       video_frame->ReadPixelsFromNativeTexture(*bitmap);
    268       break;
    269 
    270     default:
    271       NOTREACHED();
    272       break;
    273   }
    274   bitmap->notifyPixelsChanged();
    275   bitmap->unlockPixels();
    276 }
    277 
    278 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
    279     : last_frame_timestamp_(media::kNoTimestamp()) {
    280 }
    281 
    282 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
    283 
    284 void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame,
    285                                   SkCanvas* canvas,
    286                                   const gfx::RectF& dest_rect,
    287                                   uint8 alpha) {
    288   if (alpha == 0) {
    289     return;
    290   }
    291 
    292   SkRect dest;
    293   dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom());
    294 
    295   SkPaint paint;
    296   paint.setAlpha(alpha);
    297 
    298   // Paint black rectangle if there isn't a frame available or the
    299   // frame has an unexpected format.
    300   if (!video_frame ||
    301       !IsEitherYV12OrYV12AOrYV16OrNative(video_frame->format())) {
    302     canvas->drawRect(dest, paint);
    303     return;
    304   }
    305 
    306   // Scale and convert to RGB in one step if we can.
    307   if (CanFastPaint(canvas, alpha, video_frame->format())) {
    308     FastPaint(video_frame, canvas, dest);
    309     return;
    310   }
    311 
    312   // Check if we should convert and update |last_frame_|.
    313   if (last_frame_.isNull() ||
    314       video_frame->GetTimestamp() != last_frame_timestamp_) {
    315     ConvertVideoFrameToBitmap(video_frame, &last_frame_);
    316     last_frame_timestamp_ = video_frame->GetTimestamp();
    317   }
    318 
    319   // Do a slower paint using |last_frame_|.
    320   paint.setFilterBitmap(true);
    321   canvas->drawBitmapRect(last_frame_, NULL, dest, &paint);
    322 }
    323 
    324 }  // namespace media
    325