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