Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(44)

Side by Side Diff: media/filters/skcanvas_video_renderer.cc

Issue 662033002: Potential bug on drawing video on SkCanvas (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address nits, and use timer Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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"
11 #include "third_party/skia/include/core/SkCanvas.h" 11 #include "third_party/skia/include/core/SkCanvas.h"
12 #include "third_party/skia/include/core/SkImageGenerator.h" 12 #include "third_party/skia/include/core/SkImageGenerator.h"
13 #include "third_party/skia/include/gpu/GrContext.h"
13 #include "ui/gfx/skbitmap_operations.h" 14 #include "ui/gfx/skbitmap_operations.h"
14 15
15 // Skia internal format depends on a platform. On Android it is ABGR, on others 16 // Skia internal format depends on a platform. On Android it is ABGR, on others
16 // it is ARGB. 17 // it is ARGB.
17 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ 18 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \
18 SK_A32_SHIFT == 24 19 SK_A32_SHIFT == 24
19 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB 20 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB
20 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB 21 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB
21 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ 22 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \
22 SK_A32_SHIFT == 24 23 SK_A32_SHIFT == 24
23 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR 24 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR
24 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR 25 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR
25 #else 26 #else
26 #error Unexpected Skia ARGB_8888 layout! 27 #error Unexpected Skia ARGB_8888 layout!
27 #endif 28 #endif
28 29
29 namespace media { 30 namespace media {
30 31
31 static bool IsYUV(media::VideoFrame::Format format) { 32 namespace {
33
34 // This class keeps two temporary resources; software bitmap, hardware bitmap.
35 // If both bitmap are created and then only software bitmap is updated every
36 // frame, hardware bitmap outlives until the media player dies. So we delete
37 // a temporary resource if it is not used for 3 sec.
38 const int kTemporaryResourceDeletionDelay = 3; // Seconds;
39
40 bool IsYUV(media::VideoFrame::Format format) {
32 switch (format) { 41 switch (format) {
33 case VideoFrame::YV12: 42 case VideoFrame::YV12:
34 case VideoFrame::YV16: 43 case VideoFrame::YV16:
35 case VideoFrame::I420: 44 case VideoFrame::I420:
36 case VideoFrame::YV12A: 45 case VideoFrame::YV12A:
37 case VideoFrame::YV12J: 46 case VideoFrame::YV12J:
38 case VideoFrame::YV24: 47 case VideoFrame::YV24:
39 case VideoFrame::NV12: 48 case VideoFrame::NV12:
40 return true; 49 return true;
41 case VideoFrame::UNKNOWN: 50 case VideoFrame::UNKNOWN:
42 case VideoFrame::NATIVE_TEXTURE: 51 case VideoFrame::NATIVE_TEXTURE:
43 #if defined(VIDEO_HOLE) 52 #if defined(VIDEO_HOLE)
44 case VideoFrame::HOLE: 53 case VideoFrame::HOLE:
45 #endif // defined(VIDEO_HOLE) 54 #endif // defined(VIDEO_HOLE)
46 return false; 55 return false;
47 } 56 }
48 NOTREACHED() << "Invalid videoframe format provided: " << format; 57 NOTREACHED() << "Invalid videoframe format provided: " << format;
49 return false; 58 return false;
50 } 59 }
51 60
52 static bool IsJPEGColorSpace(media::VideoFrame::Format format) { 61 bool IsJPEGColorSpace(media::VideoFrame::Format format) {
53 switch (format) { 62 switch (format) {
54 case VideoFrame::YV12J: 63 case VideoFrame::YV12J:
55 return true; 64 return true;
56 case VideoFrame::YV12: 65 case VideoFrame::YV12:
57 case VideoFrame::YV16: 66 case VideoFrame::YV16:
58 case VideoFrame::I420: 67 case VideoFrame::I420:
59 case VideoFrame::YV12A: 68 case VideoFrame::YV12A:
60 case VideoFrame::YV24: 69 case VideoFrame::YV24:
61 case VideoFrame::NV12: 70 case VideoFrame::NV12:
62 case VideoFrame::UNKNOWN: 71 case VideoFrame::UNKNOWN:
63 case VideoFrame::NATIVE_TEXTURE: 72 case VideoFrame::NATIVE_TEXTURE:
64 #if defined(VIDEO_HOLE) 73 #if defined(VIDEO_HOLE)
65 case VideoFrame::HOLE: 74 case VideoFrame::HOLE:
66 #endif // defined(VIDEO_HOLE) 75 #endif // defined(VIDEO_HOLE)
67 return false; 76 return false;
68 } 77 }
69 NOTREACHED() << "Invalid videoframe format provided: " << format; 78 NOTREACHED() << "Invalid videoframe format provided: " << format;
70 return false; 79 return false;
71 } 80 }
72 81
73 static bool IsYUVOrNative(media::VideoFrame::Format format) { 82 bool IsYUVOrNative(media::VideoFrame::Format format) {
74 return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE; 83 return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE;
75 } 84 }
76 85
77 // Converts a |video_frame| to raw |rgb_pixels|. 86 // Converts a |video_frame| to raw |rgb_pixels|.
78 static void ConvertVideoFrameToRGBPixels( 87 void ConvertVideoFrameToRGBPixels(
79 const scoped_refptr<media::VideoFrame>& video_frame, 88 const scoped_refptr<media::VideoFrame>& video_frame,
80 void* rgb_pixels, 89 void* rgb_pixels,
81 size_t row_bytes) { 90 size_t row_bytes) {
82 DCHECK(IsYUVOrNative(video_frame->format())) 91 DCHECK(IsYUVOrNative(video_frame->format()))
83 << video_frame->format(); 92 << video_frame->format();
84 if (IsYUV(video_frame->format())) { 93 if (IsYUV(video_frame->format())) {
85 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane), 94 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane),
86 video_frame->stride(media::VideoFrame::kVPlane)); 95 video_frame->stride(media::VideoFrame::kVPlane));
87 } 96 }
88 97
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
198 row_bytes); 207 row_bytes);
199 video_frame->ReadPixelsFromNativeTexture(tmp); 208 video_frame->ReadPixelsFromNativeTexture(tmp);
200 break; 209 break;
201 } 210 }
202 default: 211 default:
203 NOTREACHED(); 212 NOTREACHED();
204 break; 213 break;
205 } 214 }
206 } 215 }
207 216
208 // Generates an RGB image from a VideoFrame. 217 } // anonymous namespace
218
219 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU.
209 class VideoImageGenerator : public SkImageGenerator { 220 class VideoImageGenerator : public SkImageGenerator {
210 public: 221 public:
211 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { 222 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) {
212 DCHECK(frame_.get()); 223 DCHECK(frame_.get());
213 } 224 }
214 ~VideoImageGenerator() override {} 225 ~VideoImageGenerator() override {}
215 226
216 void set_frame(const scoped_refptr<VideoFrame>& frame) { frame_ = frame; } 227 void set_frame(const scoped_refptr<VideoFrame>& frame) { frame_ = frame; }
217 228
218 protected: 229 protected:
219 bool onGetInfo(SkImageInfo* info) override { 230 bool onGetInfo(SkImageInfo* info) override {
220 info->fWidth = frame_->visible_rect().width(); 231 info->fWidth = frame_->visible_rect().width();
221 info->fHeight = frame_->visible_rect().height(); 232 info->fHeight = frame_->visible_rect().height();
222 info->fColorType = kN32_SkColorType; 233 info->fColorType = kN32_SkColorType;
223 info->fAlphaType = kPremul_SkAlphaType; 234 info->fAlphaType = kPremul_SkAlphaType;
224 return true; 235 return true;
225 } 236 }
226 237
227 bool onGetPixels(const SkImageInfo& info, 238 bool onGetPixels(const SkImageInfo& info,
228 void* pixels, 239 void* pixels,
229 size_t row_bytes, 240 size_t row_bytes,
230 SkPMColor ctable[], 241 SkPMColor ctable[],
231 int* ctable_count) override { 242 int* ctable_count) override {
232 if (!frame_.get()) 243 if (!frame_.get())
233 return false; 244 return false;
234 if (!pixels) 245 if (!pixels)
235 return false; 246 return false;
236 // If skia couldn't do the YUV conversion, we will. 247 // If skia couldn't do the YUV conversion on GPU, we will on CPU.
237 ConvertVideoFrameToRGBPixels(frame_, pixels, row_bytes); 248 ConvertVideoFrameToRGBPixels(frame_, pixels, row_bytes);
238 return true; 249 return true;
239 } 250 }
240 251
241 bool onGetYUV8Planes(SkISize sizes[3], 252 bool onGetYUV8Planes(SkISize sizes[3],
242 void* planes[3], 253 void* planes[3],
243 size_t row_bytes[3], 254 size_t row_bytes[3],
244 SkYUVColorSpace* color_space) override { 255 SkYUVColorSpace* color_space) override {
245 if (!frame_.get() || !IsYUV(frame_->format())) 256 if (!frame_.get() || !IsYUV(frame_->format()))
246 return false; 257 return false;
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
282 return true; 293 return true;
283 } 294 }
284 295
285 private: 296 private:
286 scoped_refptr<VideoFrame> frame_; 297 scoped_refptr<VideoFrame> frame_;
287 298
288 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); 299 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator);
289 }; 300 };
290 301
291 SkCanvasVideoRenderer::SkCanvasVideoRenderer() 302 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
292 : generator_(NULL), last_frame_timestamp_(media::kNoTimestamp()) { 303 : last_frame_timestamp_(media::kNoTimestamp()),
304 accelerated_generator_(NULL),
305 accelerated_last_frame_timestamp_(media::kNoTimestamp()) {
293 last_frame_.setIsVolatile(true); 306 last_frame_.setIsVolatile(true);
294 } 307 }
295 308
296 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} 309 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {
310 if (frame_deleting_timer_.IsRunning())
scherkus (not reviewing) 2014/10/28 23:24:19 Timer's destructor will take care of stopping time
311 frame_deleting_timer_.Stop();
312 if (accelerated_frame_deleting_timer_.IsRunning())
313 accelerated_frame_deleting_timer_.Stop();
314 }
297 315
298 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, 316 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
299 SkCanvas* canvas, 317 SkCanvas* canvas,
300 const gfx::RectF& dest_rect, 318 const gfx::RectF& dest_rect,
301 uint8 alpha, 319 uint8 alpha,
302 SkXfermode::Mode mode, 320 SkXfermode::Mode mode,
303 VideoRotation video_rotation) { 321 VideoRotation video_rotation) {
304 if (alpha == 0) { 322 if (alpha == 0) {
305 return; 323 return;
306 } 324 }
307 325
308 SkRect dest; 326 SkRect dest;
309 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); 327 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom());
310 328
311 SkPaint paint; 329 SkPaint paint;
312 paint.setAlpha(alpha); 330 paint.setAlpha(alpha);
313 331
314 // Paint black rectangle if there isn't a frame available or the 332 // Paint black rectangle if there isn't a frame available or the
315 // frame has an unexpected format. 333 // frame has an unexpected format.
316 if (!video_frame.get() || !IsYUVOrNative(video_frame->format())) { 334 if (!video_frame.get() || video_frame->natural_size().IsEmpty() ||
335 !IsYUVOrNative(video_frame->format())) {
317 canvas->drawRect(dest, paint); 336 canvas->drawRect(dest, paint);
318 canvas->flush(); 337 canvas->flush();
319 return; 338 return;
320 } 339 }
321 340
322 // Check if we should convert and update |last_frame_|. 341 static const base::TimeDelta timeout =
323 if (last_frame_.isNull() || 342 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay);
324 video_frame->timestamp() != last_frame_timestamp_) { 343 SkBitmap* target_frame = NULL;
325 generator_ = new VideoImageGenerator(video_frame); 344 if (canvas->getGrContext()) {
345 if (accelerated_last_frame_.isNull() ||
346 video_frame->timestamp() != accelerated_last_frame_timestamp_) {
347 accelerated_generator_ = new VideoImageGenerator(video_frame);
326 348
327 // Note: This takes ownership of |generator_|. 349 // Note: This takes ownership of |accelerated_generator_|.
328 if (!SkInstallDiscardablePixelRef(generator_, &last_frame_)) { 350 if (!SkInstallDiscardablePixelRef(accelerated_generator_,
329 NOTREACHED(); 351 &accelerated_last_frame_)) {
352 NOTREACHED();
353 }
354 DCHECK(video_frame->visible_rect().width() ==
355 accelerated_last_frame_.width() &&
356 video_frame->visible_rect().height() ==
357 accelerated_last_frame_.height());
358
359 accelerated_last_frame_timestamp_ = video_frame->timestamp();
360 } else {
361 accelerated_generator_->set_frame(video_frame);
330 } 362 }
331 DCHECK(video_frame->visible_rect().width() == last_frame_.width() && 363 target_frame = &accelerated_last_frame_;
332 video_frame->visible_rect().height() == last_frame_.height()); 364 if (accelerated_frame_deleting_timer_.IsRunning()) {
333 365 accelerated_frame_deleting_timer_.Reset();
334 last_frame_timestamp_ = video_frame->timestamp(); 366 } else {
335 } else if (generator_) { 367 accelerated_frame_deleting_timer_.Start(
336 generator_->set_frame(video_frame); 368 FROM_HERE,
369 timeout,
370 base::Bind(&SkCanvasVideoRenderer::ResetAcceleratedLastFrame,
371 base::Unretained(this)));
372 }
373 } else {
374 // Check if we should convert and update |last_frame_|.
375 if (last_frame_.isNull() ||
376 video_frame->timestamp() != last_frame_timestamp_) {
377 // Check if |bitmap| needs to be (re)allocated.
378 if (last_frame_.isNull() ||
379 last_frame_.width() != video_frame->visible_rect().width() ||
380 last_frame_.height() != video_frame->visible_rect().height()) {
381 last_frame_.allocN32Pixels(video_frame->visible_rect().width(),
382 video_frame->visible_rect().height());
383 last_frame_.setIsVolatile(true);
384 }
385 last_frame_.lockPixels();
386 ConvertVideoFrameToRGBPixels(
387 video_frame, last_frame_.getPixels(), last_frame_.rowBytes());
388 last_frame_.notifyPixelsChanged();
389 last_frame_.unlockPixels();
390 last_frame_timestamp_ = video_frame->timestamp();
391 }
392 target_frame = &last_frame_;
393 if (frame_deleting_timer_.IsRunning()) {
394 frame_deleting_timer_.Reset();
395 } else {
396 frame_deleting_timer_.Start(
397 FROM_HERE,
398 timeout,
399 base::Bind(&SkBitmap::reset, base::Unretained(&last_frame_)));
400 }
337 } 401 }
338 402
339 paint.setXfermodeMode(mode); 403 paint.setXfermodeMode(mode);
340 paint.setFilterLevel(SkPaint::kLow_FilterLevel); 404 paint.setFilterLevel(SkPaint::kLow_FilterLevel);
341 405
342 bool need_transform = 406 bool need_transform =
343 video_rotation != VIDEO_ROTATION_0 || 407 video_rotation != VIDEO_ROTATION_0 ||
344 dest_rect.size() != video_frame->visible_rect().size() || 408 dest_rect.size() != video_frame->visible_rect().size() ||
345 !dest_rect.origin().IsOrigin(); 409 !dest_rect.origin().IsOrigin();
346 if (need_transform) { 410 if (need_transform) {
(...skipping 17 matching lines...) Expand all
364 } 428 }
365 canvas->rotate(angle); 429 canvas->rotate(angle);
366 430
367 gfx::SizeF rotated_dest_size = dest_rect.size(); 431 gfx::SizeF rotated_dest_size = dest_rect.size();
368 if (video_rotation == VIDEO_ROTATION_90 || 432 if (video_rotation == VIDEO_ROTATION_90 ||
369 video_rotation == VIDEO_ROTATION_270) { 433 video_rotation == VIDEO_ROTATION_270) {
370 rotated_dest_size = 434 rotated_dest_size =
371 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); 435 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width());
372 } 436 }
373 canvas->scale( 437 canvas->scale(
374 SkFloatToScalar(rotated_dest_size.width() / last_frame_.width()), 438 SkFloatToScalar(rotated_dest_size.width() / target_frame->width()),
375 SkFloatToScalar(rotated_dest_size.height() / last_frame_.height())); 439 SkFloatToScalar(rotated_dest_size.height() / target_frame->height()));
376 canvas->translate(-SkFloatToScalar(last_frame_.width() * 0.5f), 440 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f),
377 -SkFloatToScalar(last_frame_.height() * 0.5f)); 441 -SkFloatToScalar(target_frame->height() * 0.5f));
378 } 442 }
379 canvas->drawBitmap(last_frame_, 0, 0, &paint); 443 canvas->drawBitmap(*target_frame, 0, 0, &paint);
380 if (need_transform) 444 if (need_transform)
381 canvas->restore(); 445 canvas->restore();
382 canvas->flush(); 446 canvas->flush();
383 // SkCanvas::flush() causes the generator to generate SkImage, so delete 447 // SkCanvas::flush() causes the generator to generate SkImage, so delete
384 // |video_frame| not to be outlived. 448 // |video_frame| not to be outlived.
385 if (generator_) 449 if (canvas->getGrContext())
386 generator_->set_frame(NULL); 450 accelerated_generator_->set_frame(NULL);
387 } 451 }
388 452
389 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, 453 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame,
390 SkCanvas* canvas) { 454 SkCanvas* canvas) {
391 Paint(video_frame, 455 Paint(video_frame,
392 canvas, 456 canvas,
393 video_frame->visible_rect(), 457 video_frame->visible_rect(),
394 0xff, 458 0xff,
395 SkXfermode::kSrc_Mode, 459 SkXfermode::kSrc_Mode,
396 media::VIDEO_ROTATION_0); 460 media::VIDEO_ROTATION_0);
397 } 461 }
398 462
463 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() {
464 accelerated_last_frame_.reset();
465 accelerated_generator_ = nullptr;
466 }
467
399 } // namespace media 468 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698