This source file includes following definitions.
- IsYUV
- IsEitherYUVOrNative
- IsEitherYUVOrYUVA
- IsEitherYUVOrYUVAOrNative
- CanFastPaint
- FastPaint
- ConvertVideoFrameToBitmap
- Paint
#include "media/filters/skcanvas_video_renderer.h"
#include "base/logging.h"
#include "media/base/video_frame.h"
#include "media/base/yuv_convert.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkDevice.h"
namespace media {
static bool IsYUV(media::VideoFrame::Format format) {
return format == media::VideoFrame::YV12 ||
format == media::VideoFrame::I420 ||
format == media::VideoFrame::YV16 ||
format == media::VideoFrame::YV12J;
}
static bool IsEitherYUVOrNative(media::VideoFrame::Format format) {
return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE;
}
static bool IsEitherYUVOrYUVA(media::VideoFrame::Format format) {
return IsYUV(format) || format == media::VideoFrame::YV12A;
}
static bool IsEitherYUVOrYUVAOrNative(media::VideoFrame::Format format) {
return IsEitherYUVOrNative(format) || format == media::VideoFrame::YV12A;
}
static bool CanFastPaint(SkCanvas* canvas, uint8 alpha,
media::VideoFrame::Format format) {
if (alpha != 0xFF || !IsYUV(format))
return false;
const SkMatrix& total_matrix = canvas->getTotalMatrix();
if (SkScalarNearlyZero(total_matrix.getSkewX()) &&
SkScalarNearlyZero(total_matrix.getSkewY()) &&
total_matrix.getScaleX() > 0 &&
total_matrix.getScaleY() > 0) {
SkBaseDevice* device = canvas->getDevice();
const SkBitmap::Config config = device->config();
if (config == SkBitmap::kARGB_8888_Config && device->isOpaque()) {
return true;
}
}
return false;
}
static void FastPaint(
const scoped_refptr<media::VideoFrame>& video_frame,
SkCanvas* canvas,
const SkRect& dest_rect) {
DCHECK(IsYUV(video_frame->format())) << video_frame->format();
DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane),
video_frame->stride(media::VideoFrame::kVPlane));
const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(true);
media::YUVType yuv_type = media::YV16;
int y_shift = 0;
if (video_frame->format() == media::VideoFrame::YV12 ||
video_frame->format() == media::VideoFrame::I420 ||
video_frame->format() == media::VideoFrame::YV12A) {
yuv_type = media::YV12;
y_shift = 1;
}
if (video_frame->format() == media::VideoFrame::YV12J) {
yuv_type = media::YV12;
y_shift = 1;
}
const SkMatrix& local_matrix = canvas->getTotalMatrix();
SkRect local_dest_rect;
local_matrix.mapRect(&local_dest_rect, dest_rect);
SkIRect local_dest_irect, local_dest_irect_saved;
local_dest_rect.round(&local_dest_irect);
local_dest_rect.round(&local_dest_irect_saved);
if (!local_dest_irect.intersect(canvas->getTotalClip().getBounds()))
return;
uint8* dest_rect_pointer = static_cast<uint8*>(bitmap.getPixels()) +
local_dest_irect.fTop * bitmap.rowBytes() +
local_dest_irect.fLeft * 4;
DCHECK_NE(0, dest_rect.width());
DCHECK_NE(0, dest_rect.height());
size_t frame_clip_width = local_dest_irect.width() *
video_frame->visible_rect().width() / local_dest_irect_saved.width();
size_t frame_clip_height = local_dest_irect.height() *
video_frame->visible_rect().height() / local_dest_irect_saved.height();
size_t frame_clip_left =
video_frame->visible_rect().x() +
(local_dest_irect.fLeft - local_dest_irect_saved.fLeft) *
video_frame->visible_rect().width() / local_dest_irect_saved.width();
size_t frame_clip_top =
video_frame->visible_rect().y() +
(local_dest_irect.fTop - local_dest_irect_saved.fTop) *
video_frame->visible_rect().height() / local_dest_irect_saved.height();
size_t y_offset = (video_frame->stride(media::VideoFrame::kYPlane) *
frame_clip_top) + frame_clip_left;
size_t uv_offset = (video_frame->stride(media::VideoFrame::kUPlane) *
(frame_clip_top >> y_shift)) + (frame_clip_left >> 1);
uint8* frame_clip_y =
video_frame->data(media::VideoFrame::kYPlane) + y_offset;
uint8* frame_clip_u =
video_frame->data(media::VideoFrame::kUPlane) + uv_offset;
uint8* frame_clip_v =
video_frame->data(media::VideoFrame::kVPlane) + uv_offset;
bitmap.lockPixels();
media::ScaleYUVToRGB32(frame_clip_y,
frame_clip_u,
frame_clip_v,
dest_rect_pointer,
frame_clip_width,
frame_clip_height,
local_dest_irect.width(),
local_dest_irect.height(),
video_frame->stride(media::VideoFrame::kYPlane),
video_frame->stride(media::VideoFrame::kUPlane),
bitmap.rowBytes(),
yuv_type,
media::ROTATE_0,
media::FILTER_BILINEAR);
bitmap.unlockPixels();
}
static void ConvertVideoFrameToBitmap(
const scoped_refptr<media::VideoFrame>& video_frame,
SkBitmap* bitmap) {
DCHECK(IsEitherYUVOrYUVAOrNative(video_frame->format()))
<< video_frame->format();
if (IsEitherYUVOrYUVA(video_frame->format())) {
DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane),
video_frame->stride(media::VideoFrame::kVPlane));
}
if (bitmap->isNull() ||
bitmap->width() != video_frame->visible_rect().width() ||
bitmap->height() != video_frame->visible_rect().height()) {
bitmap->setConfig(SkBitmap::kARGB_8888_Config,
video_frame->visible_rect().width(),
video_frame->visible_rect().height());
bitmap->allocPixels();
bitmap->setIsVolatile(true);
}
bitmap->lockPixels();
size_t y_offset = 0;
size_t uv_offset = 0;
if (IsEitherYUVOrYUVA(video_frame->format())) {
int y_shift = (video_frame->format() == media::VideoFrame::YV16) ? 0 : 1;
y_offset = (video_frame->stride(media::VideoFrame::kYPlane) *
video_frame->visible_rect().y()) +
video_frame->visible_rect().x();
uv_offset = (video_frame->stride(media::VideoFrame::kUPlane) *
(video_frame->visible_rect().y() >> y_shift)) +
(video_frame->visible_rect().x() >> 1);
}
switch (video_frame->format()) {
case media::VideoFrame::YV12:
case media::VideoFrame::I420:
case media::VideoFrame::YV12J:
media::ConvertYUVToRGB32(
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()),
video_frame->visible_rect().width(),
video_frame->visible_rect().height(),
video_frame->stride(media::VideoFrame::kYPlane),
video_frame->stride(media::VideoFrame::kUPlane),
bitmap->rowBytes(),
media::YV12);
break;
case media::VideoFrame::YV16:
media::ConvertYUVToRGB32(
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()),
video_frame->visible_rect().width(),
video_frame->visible_rect().height(),
video_frame->stride(media::VideoFrame::kYPlane),
video_frame->stride(media::VideoFrame::kUPlane),
bitmap->rowBytes(),
media::YV16);
break;
case media::VideoFrame::YV12A:
media::ConvertYUVAToARGB(
video_frame->data(media::VideoFrame::kYPlane) + y_offset,
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()),
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(),
media::YV12);
break;
case media::VideoFrame::NATIVE_TEXTURE:
DCHECK_EQ(video_frame->format(), media::VideoFrame::NATIVE_TEXTURE);
video_frame->ReadPixelsFromNativeTexture(*bitmap);
break;
default:
NOTREACHED();
break;
}
bitmap->notifyPixelsChanged();
bitmap->unlockPixels();
}
SkCanvasVideoRenderer::SkCanvasVideoRenderer()
: last_frame_timestamp_(media::kNoTimestamp()) {
}
SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame,
SkCanvas* canvas,
const gfx::RectF& dest_rect,
uint8 alpha) {
if (alpha == 0) {
return;
}
SkRect dest;
dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom());
SkPaint paint;
paint.setAlpha(alpha);
if (!video_frame || !IsEitherYUVOrYUVAOrNative(video_frame->format())) {
canvas->drawRect(dest, paint);
return;
}
if (CanFastPaint(canvas, alpha, video_frame->format())) {
FastPaint(video_frame, canvas, dest);
return;
}
if (last_frame_.isNull() ||
video_frame->GetTimestamp() != last_frame_timestamp_) {
ConvertVideoFrameToBitmap(video_frame, &last_frame_);
last_frame_timestamp_ = video_frame->GetTimestamp();
}
paint.setFilterBitmap(true);
canvas->drawBitmapRect(last_frame_, NULL, dest, &paint);
}
}