OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/filters/skcanvas_video_renderer.h" | 5 #include "media/filters/skcanvas_video_renderer.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "media/base/video_frame.h" | 8 #include "media/base/video_frame.h" |
9 #include "media/base/yuv_convert.h" | 9 #include "media/base/yuv_convert.h" |
10 #include "third_party/libyuv/include/libyuv.h" | 10 #include "third_party/libyuv/include/libyuv.h" |
(...skipping 11 matching lines...) Expand all Loading... |
22 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR | 22 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR |
23 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR | 23 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR |
24 #else | 24 #else |
25 #error Unexpected Skia ARGB_8888 layout! | 25 #error Unexpected Skia ARGB_8888 layout! |
26 #endif | 26 #endif |
27 | 27 |
28 namespace media { | 28 namespace media { |
29 | 29 |
30 static bool IsYUV(media::VideoFrame::Format format) { | 30 static bool IsYUV(media::VideoFrame::Format format) { |
31 return format == media::VideoFrame::YV12 || | 31 return format == media::VideoFrame::YV12 || |
| 32 format == media::VideoFrame::YV16 || |
32 format == media::VideoFrame::I420 || | 33 format == media::VideoFrame::I420 || |
| 34 format == media::VideoFrame::YV12A || |
| 35 format == media::VideoFrame::YV12J || |
| 36 format == media::VideoFrame::YV24; |
| 37 } |
| 38 |
| 39 static bool IsFastPaintYUV(media::VideoFrame::Format format) { |
| 40 return format == media::VideoFrame::YV12 || |
33 format == media::VideoFrame::YV16 || | 41 format == media::VideoFrame::YV16 || |
| 42 format == media::VideoFrame::I420 || |
34 format == media::VideoFrame::YV12J; | 43 format == media::VideoFrame::YV12J; |
35 } | 44 } |
36 | 45 |
37 static bool IsEitherYUVOrNative(media::VideoFrame::Format format) { | 46 static bool IsYUVOrNative(media::VideoFrame::Format format) { |
38 return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE; | 47 return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE; |
39 } | 48 } |
40 | 49 |
41 static bool IsEitherYUVOrYUVA(media::VideoFrame::Format format) { | |
42 return IsYUV(format) || format == media::VideoFrame::YV12A; | |
43 } | |
44 | |
45 static bool IsEitherYUVOrYUVAOrNative(media::VideoFrame::Format format) { | |
46 return IsEitherYUVOrNative(format) || format == media::VideoFrame::YV12A; | |
47 } | |
48 | |
49 // CanFastPaint is a helper method to determine the conditions for fast | 50 // CanFastPaint is a helper method to determine the conditions for fast |
50 // painting. The conditions are: | 51 // painting. The conditions are: |
51 // 1. No skew in canvas matrix. | 52 // 1. No skew in canvas matrix. |
52 // 2. No flipping nor mirroring. | 53 // 2. No flipping nor mirroring. |
53 // 3. Canvas has pixel format ARGB8888. | 54 // 3. Canvas has pixel format ARGB8888. |
54 // 4. Canvas is opaque. | 55 // 4. Canvas is opaque. |
55 // 5. Frame format is YV12, I420 or YV16. | 56 // 5. Frame format is YV12, I420 or YV16. |
56 // | 57 // |
57 // TODO(hclam): The fast paint method should support flipping and mirroring. | 58 // TODO(hclam): The fast paint method should support flipping and mirroring. |
58 // Disable the flipping and mirroring checks once we have it. | 59 // Disable the flipping and mirroring checks once we have it. |
59 static bool CanFastPaint(SkCanvas* canvas, uint8 alpha, | 60 static bool CanFastPaint(SkCanvas* canvas, uint8 alpha, |
60 media::VideoFrame::Format format) { | 61 media::VideoFrame::Format format) { |
61 if (alpha != 0xFF || !IsYUV(format)) | 62 if (alpha != 0xFF || !IsFastPaintYUV(format)) |
62 return false; | 63 return false; |
63 | 64 |
64 const SkMatrix& total_matrix = canvas->getTotalMatrix(); | 65 const SkMatrix& total_matrix = canvas->getTotalMatrix(); |
65 // Perform the following checks here: | 66 // Perform the following checks here: |
66 // 1. Check for skewing factors of the transformation matrix. They should be | 67 // 1. Check for skewing factors of the transformation matrix. They should be |
67 // zero. | 68 // zero. |
68 // 2. Check for mirroring and flipping. Make sure they are greater than zero. | 69 // 2. Check for mirroring and flipping. Make sure they are greater than zero. |
69 if (SkScalarNearlyZero(total_matrix.getSkewX()) && | 70 if (SkScalarNearlyZero(total_matrix.getSkewX()) && |
70 SkScalarNearlyZero(total_matrix.getSkewY()) && | 71 SkScalarNearlyZero(total_matrix.getSkewY()) && |
71 total_matrix.getScaleX() > 0 && | 72 total_matrix.getScaleX() > 0 && |
72 total_matrix.getScaleY() > 0) { | 73 total_matrix.getScaleY() > 0) { |
73 SkBaseDevice* device = canvas->getDevice(); | 74 SkBaseDevice* device = canvas->getDevice(); |
74 const SkBitmap::Config config = device->config(); | 75 const SkBitmap::Config config = device->config(); |
75 | 76 |
76 if (config == SkBitmap::kARGB_8888_Config && device->isOpaque()) { | 77 if (config == SkBitmap::kARGB_8888_Config && device->isOpaque()) { |
77 return true; | 78 return true; |
78 } | 79 } |
79 } | 80 } |
80 | 81 |
81 return false; | 82 return false; |
82 } | 83 } |
83 | 84 |
84 // Fast paint does YUV => RGB, scaling, blitting all in one step into the | 85 // Fast paint does YUV => RGB, scaling, blitting all in one step into the |
85 // canvas. It's not always safe and appropriate to perform fast paint. | 86 // canvas. It's not always safe and appropriate to perform fast paint. |
86 // CanFastPaint() is used to determine the conditions. | 87 // CanFastPaint() is used to determine the conditions. |
87 static void FastPaint( | 88 static void FastPaint( |
88 const scoped_refptr<media::VideoFrame>& video_frame, | 89 const scoped_refptr<media::VideoFrame>& video_frame, |
89 SkCanvas* canvas, | 90 SkCanvas* canvas, |
90 const SkRect& dest_rect) { | 91 const SkRect& dest_rect) { |
91 DCHECK(IsYUV(video_frame->format())) << video_frame->format(); | 92 DCHECK(IsFastPaintYUV(video_frame->format())) << video_frame->format(); |
92 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane), | 93 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane), |
93 video_frame->stride(media::VideoFrame::kVPlane)); | 94 video_frame->stride(media::VideoFrame::kVPlane)); |
94 | 95 |
95 const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(true); | 96 const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(true); |
96 media::YUVType yuv_type = media::YV16; | 97 media::YUVType yuv_type = media::YV16; |
97 int y_shift = 0; | 98 int y_shift = 0; |
98 if (video_frame->format() == media::VideoFrame::YV12 || | 99 if (video_frame->format() == media::VideoFrame::YV12 || |
99 video_frame->format() == media::VideoFrame::I420 || | 100 video_frame->format() == media::VideoFrame::I420) { |
100 video_frame->format() == media::VideoFrame::YV12A) { | |
101 yuv_type = media::YV12; | 101 yuv_type = media::YV12; |
102 y_shift = 1; | 102 y_shift = 1; |
103 } | 103 } |
104 | 104 |
105 if (video_frame->format() == media::VideoFrame::YV12J) { | 105 if (video_frame->format() == media::VideoFrame::YV12J) { |
106 yuv_type = media::YV12J; | 106 yuv_type = media::YV12J; |
107 y_shift = 1; | 107 y_shift = 1; |
108 } | 108 } |
109 | 109 |
110 // Transform the destination rectangle to local coordinates. | 110 // Transform the destination rectangle to local coordinates. |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
244 media::FILTER_BILINEAR); | 244 media::FILTER_BILINEAR); |
245 bitmap.unlockPixels(); | 245 bitmap.unlockPixels(); |
246 } | 246 } |
247 | 247 |
248 // Converts a VideoFrame containing YUV data to a SkBitmap containing RGB data. | 248 // Converts a VideoFrame containing YUV data to a SkBitmap containing RGB data. |
249 // | 249 // |
250 // |bitmap| will be (re)allocated to match the dimensions of |video_frame|. | 250 // |bitmap| will be (re)allocated to match the dimensions of |video_frame|. |
251 static void ConvertVideoFrameToBitmap( | 251 static void ConvertVideoFrameToBitmap( |
252 const scoped_refptr<media::VideoFrame>& video_frame, | 252 const scoped_refptr<media::VideoFrame>& video_frame, |
253 SkBitmap* bitmap) { | 253 SkBitmap* bitmap) { |
254 DCHECK(IsEitherYUVOrYUVAOrNative(video_frame->format())) | 254 DCHECK(IsYUVOrNative(video_frame->format())) |
255 << video_frame->format(); | 255 << video_frame->format(); |
256 if (IsEitherYUVOrYUVA(video_frame->format())) { | 256 if (IsYUV(video_frame->format())) { |
257 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane), | 257 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane), |
258 video_frame->stride(media::VideoFrame::kVPlane)); | 258 video_frame->stride(media::VideoFrame::kVPlane)); |
259 } | 259 } |
260 | 260 |
261 // Check if |bitmap| needs to be (re)allocated. | 261 // Check if |bitmap| needs to be (re)allocated. |
262 if (bitmap->isNull() || | 262 if (bitmap->isNull() || |
263 bitmap->width() != video_frame->visible_rect().width() || | 263 bitmap->width() != video_frame->visible_rect().width() || |
264 bitmap->height() != video_frame->visible_rect().height()) { | 264 bitmap->height() != video_frame->visible_rect().height()) { |
265 bitmap->setConfig(SkBitmap::kARGB_8888_Config, | 265 bitmap->setConfig(SkBitmap::kARGB_8888_Config, |
266 video_frame->visible_rect().width(), | 266 video_frame->visible_rect().width(), |
267 video_frame->visible_rect().height()); | 267 video_frame->visible_rect().height()); |
268 bitmap->allocPixels(); | 268 bitmap->allocPixels(); |
269 bitmap->setIsVolatile(true); | 269 bitmap->setIsVolatile(true); |
270 } | 270 } |
271 | 271 |
272 bitmap->lockPixels(); | 272 bitmap->lockPixels(); |
273 | 273 |
274 size_t y_offset = 0; | 274 size_t y_offset = 0; |
275 size_t uv_offset = 0; | 275 size_t uv_offset = 0; |
276 if (IsEitherYUVOrYUVA(video_frame->format())) { | 276 if (IsYUV(video_frame->format())) { |
277 int y_shift = (video_frame->format() == media::VideoFrame::YV16) ? 0 : 1; | 277 int y_shift = (video_frame->format() == media::VideoFrame::YV16) ? 0 : 1; |
278 // Use the "left" and "top" of the destination rect to locate the offset | 278 // Use the "left" and "top" of the destination rect to locate the offset |
279 // in Y, U and V planes. | 279 // in Y, U and V planes. |
280 y_offset = (video_frame->stride(media::VideoFrame::kYPlane) * | 280 y_offset = (video_frame->stride(media::VideoFrame::kYPlane) * |
281 video_frame->visible_rect().y()) + | 281 video_frame->visible_rect().y()) + |
282 video_frame->visible_rect().x(); | 282 video_frame->visible_rect().x(); |
283 // For format YV12, there is one U, V value per 2x2 block. | 283 // For format YV12, there is one U, V value per 2x2 block. |
284 // For format YV16, there is one U, V value per 2x1 block. | 284 // For format YV16, there is one U, V value per 2x1 block. |
285 uv_offset = (video_frame->stride(media::VideoFrame::kUPlane) * | 285 uv_offset = (video_frame->stride(media::VideoFrame::kUPlane) * |
286 (video_frame->visible_rect().y() >> y_shift)) + | 286 (video_frame->visible_rect().y() >> y_shift)) + |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 static_cast<uint8*>(bitmap->getPixels()), | 343 static_cast<uint8*>(bitmap->getPixels()), |
344 video_frame->visible_rect().width(), | 344 video_frame->visible_rect().width(), |
345 video_frame->visible_rect().height(), | 345 video_frame->visible_rect().height(), |
346 video_frame->stride(media::VideoFrame::kYPlane), | 346 video_frame->stride(media::VideoFrame::kYPlane), |
347 video_frame->stride(media::VideoFrame::kUPlane), | 347 video_frame->stride(media::VideoFrame::kUPlane), |
348 video_frame->stride(media::VideoFrame::kAPlane), | 348 video_frame->stride(media::VideoFrame::kAPlane), |
349 bitmap->rowBytes(), | 349 bitmap->rowBytes(), |
350 media::YV12); | 350 media::YV12); |
351 break; | 351 break; |
352 | 352 |
| 353 case media::VideoFrame::YV24: |
| 354 libyuv::I444ToARGB( |
| 355 video_frame->data(media::VideoFrame::kYPlane) + y_offset, |
| 356 video_frame->stride(media::VideoFrame::kYPlane), |
| 357 video_frame->data(media::VideoFrame::kUPlane) + uv_offset, |
| 358 video_frame->stride(media::VideoFrame::kUPlane), |
| 359 video_frame->data(media::VideoFrame::kVPlane) + uv_offset, |
| 360 video_frame->stride(media::VideoFrame::kVPlane), |
| 361 static_cast<uint8*>(bitmap->getPixels()), |
| 362 bitmap->rowBytes(), |
| 363 video_frame->visible_rect().width(), |
| 364 video_frame->visible_rect().height()); |
| 365 #if SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ |
| 366 SK_A32_SHIFT == 24 |
| 367 libyuv::ARGBToABGR( |
| 368 static_cast<uint8*>(bitmap->getPixels()), |
| 369 bitmap->rowBytes(), |
| 370 static_cast<uint8*>(bitmap->getPixels()), |
| 371 bitmap->rowBytes(), |
| 372 video_frame->visible_rect().width(), |
| 373 video_frame->visible_rect().height()); |
| 374 #endif |
| 375 break; |
| 376 |
353 case media::VideoFrame::NATIVE_TEXTURE: | 377 case media::VideoFrame::NATIVE_TEXTURE: |
354 DCHECK_EQ(video_frame->format(), media::VideoFrame::NATIVE_TEXTURE); | 378 DCHECK_EQ(video_frame->format(), media::VideoFrame::NATIVE_TEXTURE); |
355 video_frame->ReadPixelsFromNativeTexture(*bitmap); | 379 video_frame->ReadPixelsFromNativeTexture(*bitmap); |
356 break; | 380 break; |
357 | 381 |
358 default: | 382 default: |
359 NOTREACHED(); | 383 NOTREACHED(); |
360 break; | 384 break; |
361 } | 385 } |
362 bitmap->notifyPixelsChanged(); | 386 bitmap->notifyPixelsChanged(); |
(...skipping 15 matching lines...) Expand all Loading... |
378 } | 402 } |
379 | 403 |
380 SkRect dest; | 404 SkRect dest; |
381 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); | 405 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); |
382 | 406 |
383 SkPaint paint; | 407 SkPaint paint; |
384 paint.setAlpha(alpha); | 408 paint.setAlpha(alpha); |
385 | 409 |
386 // Paint black rectangle if there isn't a frame available or the | 410 // Paint black rectangle if there isn't a frame available or the |
387 // frame has an unexpected format. | 411 // frame has an unexpected format. |
388 if (!video_frame || !IsEitherYUVOrYUVAOrNative(video_frame->format())) { | 412 if (!video_frame || !IsYUVOrNative(video_frame->format())) { |
389 canvas->drawRect(dest, paint); | 413 canvas->drawRect(dest, paint); |
390 return; | 414 return; |
391 } | 415 } |
392 | 416 |
393 // Scale and convert to RGB in one step if we can. | 417 // Scale and convert to RGB in one step if we can. |
394 if (CanFastPaint(canvas, alpha, video_frame->format())) { | 418 if (CanFastPaint(canvas, alpha, video_frame->format())) { |
395 FastPaint(video_frame, canvas, dest); | 419 FastPaint(video_frame, canvas, dest); |
396 return; | 420 return; |
397 } | 421 } |
398 | 422 |
399 // Check if we should convert and update |last_frame_|. | 423 // Check if we should convert and update |last_frame_|. |
400 if (last_frame_.isNull() || | 424 if (last_frame_.isNull() || |
401 video_frame->timestamp() != last_frame_timestamp_) { | 425 video_frame->timestamp() != last_frame_timestamp_) { |
402 ConvertVideoFrameToBitmap(video_frame, &last_frame_); | 426 ConvertVideoFrameToBitmap(video_frame, &last_frame_); |
403 last_frame_timestamp_ = video_frame->timestamp(); | 427 last_frame_timestamp_ = video_frame->timestamp(); |
404 } | 428 } |
405 | 429 |
406 // Do a slower paint using |last_frame_|. | 430 // Paint using |last_frame_|. |
407 paint.setFilterLevel(SkPaint::kLow_FilterLevel); | 431 paint.setFilterLevel(SkPaint::kLow_FilterLevel); |
408 canvas->drawBitmapRect(last_frame_, NULL, dest, &paint); | 432 canvas->drawBitmapRect(last_frame_, NULL, dest, &paint); |
409 } | 433 } |
410 | 434 |
411 } // namespace media | 435 } // namespace media |
OLD | NEW |