Chromium Code Reviews| 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" |
| 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 Loading... | |
| 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 Loading... | |
| 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 frame_deleting_timer_( | |
| 305 FROM_HERE, | |
| 306 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), | |
| 307 this, | |
| 308 &SkCanvasVideoRenderer::ResetLastFrame), | |
| 309 accelerated_generator_(NULL), | |
| 310 accelerated_last_frame_timestamp_(media::kNoTimestamp()), | |
| 311 accelerated_frame_deleting_timer_( | |
| 312 FROM_HERE, | |
| 313 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), | |
| 314 this, | |
| 315 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) { | |
| 293 last_frame_.setIsVolatile(true); | 316 last_frame_.setIsVolatile(true); |
| 294 } | 317 } |
| 295 | 318 |
| 296 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} | 319 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} |
| 297 | 320 |
| 298 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, | 321 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, |
| 299 SkCanvas* canvas, | 322 SkCanvas* canvas, |
| 300 const gfx::RectF& dest_rect, | 323 const gfx::RectF& dest_rect, |
| 301 uint8 alpha, | 324 uint8 alpha, |
| 302 SkXfermode::Mode mode, | 325 SkXfermode::Mode mode, |
| 303 VideoRotation video_rotation) { | 326 VideoRotation video_rotation) { |
| 304 if (alpha == 0) { | 327 if (alpha == 0) { |
| 305 return; | 328 return; |
| 306 } | 329 } |
| 307 | 330 |
| 308 SkRect dest; | 331 SkRect dest; |
| 309 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); | 332 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); |
| 310 | 333 |
| 311 SkPaint paint; | 334 SkPaint paint; |
| 312 paint.setAlpha(alpha); | 335 paint.setAlpha(alpha); |
| 313 | 336 |
| 314 // Paint black rectangle if there isn't a frame available or the | 337 // Paint black rectangle if there isn't a frame available or the |
| 315 // frame has an unexpected format. | 338 // frame has an unexpected format. |
| 316 if (!video_frame.get() || !IsYUVOrNative(video_frame->format())) { | 339 if (!video_frame.get() || video_frame->natural_size().IsEmpty() || |
| 340 !IsYUVOrNative(video_frame->format())) { | |
| 317 canvas->drawRect(dest, paint); | 341 canvas->drawRect(dest, paint); |
| 318 canvas->flush(); | 342 canvas->flush(); |
| 319 return; | 343 return; |
| 320 } | 344 } |
| 321 | 345 |
| 322 // Check if we should convert and update |last_frame_|. | 346 SkBitmap* target_frame = NULL; |
| 323 if (last_frame_.isNull() || | 347 if (canvas->getGrContext()) { |
| 324 video_frame->timestamp() != last_frame_timestamp_) { | 348 if (accelerated_last_frame_.isNull() || |
| 325 generator_ = new VideoImageGenerator(video_frame); | 349 video_frame->timestamp() != accelerated_last_frame_timestamp_) { |
| 350 accelerated_generator_ = new VideoImageGenerator(video_frame); | |
| 326 | 351 |
| 327 // Note: This takes ownership of |generator_|. | 352 // Note: This takes ownership of |accelerated_generator_|. |
| 328 if (!SkInstallDiscardablePixelRef(generator_, &last_frame_)) { | 353 if (!SkInstallDiscardablePixelRef(accelerated_generator_, |
| 329 NOTREACHED(); | 354 &accelerated_last_frame_)) { |
| 355 NOTREACHED(); | |
| 356 } | |
| 357 DCHECK(video_frame->visible_rect().width() == | |
| 358 accelerated_last_frame_.width() && | |
| 359 video_frame->visible_rect().height() == | |
| 360 accelerated_last_frame_.height()); | |
| 361 | |
| 362 accelerated_last_frame_timestamp_ = video_frame->timestamp(); | |
| 363 } else { | |
| 364 accelerated_generator_->set_frame(video_frame); | |
| 330 } | 365 } |
| 331 DCHECK(video_frame->visible_rect().width() == last_frame_.width() && | 366 target_frame = &accelerated_last_frame_; |
| 332 video_frame->visible_rect().height() == last_frame_.height()); | 367 accelerated_frame_deleting_timer_.Reset(); |
| 333 | 368 } else { |
| 334 last_frame_timestamp_ = video_frame->timestamp(); | 369 // Check if we should convert and update |last_frame_|. |
| 335 } else if (generator_) { | 370 if (last_frame_.isNull() || |
| 336 generator_->set_frame(video_frame); | 371 video_frame->timestamp() != last_frame_timestamp_) { |
| 372 // Check if |bitmap| needs to be (re)allocated. | |
| 373 if (last_frame_.isNull() || | |
| 374 last_frame_.width() != video_frame->visible_rect().width() || | |
| 375 last_frame_.height() != video_frame->visible_rect().height()) { | |
| 376 last_frame_.allocN32Pixels(video_frame->visible_rect().width(), | |
| 377 video_frame->visible_rect().height()); | |
| 378 last_frame_.setIsVolatile(true); | |
| 379 } | |
| 380 last_frame_.lockPixels(); | |
| 381 ConvertVideoFrameToRGBPixels( | |
| 382 video_frame, last_frame_.getPixels(), last_frame_.rowBytes()); | |
| 383 last_frame_.notifyPixelsChanged(); | |
| 384 last_frame_.unlockPixels(); | |
| 385 last_frame_timestamp_ = video_frame->timestamp(); | |
| 386 } | |
| 387 target_frame = &last_frame_; | |
| 388 frame_deleting_timer_.Reset(); | |
| 337 } | 389 } |
| 338 | 390 |
| 339 paint.setXfermodeMode(mode); | 391 paint.setXfermodeMode(mode); |
| 340 paint.setFilterLevel(SkPaint::kLow_FilterLevel); | 392 paint.setFilterLevel(SkPaint::kLow_FilterLevel); |
| 341 | 393 |
| 342 bool need_transform = | 394 bool need_transform = |
| 343 video_rotation != VIDEO_ROTATION_0 || | 395 video_rotation != VIDEO_ROTATION_0 || |
| 344 dest_rect.size() != video_frame->visible_rect().size() || | 396 dest_rect.size() != video_frame->visible_rect().size() || |
| 345 !dest_rect.origin().IsOrigin(); | 397 !dest_rect.origin().IsOrigin(); |
| 346 if (need_transform) { | 398 if (need_transform) { |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 364 } | 416 } |
| 365 canvas->rotate(angle); | 417 canvas->rotate(angle); |
| 366 | 418 |
| 367 gfx::SizeF rotated_dest_size = dest_rect.size(); | 419 gfx::SizeF rotated_dest_size = dest_rect.size(); |
| 368 if (video_rotation == VIDEO_ROTATION_90 || | 420 if (video_rotation == VIDEO_ROTATION_90 || |
| 369 video_rotation == VIDEO_ROTATION_270) { | 421 video_rotation == VIDEO_ROTATION_270) { |
| 370 rotated_dest_size = | 422 rotated_dest_size = |
| 371 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); | 423 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); |
| 372 } | 424 } |
| 373 canvas->scale( | 425 canvas->scale( |
| 374 SkFloatToScalar(rotated_dest_size.width() / last_frame_.width()), | 426 SkFloatToScalar(rotated_dest_size.width() / target_frame->width()), |
| 375 SkFloatToScalar(rotated_dest_size.height() / last_frame_.height())); | 427 SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); |
| 376 canvas->translate(-SkFloatToScalar(last_frame_.width() * 0.5f), | 428 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), |
| 377 -SkFloatToScalar(last_frame_.height() * 0.5f)); | 429 -SkFloatToScalar(target_frame->height() * 0.5f)); |
| 378 } | 430 } |
| 379 canvas->drawBitmap(last_frame_, 0, 0, &paint); | 431 canvas->drawBitmap(*target_frame, 0, 0, &paint); |
| 380 if (need_transform) | 432 if (need_transform) |
| 381 canvas->restore(); | 433 canvas->restore(); |
| 382 canvas->flush(); | 434 canvas->flush(); |
| 383 // SkCanvas::flush() causes the generator to generate SkImage, so delete | 435 // SkCanvas::flush() causes the generator to generate SkImage, so delete |
| 384 // |video_frame| not to be outlived. | 436 // |video_frame| not to be outlived. |
| 385 if (generator_) | 437 if (canvas->getGrContext()) |
| 386 generator_->set_frame(NULL); | 438 accelerated_generator_->set_frame(NULL); |
| 387 } | 439 } |
| 388 | 440 |
| 389 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, | 441 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, |
| 390 SkCanvas* canvas) { | 442 SkCanvas* canvas) { |
| 391 Paint(video_frame, | 443 Paint(video_frame, |
| 392 canvas, | 444 canvas, |
| 393 video_frame->visible_rect(), | 445 video_frame->visible_rect(), |
| 394 0xff, | 446 0xff, |
| 395 SkXfermode::kSrc_Mode, | 447 SkXfermode::kSrc_Mode, |
| 396 media::VIDEO_ROTATION_0); | 448 media::VIDEO_ROTATION_0); |
| 397 } | 449 } |
| 398 | 450 |
| 451 void SkCanvasVideoRenderer::ResetLastFrame() { | |
| 452 last_frame_.reset(); | |
| 453 last_frame_timestamp_ = base::TimeDelta(); | |
|
scherkus (not reviewing)
2014/10/29 17:24:09
use media::kNoTimestamp()
| |
| 454 } | |
| 455 | |
| 456 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { | |
| 457 accelerated_last_frame_.reset(); | |
| 458 accelerated_generator_ = nullptr; | |
| 459 accelerated_last_frame_timestamp_ = base::TimeDelta(); | |
|
scherkus (not reviewing)
2014/10/29 17:24:09
use media::kNoTimestamp()
| |
| 460 } | |
| 461 | |
| 399 } // namespace media | 462 } // namespace media |
| OLD | NEW |