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

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: 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;
dshwang 2014/10/17 15:23:50 In crrev.com/445013002
scherkus (not reviewing) 2014/10/22 23:17:36 Is this actually a problem in practice? I'm wary o
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.
scherkus (not reviewing) 2014/10/22 23:17:36 what's this new comment referring to? it makes it
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 virtual ~VideoImageGenerator() {} 225 virtual ~VideoImageGenerator() {}
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 virtual bool onGetInfo(SkImageInfo* info) override { 230 virtual 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 virtual bool onGetPixels(const SkImageInfo& info, 238 virtual 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 virtual bool onGetYUV8Planes(SkISize sizes[3], 252 virtual 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() {}
297 310
298 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, 311 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
299 SkCanvas* canvas, 312 SkCanvas* canvas,
300 const gfx::RectF& dest_rect, 313 const gfx::RectF& dest_rect,
301 uint8 alpha, 314 uint8 alpha,
302 SkXfermode::Mode mode, 315 SkXfermode::Mode mode,
303 VideoRotation video_rotation) { 316 VideoRotation video_rotation) {
304 if (alpha == 0) { 317 if (alpha == 0) {
305 return; 318 return;
306 } 319 }
307 320
308 SkRect dest; 321 SkRect dest;
309 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); 322 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom());
310 323
311 SkPaint paint; 324 SkPaint paint;
312 paint.setAlpha(alpha); 325 paint.setAlpha(alpha);
313 326
314 // Paint black rectangle if there isn't a frame available or the 327 // Paint black rectangle if there isn't a frame available or the
315 // frame has an unexpected format. 328 // frame has an unexpected format.
316 if (!video_frame.get() || !IsYUVOrNative(video_frame->format())) { 329 if (!video_frame.get() || video_frame->natural_size().IsEmpty() ||
scherkus (not reviewing) 2014/10/22 23:17:36 out of curiosity, is this change necessary?
330 !IsYUVOrNative(video_frame->format())) {
317 canvas->drawRect(dest, paint); 331 canvas->drawRect(dest, paint);
318 canvas->flush(); 332 canvas->flush();
319 return; 333 return;
320 } 334 }
321 335
336 bool accelerated = false;
scherkus (not reviewing) 2014/10/22 23:17:36 I would replace this with a SkBitmap* target_frame
337 if (canvas->getGrContext()) {
338 if (accelerated_last_frame_.isNull() ||
339 video_frame->timestamp() != accelerated_last_frame_timestamp_) {
340 accelerated_generator_ = new VideoImageGenerator(video_frame);
341
342 // Note: This takes ownership of |accelerated_generator_|.
343 if (!SkInstallDiscardablePixelRef(accelerated_generator_,
344 &accelerated_last_frame_)) {
345 NOTREACHED();
346 }
347 DCHECK(video_frame->visible_rect().width() ==
348 accelerated_last_frame_.width() &&
349 video_frame->visible_rect().height() ==
350 accelerated_last_frame_.height());
351
352 accelerated_last_frame_timestamp_ = video_frame->timestamp();
353 } else {
354 accelerated_generator_->set_frame(video_frame);
355 }
356 accelerated = true;
357 }
358
322 // Check if we should convert and update |last_frame_|. 359 // Check if we should convert and update |last_frame_|.
323 if (last_frame_.isNull() || 360 if (!accelerated && (last_frame_.isNull() ||
324 video_frame->timestamp() != last_frame_timestamp_) { 361 video_frame->timestamp() != last_frame_timestamp_)) {
325 generator_ = new VideoImageGenerator(video_frame); 362 // Check if |bitmap| needs to be (re)allocated.
326 363 if (last_frame_.isNull() ||
327 // Note: This takes ownership of |generator_|. 364 last_frame_.width() != video_frame->visible_rect().width() ||
328 if (!SkInstallDiscardablePixelRef(generator_, &last_frame_)) { 365 last_frame_.height() != video_frame->visible_rect().height()) {
329 NOTREACHED(); 366 last_frame_.allocN32Pixels(video_frame->visible_rect().width(),
367 video_frame->visible_rect().height());
368 last_frame_.setIsVolatile(true);
330 } 369 }
331 DCHECK(video_frame->visible_rect().width() == last_frame_.width() && 370 last_frame_.lockPixels();
332 video_frame->visible_rect().height() == last_frame_.height()); 371 ConvertVideoFrameToRGBPixels(
333 372 video_frame, last_frame_.getPixels(), last_frame_.rowBytes());
373 last_frame_.notifyPixelsChanged();
374 last_frame_.unlockPixels();
334 last_frame_timestamp_ = video_frame->timestamp(); 375 last_frame_timestamp_ = video_frame->timestamp();
335 } else if (generator_) {
336 generator_->set_frame(video_frame);
337 } 376 }
338 377
339 paint.setXfermodeMode(mode); 378 paint.setXfermodeMode(mode);
340 paint.setFilterLevel(SkPaint::kLow_FilterLevel); 379 paint.setFilterLevel(SkPaint::kLow_FilterLevel);
341 380
381 SkBitmap& target_frame = accelerated ? accelerated_last_frame_ : last_frame_;
342 bool need_transform = 382 bool need_transform =
343 video_rotation != VIDEO_ROTATION_0 || 383 video_rotation != VIDEO_ROTATION_0 ||
344 dest_rect.size() != video_frame->visible_rect().size() || 384 dest_rect.size() != video_frame->visible_rect().size() ||
345 !dest_rect.origin().IsOrigin(); 385 !dest_rect.origin().IsOrigin();
346 if (need_transform) { 386 if (need_transform) {
347 canvas->save(); 387 canvas->save();
348 canvas->translate( 388 canvas->translate(
349 SkFloatToScalar(dest_rect.x() + (dest_rect.width() * 0.5f)), 389 SkFloatToScalar(dest_rect.x() + (dest_rect.width() * 0.5f)),
350 SkFloatToScalar(dest_rect.y() + (dest_rect.height() * 0.5f))); 390 SkFloatToScalar(dest_rect.y() + (dest_rect.height() * 0.5f)));
351 SkScalar angle = SkFloatToScalar(0.0f); 391 SkScalar angle = SkFloatToScalar(0.0f);
(...skipping 12 matching lines...) Expand all
364 } 404 }
365 canvas->rotate(angle); 405 canvas->rotate(angle);
366 406
367 gfx::SizeF rotated_dest_size = dest_rect.size(); 407 gfx::SizeF rotated_dest_size = dest_rect.size();
368 if (video_rotation == VIDEO_ROTATION_90 || 408 if (video_rotation == VIDEO_ROTATION_90 ||
369 video_rotation == VIDEO_ROTATION_270) { 409 video_rotation == VIDEO_ROTATION_270) {
370 rotated_dest_size = 410 rotated_dest_size =
371 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); 411 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width());
372 } 412 }
373 canvas->scale( 413 canvas->scale(
374 SkFloatToScalar(rotated_dest_size.width() / last_frame_.width()), 414 SkFloatToScalar(rotated_dest_size.width() / target_frame.width()),
375 SkFloatToScalar(rotated_dest_size.height() / last_frame_.height())); 415 SkFloatToScalar(rotated_dest_size.height() / target_frame.height()));
376 canvas->translate(-SkFloatToScalar(last_frame_.width() * 0.5f), 416 canvas->translate(-SkFloatToScalar(target_frame.width() * 0.5f),
377 -SkFloatToScalar(last_frame_.height() * 0.5f)); 417 -SkFloatToScalar(target_frame.height() * 0.5f));
378 } 418 }
379 canvas->drawBitmap(last_frame_, 0, 0, &paint); 419 canvas->drawBitmap(target_frame, 0, 0, &paint);
380 if (need_transform) 420 if (need_transform)
381 canvas->restore(); 421 canvas->restore();
382 canvas->flush(); 422 canvas->flush();
383 // SkCanvas::flush() causes the generator to generate SkImage, so delete 423 // SkCanvas::flush() causes the generator to generate SkImage, so delete
384 // |video_frame| not to be outlived. 424 // |video_frame| not to be outlived.
385 if (generator_) 425 if (accelerated_generator_)
386 generator_->set_frame(NULL); 426 accelerated_generator_->set_frame(NULL);
427 CleanUpTemporaryBuffers();
387 } 428 }
388 429
389 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, 430 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame,
390 SkCanvas* canvas) { 431 SkCanvas* canvas) {
391 Paint(video_frame, 432 Paint(video_frame,
392 canvas, 433 canvas,
393 video_frame->visible_rect(), 434 video_frame->visible_rect(),
394 0xff, 435 0xff,
395 SkXfermode::kSrc_Mode, 436 SkXfermode::kSrc_Mode,
396 media::VIDEO_ROTATION_0); 437 media::VIDEO_ROTATION_0);
397 } 438 }
398 439
440 void SkCanvasVideoRenderer::CleanUpTemporaryBuffers() {
441 static const base::TimeDelta buffer_time =
442 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay);
443 // Find the latest time stamp.
444 base::TimeDelta last_timestamp =
445 accelerated_last_frame_timestamp_ > last_frame_timestamp_
446 ? accelerated_last_frame_timestamp_
447 : last_frame_timestamp_;
448 // Delete a too old resource.
449 if (last_timestamp > last_frame_timestamp_ + buffer_time &&
450 !last_frame_.isNull()) {
451 last_frame_.reset();
452 }
453 if (last_timestamp > accelerated_last_frame_timestamp_ + buffer_time &&
454 !accelerated_last_frame_.isNull()) {
455 accelerated_last_frame_.reset();
456 }
457 }
458
399 } // namespace media 459 } // namespace media
OLDNEW
« media/filters/skcanvas_video_renderer.h ('K') | « media/filters/skcanvas_video_renderer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698