Fix some potential after free errors on TestCompletionCallback
[chromium-blink-merge.git] / media / filters / skcanvas_video_renderer.cc
blob5a889e333a99cbbca0906a16a659e8128d6de1e8
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 IsEitherYV12OrYV16(media::VideoFrame::Format format) {
16 return format == media::VideoFrame::YV12 || format == media::VideoFrame::YV16;
19 static bool IsEitherYV12OrYV16OrNative(media::VideoFrame::Format format) {
20 return IsEitherYV12OrYV16(format) ||
21 format == media::VideoFrame::NATIVE_TEXTURE;
24 static bool IsEitherYV12OrYV12AOrYV16(media::VideoFrame::Format format) {
25 return IsEitherYV12OrYV16(format) ||
26 format == media::VideoFrame::YV12A;
29 static bool IsEitherYV12OrYV12AOrYV16OrNative(
30 media::VideoFrame::Format format) {
31 return IsEitherYV12OrYV16OrNative(format) ||
32 format == media::VideoFrame::YV12A;
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.
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;
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();
62 if (config == SkBitmap::kARGB_8888_Config && device->isOpaque()) {
63 return true;
67 return false;
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));
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;
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);
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);
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;
107 // At this point |local_dest_irect| contains the rect that we should draw
108 // to within the clipping rect.
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;
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();
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();
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;
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;
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();
179 // Converts a VideoFrame containing YUV data to a SkBitmap containing RGB data.
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));
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);
203 bitmap->lockPixels();
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);
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;
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;
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;
265 case media::VideoFrame::NATIVE_TEXTURE:
266 DCHECK_EQ(video_frame->format(), media::VideoFrame::NATIVE_TEXTURE);
267 video_frame->ReadPixelsFromNativeTexture(*bitmap);
268 break;
270 default:
271 NOTREACHED();
272 break;
274 bitmap->notifyPixelsChanged();
275 bitmap->unlockPixels();
278 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
279 : last_frame_timestamp_(media::kNoTimestamp()) {
282 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
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;
292 SkRect dest;
293 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom());
295 SkPaint paint;
296 paint.setAlpha(alpha);
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;
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;
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();
319 // Do a slower paint using |last_frame_|.
320 paint.setFilterBitmap(true);
321 canvas->drawBitmapRect(last_frame_, NULL, dest, &paint);
324 } // namespace media