| Index: media/filters/skcanvas_video_renderer.cc
|
| diff --git a/media/filters/skcanvas_video_renderer.cc b/media/filters/skcanvas_video_renderer.cc
|
| index 062e317b71da03476438e156bb2f5d2cedc47b00..cb5793022ae349b79e2ccbc231686497ddcd7a12 100644
|
| --- a/media/filters/skcanvas_video_renderer.cc
|
| +++ b/media/filters/skcanvas_video_renderer.cc
|
| @@ -5,10 +5,16 @@
|
| #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/gpu/GrContext.h"
|
| +#include "third_party/skia/include/gpu/SkGrPixelRef.h"
|
|
|
| // Skia internal format depends on a platform. On Android it is ABGR, on others
|
| // it is ARGB.
|
| @@ -26,7 +32,9 @@
|
|
|
| namespace media {
|
|
|
| -static bool IsYUV(media::VideoFrame::Format format) {
|
| +namespace {
|
| +
|
| +bool IsYUV(media::VideoFrame::Format format) {
|
| return format == media::VideoFrame::YV12 ||
|
| format == media::VideoFrame::YV16 ||
|
| format == media::VideoFrame::I420 ||
|
| @@ -35,16 +43,15 @@ static bool IsYUV(media::VideoFrame::Format format) {
|
| format == media::VideoFrame::YV24;
|
| }
|
|
|
| -static bool IsYUVOrNative(media::VideoFrame::Format format) {
|
| +bool IsYUVOrNative(media::VideoFrame::Format format) {
|
| return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE;
|
| }
|
|
|
| // Converts a VideoFrame containing YUV data to a SkBitmap containing RGB data.
|
| //
|
| // |bitmap| will be (re)allocated to match the dimensions of |video_frame|.
|
| -static void ConvertVideoFrameToBitmap(
|
| - const scoped_refptr<media::VideoFrame>& video_frame,
|
| - SkBitmap* bitmap) {
|
| +void ConvertVideoFrameToBitmap(media::VideoFrame* video_frame,
|
| + SkBitmap* bitmap) {
|
| DCHECK(IsYUVOrNative(video_frame->format()))
|
| << video_frame->format();
|
| if (IsYUV(video_frame->format())) {
|
| @@ -179,16 +186,95 @@ static void ConvertVideoFrameToBitmap(
|
| bitmap->unlockPixels();
|
| }
|
|
|
| +bool EnsureTextureBackedSkBitmap(GrContext* gr,
|
| + SkBitmap* bitmap,
|
| + const gfx::Size& size,
|
| + GrSurfaceOrigin origin,
|
| + GrPixelConfig config) {
|
| + if (!bitmap->getTexture() || bitmap->width() != size.width() ||
|
| + bitmap->height() != size.height()) {
|
| + if (!gr)
|
| + return false;
|
| + GrTextureDesc desc;
|
| + desc.fConfig = config;
|
| + desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
|
| + desc.fSampleCnt = 0;
|
| + desc.fOrigin = origin;
|
| + desc.fWidth = size.width();
|
| + desc.fHeight = size.height();
|
| + skia::RefPtr<GrTexture> texture;
|
| + texture = skia::AdoptRef(gr->createUncachedTexture(desc, 0, 0));
|
| + if (!texture.get())
|
| + return false;
|
| +
|
| + SkImageInfo info = SkImageInfo::MakeN32Premul(desc.fWidth, desc.fHeight);
|
| + SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (info, texture.get()));
|
| + if (!pixelRef)
|
| + return false;
|
| + bitmap->setInfo(info);
|
| + bitmap->setPixelRef(pixelRef)->unref();
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool ConvertVideoFrameToTexture(
|
| + VideoFrame* video_frame,
|
| + SkBitmap* bitmap,
|
| + SkCanvasVideoRenderer::ContextProvider* context_provider) {
|
| + DCHECK(context_provider &&
|
| + video_frame->format() == VideoFrame::NATIVE_TEXTURE);
|
| + gpu::gles2::GLES2Interface* gl = context_provider->gl;
|
| + DCHECK(gl);
|
| +
|
| + // 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 (!EnsureTextureBackedSkBitmap(context_provider->gr_context,
|
| + bitmap,
|
| + video_frame->visible_rect().size(),
|
| + kTopLeft_GrSurfaceOrigin,
|
| + kSkia8888_GrPixelConfig)) {
|
| + return false;
|
| + }
|
| +
|
| + unsigned textureId =
|
| + static_cast<unsigned>((bitmap->getTexture())->getTextureHandle());
|
| + SkCanvasVideoRenderer::CopyVideoFrameToTexture(
|
| + gl, video_frame, textureId, 0, GL_RGBA, GL_UNSIGNED_BYTE, true, false);
|
| + return true;
|
| +}
|
| +
|
| +class SyncPointClientImpl : public VideoFrame::SyncPointClient {
|
| + public:
|
| + explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
|
| + virtual ~SyncPointClientImpl() {}
|
| + virtual uint32 InsertSyncPoint() OVERRIDE {
|
| + return gl_->InsertSyncPointCHROMIUM();
|
| + }
|
| + virtual void WaitSyncPoint(uint32 sync_point) OVERRIDE {
|
| + gl_->WaitSyncPointCHROMIUM(sync_point);
|
| + }
|
| +
|
| + private:
|
| + gpu::gles2::GLES2Interface* gl_;
|
| +};
|
| +
|
| +} // anonymous namespace
|
| +
|
| SkCanvasVideoRenderer::SkCanvasVideoRenderer()
|
| - : last_frame_timestamp_(media::kNoTimestamp()) {
|
| + : last_frame_timestamp_(media::kNoTimestamp()),
|
| + accelerated_last_frame_timestamp_(media::kNoTimestamp()) {
|
| }
|
|
|
| SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
|
|
|
| -void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame,
|
| - SkCanvas* canvas,
|
| - const gfx::RectF& dest_rect,
|
| - uint8 alpha) {
|
| +void SkCanvasVideoRenderer::Paint(
|
| + media::VideoFrame* video_frame,
|
| + SkCanvas* canvas,
|
| + const gfx::RectF& dest_rect,
|
| + uint8 alpha,
|
| + SkCanvasVideoRenderer::ContextProvider* context_provider) {
|
| if (alpha == 0) {
|
| return;
|
| }
|
| @@ -201,14 +287,36 @@ void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame,
|
|
|
| // Paint black rectangle if there isn't a frame available or the
|
| // frame has an unexpected format.
|
| - if (!video_frame || !IsYUVOrNative(video_frame->format())) {
|
| + if (!video_frame || video_frame->natural_size().IsEmpty() ||
|
| + !IsYUVOrNative(video_frame->format())) {
|
| canvas->drawRect(dest, paint);
|
| return;
|
| }
|
|
|
| + bool accelerated = false;
|
| + if (context_provider &&
|
| + video_frame->format() == media::VideoFrame::NATIVE_TEXTURE &&
|
| + canvas->getGrContext()) {
|
| + // TODO(dshwang): Android video decoder doesn't update the timestamp on a
|
| + // VideoFrame. To reduce redundant copy, Android should update the
|
| + // timestamp.
|
| + if (accelerated_last_frame_.isNull() ||
|
| + video_frame->timestamp() != accelerated_last_frame_timestamp_ ||
|
| + video_frame->timestamp() == base::TimeDelta()) {
|
| + accelerated = ConvertVideoFrameToTexture(
|
| + video_frame, &accelerated_last_frame_, context_provider);
|
| + if (accelerated) {
|
| + accelerated_last_frame_timestamp_ = video_frame->timestamp();
|
| + }
|
| + } else {
|
| + DCHECK(accelerated_last_frame_.getTexture());
|
| + accelerated = true;
|
| + }
|
| + }
|
| +
|
| // Check if we should convert and update |last_frame_|.
|
| - if (last_frame_.isNull() ||
|
| - video_frame->timestamp() != last_frame_timestamp_) {
|
| + if (!accelerated && (last_frame_.isNull() ||
|
| + video_frame->timestamp() != last_frame_timestamp_)) {
|
| ConvertVideoFrameToBitmap(video_frame, &last_frame_);
|
| last_frame_timestamp_ = video_frame->timestamp();
|
| }
|
| @@ -217,9 +325,64 @@ void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame,
|
| // this means we don't need the extra cost of clearing the buffer first.
|
| paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode));
|
|
|
| - // Paint using |last_frame_|.
|
| - paint.setFilterLevel(SkPaint::kLow_FilterLevel);
|
| - canvas->drawBitmapRect(last_frame_, NULL, dest, &paint);
|
| + canvas->drawBitmapRect(
|
| + accelerated ? accelerated_last_frame_ : last_frame_, NULL, dest, &paint);
|
| +
|
| + CleanUpTemporaryBuffers();
|
| +}
|
| +
|
| +// If a buffer is not used by 3 sec, remove it.
|
| +void SkCanvasVideoRenderer::CleanUpTemporaryBuffers() {
|
| + static const base::TimeDelta buffer_time = base::TimeDelta::FromSeconds(3);
|
| + base::TimeDelta last_timestamp =
|
| + accelerated_last_frame_timestamp_ > last_frame_timestamp_
|
| + ? accelerated_last_frame_timestamp_
|
| + : last_frame_timestamp_;
|
| + if (last_timestamp > last_frame_timestamp_ + buffer_time &&
|
| + !last_frame_.isNull())
|
| + last_frame_.reset();
|
| + if (last_timestamp > accelerated_last_frame_timestamp_ + buffer_time &&
|
| + !accelerated_last_frame_.isNull())
|
| + accelerated_last_frame_.reset();
|
| +}
|
| +
|
| +// static
|
| +void SkCanvasVideoRenderer::CopyVideoFrameToTexture(
|
| + 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);
|
| }
|
|
|
| } // namespace media
|
|
|