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 "gpu/GLES2/gl2extchromium.h" | |
| 9 #include "gpu/command_buffer/client/gles2_interface.h" | |
| 10 #include "gpu/command_buffer/common/mailbox_holder.h" | |
| 8 #include "media/base/video_frame.h" | 11 #include "media/base/video_frame.h" |
| 9 #include "media/base/yuv_convert.h" | 12 #include "media/base/yuv_convert.h" |
| 13 #include "skia/ext/refptr.h" | |
| 10 #include "third_party/libyuv/include/libyuv.h" | 14 #include "third_party/libyuv/include/libyuv.h" |
| 11 #include "third_party/skia/include/core/SkCanvas.h" | |
| 12 #include "third_party/skia/include/core/SkImageGenerator.h" | 15 #include "third_party/skia/include/core/SkImageGenerator.h" |
| 13 #include "third_party/skia/include/gpu/GrContext.h" | 16 #include "third_party/skia/include/gpu/GrContext.h" |
| 17 #include "third_party/skia/include/gpu/SkGrPixelRef.h" | |
| 14 #include "ui/gfx/skbitmap_operations.h" | 18 #include "ui/gfx/skbitmap_operations.h" |
| 15 | 19 |
| 16 // Skia internal format depends on a platform. On Android it is ABGR, on others | 20 // Skia internal format depends on a platform. On Android it is ABGR, on others |
| 17 // it is ARGB. | 21 // it is ARGB. |
| 18 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ | 22 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ |
| 19 SK_A32_SHIFT == 24 | 23 SK_A32_SHIFT == 24 |
| 20 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB | 24 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB |
| 21 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB | 25 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB |
| 22 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ | 26 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ |
| 23 SK_A32_SHIFT == 24 | 27 SK_A32_SHIFT == 24 |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 207 row_bytes); | 211 row_bytes); |
| 208 video_frame->ReadPixelsFromNativeTexture(tmp); | 212 video_frame->ReadPixelsFromNativeTexture(tmp); |
| 209 break; | 213 break; |
| 210 } | 214 } |
| 211 default: | 215 default: |
| 212 NOTREACHED(); | 216 NOTREACHED(); |
| 213 break; | 217 break; |
| 214 } | 218 } |
| 215 } | 219 } |
| 216 | 220 |
| 221 bool IsSkBitmapProperlySizedTexture(const SkBitmap* bitmap, | |
| 222 const gfx::Size& size) { | |
| 223 return bitmap->getTexture() && bitmap->width() == size.width() && | |
| 224 bitmap->height() == size.height(); | |
| 225 } | |
| 226 | |
| 227 bool AllocateSkBitmapTexture(GrContext* gr, | |
| 228 SkBitmap* bitmap, | |
| 229 const gfx::Size& size) { | |
| 230 DCHECK(gr); | |
| 231 GrTextureDesc desc; | |
| 232 // Use kRGBA_8888_GrPixelConfig, not kSkia8888_GrPixelConfig, because | |
| 233 // glCopyTextureChromium doesn't support GL_BGRA_EXT as internal format. | |
| 234 desc.fConfig = kRGBA_8888_GrPixelConfig; | |
| 235 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; | |
| 236 desc.fSampleCnt = 0; | |
| 237 desc.fOrigin = kTopLeft_GrSurfaceOrigin; | |
| 238 desc.fWidth = size.width(); | |
| 239 desc.fHeight = size.height(); | |
| 240 skia::RefPtr<GrTexture> texture = skia::AdoptRef( | |
| 241 gr->refScratchTexture(desc, GrContext::kExact_ScratchTexMatch)); | |
| 242 if (!texture.get()) | |
| 243 return false; | |
| 244 | |
| 245 SkImageInfo info = SkImageInfo::MakeN32Premul(desc.fWidth, desc.fHeight); | |
| 246 SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (info, texture.get())); | |
|
scherkus (not reviewing)
2014/10/30 20:40:12
pixelRef -> pixel_ref
dshwang
2014/10/31 09:29:17
oops, Thank you.
| |
| 247 if (!pixelRef) | |
| 248 return false; | |
| 249 bitmap->setInfo(info); | |
| 250 bitmap->setPixelRef(pixelRef)->unref(); | |
| 251 return true; | |
| 252 } | |
| 253 | |
| 254 bool CopyVideoFrameTextureToSkBitmapTexture( | |
| 255 VideoFrame* video_frame, | |
|
scherkus (not reviewing)
2014/10/30 20:40:13
const scoped_refptr<VideoFrame>&
dshwang
2014/10/31 09:29:17
not needed because it doesn't take a ref
| |
| 256 SkBitmap* bitmap, | |
| 257 const Context3DProvider& context_provider) { | |
| 258 // Check if we could reuse existing texture based bitmap. | |
| 259 // Otherwise, release existing texture based bitmap and allocate | |
| 260 // a new one based on video size. | |
| 261 if (!IsSkBitmapProperlySizedTexture(bitmap, | |
| 262 video_frame->visible_rect().size())) { | |
| 263 if (!AllocateSkBitmapTexture(context_provider.gr_context, | |
| 264 bitmap, | |
| 265 video_frame->visible_rect().size())) { | |
| 266 return false; | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 unsigned texture_id = | |
| 271 static_cast<unsigned>((bitmap->getTexture())->getTextureHandle()); | |
| 272 // If CopyVideoFrameToTexture() changes the state of the |texture_id|, it's | |
|
scherkus (not reviewing)
2014/10/30 20:40:12
"CopyVideoFrameToTexture" doesn't refer to a funct
dshwang
2014/10/31 09:29:17
Thank you!
| |
| 273 // needed to invalidate the state cached in skia, but currently the state | |
| 274 // isn't changed. | |
| 275 SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture(context_provider.gl, | |
| 276 video_frame, | |
| 277 texture_id, | |
| 278 0, | |
| 279 GL_RGBA, | |
| 280 GL_UNSIGNED_BYTE, | |
| 281 true, | |
| 282 false); | |
| 283 return true; | |
| 284 } | |
| 285 | |
| 286 class SyncPointClientImpl : public VideoFrame::SyncPointClient { | |
| 287 public: | |
| 288 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} | |
| 289 virtual ~SyncPointClientImpl() {} | |
|
scherkus (not reviewing)
2014/10/30 20:40:12
s/virtual/override/ everywhere
dshwang
2014/10/31 09:29:17
interesting. Done.
| |
| 290 virtual uint32 InsertSyncPoint() override { | |
| 291 return gl_->InsertSyncPointCHROMIUM(); | |
| 292 } | |
| 293 virtual void WaitSyncPoint(uint32 sync_point) override { | |
| 294 gl_->WaitSyncPointCHROMIUM(sync_point); | |
| 295 } | |
| 296 | |
| 297 private: | |
| 298 gpu::gles2::GLES2Interface* gl_; | |
| 299 }; | |
|
scherkus (not reviewing)
2014/10/30 20:40:12
DISALLOW_COPY_AND_ASSIGN()
dshwang
2014/10/31 09:29:17
Done.
| |
| 300 | |
| 217 } // anonymous namespace | 301 } // anonymous namespace |
| 218 | 302 |
| 219 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. | 303 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. |
| 220 class VideoImageGenerator : public SkImageGenerator { | 304 class VideoImageGenerator : public SkImageGenerator { |
| 221 public: | 305 public: |
| 222 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { | 306 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { |
| 223 DCHECK(frame_.get()); | 307 DCHECK(frame_.get()); |
| 224 } | 308 } |
| 225 ~VideoImageGenerator() override {} | 309 ~VideoImageGenerator() override {} |
| 226 | 310 |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 299 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); | 383 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); |
| 300 }; | 384 }; |
| 301 | 385 |
| 302 SkCanvasVideoRenderer::SkCanvasVideoRenderer() | 386 SkCanvasVideoRenderer::SkCanvasVideoRenderer() |
| 303 : last_frame_timestamp_(media::kNoTimestamp()), | 387 : last_frame_timestamp_(media::kNoTimestamp()), |
| 304 frame_deleting_timer_( | 388 frame_deleting_timer_( |
| 305 FROM_HERE, | 389 FROM_HERE, |
| 306 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), | 390 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), |
| 307 this, | 391 this, |
| 308 &SkCanvasVideoRenderer::ResetLastFrame), | 392 &SkCanvasVideoRenderer::ResetLastFrame), |
| 309 accelerated_generator_(NULL), | 393 accelerated_generator_(nullptr), |
| 310 accelerated_last_frame_timestamp_(media::kNoTimestamp()), | 394 accelerated_last_frame_timestamp_(media::kNoTimestamp()), |
| 311 accelerated_frame_deleting_timer_( | 395 accelerated_frame_deleting_timer_( |
| 312 FROM_HERE, | 396 FROM_HERE, |
| 313 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), | 397 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), |
| 314 this, | 398 this, |
| 315 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) { | 399 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) { |
| 316 last_frame_.setIsVolatile(true); | 400 last_frame_.setIsVolatile(true); |
| 317 } | 401 } |
| 318 | 402 |
| 319 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} | 403 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} |
| 320 | 404 |
| 321 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, | 405 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, |
| 322 SkCanvas* canvas, | 406 SkCanvas* canvas, |
| 323 const gfx::RectF& dest_rect, | 407 const gfx::RectF& dest_rect, |
| 324 uint8 alpha, | 408 uint8 alpha, |
| 325 SkXfermode::Mode mode, | 409 SkXfermode::Mode mode, |
| 326 VideoRotation video_rotation) { | 410 VideoRotation video_rotation, |
| 411 const Context3DProvider& context_provider) { | |
| 327 if (alpha == 0) { | 412 if (alpha == 0) { |
| 328 return; | 413 return; |
| 329 } | 414 } |
| 330 | 415 |
| 331 SkRect dest; | 416 SkRect dest; |
| 332 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); | 417 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); |
| 333 | 418 |
| 334 SkPaint paint; | 419 SkPaint paint; |
| 335 paint.setAlpha(alpha); | 420 paint.setAlpha(alpha); |
| 336 | 421 |
| 337 // Paint black rectangle if there isn't a frame available or the | 422 // Paint black rectangle if there isn't a frame available or the |
| 338 // frame has an unexpected format. | 423 // frame has an unexpected format. |
| 339 if (!video_frame.get() || video_frame->natural_size().IsEmpty() || | 424 if (!video_frame.get() || video_frame->natural_size().IsEmpty() || |
| 340 !IsYUVOrNative(video_frame->format())) { | 425 !IsYUVOrNative(video_frame->format())) { |
| 341 canvas->drawRect(dest, paint); | 426 canvas->drawRect(dest, paint); |
| 342 canvas->flush(); | 427 canvas->flush(); |
| 343 return; | 428 return; |
| 344 } | 429 } |
| 345 | 430 |
| 346 SkBitmap* target_frame = NULL; | 431 SkBitmap* target_frame = nullptr; |
| 347 if (canvas->getGrContext()) { | 432 if (canvas->getGrContext()) { |
| 433 // TODO(dshwang): Android video decoder doesn't update the timestamp on a | |
| 434 // VideoFrame. To reduce redundant copy, Android should update the | |
| 435 // timestamp. | |
| 348 if (accelerated_last_frame_.isNull() || | 436 if (accelerated_last_frame_.isNull() || |
| 437 #if defined(OS_ANDROID) | |
| 438 video_frame->timestamp() == base::TimeDelta() || | |
| 439 #endif | |
| 349 video_frame->timestamp() != accelerated_last_frame_timestamp_) { | 440 video_frame->timestamp() != accelerated_last_frame_timestamp_) { |
| 350 accelerated_generator_ = new VideoImageGenerator(video_frame); | 441 if (video_frame->format() == media::VideoFrame::NATIVE_TEXTURE) { |
| 442 // Draw HW Video on HW Canvas. | |
| 443 DCHECK(context_provider.gl && context_provider.gr_context); | |
|
scherkus (not reviewing)
2014/10/30 20:40:13
nit: break out into two DCHECKs so we know which o
dshwang
2014/10/31 09:29:17
Done.
| |
| 444 DCHECK(!accelerated_generator_); | |
| 445 if (!CopyVideoFrameTextureToSkBitmapTexture(video_frame.get(), | |
|
scherkus (not reviewing)
2014/10/30 20:40:13
you can drop the .get() if you switch to using con
dshwang
2014/10/31 09:29:17
CopyVideoFrameTextureToSkBitmapTexture doesn't tak
| |
| 446 &accelerated_last_frame_, | |
| 447 context_provider)) { | |
| 448 NOTREACHED(); | |
| 449 return; | |
| 450 } | |
| 451 } else { | |
| 452 // Draw SW Video on HW Canvas. | |
| 453 accelerated_generator_ = new VideoImageGenerator(video_frame); | |
| 351 | 454 |
| 352 // Note: This takes ownership of |accelerated_generator_|. | 455 // Note: This takes ownership of |accelerated_generator_|. |
| 353 if (!SkInstallDiscardablePixelRef(accelerated_generator_, | 456 if (!SkInstallDiscardablePixelRef(accelerated_generator_, |
| 354 &accelerated_last_frame_)) { | 457 &accelerated_last_frame_)) { |
| 355 NOTREACHED(); | 458 NOTREACHED(); |
| 459 return; | |
| 460 } | |
| 356 } | 461 } |
| 357 DCHECK(video_frame->visible_rect().width() == | 462 DCHECK(video_frame->visible_rect().width() == |
| 358 accelerated_last_frame_.width() && | 463 accelerated_last_frame_.width() && |
| 359 video_frame->visible_rect().height() == | 464 video_frame->visible_rect().height() == |
| 360 accelerated_last_frame_.height()); | 465 accelerated_last_frame_.height()); |
| 361 | 466 |
| 362 accelerated_last_frame_timestamp_ = video_frame->timestamp(); | 467 accelerated_last_frame_timestamp_ = video_frame->timestamp(); |
| 363 } else { | 468 } else if (accelerated_generator_) { |
| 364 accelerated_generator_->set_frame(video_frame); | 469 accelerated_generator_->set_frame(video_frame); |
| 365 } | 470 } |
| 366 target_frame = &accelerated_last_frame_; | 471 target_frame = &accelerated_last_frame_; |
| 367 accelerated_frame_deleting_timer_.Reset(); | 472 accelerated_frame_deleting_timer_.Reset(); |
| 368 } else { | 473 } else { |
| 369 // Check if we should convert and update |last_frame_|. | 474 // Draw both SW and HW Video on SW Canvas. |
| 370 if (last_frame_.isNull() || | 475 if (last_frame_.isNull() || |
| 371 video_frame->timestamp() != last_frame_timestamp_) { | 476 video_frame->timestamp() != last_frame_timestamp_) { |
| 372 // Check if |bitmap| needs to be (re)allocated. | 477 // Check if |bitmap| needs to be (re)allocated. |
| 373 if (last_frame_.isNull() || | 478 if (last_frame_.isNull() || |
| 374 last_frame_.width() != video_frame->visible_rect().width() || | 479 last_frame_.width() != video_frame->visible_rect().width() || |
| 375 last_frame_.height() != video_frame->visible_rect().height()) { | 480 last_frame_.height() != video_frame->visible_rect().height()) { |
| 376 last_frame_.allocN32Pixels(video_frame->visible_rect().width(), | 481 last_frame_.allocN32Pixels(video_frame->visible_rect().width(), |
| 377 video_frame->visible_rect().height()); | 482 video_frame->visible_rect().height()); |
| 378 last_frame_.setIsVolatile(true); | 483 last_frame_.setIsVolatile(true); |
| 379 } | 484 } |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 427 SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); | 532 SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); |
| 428 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), | 533 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), |
| 429 -SkFloatToScalar(target_frame->height() * 0.5f)); | 534 -SkFloatToScalar(target_frame->height() * 0.5f)); |
| 430 } | 535 } |
| 431 canvas->drawBitmap(*target_frame, 0, 0, &paint); | 536 canvas->drawBitmap(*target_frame, 0, 0, &paint); |
| 432 if (need_transform) | 537 if (need_transform) |
| 433 canvas->restore(); | 538 canvas->restore(); |
| 434 canvas->flush(); | 539 canvas->flush(); |
| 435 // SkCanvas::flush() causes the generator to generate SkImage, so delete | 540 // SkCanvas::flush() causes the generator to generate SkImage, so delete |
| 436 // |video_frame| not to be outlived. | 541 // |video_frame| not to be outlived. |
| 437 if (canvas->getGrContext()) | 542 if (canvas->getGrContext() && accelerated_generator_) |
| 438 accelerated_generator_->set_frame(NULL); | 543 accelerated_generator_->set_frame(nullptr); |
| 439 } | 544 } |
| 440 | 545 |
| 441 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, | 546 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, |
| 442 SkCanvas* canvas) { | 547 SkCanvas* canvas) { |
| 443 Paint(video_frame, | 548 Paint(video_frame, |
| 444 canvas, | 549 canvas, |
| 445 video_frame->visible_rect(), | 550 video_frame->visible_rect(), |
| 446 0xff, | 551 0xff, |
| 447 SkXfermode::kSrc_Mode, | 552 SkXfermode::kSrc_Mode, |
| 448 media::VIDEO_ROTATION_0); | 553 media::VIDEO_ROTATION_0, |
| 554 media::Context3DProvider()); | |
| 555 } | |
| 556 | |
| 557 // static | |
| 558 void SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture( | |
| 559 gpu::gles2::GLES2Interface* gl, | |
| 560 VideoFrame* video_frame, | |
| 561 unsigned int texture, | |
| 562 unsigned int level, | |
| 563 unsigned int internal_format, | |
| 564 unsigned int type, | |
| 565 bool premultiply_alpha, | |
| 566 bool flip_y) { | |
| 567 DCHECK(video_frame && video_frame->format() == VideoFrame::NATIVE_TEXTURE); | |
| 568 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder(); | |
| 569 DCHECK(mailbox_holder->texture_target == GL_TEXTURE_2D || | |
| 570 mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES); | |
| 571 | |
| 572 gl->WaitSyncPointCHROMIUM(mailbox_holder->sync_point); | |
| 573 uint32 source_texture = gl->CreateAndConsumeTextureCHROMIUM( | |
| 574 mailbox_holder->texture_target, mailbox_holder->mailbox.name); | |
| 575 | |
| 576 // The video is stored in a unmultiplied format, so premultiply | |
| 577 // if necessary. | |
| 578 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, premultiply_alpha); | |
| 579 // Application itself needs to take care of setting the right |flip_y| | |
| 580 // value down to get the expected result. | |
| 581 // "flip_y == true" means to reverse the video orientation while | |
| 582 // "flip_y == false" means to keep the intrinsic orientation. | |
| 583 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y); | |
| 584 gl->CopyTextureCHROMIUM( | |
| 585 GL_TEXTURE_2D, source_texture, texture, level, internal_format, type); | |
| 586 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false); | |
| 587 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false); | |
| 588 | |
| 589 gl->DeleteTextures(1, &source_texture); | |
| 590 gl->Flush(); | |
| 591 | |
| 592 SyncPointClientImpl client(gl); | |
| 593 video_frame->UpdateReleaseSyncPoint(&client); | |
| 449 } | 594 } |
| 450 | 595 |
| 451 void SkCanvasVideoRenderer::ResetLastFrame() { | 596 void SkCanvasVideoRenderer::ResetLastFrame() { |
| 452 last_frame_.reset(); | 597 last_frame_.reset(); |
| 453 last_frame_timestamp_ = media::kNoTimestamp(); | 598 last_frame_timestamp_ = media::kNoTimestamp(); |
| 454 } | 599 } |
| 455 | 600 |
| 456 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { | 601 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { |
| 457 accelerated_last_frame_.reset(); | 602 accelerated_last_frame_.reset(); |
| 458 accelerated_generator_ = nullptr; | 603 accelerated_generator_ = nullptr; |
| 459 accelerated_last_frame_timestamp_ = media::kNoTimestamp(); | 604 accelerated_last_frame_timestamp_ = media::kNoTimestamp(); |
| 460 } | 605 } |
| 461 | 606 |
| 462 } // namespace media | 607 } // namespace media |
| OLD | NEW |