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/blink/skcanvas_video_renderer.h" | 5 #include "media/blink/skcanvas_video_renderer.h" |
| 6 | 6 |
| 7 #include "gpu/GLES2/gl2extchromium.h" | 7 #include "gpu/GLES2/gl2extchromium.h" |
| 8 #include "gpu/command_buffer/client/gles2_interface.h" | 8 #include "gpu/command_buffer/client/gles2_interface.h" |
| 9 #include "gpu/command_buffer/common/mailbox_holder.h" | 9 #include "gpu/command_buffer/common/mailbox_holder.h" |
| 10 #include "media/base/video_frame.h" | 10 #include "media/base/video_frame.h" |
| 11 #include "media/base/yuv_convert.h" | 11 #include "media/base/yuv_convert.h" |
| 12 #include "skia/ext/refptr.h" | 12 #include "skia/ext/refptr.h" |
| 13 #include "third_party/libyuv/include/libyuv.h" | 13 #include "third_party/libyuv/include/libyuv.h" |
| 14 #include "third_party/skia/include/core/SkCanvas.h" | 14 #include "third_party/skia/include/core/SkCanvas.h" |
| 15 #include "third_party/skia/include/core/SkImage.h" | |
| 15 #include "third_party/skia/include/core/SkImageGenerator.h" | 16 #include "third_party/skia/include/core/SkImageGenerator.h" |
| 16 #include "third_party/skia/include/gpu/GrContext.h" | 17 #include "third_party/skia/include/gpu/GrContext.h" |
| 18 #include "third_party/skia/include/gpu/GrPaint.h" | |
| 19 #include "third_party/skia/include/gpu/GrTexture.h" | |
| 17 #include "third_party/skia/include/gpu/GrTextureProvider.h" | 20 #include "third_party/skia/include/gpu/GrTextureProvider.h" |
| 21 #include "third_party/skia/include/gpu/SkGr.h" | |
| 18 #include "third_party/skia/include/gpu/SkGrPixelRef.h" | 22 #include "third_party/skia/include/gpu/SkGrPixelRef.h" |
| 19 #include "ui/gfx/skbitmap_operations.h" | 23 #include "ui/gfx/skbitmap_operations.h" |
| 20 | 24 |
| 21 // Skia internal format depends on a platform. On Android it is ABGR, on others | 25 // Skia internal format depends on a platform. On Android it is ABGR, on others |
| 22 // it is ARGB. | 26 // it is ARGB. |
| 23 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ | 27 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ |
| 24 SK_A32_SHIFT == 24 | 28 SK_A32_SHIFT == 24 |
| 25 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB | 29 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB |
| 26 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB | 30 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB |
| 27 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ | 31 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 77 | 81 |
| 78 SkImageInfo info = SkImageInfo::MakeN32Premul(desc.fWidth, desc.fHeight); | 82 SkImageInfo info = SkImageInfo::MakeN32Premul(desc.fWidth, desc.fHeight); |
| 79 SkGrPixelRef* pixel_ref = SkNEW_ARGS(SkGrPixelRef, (info, texture.get())); | 83 SkGrPixelRef* pixel_ref = SkNEW_ARGS(SkGrPixelRef, (info, texture.get())); |
| 80 if (!pixel_ref) | 84 if (!pixel_ref) |
| 81 return false; | 85 return false; |
| 82 bitmap->setInfo(info); | 86 bitmap->setInfo(info); |
| 83 bitmap->setPixelRef(pixel_ref)->unref(); | 87 bitmap->setPixelRef(pixel_ref)->unref(); |
| 84 return true; | 88 return true; |
| 85 } | 89 } |
| 86 | 90 |
| 87 bool CopyVideoFrameTextureToSkBitmapTexture(VideoFrame* video_frame, | 91 class SyncPointClientImpl : public VideoFrame::SyncPointClient { |
| 88 SkBitmap* bitmap, | 92 public: |
| 89 const Context3D& context_3d) { | 93 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} |
| 94 ~SyncPointClientImpl() override {} | |
| 95 uint32 InsertSyncPoint() override { return gl_->InsertSyncPointCHROMIUM(); } | |
| 96 void WaitSyncPoint(uint32 sync_point) override { | |
| 97 gl_->WaitSyncPointCHROMIUM(sync_point); | |
| 98 } | |
| 99 | |
| 100 private: | |
| 101 gpu::gles2::GLES2Interface* gl_; | |
| 102 | |
| 103 DISALLOW_IMPLICIT_CONSTRUCTORS(SyncPointClientImpl); | |
| 104 }; | |
| 105 | |
| 106 scoped_ptr<SkImage> CreateSkImageFromVideoFrameNativeTextures( | |
| 107 VideoFrame* video_frame, | |
| 108 const Context3D& context_3d) { | |
| 109 // Support only TEXTURE_YUV_420. | |
| 110 DCHECK(video_frame->storage_type() == VideoFrame::STORAGE_TEXTURE); | |
| 111 DCHECK_EQ(media::VideoFrame::I420, video_frame->format()); | |
| 112 DCHECK_EQ(3u, media::VideoFrame::NumPlanes(video_frame->format())); | |
| 113 | |
| 114 gpu::gles2::GLES2Interface* gl = context_3d.gl; | |
| 115 DCHECK(gl); | |
| 116 gfx::Size ya_tex_size = video_frame->coded_size(); | |
| 117 gfx::Size uv_tex_size((ya_tex_size.width() + 1) / 2, | |
| 118 (ya_tex_size.height() + 1) / 2); | |
| 119 | |
| 120 unsigned source_textures[3] = {0}; | |
| 121 for (size_t i = 0; i < media::VideoFrame::NumPlanes(video_frame->format()); | |
| 122 ++i) { | |
| 123 // Get the texture from the mailbox and wrap it in a GrTexture. | |
| 124 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(i); | |
| 125 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || | |
| 126 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES || | |
| 127 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB); | |
| 128 gl->WaitSyncPointCHROMIUM(mailbox_holder.sync_point); | |
| 129 source_textures[i] = gl->CreateAndConsumeTextureCHROMIUM( | |
| 130 mailbox_holder.texture_target, mailbox_holder.mailbox.name); | |
| 131 } | |
| 132 GrBackendObject handles[3] = { | |
| 133 source_textures[0], source_textures[1], source_textures[2]}; | |
| 134 | |
| 135 SkISize yuvSizes[] = { | |
| 136 {ya_tex_size.width(), ya_tex_size.height()}, | |
| 137 {uv_tex_size.width(), uv_tex_size.height()}, | |
| 138 {uv_tex_size.width(), uv_tex_size.height()}, | |
| 139 }; | |
| 140 SkImage* img = SkImage::NewFromYUVTexturesCopy( | |
| 141 context_3d.gr_context, kRec601_SkYUVColorSpace, handles, yuvSizes, | |
| 142 kTopLeft_GrSurfaceOrigin); | |
| 143 DCHECK(img); | |
| 144 gl->DeleteTextures(3, source_textures); | |
| 145 SyncPointClientImpl client(gl); | |
| 146 video_frame->UpdateReleaseSyncPoint(&client); | |
| 147 return make_scoped_ptr(img); | |
| 148 } | |
| 149 | |
| 150 bool CopyVideoFrameSingleTextureToSkBitmap(VideoFrame* video_frame, | |
| 151 SkBitmap* bitmap, | |
| 152 const Context3D& context_3d) { | |
| 90 // Check if we could reuse existing texture based bitmap. | 153 // Check if we could reuse existing texture based bitmap. |
| 91 // Otherwise, release existing texture based bitmap and allocate | 154 // Otherwise, release existing texture based bitmap and allocate |
| 92 // a new one based on video size. | 155 // a new one based on video size. |
| 93 if (!IsSkBitmapProperlySizedTexture(bitmap, | 156 if (!IsSkBitmapProperlySizedTexture(bitmap, |
| 94 video_frame->visible_rect().size())) { | 157 video_frame->visible_rect().size())) { |
| 95 if (!AllocateSkBitmapTexture(context_3d.gr_context, bitmap, | 158 if (!AllocateSkBitmapTexture(context_3d.gr_context, bitmap, |
| 96 video_frame->visible_rect().size())) { | 159 video_frame->visible_rect().size())) { |
| 97 return false; | 160 return false; |
| 98 } | 161 } |
| 99 } | 162 } |
| 100 | 163 |
| 101 unsigned texture_id = | 164 unsigned texture_id = |
| 102 static_cast<unsigned>((bitmap->getTexture())->getTextureHandle()); | 165 static_cast<unsigned>((bitmap->getTexture())->getTextureHandle()); |
| 103 // If CopyVideoFrameTextureToGLTexture() changes the state of the | 166 // If CopyVideoFrameSingleTextureToGLTexture() changes the state of the |
| 104 // |texture_id|, it's needed to invalidate the state cached in skia, | 167 // |texture_id|, it's needed to invalidate the state cached in skia, |
| 105 // but currently the state isn't changed. | 168 // but currently the state isn't changed. |
| 106 SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture( | 169 |
| 170 SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture( | |
| 107 context_3d.gl, video_frame, texture_id, GL_RGBA, GL_UNSIGNED_BYTE, true, | 171 context_3d.gl, video_frame, texture_id, GL_RGBA, GL_UNSIGNED_BYTE, true, |
| 108 false); | 172 false); |
| 109 bitmap->notifyPixelsChanged(); | 173 bitmap->notifyPixelsChanged(); |
| 110 return true; | 174 return true; |
| 111 } | 175 } |
| 112 | 176 |
| 113 class SyncPointClientImpl : public VideoFrame::SyncPointClient { | |
| 114 public: | |
| 115 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} | |
| 116 ~SyncPointClientImpl() override {} | |
| 117 uint32 InsertSyncPoint() override { return gl_->InsertSyncPointCHROMIUM(); } | |
| 118 void WaitSyncPoint(uint32 sync_point) override { | |
| 119 gl_->WaitSyncPointCHROMIUM(sync_point); | |
| 120 } | |
| 121 | |
| 122 private: | |
| 123 gpu::gles2::GLES2Interface* gl_; | |
| 124 | |
| 125 DISALLOW_IMPLICIT_CONSTRUCTORS(SyncPointClientImpl); | |
| 126 }; | |
| 127 | |
| 128 } // anonymous namespace | 177 } // anonymous namespace |
| 129 | 178 |
| 130 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. | 179 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. |
| 131 class VideoImageGenerator : public SkImageGenerator { | 180 class VideoImageGenerator : public SkImageGenerator { |
| 132 public: | 181 public: |
| 133 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) | 182 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) |
| 134 : SkImageGenerator( | 183 : SkImageGenerator( |
| 135 SkImageInfo::MakeN32Premul(frame->visible_rect().width(), | 184 SkImageInfo::MakeN32Premul(frame->visible_rect().width(), |
| 136 frame->visible_rect().height())) | 185 frame->visible_rect().height())) |
| 137 , frame_(frame) { | 186 , frame_(frame) { |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 283 // In SW Canvas case, rely on skia drawing Ganesh SkBitmap on SW SkCanvas. | 332 // In SW Canvas case, rely on skia drawing Ganesh SkBitmap on SW SkCanvas. |
| 284 if (accelerated_last_frame_.isNull() || | 333 if (accelerated_last_frame_.isNull() || |
| 285 video_frame->timestamp() != accelerated_last_frame_timestamp_) { | 334 video_frame->timestamp() != accelerated_last_frame_timestamp_) { |
| 286 DCHECK(context_3d.gl); | 335 DCHECK(context_3d.gl); |
| 287 DCHECK(context_3d.gr_context); | 336 DCHECK(context_3d.gr_context); |
| 288 if (accelerated_generator_) { | 337 if (accelerated_generator_) { |
| 289 // Reset SkBitmap used in SWVideo-to-HWCanvas path. | 338 // Reset SkBitmap used in SWVideo-to-HWCanvas path. |
| 290 accelerated_last_frame_.reset(); | 339 accelerated_last_frame_.reset(); |
| 291 accelerated_generator_ = nullptr; | 340 accelerated_generator_ = nullptr; |
| 292 } | 341 } |
| 293 if (!CopyVideoFrameTextureToSkBitmapTexture( | 342 |
| 294 video_frame.get(), &accelerated_last_frame_, context_3d)) { | 343 if (media::VideoFrame::NumPlanes(video_frame->format()) == 1) { |
| 295 NOTREACHED(); | 344 accelerated_last_image_.reset(); |
| 296 return; | 345 if (!CopyVideoFrameSingleTextureToSkBitmap( |
| 346 video_frame.get(), &accelerated_last_frame_, context_3d)) { | |
| 347 NOTREACHED(); | |
| 348 return; | |
| 349 } | |
| 350 DCHECK(video_frame->visible_rect().width() == | |
| 351 accelerated_last_frame_.width() && | |
| 352 video_frame->visible_rect().height() == | |
| 353 accelerated_last_frame_.height()); | |
| 354 } else { | |
| 355 accelerated_last_image_ = CreateSkImageFromVideoFrameNativeTextures( | |
|
dshwang
2015/06/10 17:38:37
I'm not good at naming but I think the function na
Daniele Castagna
2015/06/10 17:42:34
How do you like CreateSkImageFromVideoFrameYUVText
dshwang
2015/06/10 17:45:05
I buy it :)
Daniele Castagna
2015/06/10 18:02:27
Done.
| |
| 356 video_frame.get(), context_3d); | |
| 357 DCHECK(accelerated_last_image_); | |
| 297 } | 358 } |
| 298 DCHECK(video_frame->visible_rect().width() == | |
| 299 accelerated_last_frame_.width() && | |
| 300 video_frame->visible_rect().height() == | |
| 301 accelerated_last_frame_.height()); | |
| 302 | |
| 303 accelerated_last_frame_timestamp_ = video_frame->timestamp(); | 359 accelerated_last_frame_timestamp_ = video_frame->timestamp(); |
| 304 } | 360 } |
| 305 target_frame = &accelerated_last_frame_; | 361 target_frame = &accelerated_last_frame_; |
| 306 accelerated_frame_deleting_timer_.Reset(); | 362 accelerated_frame_deleting_timer_.Reset(); |
| 307 } else if (canvas->getGrContext()) { | 363 } else if (canvas->getGrContext()) { |
| 308 if (accelerated_last_frame_.isNull() || | 364 if (accelerated_last_frame_.isNull() || |
| 309 video_frame->timestamp() != accelerated_last_frame_timestamp_) { | 365 video_frame->timestamp() != accelerated_last_frame_timestamp_) { |
| 310 // Draw SW Video on HW Canvas. | 366 // Draw SW Video on HW Canvas. |
| 311 if (!accelerated_generator_ && !accelerated_last_frame_.isNull()) { | 367 if (!accelerated_generator_ && !accelerated_last_frame_.isNull()) { |
| 312 // Reset SkBitmap used in HWVideo-to-HWCanvas path. | 368 // Reset SkBitmap used in HWVideo-to-HWCanvas path. |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 388 video_rotation == VIDEO_ROTATION_270) { | 444 video_rotation == VIDEO_ROTATION_270) { |
| 389 rotated_dest_size = | 445 rotated_dest_size = |
| 390 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); | 446 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); |
| 391 } | 447 } |
| 392 canvas->scale( | 448 canvas->scale( |
| 393 SkFloatToScalar(rotated_dest_size.width() / target_frame->width()), | 449 SkFloatToScalar(rotated_dest_size.width() / target_frame->width()), |
| 394 SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); | 450 SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); |
| 395 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), | 451 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), |
| 396 -SkFloatToScalar(target_frame->height() * 0.5f)); | 452 -SkFloatToScalar(target_frame->height() * 0.5f)); |
| 397 } | 453 } |
| 398 canvas->drawBitmap(*target_frame, 0, 0, &paint); | 454 if (accelerated_last_image_) { |
| 455 canvas->drawImage(accelerated_last_image_.get(), 0, 0, &paint); | |
| 456 } else { | |
| 457 canvas->drawBitmap(*target_frame, 0, 0, &paint); | |
| 458 } | |
| 399 if (need_transform) | 459 if (need_transform) |
| 400 canvas->restore(); | 460 canvas->restore(); |
| 401 canvas->flush(); | 461 canvas->flush(); |
| 402 // SkCanvas::flush() causes the generator to generate SkImage, so delete | 462 // SkCanvas::flush() causes the generator to generate SkImage, so delete |
| 403 // |video_frame| not to be outlived. | 463 // |video_frame| not to be outlived. |
| 404 if (canvas->getGrContext() && accelerated_generator_) | 464 if (canvas->getGrContext() && accelerated_generator_) |
| 405 accelerated_generator_->set_frame(nullptr); | 465 accelerated_generator_->set_frame(nullptr); |
| 406 } | 466 } |
| 407 | 467 |
| 408 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, | 468 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 541 case VideoFrame::NV12: | 601 case VideoFrame::NV12: |
| 542 #endif | 602 #endif |
| 543 case VideoFrame::ARGB: | 603 case VideoFrame::ARGB: |
| 544 case VideoFrame::XRGB: | 604 case VideoFrame::XRGB: |
| 545 case VideoFrame::UNKNOWN: | 605 case VideoFrame::UNKNOWN: |
| 546 NOTREACHED(); | 606 NOTREACHED(); |
| 547 } | 607 } |
| 548 } | 608 } |
| 549 | 609 |
| 550 // static | 610 // static |
| 551 void SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture( | 611 void SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture( |
| 552 gpu::gles2::GLES2Interface* gl, | 612 gpu::gles2::GLES2Interface* gl, |
| 553 VideoFrame* video_frame, | 613 VideoFrame* video_frame, |
| 554 unsigned int texture, | 614 unsigned int texture, |
| 555 unsigned int internal_format, | 615 unsigned int internal_format, |
| 556 unsigned int type, | 616 unsigned int type, |
| 557 bool premultiply_alpha, | 617 bool premultiply_alpha, |
| 558 bool flip_y) { | 618 bool flip_y) { |
| 559 DCHECK(video_frame); | 619 DCHECK(video_frame); |
| 560 DCHECK_EQ(video_frame->storage_type(), VideoFrame::STORAGE_TEXTURE); | 620 DCHECK_EQ(video_frame->storage_type(), VideoFrame::STORAGE_TEXTURE); |
| 561 DCHECK_EQ(1u, VideoFrame::NumPlanes(video_frame->format())); | 621 DCHECK_EQ(1u, VideoFrame::NumPlanes(video_frame->format())); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 588 SyncPointClientImpl client(gl); | 648 SyncPointClientImpl client(gl); |
| 589 video_frame->UpdateReleaseSyncPoint(&client); | 649 video_frame->UpdateReleaseSyncPoint(&client); |
| 590 } | 650 } |
| 591 | 651 |
| 592 void SkCanvasVideoRenderer::ResetLastFrame() { | 652 void SkCanvasVideoRenderer::ResetLastFrame() { |
| 593 last_frame_.reset(); | 653 last_frame_.reset(); |
| 594 last_frame_timestamp_ = media::kNoTimestamp(); | 654 last_frame_timestamp_ = media::kNoTimestamp(); |
| 595 } | 655 } |
| 596 | 656 |
| 597 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { | 657 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { |
| 658 accelerated_last_image_.reset(); | |
| 598 accelerated_last_frame_.reset(); | 659 accelerated_last_frame_.reset(); |
| 599 accelerated_generator_ = nullptr; | 660 accelerated_generator_ = nullptr; |
| 600 accelerated_last_frame_timestamp_ = media::kNoTimestamp(); | 661 accelerated_last_frame_timestamp_ = media::kNoTimestamp(); |
| 601 } | 662 } |
| 602 | 663 |
| 603 } // namespace media | 664 } // namespace media |
| OLD | NEW |