Chromium Code Reviews| Index: media/blink/skcanvas_video_renderer.cc |
| diff --git a/media/blink/skcanvas_video_renderer.cc b/media/blink/skcanvas_video_renderer.cc |
| index f86e118d753d49dbb135b1ac3a742487ff75477a..110b7027be2caba9f66649495c66fc48d79db549 100644 |
| --- a/media/blink/skcanvas_video_renderer.cc |
| +++ b/media/blink/skcanvas_video_renderer.cc |
| @@ -12,11 +12,11 @@ |
| #include "skia/ext/refptr.h" |
| #include "third_party/libyuv/include/libyuv.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| +#include "third_party/skia/include/core/SkImage.h" |
| #include "third_party/skia/include/core/SkImageGenerator.h" |
| #include "third_party/skia/include/gpu/GrContext.h" |
| #include "third_party/skia/include/gpu/GrTextureProvider.h" |
| #include "third_party/skia/include/gpu/SkGrPixelRef.h" |
| -#include "ui/gfx/skbitmap_operations.h" |
| // Skia internal format depends on a platform. On Android it is ABGR, on others |
| // it is ARGB. |
| @@ -36,11 +36,11 @@ namespace media { |
| namespace { |
| -// This class keeps two temporary resources; software bitmap, hardware bitmap. |
| -// If both bitmap are created and then only software bitmap is updated every |
| -// frame, hardware bitmap outlives until the media player dies. So we delete |
| -// a temporary resource if it is not used for 3 sec. |
| -const int kTemporaryResourceDeletionDelay = 3; // Seconds; |
| +bool IsCanvasDrawingOnPicture(SkCanvas* canvas) { |
| + SkImageInfo info; |
| + size_t rowBytes; |
| + return !canvas->getGrContext() && !canvas->peekPixels(&info, &rowBytes); |
| +} |
| bool IsYUV(media::VideoFrame::Format format) { |
| switch (format) { |
| @@ -75,66 +75,6 @@ bool IsYUVOrNative(media::VideoFrame::Format format) { |
| return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE; |
| } |
| -bool IsSkBitmapProperlySizedTexture(const SkBitmap* bitmap, |
| - const gfx::Size& size) { |
| - return bitmap->getTexture() && bitmap->width() == size.width() && |
| - bitmap->height() == size.height(); |
| -} |
| - |
| -bool AllocateSkBitmapTexture(GrContext* gr, |
| - SkBitmap* bitmap, |
| - const gfx::Size& size) { |
| - DCHECK(gr); |
| - GrTextureDesc desc; |
| - // Use kRGBA_8888_GrPixelConfig, not kSkia8888_GrPixelConfig, to avoid |
| - // RGBA to BGRA conversion. |
| - desc.fConfig = kRGBA_8888_GrPixelConfig; |
| - desc.fFlags = kRenderTarget_GrSurfaceFlag; |
| - desc.fSampleCnt = 0; |
| - desc.fOrigin = kTopLeft_GrSurfaceOrigin; |
| - desc.fWidth = size.width(); |
| - desc.fHeight = size.height(); |
| - skia::RefPtr<GrTexture> texture = skia::AdoptRef( |
| - gr->textureProvider()->refScratchTexture( |
| - desc, GrTextureProvider::kExact_ScratchTexMatch)); |
| - if (!texture.get()) |
| - return false; |
| - |
| - SkImageInfo info = SkImageInfo::MakeN32Premul(desc.fWidth, desc.fHeight); |
| - SkGrPixelRef* pixel_ref = SkNEW_ARGS(SkGrPixelRef, (info, texture.get())); |
| - if (!pixel_ref) |
| - return false; |
| - bitmap->setInfo(info); |
| - bitmap->setPixelRef(pixel_ref)->unref(); |
| - return true; |
| -} |
| - |
| -bool CopyVideoFrameTextureToSkBitmapTexture(VideoFrame* video_frame, |
| - SkBitmap* bitmap, |
| - const Context3D& context_3d) { |
| - // Check if we could reuse existing texture based bitmap. |
| - // Otherwise, release existing texture based bitmap and allocate |
| - // a new one based on video size. |
| - if (!IsSkBitmapProperlySizedTexture(bitmap, |
| - video_frame->visible_rect().size())) { |
| - if (!AllocateSkBitmapTexture(context_3d.gr_context, bitmap, |
| - video_frame->visible_rect().size())) { |
| - return false; |
| - } |
| - } |
| - |
| - unsigned texture_id = |
| - static_cast<unsigned>((bitmap->getTexture())->getTextureHandle()); |
| - // If CopyVideoFrameTextureToGLTexture() changes the state of the |
| - // |texture_id|, it's needed to invalidate the state cached in skia, |
| - // but currently the state isn't changed. |
| - SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture( |
| - context_3d.gl, video_frame, texture_id, GL_RGBA, GL_UNSIGNED_BYTE, true, |
| - false); |
| - bitmap->notifyPixelsChanged(); |
| - return true; |
| -} |
| - |
| class SyncPointClientImpl : public VideoFrame::SyncPointClient { |
| public: |
| explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} |
| @@ -150,6 +90,35 @@ class SyncPointClientImpl : public VideoFrame::SyncPointClient { |
| DISALLOW_IMPLICIT_CONSTRUCTORS(SyncPointClientImpl); |
| }; |
| +// Creates a SkImage from a |video_frame| backed by native resources. |
| +// The SkImage will not take ownership of the underlying resources and the |
| +// caller will have to delete them after the SkImage is used. |
| +// |source_textures| is a pointer to an array that will be filled with |
| +// texture ids backing the returned image. |
| +SkImage* NewSkImageFromVideoFrameNative( |
| + const scoped_refptr<VideoFrame>& video_frame, |
| + const Context3D& context_3d, |
| + unsigned* source_textures) { |
| + // TODO(dcastagna): Add support for YUV420. |
| + DCHECK_EQ(VideoFrame::TEXTURE_RGBA, video_frame->texture_format()); |
| + |
| + // Get the texture from the mailbox and wrap it in a GrTexture. |
| + const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0); |
| + DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || |
| + mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB); |
|
reveman
2015/05/27 17:54:20
GL_TEXTURE_EXTERNAL_OES? required for SurfaceTextu
Daniele Castagna
2015/05/27 18:12:35
Done.
|
| + gpu::gles2::GLES2Interface* gl = context_3d.gl; |
| + gl->WaitSyncPointCHROMIUM(mailbox_holder.sync_point); |
| + *source_textures = gl->CreateAndConsumeTextureCHROMIUM( |
| + mailbox_holder.texture_target, mailbox_holder.mailbox.name); |
| + GrBackendTextureDesc desc; |
| + desc.fOrigin = kTopLeft_GrSurfaceOrigin; |
| + desc.fWidth = video_frame->coded_size().width(); |
| + desc.fHeight = video_frame->coded_size().height(); |
| + desc.fConfig = kRGBA_8888_GrPixelConfig; |
| + desc.fTextureHandle = *source_textures; |
| + return SkImage::NewFromTexture(context_3d.gr_context, desc); |
| +} |
| + |
| } // anonymous namespace |
| // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. |
| @@ -255,24 +224,11 @@ class VideoImageGenerator : public SkImageGenerator { |
| DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); |
| }; |
| -SkCanvasVideoRenderer::SkCanvasVideoRenderer() |
| - : last_frame_timestamp_(media::kNoTimestamp()), |
| - frame_deleting_timer_( |
| - FROM_HERE, |
| - base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), |
| - this, |
| - &SkCanvasVideoRenderer::ResetLastFrame), |
| - accelerated_generator_(nullptr), |
| - accelerated_last_frame_timestamp_(media::kNoTimestamp()), |
| - accelerated_frame_deleting_timer_( |
| - FROM_HERE, |
| - base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), |
| - this, |
| - &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) { |
| - last_frame_.setIsVolatile(true); |
| +SkCanvasVideoRenderer::SkCanvasVideoRenderer() { |
| } |
| -SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} |
| +SkCanvasVideoRenderer::~SkCanvasVideoRenderer() { |
| +} |
| void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, |
| SkCanvas* canvas, |
| @@ -300,84 +256,16 @@ void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, |
| return; |
| } |
| - SkBitmap* target_frame = nullptr; |
| - |
| + unsigned texture_id_ = 0; |
|
reveman
2015/05/27 17:54:20
nit: texture_id
Daniele Castagna
2015/05/27 18:12:35
Done.
|
| + SkImage* target_frame = nullptr; |
| if (video_frame->format() == VideoFrame::NATIVE_TEXTURE) { |
| - // Draw HW Video on both SW and HW Canvas. |
| - // In SW Canvas case, rely on skia drawing Ganesh SkBitmap on SW SkCanvas. |
| - if (accelerated_last_frame_.isNull() || |
| - video_frame->timestamp() != accelerated_last_frame_timestamp_) { |
| - DCHECK(context_3d.gl); |
| - DCHECK(context_3d.gr_context); |
| - if (accelerated_generator_) { |
| - // Reset SkBitmap used in SWVideo-to-HWCanvas path. |
| - accelerated_last_frame_.reset(); |
| - accelerated_generator_ = nullptr; |
| - } |
| - if (!CopyVideoFrameTextureToSkBitmapTexture( |
| - video_frame.get(), &accelerated_last_frame_, context_3d)) { |
| - NOTREACHED(); |
| - return; |
| - } |
| - DCHECK(video_frame->visible_rect().width() == |
| - accelerated_last_frame_.width() && |
| - video_frame->visible_rect().height() == |
| - accelerated_last_frame_.height()); |
| - |
| - accelerated_last_frame_timestamp_ = video_frame->timestamp(); |
| - } |
| - target_frame = &accelerated_last_frame_; |
| - accelerated_frame_deleting_timer_.Reset(); |
| - } else if (canvas->getGrContext()) { |
| - DCHECK(video_frame->format() != VideoFrame::NATIVE_TEXTURE); |
| - if (accelerated_last_frame_.isNull() || |
| - video_frame->timestamp() != accelerated_last_frame_timestamp_) { |
| - // Draw SW Video on HW Canvas. |
| - if (!accelerated_generator_ && !accelerated_last_frame_.isNull()) { |
| - // Reset SkBitmap used in HWVideo-to-HWCanvas path. |
| - accelerated_last_frame_.reset(); |
| - } |
| - accelerated_generator_ = new VideoImageGenerator(video_frame); |
| - |
| - // Note: This takes ownership of |accelerated_generator_|. |
| - if (!SkInstallDiscardablePixelRef(accelerated_generator_, |
| - &accelerated_last_frame_)) { |
| - NOTREACHED(); |
| - return; |
| - } |
| - DCHECK(video_frame->visible_rect().width() == |
| - accelerated_last_frame_.width() && |
| - video_frame->visible_rect().height() == |
| - accelerated_last_frame_.height()); |
| - |
| - accelerated_last_frame_timestamp_ = video_frame->timestamp(); |
| - } else if (accelerated_generator_) { |
| - accelerated_generator_->set_frame(video_frame); |
| - } |
| - target_frame = &accelerated_last_frame_; |
| - accelerated_frame_deleting_timer_.Reset(); |
| + DCHECK(context_3d.gr_context); |
| + DCHECK(context_3d.gl); |
| + target_frame = |
| + NewSkImageFromVideoFrameNative(video_frame, context_3d, &texture_id_); |
| } else { |
| - // Draw SW Video on SW Canvas. |
| - DCHECK(video_frame->format() != VideoFrame::NATIVE_TEXTURE); |
| - if (last_frame_.isNull() || |
| - video_frame->timestamp() != last_frame_timestamp_) { |
| - // Check if |bitmap| needs to be (re)allocated. |
| - if (last_frame_.isNull() || |
| - last_frame_.width() != video_frame->visible_rect().width() || |
| - last_frame_.height() != video_frame->visible_rect().height()) { |
| - last_frame_.allocN32Pixels(video_frame->visible_rect().width(), |
| - video_frame->visible_rect().height()); |
| - last_frame_.setIsVolatile(true); |
| - } |
| - last_frame_.lockPixels(); |
| - ConvertVideoFrameToRGBPixels( |
| - video_frame, last_frame_.getPixels(), last_frame_.rowBytes()); |
| - last_frame_.notifyPixelsChanged(); |
| - last_frame_.unlockPixels(); |
| - last_frame_timestamp_ = video_frame->timestamp(); |
| - } |
| - target_frame = &last_frame_; |
| - frame_deleting_timer_.Reset(); |
| + VideoImageGenerator* generator = new VideoImageGenerator(video_frame); |
| + target_frame = SkImage::NewFromGenerator(generator); |
| } |
| paint.setXfermodeMode(mode); |
| @@ -420,14 +308,21 @@ void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, |
| canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), |
| -SkFloatToScalar(target_frame->height() * 0.5f)); |
| } |
| - canvas->drawBitmap(*target_frame, 0, 0, &paint); |
| + canvas->drawImage(target_frame, 0, 0, &paint); |
| if (need_transform) |
| canvas->restore(); |
| canvas->flush(); |
| - // SkCanvas::flush() causes the generator to generate SkImage, so delete |
| - // |video_frame| not to be outlived. |
| - if (canvas->getGrContext() && accelerated_generator_) |
| - accelerated_generator_->set_frame(nullptr); |
| + SkSafeSetNull(target_frame); |
| + // When target_frame is created from a backend texture, calling |
| + // textureSkCanvas::flush() causes Skia to use all the resources backing the |
| + // SkImage and the underlying texture can be delete. |
| + // TODO(dcastagna): release the texture in a callback once Skia API exposes |
| + // the functionality. |
| + if (texture_id_) { |
| + DCHECK(!IsCanvasDrawingOnPicture(canvas)); |
| + DCHECK(context_3d.gl); |
| + context_3d.gl->DeleteTextures(1, &texture_id_); |
| + } |
| } |
| void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, |
| @@ -614,15 +509,4 @@ void SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture( |
| video_frame->UpdateReleaseSyncPoint(&client); |
| } |
| -void SkCanvasVideoRenderer::ResetLastFrame() { |
| - last_frame_.reset(); |
| - last_frame_timestamp_ = media::kNoTimestamp(); |
| -} |
| - |
| -void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { |
| - accelerated_last_frame_.reset(); |
| - accelerated_generator_ = nullptr; |
| - accelerated_last_frame_timestamp_ = media::kNoTimestamp(); |
| -} |
| - |
| } // namespace media |