| Index: media/blink/skcanvas_video_renderer.cc
|
| diff --git a/media/blink/skcanvas_video_renderer.cc b/media/blink/skcanvas_video_renderer.cc
|
| index 572064e8c1c0d9b315eb7fa74f1b37bc7ee6f338..db711dc8f55c96ab06bd17b9a5358017600dbc7c 100644
|
| --- a/media/blink/skcanvas_video_renderer.cc
|
| +++ b/media/blink/skcanvas_video_renderer.cc
|
| @@ -18,9 +18,6 @@
|
| #include "third_party/skia/include/gpu/GrPaint.h"
|
| #include "third_party/skia/include/gpu/GrTexture.h"
|
| #include "third_party/skia/include/gpu/GrTextureProvider.h"
|
| -#include "third_party/skia/include/gpu/SkGr.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.
|
| @@ -40,54 +37,17 @@ 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.
|
| +// This class keeps the last image drawn.
|
| +// We delete the temporary resource if it is not used for 3 seconds.
|
| const int kTemporaryResourceDeletionDelay = 3; // Seconds;
|
|
|
| -bool CheckColorSpace(const scoped_refptr<VideoFrame>& video_frame,
|
| - ColorSpace color_space) {
|
| +bool CheckColorSpace(const VideoFrame* video_frame, ColorSpace color_space) {
|
| int result;
|
| return video_frame->metadata()->GetInteger(
|
| VideoFrameMetadata::COLOR_SPACE, &result) &&
|
| result == color_space;
|
| }
|
|
|
| -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;
|
| -}
|
| -
|
| class SyncPointClientImpl : public VideoFrame::SyncPointClient {
|
| public:
|
| explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
|
| @@ -103,8 +63,8 @@ class SyncPointClientImpl : public VideoFrame::SyncPointClient {
|
| DISALLOW_IMPLICIT_CONSTRUCTORS(SyncPointClientImpl);
|
| };
|
|
|
| -scoped_ptr<SkImage> CreateSkImageFromVideoFrameYUVTextures(
|
| - VideoFrame* video_frame,
|
| +skia::RefPtr<SkImage> NewSkImageFromVideoFrameYUVTextures(
|
| + const VideoFrame* video_frame,
|
| const Context3D& context_3d) {
|
| // Support only TEXTURE_YUV_420.
|
| DCHECK(video_frame->HasTextures());
|
| @@ -164,36 +124,54 @@ scoped_ptr<SkImage> CreateSkImageFromVideoFrameYUVTextures(
|
| kTopLeft_GrSurfaceOrigin);
|
| DCHECK(img);
|
| gl->DeleteTextures(3, source_textures);
|
| - SyncPointClientImpl client(gl);
|
| - video_frame->UpdateReleaseSyncPoint(&client);
|
| - return make_scoped_ptr(img);
|
| + return skia::AdoptRef(img);
|
| }
|
|
|
| -bool CopyVideoFrameSingleTextureToSkBitmap(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;
|
| - }
|
| - }
|
| +bool ShouldCacheVideoFrameSkImage(const VideoFrame* video_frame) {
|
| + return !video_frame->HasTextures() ||
|
| + media::VideoFrame::NumPlanes(video_frame->format()) != 1 ||
|
| + video_frame->mailbox_holder(0).texture_target != GL_TEXTURE_2D;
|
| +}
|
| +
|
| +// Creates a SkImage from a |video_frame| backed by native resources.
|
| +// The SkImage will take ownership of the underlying resource.
|
| +skia::RefPtr<SkImage> NewSkImageFromVideoFrameNative(
|
| + VideoFrame* video_frame,
|
| + const Context3D& context_3d) {
|
| + DCHECK_EQ(PIXEL_FORMAT_ARGB, video_frame->format());
|
|
|
| - unsigned texture_id =
|
| - static_cast<unsigned>((bitmap->getTexture())->getTextureHandle());
|
| - // If CopyVideoFrameSingleTextureToGLTexture() 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::CopyVideoFrameSingleTextureToGLTexture(
|
| - context_3d.gl, video_frame, texture_id, GL_RGBA, GL_UNSIGNED_BYTE, true,
|
| - false);
|
| - bitmap->notifyPixelsChanged();
|
| - return true;
|
| + 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 ||
|
| + mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES)
|
| + << mailbox_holder.texture_target;
|
| +
|
| + gpu::gles2::GLES2Interface* gl = context_3d.gl;
|
| + unsigned source_texture = 0;
|
| + if (mailbox_holder.texture_target != GL_TEXTURE_2D) {
|
| + // TODO(dcastagna): At the moment Skia doesn't support targets different
|
| + // than GL_TEXTURE_2D. Avoid this copy once
|
| + // https://code.google.com/p/skia/issues/detail?id=3868 is addressed.
|
| + gl->GenTextures(1, &source_texture);
|
| + DCHECK(source_texture);
|
| + gl->BindTexture(GL_TEXTURE_2D, source_texture);
|
| + SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
|
| + gl, video_frame, source_texture, GL_RGBA, GL_UNSIGNED_BYTE, true,
|
| + false);
|
| + } else {
|
| + gl->WaitSyncPointCHROMIUM(mailbox_holder.sync_point);
|
| + source_texture = gl->CreateAndConsumeTextureCHROMIUM(
|
| + mailbox_holder.texture_target, mailbox_holder.mailbox.name);
|
| + }
|
| + GrBackendTextureDesc desc;
|
| + desc.fFlags = kRenderTarget_GrBackendTextureFlag;
|
| + 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_texture;
|
| + return skia::AdoptRef(
|
| + SkImage::NewFromAdoptedTexture(context_3d.gr_context, desc));
|
| }
|
|
|
| } // anonymous namespace
|
| @@ -204,25 +182,21 @@ class VideoImageGenerator : public SkImageGenerator {
|
| VideoImageGenerator(const scoped_refptr<VideoFrame>& frame)
|
| : SkImageGenerator(
|
| SkImageInfo::MakeN32Premul(frame->visible_rect().width(),
|
| - frame->visible_rect().height()))
|
| - , frame_(frame) {
|
| - DCHECK(frame_.get());
|
| + frame->visible_rect().height())),
|
| + frame_(frame) {
|
| + DCHECK(!frame_->HasTextures());
|
| }
|
| ~VideoImageGenerator() override {}
|
|
|
| - void set_frame(const scoped_refptr<VideoFrame>& frame) { frame_ = frame; }
|
| -
|
| protected:
|
| bool onGetPixels(const SkImageInfo& info,
|
| void* pixels,
|
| size_t row_bytes,
|
| SkPMColor ctable[],
|
| int* ctable_count) override {
|
| - if (!frame_.get())
|
| - return false;
|
| // If skia couldn't do the YUV conversion on GPU, we will on CPU.
|
| - SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
|
| - frame_, pixels, row_bytes);
|
| + SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(frame_.get(), pixels,
|
| + row_bytes);
|
| return true;
|
| }
|
|
|
| @@ -230,7 +204,7 @@ class VideoImageGenerator : public SkImageGenerator {
|
| void* planes[3],
|
| size_t row_bytes[3],
|
| SkYUVColorSpace* color_space) override {
|
| - if (!frame_.get() || !media::IsYuvPlanar(frame_->format()) ||
|
| + if (!media::IsYuvPlanar(frame_->format()) ||
|
| // TODO(rileya): Skia currently doesn't support YUVA conversion. Remove
|
| // this case once it does. As-is we will fall back on the pure-software
|
| // path in this case.
|
| @@ -239,9 +213,9 @@ class VideoImageGenerator : public SkImageGenerator {
|
| }
|
|
|
| if (color_space) {
|
| - if (CheckColorSpace(frame_, COLOR_SPACE_JPEG))
|
| + if (CheckColorSpace(frame_.get(), COLOR_SPACE_JPEG))
|
| *color_space = kJPEG_SkYUVColorSpace;
|
| - else if (CheckColorSpace(frame_, COLOR_SPACE_HD_REC709))
|
| + else if (CheckColorSpace(frame_.get(), COLOR_SPACE_HD_REC709))
|
| *color_space = kRec709_SkYUVColorSpace;
|
| else
|
| *color_space = kRec601_SkYUVColorSpace;
|
| @@ -302,23 +276,15 @@ class VideoImageGenerator : public SkImageGenerator {
|
| };
|
|
|
| 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_(
|
| + : last_image_deleting_timer_(
|
| FROM_HERE,
|
| base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay),
|
| this,
|
| - &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) {
|
| - last_frame_.setIsVolatile(true);
|
| -}
|
| + &SkCanvasVideoRenderer::ResetCache) {}
|
|
|
| -SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
|
| +SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {
|
| + ResetCache();
|
| +}
|
|
|
| void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
|
| SkCanvas* canvas,
|
| @@ -347,91 +313,32 @@ void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
|
| return;
|
| }
|
|
|
| - SkBitmap* target_frame = nullptr;
|
| + gpu::gles2::GLES2Interface* gl = context_3d.gl;
|
|
|
| - if (video_frame->HasTextures()) {
|
| - // 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);
|
| + if (!last_image_ || video_frame->timestamp() != last_timestamp_) {
|
| + ResetCache();
|
| + // Generate a new image.
|
| + // Note: Skia will hold onto |video_frame| via |video_generator| only when
|
| + // |video_frame| is software.
|
| + // Holding |video_frame| longer than this call when using GPUVideoDecoder
|
| + // could cause problems since the pool of VideoFrames has a fixed size.
|
| + if (video_frame->HasTextures()) {
|
| DCHECK(context_3d.gr_context);
|
| - if (accelerated_generator_) {
|
| - // Reset SkBitmap used in SWVideo-to-HWCanvas path.
|
| - accelerated_last_frame_.reset();
|
| - accelerated_generator_ = nullptr;
|
| - }
|
| -
|
| + DCHECK(gl);
|
| if (media::VideoFrame::NumPlanes(video_frame->format()) == 1) {
|
| - accelerated_last_image_.reset();
|
| - if (!CopyVideoFrameSingleTextureToSkBitmap(
|
| - 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());
|
| + last_image_ =
|
| + NewSkImageFromVideoFrameNative(video_frame.get(), context_3d);
|
| } else {
|
| - accelerated_last_image_ = CreateSkImageFromVideoFrameYUVTextures(
|
| - video_frame.get(), context_3d);
|
| - DCHECK(accelerated_last_image_);
|
| + last_image_ =
|
| + NewSkImageFromVideoFrameYUVTextures(video_frame.get(), context_3d);
|
| }
|
| - accelerated_last_frame_timestamp_ = video_frame->timestamp();
|
| + } else {
|
| + auto video_generator = new VideoImageGenerator(video_frame);
|
| + last_image_ = skia::AdoptRef(SkImage::NewFromGenerator(video_generator));
|
| }
|
| - target_frame = &accelerated_last_frame_;
|
| - accelerated_frame_deleting_timer_.Reset();
|
| - } else if (canvas->getGrContext()) {
|
| - 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();
|
| - } else {
|
| - // Draw SW Video on SW Canvas.
|
| - DCHECK(video_frame->IsMappable());
|
| - 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();
|
| + last_timestamp_ = video_frame->timestamp();
|
| }
|
| + last_image_deleting_timer_.Reset();
|
|
|
| paint.setXfermodeMode(mode);
|
| paint.setFilterQuality(kLow_SkFilterQuality);
|
| @@ -468,23 +375,26 @@ void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
|
| gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width());
|
| }
|
| canvas->scale(
|
| - SkFloatToScalar(rotated_dest_size.width() / target_frame->width()),
|
| - SkFloatToScalar(rotated_dest_size.height() / target_frame->height()));
|
| - canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f),
|
| - -SkFloatToScalar(target_frame->height() * 0.5f));
|
| - }
|
| - if (accelerated_last_image_) {
|
| - canvas->drawImage(accelerated_last_image_.get(), 0, 0, &paint);
|
| - } else {
|
| - canvas->drawBitmap(*target_frame, 0, 0, &paint);
|
| + SkFloatToScalar(rotated_dest_size.width() / last_image_->width()),
|
| + SkFloatToScalar(rotated_dest_size.height() / last_image_->height()));
|
| + canvas->translate(-SkFloatToScalar(last_image_->width() * 0.5f),
|
| + -SkFloatToScalar(last_image_->height() * 0.5f));
|
| }
|
| + canvas->drawImage(last_image_.get(), 0, 0, &paint);
|
| +
|
| if (need_transform)
|
| canvas->restore();
|
| + // Make sure to flush so we can remove the videoframe from the generator.
|
| 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);
|
| +
|
| + if (!ShouldCacheVideoFrameSkImage(video_frame.get()))
|
| + ResetCache();
|
| +
|
| + if (video_frame->HasTextures()) {
|
| + DCHECK(gl);
|
| + SyncPointClientImpl client(gl);
|
| + video_frame->UpdateReleaseSyncPoint(&client);
|
| + }
|
| }
|
|
|
| void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame,
|
| @@ -496,7 +406,7 @@ void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame,
|
|
|
| // static
|
| void SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
|
| - const scoped_refptr<VideoFrame>& video_frame,
|
| + const VideoFrame* video_frame,
|
| void* rgb_pixels,
|
| size_t row_bytes) {
|
| if (!video_frame->IsMappable()) {
|
| @@ -644,7 +554,8 @@ void SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
|
| 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 ||
|
| - mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES);
|
| + mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES)
|
| + << mailbox_holder.texture_target;
|
|
|
| gl->WaitSyncPointCHROMIUM(mailbox_holder.sync_point);
|
| uint32 source_texture = gl->CreateAndConsumeTextureCHROMIUM(
|
| @@ -667,16 +578,10 @@ void SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
|
| video_frame->UpdateReleaseSyncPoint(&client);
|
| }
|
|
|
| -void SkCanvasVideoRenderer::ResetLastFrame() {
|
| - last_frame_.reset();
|
| - last_frame_timestamp_ = media::kNoTimestamp();
|
| -}
|
| -
|
| -void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() {
|
| - accelerated_last_image_.reset();
|
| - accelerated_last_frame_.reset();
|
| - accelerated_generator_ = nullptr;
|
| - accelerated_last_frame_timestamp_ = media::kNoTimestamp();
|
| +void SkCanvasVideoRenderer::ResetCache() {
|
| + // Clear cached values.
|
| + last_image_ = nullptr;
|
| + last_timestamp_ = kNoTimestamp();
|
| }
|
|
|
| } // namespace media
|
|
|