summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authordongseong.hwang <dongseong.hwang@intel.com>2015-01-08 12:11:13 -0800
committerCommit bot <commit-bot@chromium.org>2015-01-08 20:12:02 +0000
commit0c4e9d8781aea6e52fdb4a7aee978817910c67ea (patch)
treef7028dbfd1cba76dacf55f9f0389c4752c6fbd8c /media
parentd2386aadf6c8350dabff4f820f2b73f44c487caa (diff)
downloadchromium_src-0c4e9d8781aea6e52fdb4a7aee978817910c67ea.zip
chromium_src-0c4e9d8781aea6e52fdb4a7aee978817910c67ea.tar.gz
chromium_src-0c4e9d8781aea6e52fdb4a7aee978817910c67ea.tar.bz2
media: Optimize HW Video to 2D Canvas copy.
Currently, when we draws GPU decoded Video on accelerated 2D Canvas, chromium reads back pixel from GPU and then uploads the pixel to GPU to make a SkBitmap. It's so inefficient for both speed and battery. On the other hand, only Android copies GPU-GPU in this case, but Android doesn't have cache mechanism which SkCanvasVideoRenderer provides. This CL makes all platforms copy gpu-gpu with cache mechanism. Cache mechanism is useful when 2d canvas draws a video frame many times. e.g. http://craftymind.com/factory/html5video/CanvasVideo.html In addition, fix white video background on Android when not loaded. Other platforms draw black background thanks to SkCanvasVideoRenderer::Paint(). In detail of the changes; 1. Implement gpu-gpu copy in SkCanvasVideoRenderer::Paint() like previous WebMediaPlayerAndroid::paint(). 2. Move duplicated GPU code on WebMediaPlayerImpl and WebMediaPlayerAndroid to SkCanvasVideoRenderer. Perf data on i5 IvyBridge blink_perf.all:Canvas_draw-video-to-hw-accelerated-canvas-2d 15.8x speed up: 116.27 runs/s -> 1847.23 runs/s NOTE: measure after disabling cache in SkCanvasVideoRenderer BUG=401058, 263667 Review URL: https://codereview.chromium.org/445013002 Cr-Commit-Position: refs/heads/master@{#310577}
Diffstat (limited to 'media')
-rw-r--r--media/BUILD.gn1
-rw-r--r--media/DEPS1
-rw-r--r--media/blink/BUILD.gn1
-rw-r--r--media/blink/DEPS5
-rw-r--r--media/blink/media_blink.gyp1
-rw-r--r--media/blink/webmediaplayer_impl.cc87
-rw-r--r--media/blink/webmediaplayer_impl.h7
-rw-r--r--media/blink/webmediaplayer_params.cc4
-rw-r--r--media/blink/webmediaplayer_params.h16
-rw-r--r--media/filters/context_3d.h36
-rw-r--r--media/filters/skcanvas_video_renderer.cc167
-rw-r--r--media/filters/skcanvas_video_renderer.h18
-rw-r--r--media/filters/skcanvas_video_renderer_unittest.cc11
-rw-r--r--media/media.gyp1
14 files changed, 254 insertions, 102 deletions
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 893a63e..706ef55 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -100,6 +100,7 @@ component("media") {
"filters/audio_renderer_impl.h",
"filters/chunk_demuxer.cc",
"filters/chunk_demuxer.h",
+ "filters/context_3d.h",
"filters/decoder_selector.cc",
"filters/decoder_selector.h",
"filters/decoder_stream.cc",
diff --git a/media/DEPS b/media/DEPS
index f9c29be..57eecbb 100644
--- a/media/DEPS
+++ b/media/DEPS
@@ -2,6 +2,7 @@
include_rules = [
"+gpu",
"+jni",
+ "+skia/ext",
"+third_party/ffmpeg",
"+third_party/libvpx",
"+third_party/libyuv",
diff --git a/media/blink/BUILD.gn b/media/blink/BUILD.gn
index f9f5f98..46fecb0 100644
--- a/media/blink/BUILD.gn
+++ b/media/blink/BUILD.gn
@@ -8,6 +8,7 @@ component("blink") {
"//base",
"//cc",
"//cc/blink",
+ "//gpu/blink",
"//media",
"//media:shared_memory_support",
"//net",
diff --git a/media/blink/DEPS b/media/blink/DEPS
index 5c05816..e92dacb 100644
--- a/media/blink/DEPS
+++ b/media/blink/DEPS
@@ -1,10 +1,11 @@
include_rules = [
+ "+cc/blink/web_layer_impl.h",
"+cc/layers/video_frame_provider.h",
"+cc/layers/video_layer.h",
- "+cc/blink/web_layer_impl.h",
+ "+gpu/blink",
"+media",
"+net/base",
"+net/http",
"+third_party/WebKit/public/platform",
"+third_party/WebKit/public/web",
-] \ No newline at end of file
+]
diff --git a/media/blink/media_blink.gyp b/media/blink/media_blink.gyp
index 35d0480..71751fe 100644
--- a/media/blink/media_blink.gyp
+++ b/media/blink/media_blink.gyp
@@ -12,6 +12,7 @@
'../../base/base.gyp:base',
'../../cc/cc.gyp:cc',
'../../cc/blink/cc_blink.gyp:cc_blink',
+ '../../gpu/blink/gpu_blink.gyp:gpu_blink',
'../../ui/gfx/gfx.gyp:gfx_geometry',
'../../net/net.gyp:net',
'../../third_party/WebKit/public/blink.gyp:blink',
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index f138f0e..4b67c9d 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -22,8 +22,7 @@
#include "base/synchronization/waitable_event.h"
#include "cc/blink/web_layer_impl.h"
#include "cc/layers/video_layer.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "gpu/blink/webgraphicscontext3d_impl.h"
#include "media/audio/null_audio_sink.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/cdm_context.h"
@@ -39,7 +38,6 @@
#include "media/blink/webcontentdecryptionmodule_impl.h"
#include "media/blink/webinbandtexttrack_impl.h"
#include "media/blink/webmediaplayer_delegate.h"
-#include "media/blink/webmediaplayer_params.h"
#include "media/blink/webmediaplayer_util.h"
#include "media/blink/webmediasource_impl.h"
#include "media/filters/chunk_demuxer.h"
@@ -81,23 +79,6 @@ namespace {
const double kMinRate = 0.0625;
const double kMaxRate = 16.0;
-class SyncPointClientImpl : public media::VideoFrame::SyncPointClient {
- public:
- explicit SyncPointClientImpl(
- blink::WebGraphicsContext3D* web_graphics_context)
- : web_graphics_context_(web_graphics_context) {}
- ~SyncPointClientImpl() override {}
- uint32 InsertSyncPoint() override {
- return web_graphics_context_->insertSyncPoint();
- }
- void WaitSyncPoint(uint32 sync_point) override {
- web_graphics_context_->waitSyncPoint(sync_point);
- }
-
- private:
- blink::WebGraphicsContext3D* web_graphics_context_;
-};
-
} // namespace
namespace media {
@@ -153,6 +134,7 @@ WebMediaPlayerImpl::WebMediaPlayerImpl(
client_(client),
delegate_(delegate),
defer_load_cb_(params.defer_load_cb()),
+ context_3d_cb_(params.context_3d_cb()),
supports_save_(true),
chunk_demuxer_(NULL),
compositor_task_runner_(params.compositor_task_runner()),
@@ -538,13 +520,18 @@ void WebMediaPlayerImpl::paint(blink::WebCanvas* canvas,
GetCurrentFrameFromCompositor();
gfx::Rect gfx_rect(rect);
-
- skcanvas_video_renderer_.Paint(video_frame,
- canvas,
- gfx_rect,
- alpha,
- mode,
- pipeline_metadata_.video_rotation);
+ Context3D context_3d;
+ if (video_frame.get() &&
+ video_frame->format() == VideoFrame::NATIVE_TEXTURE) {
+ if (!context_3d_cb_.is_null()) {
+ context_3d = context_3d_cb_.Run();
+ }
+ // GPU Process crashed.
+ if (!context_3d.gl)
+ return;
+ }
+ skcanvas_video_renderer_.Paint(video_frame, canvas, gfx_rect, alpha, mode,
+ pipeline_metadata_.video_rotation, context_3d);
}
bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const {
@@ -604,43 +591,19 @@ bool WebMediaPlayerImpl::copyVideoTextureToPlatformTexture(
scoped_refptr<VideoFrame> video_frame =
GetCurrentFrameFromCompositor();
- if (!video_frame.get())
- return false;
- if (video_frame->format() != VideoFrame::NATIVE_TEXTURE)
- return false;
-
- const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
- if (mailbox_holder->texture_target != GL_TEXTURE_2D)
+ if (!video_frame.get() ||
+ video_frame->format() != VideoFrame::NATIVE_TEXTURE) {
return false;
+ }
- web_graphics_context->waitSyncPoint(mailbox_holder->sync_point);
- uint32 source_texture = web_graphics_context->createAndConsumeTextureCHROMIUM(
- GL_TEXTURE_2D, mailbox_holder->mailbox.name);
-
- // The video is stored in a unmultiplied format, so premultiply
- // if necessary.
- web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
- premultiply_alpha);
- // Application itself needs to take care of setting the right flip_y
- // value down to get the expected result.
- // flip_y==true means to reverse the video orientation while
- // flip_y==false means to keep the intrinsic orientation.
- web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
- web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D,
- source_texture,
- texture,
- level,
- internal_format,
- type);
- web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
- web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM,
- false);
-
- web_graphics_context->deleteTexture(source_texture);
- web_graphics_context->flush();
-
- SyncPointClientImpl client(web_graphics_context);
- video_frame->UpdateReleaseSyncPoint(&client);
+ // TODO(dshwang): need more elegant way to convert WebGraphicsContext3D to
+ // GLES2Interface.
+ gpu::gles2::GLES2Interface* gl =
+ static_cast<gpu_blink::WebGraphicsContext3DImpl*>(web_graphics_context)
+ ->GetGLInterface();
+ SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture(
+ gl, video_frame.get(), texture, level, internal_format, type,
+ premultiply_alpha, flip_y);
return true;
}
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 07f1932..db540f6 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -23,15 +23,16 @@
#include "media/blink/buffered_data_source_host_impl.h"
#include "media/blink/encrypted_media_player_support.h"
#include "media/blink/video_frame_compositor.h"
+#include "media/blink/webmediaplayer_params.h"
#include "media/filters/skcanvas_video_renderer.h"
#include "third_party/WebKit/public/platform/WebAudioSourceProvider.h"
#include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h"
-#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
#include "third_party/WebKit/public/platform/WebMediaPlayer.h"
#include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
#include "url/gurl.h"
namespace blink {
+class WebGraphicsContext3D;
class WebLocalFrame;
}
@@ -52,7 +53,6 @@ class MediaLog;
class VideoFrameCompositor;
class WebAudioSourceProviderImpl;
class WebMediaPlayerDelegate;
-class WebMediaPlayerParams;
class WebTextTrackImpl;
// The canonical implementation of blink::WebMediaPlayer that's backed by
@@ -278,7 +278,8 @@ class MEDIA_EXPORT WebMediaPlayerImpl
base::WeakPtr<WebMediaPlayerDelegate> delegate_;
- base::Callback<void(const base::Closure&)> defer_load_cb_;
+ WebMediaPlayerParams::DeferLoadCB defer_load_cb_;
+ WebMediaPlayerParams::Context3DCB context_3d_cb_;
// Routes audio playback to either AudioRendererSink or WebAudio.
scoped_refptr<WebAudioSourceProviderImpl> audio_source_provider_;
diff --git a/media/blink/webmediaplayer_params.cc b/media/blink/webmediaplayer_params.cc
index b7382ec..df7f5b9 100644
--- a/media/blink/webmediaplayer_params.cc
+++ b/media/blink/webmediaplayer_params.cc
@@ -11,17 +11,19 @@
namespace media {
WebMediaPlayerParams::WebMediaPlayerParams(
- const base::Callback<void(const base::Closure&)>& defer_load_cb,
+ const DeferLoadCB& defer_load_cb,
const scoped_refptr<AudioRendererSink>& audio_renderer_sink,
const scoped_refptr<MediaLog>& media_log,
const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
+ const Context3DCB& context_3d_cb,
blink::WebContentDecryptionModule* initial_cdm)
: defer_load_cb_(defer_load_cb),
audio_renderer_sink_(audio_renderer_sink),
media_log_(media_log),
media_task_runner_(media_task_runner),
compositor_task_runner_(compositor_task_runner),
+ context_3d_cb_(context_3d_cb),
initial_cdm_(initial_cdm) {
}
diff --git a/media/blink/webmediaplayer_params.h b/media/blink/webmediaplayer_params.h
index 121ee14..6893ed6 100644
--- a/media/blink/webmediaplayer_params.h
+++ b/media/blink/webmediaplayer_params.h
@@ -8,6 +8,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "media/base/media_export.h"
+#include "media/filters/context_3d.h"
namespace base {
class SingleThreadTaskRunner;
@@ -28,22 +29,22 @@ class MediaLog;
class MEDIA_EXPORT WebMediaPlayerParams {
public:
typedef base::Callback<void(const base::Closure&)> DeferLoadCB;
+ typedef base::Callback<Context3D()> Context3DCB;
- // |defer_load_cb|, |audio_renderer_sink|, and |compositor_task_runner| may be
- // null.
+ // |defer_load_cb|, |audio_renderer_sink|, |compositor_task_runner|, and
+ // |context_3d_cb| may be null.
WebMediaPlayerParams(
const DeferLoadCB& defer_load_cb,
const scoped_refptr<AudioRendererSink>& audio_renderer_sink,
const scoped_refptr<MediaLog>& media_log,
const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
+ const Context3DCB& context_3d,
blink::WebContentDecryptionModule* initial_cdm);
~WebMediaPlayerParams();
- base::Callback<void(const base::Closure&)> defer_load_cb() const {
- return defer_load_cb_;
- }
+ DeferLoadCB defer_load_cb() const { return defer_load_cb_; }
const scoped_refptr<AudioRendererSink>& audio_renderer_sink() const {
return audio_renderer_sink_;
@@ -66,12 +67,15 @@ class MEDIA_EXPORT WebMediaPlayerParams {
return initial_cdm_;
}
+ Context3DCB context_3d_cb() const { return context_3d_cb_; }
+
private:
- base::Callback<void(const base::Closure&)> defer_load_cb_;
+ DeferLoadCB defer_load_cb_;
scoped_refptr<AudioRendererSink> audio_renderer_sink_;
scoped_refptr<MediaLog> media_log_;
scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+ Context3DCB context_3d_cb_;
blink::WebContentDecryptionModule* initial_cdm_;
DISALLOW_IMPLICIT_CONSTRUCTORS(WebMediaPlayerParams);
diff --git a/media/filters/context_3d.h b/media/filters/context_3d.h
new file mode 100644
index 0000000..ccb0c7c
--- /dev/null
+++ b/media/filters/context_3d.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_FILTERS_CONTEXT_3D_H_
+#define MEDIA_FILTERS_CONTEXT_3D_H_
+
+class GrContext;
+
+namespace gpu {
+namespace gles2 {
+class GLES2Interface;
+}
+}
+
+namespace media {
+
+// This struct can be used to make media use gpu::gles2::GLES2Interface and
+// GrContext.
+// Usage:
+// gpu::gles2::GLES2Interface* gl = ...;
+// GrContext* gr_context = ...;
+// Context3D context_3d(gl, gr_context);
+
+struct Context3D {
+ Context3D() : gl(nullptr), gr_context(nullptr) {}
+ Context3D(gpu::gles2::GLES2Interface* gl_, class GrContext* gr_context_)
+ : gl(gl_), gr_context(gr_context_) {}
+
+ gpu::gles2::GLES2Interface* gl;
+ class GrContext* gr_context;
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_CONTEXT_3D_H_
diff --git a/media/filters/skcanvas_video_renderer.cc b/media/filters/skcanvas_video_renderer.cc
index 2243445..35cc94e 100644
--- a/media/filters/skcanvas_video_renderer.cc
+++ b/media/filters/skcanvas_video_renderer.cc
@@ -4,13 +4,17 @@
#include "media/filters/skcanvas_video_renderer.h"
-#include "base/logging.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
#include "media/base/video_frame.h"
#include "media/base/yuv_convert.h"
+#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/SkImageGenerator.h"
#include "third_party/skia/include/gpu/GrContext.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
@@ -214,6 +218,79 @@ void ConvertVideoFrameToRGBPixels(
}
}
+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_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+ desc.fSampleCnt = 0;
+ desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+ desc.fWidth = size.width();
+ desc.fHeight = size.height();
+ skia::RefPtr<GrTexture> texture = skia::AdoptRef(
+ gr->refScratchTexture(desc, GrContext::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, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ true, false);
+ return true;
+}
+
+class SyncPointClientImpl : public VideoFrame::SyncPointClient {
+ public:
+ explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
+ ~SyncPointClientImpl() override {}
+ uint32 InsertSyncPoint() override { return gl_->InsertSyncPointCHROMIUM(); }
+ void WaitSyncPoint(uint32 sync_point) override {
+ gl_->WaitSyncPointCHROMIUM(sync_point);
+ }
+
+ private:
+ gpu::gles2::GLES2Interface* gl_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SyncPointClientImpl);
+};
+
} // anonymous namespace
// Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU.
@@ -306,7 +383,7 @@ SkCanvasVideoRenderer::SkCanvasVideoRenderer()
base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay),
this,
&SkCanvasVideoRenderer::ResetLastFrame),
- accelerated_generator_(NULL),
+ accelerated_generator_(nullptr),
accelerated_last_frame_timestamp_(media::kNoTimestamp()),
accelerated_frame_deleting_timer_(
FROM_HERE,
@@ -323,7 +400,8 @@ void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
const gfx::RectF& dest_rect,
uint8 alpha,
SkXfermode::Mode mode,
- VideoRotation video_rotation) {
+ VideoRotation video_rotation,
+ const Context3D& context_3d) {
if (alpha == 0) {
return;
}
@@ -343,16 +421,30 @@ void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
return;
}
- SkBitmap* target_frame = NULL;
+ SkBitmap* target_frame = nullptr;
if (canvas->getGrContext()) {
if (accelerated_last_frame_.isNull() ||
video_frame->timestamp() != accelerated_last_frame_timestamp_) {
- accelerated_generator_ = new VideoImageGenerator(video_frame);
-
- // Note: This takes ownership of |accelerated_generator_|.
- if (!SkInstallDiscardablePixelRef(accelerated_generator_,
- &accelerated_last_frame_)) {
- NOTREACHED();
+ if (video_frame->format() == VideoFrame::NATIVE_TEXTURE) {
+ DCHECK(context_3d.gl);
+ DCHECK(context_3d.gr_context);
+ // Draw HW Video on HW Canvas.
+ DCHECK(!accelerated_generator_);
+ if (!CopyVideoFrameTextureToSkBitmapTexture(
+ video_frame.get(), &accelerated_last_frame_, context_3d)) {
+ NOTREACHED();
+ return;
+ }
+ } else {
+ // Draw SW Video on HW Canvas.
+ 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() &&
@@ -360,13 +452,13 @@ void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
accelerated_last_frame_.height());
accelerated_last_frame_timestamp_ = video_frame->timestamp();
- } else {
+ } else if (accelerated_generator_) {
accelerated_generator_->set_frame(video_frame);
}
target_frame = &accelerated_last_frame_;
accelerated_frame_deleting_timer_.Reset();
} else {
- // Check if we should convert and update |last_frame_|.
+ // Draw both SW and HW Video on SW Canvas.
if (last_frame_.isNull() ||
video_frame->timestamp() != last_frame_timestamp_) {
// Check if |bitmap| needs to be (re)allocated.
@@ -434,18 +526,53 @@ void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
canvas->flush();
// SkCanvas::flush() causes the generator to generate SkImage, so delete
// |video_frame| not to be outlived.
- if (canvas->getGrContext())
- accelerated_generator_->set_frame(NULL);
+ if (canvas->getGrContext() && accelerated_generator_)
+ accelerated_generator_->set_frame(nullptr);
}
void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame,
SkCanvas* canvas) {
- Paint(video_frame,
- canvas,
- video_frame->visible_rect(),
- 0xff,
- SkXfermode::kSrc_Mode,
- media::VIDEO_ROTATION_0);
+ Paint(video_frame, canvas, video_frame->visible_rect(), 0xff,
+ SkXfermode::kSrc_Mode, media::VIDEO_ROTATION_0, Context3D());
+}
+
+// static
+void SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture(
+ gpu::gles2::GLES2Interface* gl,
+ VideoFrame* video_frame,
+ unsigned int texture,
+ unsigned int level,
+ unsigned int internal_format,
+ unsigned int type,
+ bool premultiply_alpha,
+ bool flip_y) {
+ DCHECK(video_frame && video_frame->format() == VideoFrame::NATIVE_TEXTURE);
+ const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
+ DCHECK(mailbox_holder->texture_target == GL_TEXTURE_2D ||
+ mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES);
+
+ gl->WaitSyncPointCHROMIUM(mailbox_holder->sync_point);
+ uint32 source_texture = gl->CreateAndConsumeTextureCHROMIUM(
+ mailbox_holder->texture_target, mailbox_holder->mailbox.name);
+
+ // The video is stored in a unmultiplied format, so premultiply
+ // if necessary.
+ gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, premultiply_alpha);
+ // Application itself needs to take care of setting the right |flip_y|
+ // value down to get the expected result.
+ // "flip_y == true" means to reverse the video orientation while
+ // "flip_y == false" means to keep the intrinsic orientation.
+ gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
+ gl->CopyTextureCHROMIUM(GL_TEXTURE_2D, source_texture, texture, level,
+ internal_format, type);
+ gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
+ gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false);
+
+ gl->DeleteTextures(1, &source_texture);
+ gl->Flush();
+
+ SyncPointClientImpl client(gl);
+ video_frame->UpdateReleaseSyncPoint(&client);
}
void SkCanvasVideoRenderer::ResetLastFrame() {
diff --git a/media/filters/skcanvas_video_renderer.h b/media/filters/skcanvas_video_renderer.h
index f9cb5ef..8aa148af 100644
--- a/media/filters/skcanvas_video_renderer.h
+++ b/media/filters/skcanvas_video_renderer.h
@@ -10,6 +10,7 @@
#include "base/timer/timer.h"
#include "media/base/media_export.h"
#include "media/base/video_rotation.h"
+#include "media/filters/context_3d.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkXfermode.h"
#include "ui/gfx/geometry/rect.h"
@@ -30,6 +31,8 @@ class MEDIA_EXPORT SkCanvasVideoRenderer {
// Paints |video_frame| on |canvas|, scaling and rotating the result to fit
// dimensions specified by |dest_rect|.
+ // If the format of |video_frame| is VideoFrame::NATIVE_TEXTURE and |canvas|
+ // is ganeshed, |context_3d| must be provided.
//
// Black will be painted on |canvas| if |video_frame| is null.
void Paint(const scoped_refptr<VideoFrame>& video_frame,
@@ -37,11 +40,24 @@ class MEDIA_EXPORT SkCanvasVideoRenderer {
const gfx::RectF& dest_rect,
uint8 alpha,
SkXfermode::Mode mode,
- VideoRotation video_rotation);
+ VideoRotation video_rotation,
+ const Context3D& context_3d);
// Copy |video_frame| on |canvas|.
void Copy(const scoped_refptr<VideoFrame>&, SkCanvas* canvas);
+ // Copy the contents of texture of |video_frame| to texture |texture|.
+ // |level|, |internal_format|, |type| specify target texture |texture|.
+ // The format of |video_frame| must be VideoFrame::NATIVE_TEXTURE.
+ static void CopyVideoFrameTextureToGLTexture(gpu::gles2::GLES2Interface* gl,
+ VideoFrame* video_frame,
+ unsigned int texture,
+ unsigned int level,
+ unsigned int internal_format,
+ unsigned int type,
+ bool premultiply_alpha,
+ bool flip_y);
+
private:
void ResetLastFrame();
void ResetAcceleratedLastFrame();
diff --git a/media/filters/skcanvas_video_renderer_unittest.cc b/media/filters/skcanvas_video_renderer_unittest.cc
index 8e1ba59..a596508 100644
--- a/media/filters/skcanvas_video_renderer_unittest.cc
+++ b/media/filters/skcanvas_video_renderer_unittest.cc
@@ -197,12 +197,8 @@ SkCanvasVideoRendererTest::SkCanvasVideoRendererTest()
SkCanvasVideoRendererTest::~SkCanvasVideoRendererTest() {}
void SkCanvasVideoRendererTest::PaintWithoutFrame(SkCanvas* canvas) {
- renderer_.Paint(NULL,
- canvas,
- kNaturalRect,
- 0xFF,
- SkXfermode::kSrcOver_Mode,
- VIDEO_ROTATION_0);
+ renderer_.Paint(nullptr, canvas, kNaturalRect, 0xFF,
+ SkXfermode::kSrcOver_Mode, VIDEO_ROTATION_0, Context3D());
}
void SkCanvasVideoRendererTest::Paint(
@@ -237,7 +233,8 @@ void SkCanvasVideoRendererTest::PaintRotated(
media::FillYUV(video_frame.get(), 29, 255, 107);
break;
}
- renderer_.Paint(video_frame, canvas, dest_rect, 0xFF, mode, video_rotation);
+ renderer_.Paint(video_frame, canvas, dest_rect, 0xFF, mode, video_rotation,
+ Context3D());
}
void SkCanvasVideoRendererTest::Copy(
diff --git a/media/media.gyp b/media/media.gyp
index 44cfbe8..e5490b1 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -419,6 +419,7 @@
'filters/blocking_url_protocol.h',
'filters/chunk_demuxer.cc',
'filters/chunk_demuxer.h',
+ 'filters/context_3d.h',
'filters/decoder_selector.cc',
'filters/decoder_selector.h',
'filters/decoder_stream.cc',