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" |
| 17 #include "third_party/skia/include/gpu/GrTextureProvider.h" | 18 #include "third_party/skia/include/gpu/GrTextureProvider.h" |
| 18 #include "third_party/skia/include/gpu/SkGrPixelRef.h" | |
| 19 #include "ui/gfx/skbitmap_operations.h" | |
| 20 | 19 |
| 21 // Skia internal format depends on a platform. On Android it is ABGR, on others | 20 // Skia internal format depends on a platform. On Android it is ABGR, on others |
| 22 // it is ARGB. | 21 // it is ARGB. |
| 23 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ | 22 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ |
| 24 SK_A32_SHIFT == 24 | 23 SK_A32_SHIFT == 24 |
| 25 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB | 24 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB |
| 26 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB | 25 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB |
| 27 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ | 26 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ |
| 28 SK_A32_SHIFT == 24 | 27 SK_A32_SHIFT == 24 |
| 29 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR | 28 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR |
| 30 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR | 29 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR |
| 31 #else | 30 #else |
| 32 #error Unexpected Skia ARGB_8888 layout! | 31 #error Unexpected Skia ARGB_8888 layout! |
| 33 #endif | 32 #endif |
| 34 | 33 |
| 35 namespace media { | 34 namespace media { |
| 36 | 35 |
| 37 namespace { | 36 namespace { |
| 38 | 37 |
| 39 // This class keeps two temporary resources; software bitmap, hardware bitmap. | |
| 40 // If both bitmap are created and then only software bitmap is updated every | |
| 41 // frame, hardware bitmap outlives until the media player dies. So we delete | |
| 42 // a temporary resource if it is not used for 3 sec. | |
| 43 const int kTemporaryResourceDeletionDelay = 3; // Seconds; | |
| 44 | |
| 45 bool CheckColorSpace(const scoped_refptr<VideoFrame>& video_frame, | 38 bool CheckColorSpace(const scoped_refptr<VideoFrame>& video_frame, |
| 46 VideoFrame::ColorSpace color_space) { | 39 VideoFrame::ColorSpace color_space) { |
| 47 int result; | 40 int result; |
| 48 return video_frame->metadata()->GetInteger( | 41 return video_frame->metadata()->GetInteger( |
| 49 media::VideoFrameMetadata::COLOR_SPACE, &result) && | 42 media::VideoFrameMetadata::COLOR_SPACE, &result) && |
| 50 result == color_space; | 43 result == color_space; |
| 51 } | 44 } |
| 52 | 45 |
| 53 bool IsSkBitmapProperlySizedTexture(const SkBitmap* bitmap, | |
| 54 const gfx::Size& size) { | |
| 55 return bitmap->getTexture() && bitmap->width() == size.width() && | |
| 56 bitmap->height() == size.height(); | |
| 57 } | |
| 58 | |
| 59 bool AllocateSkBitmapTexture(GrContext* gr, | |
| 60 SkBitmap* bitmap, | |
| 61 const gfx::Size& size) { | |
| 62 DCHECK(gr); | |
| 63 GrTextureDesc desc; | |
| 64 // Use kRGBA_8888_GrPixelConfig, not kSkia8888_GrPixelConfig, to avoid | |
| 65 // RGBA to BGRA conversion. | |
| 66 desc.fConfig = kRGBA_8888_GrPixelConfig; | |
| 67 desc.fFlags = kRenderTarget_GrSurfaceFlag; | |
| 68 desc.fSampleCnt = 0; | |
| 69 desc.fOrigin = kTopLeft_GrSurfaceOrigin; | |
| 70 desc.fWidth = size.width(); | |
| 71 desc.fHeight = size.height(); | |
| 72 skia::RefPtr<GrTexture> texture = skia::AdoptRef( | |
| 73 gr->textureProvider()->refScratchTexture( | |
| 74 desc, GrTextureProvider::kExact_ScratchTexMatch)); | |
| 75 if (!texture.get()) | |
| 76 return false; | |
| 77 | |
| 78 SkImageInfo info = SkImageInfo::MakeN32Premul(desc.fWidth, desc.fHeight); | |
| 79 SkGrPixelRef* pixel_ref = SkNEW_ARGS(SkGrPixelRef, (info, texture.get())); | |
| 80 if (!pixel_ref) | |
| 81 return false; | |
| 82 bitmap->setInfo(info); | |
| 83 bitmap->setPixelRef(pixel_ref)->unref(); | |
| 84 return true; | |
| 85 } | |
| 86 | |
| 87 bool CopyVideoFrameTextureToSkBitmapTexture(VideoFrame* video_frame, | |
| 88 SkBitmap* bitmap, | |
| 89 const Context3D& context_3d) { | |
| 90 // Check if we could reuse existing texture based bitmap. | |
| 91 // Otherwise, release existing texture based bitmap and allocate | |
| 92 // a new one based on video size. | |
| 93 if (!IsSkBitmapProperlySizedTexture(bitmap, | |
| 94 video_frame->visible_rect().size())) { | |
| 95 if (!AllocateSkBitmapTexture(context_3d.gr_context, bitmap, | |
| 96 video_frame->visible_rect().size())) { | |
| 97 return false; | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 unsigned texture_id = | |
| 102 static_cast<unsigned>((bitmap->getTexture())->getTextureHandle()); | |
| 103 // If CopyVideoFrameTextureToGLTexture() changes the state of the | |
| 104 // |texture_id|, it's needed to invalidate the state cached in skia, | |
| 105 // but currently the state isn't changed. | |
| 106 SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture( | |
| 107 context_3d.gl, video_frame, texture_id, GL_RGBA, GL_UNSIGNED_BYTE, true, | |
| 108 false); | |
| 109 bitmap->notifyPixelsChanged(); | |
| 110 return true; | |
| 111 } | |
| 112 | |
| 113 class SyncPointClientImpl : public VideoFrame::SyncPointClient { | 46 class SyncPointClientImpl : public VideoFrame::SyncPointClient { |
| 114 public: | 47 public: |
| 115 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} | 48 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} |
| 116 ~SyncPointClientImpl() override {} | 49 ~SyncPointClientImpl() override {} |
| 117 uint32 InsertSyncPoint() override { return gl_->InsertSyncPointCHROMIUM(); } | 50 uint32 InsertSyncPoint() override { return gl_->InsertSyncPointCHROMIUM(); } |
| 118 void WaitSyncPoint(uint32 sync_point) override { | 51 void WaitSyncPoint(uint32 sync_point) override { |
| 119 gl_->WaitSyncPointCHROMIUM(sync_point); | 52 gl_->WaitSyncPointCHROMIUM(sync_point); |
| 120 } | 53 } |
| 121 | 54 |
| 122 private: | 55 private: |
| 123 gpu::gles2::GLES2Interface* gl_; | 56 gpu::gles2::GLES2Interface* gl_; |
| 124 | 57 |
| 125 DISALLOW_IMPLICIT_CONSTRUCTORS(SyncPointClientImpl); | 58 DISALLOW_IMPLICIT_CONSTRUCTORS(SyncPointClientImpl); |
| 126 }; | 59 }; |
| 127 | 60 |
| 61 // Creates a SkImage from a |video_frame| backed by native resources. | |
| 62 // The SkImage will not take ownership of the underlying resources and the | |
| 63 // caller will have to delete them after the SkImage is used. | |
| 64 // |source_textures| is a pointer to an array that will be filled with | |
| 65 // texture ids backing the returned image. | |
| 66 skia::RefPtr<SkImage> NewSkImageFromVideoFrameNative( | |
| 67 const scoped_refptr<VideoFrame>& video_frame, | |
| 68 const Context3D& context_3d, | |
| 69 unsigned* source_textures) { | |
| 70 // TODO(dcastagna): Add support for YUV420. | |
| 71 DCHECK_EQ(VideoFrame::ARGB, video_frame->format()); | |
| 72 | |
| 73 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0); | |
| 74 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || | |
| 75 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB || | |
| 76 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES) | |
| 77 << mailbox_holder.texture_target; | |
| 78 | |
| 79 gpu::gles2::GLES2Interface* gl = context_3d.gl; | |
| 80 if (mailbox_holder.texture_target != GL_TEXTURE_2D) { | |
| 81 // TODO(dcastagna): At the moment Skia doesn't support targets different | |
| 82 // than GL_TEXTURE_2D. Avoid this copy once | |
| 83 // https://code.google.com/p/skia/issues/detail?id=3868 is addressed. | |
| 84 gl->GenTextures(1, source_textures); | |
| 85 DCHECK(*source_textures); | |
| 86 gl->BindTexture(GL_TEXTURE_2D, *source_textures); | |
| 87 SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture( | |
| 88 gl, video_frame.get(), *source_textures, GL_RGBA, GL_UNSIGNED_BYTE, | |
| 89 true, false); | |
| 90 } else { | |
| 91 gl->WaitSyncPointCHROMIUM(mailbox_holder.sync_point); | |
| 92 *source_textures = gl->CreateAndConsumeTextureCHROMIUM( | |
| 93 mailbox_holder.texture_target, mailbox_holder.mailbox.name); | |
|
dshwang
2015/06/05 18:58:16
Following two lines is needed to insert sync point
dshwang
2015/06/05 19:08:41
ah, I found you insert sync point at the end of dr
dshwang
2015/06/08 12:21:43
In this case, this class should not cache the text
Daniele Castagna
2015/06/09 23:21:11
The lifetime of the texture id is currently not we
dshwang
2015/06/10 07:17:39
I guess final solution would be to not cache. why
| |
| 94 } | |
| 95 GrBackendTextureDesc desc; | |
| 96 desc.fOrigin = kTopLeft_GrSurfaceOrigin; | |
| 97 desc.fWidth = video_frame->coded_size().width(); | |
| 98 desc.fHeight = video_frame->coded_size().height(); | |
| 99 desc.fConfig = kRGBA_8888_GrPixelConfig; | |
| 100 desc.fTextureHandle = *source_textures; | |
| 101 return skia::AdoptRef(SkImage::NewFromTexture(context_3d.gr_context, desc)); | |
| 102 } | |
| 103 | |
| 128 } // anonymous namespace | 104 } // anonymous namespace |
| 129 | 105 |
| 130 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. | 106 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. |
| 131 class VideoImageGenerator : public SkImageGenerator { | 107 class VideoImageGenerator : public SkImageGenerator { |
| 132 public: | 108 public: |
| 133 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) | 109 VideoImageGenerator(VideoFrame* frame) |
| 134 : SkImageGenerator( | 110 : SkImageGenerator( |
| 135 SkImageInfo::MakeN32Premul(frame->visible_rect().width(), | 111 SkImageInfo::MakeN32Premul(frame->visible_rect().width(), |
| 136 frame->visible_rect().height())) | 112 frame->visible_rect().height())), |
| 137 , frame_(frame) { | 113 frame_(frame) {} |
| 138 DCHECK(frame_.get()); | |
| 139 } | |
| 140 ~VideoImageGenerator() override {} | 114 ~VideoImageGenerator() override {} |
| 141 | 115 |
| 142 void set_frame(const scoped_refptr<VideoFrame>& frame) { frame_ = frame; } | 116 void set_frame(VideoFrame* frame) { frame_ = frame; } |
| 143 | 117 |
| 144 protected: | 118 protected: |
| 145 Result onGetPixels(const SkImageInfo& info, | 119 Result onGetPixels(const SkImageInfo& info, |
| 146 void* pixels, | 120 void* pixels, |
| 147 size_t row_bytes, | 121 size_t row_bytes, |
| 148 SkPMColor ctable[], | 122 SkPMColor ctable[], |
| 149 int* ctable_count) override { | 123 int* ctable_count) override { |
| 150 if (!frame_.get()) | 124 if (!frame_) |
| 151 return kInvalidInput; | 125 return kInvalidInput; |
| 152 // If skia couldn't do the YUV conversion on GPU, we will on CPU. | 126 // If skia couldn't do the YUV conversion on GPU, we will on CPU. |
| 153 SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels( | 127 SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels( |
| 154 frame_, pixels, row_bytes); | 128 frame_, pixels, row_bytes); |
| 155 return kSuccess; | 129 return kSuccess; |
| 156 } | 130 } |
| 157 | 131 |
| 158 bool onGetYUV8Planes(SkISize sizes[3], | 132 bool onGetYUV8Planes(SkISize sizes[3], |
| 159 void* planes[3], | 133 void* planes[3], |
| 160 size_t row_bytes[3], | 134 size_t row_bytes[3], |
| 161 SkYUVColorSpace* color_space) override { | 135 SkYUVColorSpace* color_space) override { |
| 162 if (!frame_.get() || !media::VideoFrame::IsYuvPlanar(frame_->format()) || | 136 if (!frame_ || !media::VideoFrame::IsYuvPlanar(frame_->format()) || |
| 163 // TODO(rileya): Skia currently doesn't support Rec709 YUV conversion, | 137 // TODO(rileya): Skia currently doesn't support Rec709 YUV conversion, |
| 164 // or YUVA conversion. Remove this case once it does. As-is we will | 138 // or YUVA conversion. Remove this case once it does. As-is we will |
| 165 // fall back on the pure-software path in this case. | 139 // fall back on the pure-software path in this case. |
| 166 CheckColorSpace(frame_, VideoFrame::COLOR_SPACE_HD_REC709) || | 140 CheckColorSpace(frame_, VideoFrame::COLOR_SPACE_HD_REC709) || |
| 167 frame_->format() == VideoFrame::YV12A) { | 141 frame_->format() == VideoFrame::YV12A) { |
| 168 return false; | 142 return false; |
| 169 } | 143 } |
| 170 | 144 |
| 171 if (color_space) { | 145 if (color_space) { |
| 172 if (CheckColorSpace(frame_, VideoFrame::COLOR_SPACE_JPEG)) | 146 if (CheckColorSpace(frame_, VideoFrame::COLOR_SPACE_JPEG)) |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 218 in_line += in_line_stride; | 192 in_line += in_line_stride; |
| 219 out_line += out_line_stride; | 193 out_line += out_line_stride; |
| 220 } | 194 } |
| 221 } | 195 } |
| 222 } | 196 } |
| 223 } | 197 } |
| 224 return true; | 198 return true; |
| 225 } | 199 } |
| 226 | 200 |
| 227 private: | 201 private: |
| 228 scoped_refptr<VideoFrame> frame_; | 202 VideoFrame* frame_; |
| 229 | 203 |
| 230 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); | 204 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); |
| 231 }; | 205 }; |
| 232 | 206 |
| 233 SkCanvasVideoRenderer::SkCanvasVideoRenderer() | 207 SkCanvasVideoRenderer::SkCanvasVideoRenderer() { |
| 234 : last_frame_timestamp_(media::kNoTimestamp()), | |
| 235 frame_deleting_timer_( | |
|
dshwang
2015/06/05 19:08:41
I think it's needed.
e.g. if some canvas draws vid
Daniele Castagna
2015/06/05 22:05:32
SkImage caches the result internally and I'd assum
dshwang
2015/06/08 12:21:43
3 second timer was introduced to eagerly remove ca
Daniele Castagna
2015/06/09 23:21:11
My rationale for removing the delaytimer is that n
dshwang
2015/06/10 07:17:39
How will you remove cache for software path?
| |
| 236 FROM_HERE, | |
| 237 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), | |
| 238 this, | |
| 239 &SkCanvasVideoRenderer::ResetLastFrame), | |
| 240 accelerated_generator_(nullptr), | |
| 241 accelerated_last_frame_timestamp_(media::kNoTimestamp()), | |
| 242 accelerated_frame_deleting_timer_( | |
| 243 FROM_HERE, | |
| 244 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), | |
| 245 this, | |
| 246 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) { | |
| 247 last_frame_.setIsVolatile(true); | |
| 248 } | 208 } |
| 249 | 209 |
| 250 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} | 210 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() { |
| 211 ResetCache(); | |
| 212 } | |
| 251 | 213 |
| 252 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, | 214 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, |
| 253 SkCanvas* canvas, | 215 SkCanvas* canvas, |
| 254 const gfx::RectF& dest_rect, | 216 const gfx::RectF& dest_rect, |
| 255 uint8 alpha, | 217 uint8 alpha, |
| 256 SkXfermode::Mode mode, | 218 SkXfermode::Mode mode, |
| 257 VideoRotation video_rotation, | 219 VideoRotation video_rotation, |
| 258 const Context3D& context_3d) { | 220 const Context3D& context_3d) { |
| 259 if (alpha == 0) { | 221 if (alpha == 0) { |
| 260 return; | 222 return; |
| 261 } | 223 } |
| 262 | 224 |
| 263 SkRect dest; | 225 SkRect dest; |
| 264 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); | 226 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); |
| 265 | 227 |
| 266 SkPaint paint; | 228 SkPaint paint; |
| 267 paint.setAlpha(alpha); | 229 paint.setAlpha(alpha); |
| 268 | 230 |
| 269 // Paint black rectangle if there isn't a frame available or the | 231 // Paint black rectangle if there isn't a frame available or the |
| 270 // frame has an unexpected format. | 232 // frame has an unexpected format. |
| 271 if (!video_frame.get() || video_frame->natural_size().IsEmpty() || | 233 if (!video_frame.get() || video_frame->natural_size().IsEmpty() || |
| 272 !(media::VideoFrame::IsYuvPlanar(video_frame->format()) || | 234 !(media::VideoFrame::IsYuvPlanar(video_frame->format()) || |
| 273 (video_frame->storage_type() == media::VideoFrame::STORAGE_TEXTURE))) { | 235 (video_frame->storage_type() == media::VideoFrame::STORAGE_TEXTURE))) { |
| 274 canvas->drawRect(dest, paint); | 236 canvas->drawRect(dest, paint); |
| 275 canvas->flush(); | 237 canvas->flush(); |
| 276 return; | 238 return; |
| 277 } | 239 } |
| 278 | 240 |
| 279 SkBitmap* target_frame = nullptr; | 241 gpu::gles2::GLES2Interface* gl = context_3d.gl; |
| 280 | 242 |
| 281 if (video_frame->storage_type() == VideoFrame::STORAGE_TEXTURE) { | 243 // This might be wrong, two different videos could be drawn to the same |
| 282 // Draw HW Video on both SW and HW Canvas. | 244 // canvas and the two different videoframes could have the same timestamp. |
| 283 // In SW Canvas case, rely on skia drawing Ganesh SkBitmap on SW SkCanvas. | 245 if (last_image_ && video_frame->timestamp() == last_timestamp_) { |
| 284 if (accelerated_last_frame_.isNull() || | 246 // |last_image_| can be reused. |
| 285 video_frame->timestamp() != accelerated_last_frame_timestamp_) { | 247 // |video_generator_| will be set only if the last cache videoframe was |
| 286 DCHECK(context_3d.gl); | 248 // a software videoframe. |
| 249 if (video_generator_) | |
| 250 video_generator_->set_frame(video_frame.get()); | |
| 251 } else { | |
| 252 ResetCache(); | |
| 253 // Generate a new image. | |
| 254 if (video_frame->storage_type() == VideoFrame::STORAGE_TEXTURE) { | |
| 287 DCHECK(context_3d.gr_context); | 255 DCHECK(context_3d.gr_context); |
| 288 if (accelerated_generator_) { | 256 DCHECK(gl); |
| 289 // Reset SkBitmap used in SWVideo-to-HWCanvas path. | 257 last_image_ = NewSkImageFromVideoFrameNative(video_frame, context_3d, |
| 290 accelerated_last_frame_.reset(); | 258 &last_texture_id_); |
| 291 accelerated_generator_ = nullptr; | 259 last_gl_ = gl; |
| 292 } | 260 } else { |
| 293 if (!CopyVideoFrameTextureToSkBitmapTexture( | 261 video_generator_ = new VideoImageGenerator(video_frame.get()); |
| 294 video_frame.get(), &accelerated_last_frame_, context_3d)) { | 262 last_image_ = skia::AdoptRef(SkImage::NewFromGenerator(video_generator_)); |
| 295 NOTREACHED(); | |
| 296 return; | |
| 297 } | |
| 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(); | |
| 304 } | 263 } |
| 305 target_frame = &accelerated_last_frame_; | 264 last_timestamp_ = video_frame->timestamp(); |
| 306 accelerated_frame_deleting_timer_.Reset(); | |
| 307 } else if (canvas->getGrContext()) { | |
| 308 if (accelerated_last_frame_.isNull() || | |
| 309 video_frame->timestamp() != accelerated_last_frame_timestamp_) { | |
| 310 // Draw SW Video on HW Canvas. | |
| 311 if (!accelerated_generator_ && !accelerated_last_frame_.isNull()) { | |
| 312 // Reset SkBitmap used in HWVideo-to-HWCanvas path. | |
| 313 accelerated_last_frame_.reset(); | |
| 314 } | |
| 315 accelerated_generator_ = new VideoImageGenerator(video_frame); | |
| 316 | |
| 317 // Note: This takes ownership of |accelerated_generator_|. | |
| 318 if (!SkInstallDiscardablePixelRef(accelerated_generator_, | |
| 319 &accelerated_last_frame_)) { | |
| 320 NOTREACHED(); | |
| 321 return; | |
| 322 } | |
| 323 DCHECK(video_frame->visible_rect().width() == | |
| 324 accelerated_last_frame_.width() && | |
| 325 video_frame->visible_rect().height() == | |
| 326 accelerated_last_frame_.height()); | |
| 327 | |
| 328 accelerated_last_frame_timestamp_ = video_frame->timestamp(); | |
| 329 } else if (accelerated_generator_) { | |
| 330 accelerated_generator_->set_frame(video_frame); | |
| 331 } | |
| 332 target_frame = &accelerated_last_frame_; | |
| 333 accelerated_frame_deleting_timer_.Reset(); | |
| 334 } else { | |
| 335 // Draw SW Video on SW Canvas. | |
| 336 DCHECK(VideoFrame::IsMappable(video_frame->storage_type())); | |
| 337 if (last_frame_.isNull() || | |
| 338 video_frame->timestamp() != last_frame_timestamp_) { | |
| 339 // Check if |bitmap| needs to be (re)allocated. | |
| 340 if (last_frame_.isNull() || | |
| 341 last_frame_.width() != video_frame->visible_rect().width() || | |
| 342 last_frame_.height() != video_frame->visible_rect().height()) { | |
| 343 last_frame_.allocN32Pixels(video_frame->visible_rect().width(), | |
| 344 video_frame->visible_rect().height()); | |
| 345 last_frame_.setIsVolatile(true); | |
| 346 } | |
| 347 last_frame_.lockPixels(); | |
| 348 ConvertVideoFrameToRGBPixels( | |
| 349 video_frame, last_frame_.getPixels(), last_frame_.rowBytes()); | |
|
dshwang
2015/06/08 12:21:43
does VideoGenerator way have same performance to c
| |
| 350 last_frame_.notifyPixelsChanged(); | |
| 351 last_frame_.unlockPixels(); | |
| 352 last_frame_timestamp_ = video_frame->timestamp(); | |
| 353 } | |
| 354 target_frame = &last_frame_; | |
| 355 frame_deleting_timer_.Reset(); | |
| 356 } | 265 } |
| 357 | 266 |
| 358 paint.setXfermodeMode(mode); | 267 paint.setXfermodeMode(mode); |
| 359 paint.setFilterQuality(kLow_SkFilterQuality); | 268 paint.setFilterQuality(kLow_SkFilterQuality); |
| 360 | 269 |
| 361 bool need_transform = | 270 bool need_transform = |
| 362 video_rotation != VIDEO_ROTATION_0 || | 271 video_rotation != VIDEO_ROTATION_0 || |
| 363 dest_rect.size() != video_frame->visible_rect().size() || | 272 dest_rect.size() != video_frame->visible_rect().size() || |
| 364 !dest_rect.origin().IsOrigin(); | 273 !dest_rect.origin().IsOrigin(); |
| 365 if (need_transform) { | 274 if (need_transform) { |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 383 } | 292 } |
| 384 canvas->rotate(angle); | 293 canvas->rotate(angle); |
| 385 | 294 |
| 386 gfx::SizeF rotated_dest_size = dest_rect.size(); | 295 gfx::SizeF rotated_dest_size = dest_rect.size(); |
| 387 if (video_rotation == VIDEO_ROTATION_90 || | 296 if (video_rotation == VIDEO_ROTATION_90 || |
| 388 video_rotation == VIDEO_ROTATION_270) { | 297 video_rotation == VIDEO_ROTATION_270) { |
| 389 rotated_dest_size = | 298 rotated_dest_size = |
| 390 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); | 299 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); |
| 391 } | 300 } |
| 392 canvas->scale( | 301 canvas->scale( |
| 393 SkFloatToScalar(rotated_dest_size.width() / target_frame->width()), | 302 SkFloatToScalar(rotated_dest_size.width() / last_image_->width()), |
| 394 SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); | 303 SkFloatToScalar(rotated_dest_size.height() / last_image_->height())); |
| 395 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), | 304 canvas->translate(-SkFloatToScalar(last_image_->width() * 0.5f), |
| 396 -SkFloatToScalar(target_frame->height() * 0.5f)); | 305 -SkFloatToScalar(last_image_->height() * 0.5f)); |
| 397 } | 306 } |
| 398 canvas->drawBitmap(*target_frame, 0, 0, &paint); | 307 |
| 308 canvas->drawImage(last_image_.get(), 0, 0, &paint); | |
| 399 if (need_transform) | 309 if (need_transform) |
| 400 canvas->restore(); | 310 canvas->restore(); |
| 311 // Make sure to flush so we can remove the videoframe from the generator. | |
| 401 canvas->flush(); | 312 canvas->flush(); |
| 402 // SkCanvas::flush() causes the generator to generate SkImage, so delete | 313 |
| 403 // |video_frame| not to be outlived. | 314 if (last_texture_id_) { |
| 404 if (canvas->getGrContext() && accelerated_generator_) | 315 SyncPointClientImpl client(gl); |
| 405 accelerated_generator_->set_frame(nullptr); | 316 // Make sure the video_frame can go back to the pool after the native |
| 317 // texture has been used for drawing. | |
| 318 video_frame->UpdateReleaseSyncPoint(&client); | |
|
dshwang
2015/06/08 12:21:43
It's needed only for GL_TEXTURE_2D case.
Daniele Castagna
2015/06/09 23:21:11
That's true, but considering that eventually it wi
dshwang
2015/06/10 07:17:39
Acknowledged.
| |
| 319 } | |
| 320 | |
| 321 // TODO(dcastagna): here we're assuming |video_generator_| will still be valid | |
| 322 // after SkImage::NewFromGenerator took the ownership. | |
| 323 // Fix this once https://code.google.com/p/skia/issues/detail?id=3870 is | |
| 324 // addressed. | |
| 325 if (video_generator_) | |
| 326 video_generator_->set_frame(nullptr); | |
| 406 } | 327 } |
| 407 | 328 |
| 408 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, | 329 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, |
| 409 SkCanvas* canvas, | 330 SkCanvas* canvas, |
| 410 const Context3D& context_3d) { | 331 const Context3D& context_3d) { |
| 411 Paint(video_frame, canvas, video_frame->visible_rect(), 0xff, | 332 Paint(video_frame, canvas, video_frame->visible_rect(), 0xff, |
| 412 SkXfermode::kSrc_Mode, media::VIDEO_ROTATION_0, context_3d); | 333 SkXfermode::kSrc_Mode, media::VIDEO_ROTATION_0, context_3d); |
| 413 } | 334 } |
| 414 | 335 |
| 415 // static | 336 // static |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 556 unsigned int type, | 477 unsigned int type, |
| 557 bool premultiply_alpha, | 478 bool premultiply_alpha, |
| 558 bool flip_y) { | 479 bool flip_y) { |
| 559 DCHECK(video_frame); | 480 DCHECK(video_frame); |
| 560 DCHECK_EQ(video_frame->storage_type(), VideoFrame::STORAGE_TEXTURE); | 481 DCHECK_EQ(video_frame->storage_type(), VideoFrame::STORAGE_TEXTURE); |
| 561 DCHECK_EQ(1u, VideoFrame::NumPlanes(video_frame->format())); | 482 DCHECK_EQ(1u, VideoFrame::NumPlanes(video_frame->format())); |
| 562 | 483 |
| 563 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0); | 484 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0); |
| 564 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || | 485 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || |
| 565 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB || | 486 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB || |
| 566 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES); | 487 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES) |
| 488 << mailbox_holder.texture_target; | |
| 567 | 489 |
| 568 gl->WaitSyncPointCHROMIUM(mailbox_holder.sync_point); | 490 gl->WaitSyncPointCHROMIUM(mailbox_holder.sync_point); |
| 569 uint32 source_texture = gl->CreateAndConsumeTextureCHROMIUM( | 491 uint32 source_texture = gl->CreateAndConsumeTextureCHROMIUM( |
| 570 mailbox_holder.texture_target, mailbox_holder.mailbox.name); | 492 mailbox_holder.texture_target, mailbox_holder.mailbox.name); |
| 571 | 493 |
| 572 // The video is stored in a unmultiplied format, so premultiply | 494 // The video is stored in a unmultiplied format, so premultiply |
| 573 // if necessary. | 495 // if necessary. |
| 574 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, premultiply_alpha); | 496 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, premultiply_alpha); |
| 575 // Application itself needs to take care of setting the right |flip_y| | 497 // Application itself needs to take care of setting the right |flip_y| |
| 576 // value down to get the expected result. | 498 // value down to get the expected result. |
| 577 // "flip_y == true" means to reverse the video orientation while | 499 // "flip_y == true" means to reverse the video orientation while |
| 578 // "flip_y == false" means to keep the intrinsic orientation. | 500 // "flip_y == false" means to keep the intrinsic orientation. |
| 579 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y); | 501 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y); |
| 580 gl->CopyTextureCHROMIUM(GL_TEXTURE_2D, source_texture, texture, | 502 gl->CopyTextureCHROMIUM(GL_TEXTURE_2D, source_texture, texture, |
| 581 internal_format, type); | 503 internal_format, type); |
| 582 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false); | 504 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false); |
| 583 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false); | 505 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false); |
| 584 | 506 |
| 585 gl->DeleteTextures(1, &source_texture); | 507 gl->DeleteTextures(1, &source_texture); |
| 586 gl->Flush(); | 508 gl->Flush(); |
| 587 | 509 |
| 588 SyncPointClientImpl client(gl); | 510 SyncPointClientImpl client(gl); |
| 589 video_frame->UpdateReleaseSyncPoint(&client); | 511 video_frame->UpdateReleaseSyncPoint(&client); |
| 590 } | 512 } |
| 591 | 513 |
| 592 void SkCanvasVideoRenderer::ResetLastFrame() { | 514 void SkCanvasVideoRenderer::ResetCache() { |
| 593 last_frame_.reset(); | 515 // Clear cached values. |
| 594 last_frame_timestamp_ = media::kNoTimestamp(); | 516 video_generator_ = nullptr; |
| 595 } | 517 last_image_ = nullptr; |
| 596 | 518 last_timestamp_ = kNoTimestamp(); |
| 597 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { | 519 if (last_texture_id_) { |
| 598 accelerated_last_frame_.reset(); | 520 DCHECK(last_gl_); |
| 599 accelerated_generator_ = nullptr; | 521 last_gl_->DeleteTextures(1, &last_texture_id_); |
| 600 accelerated_last_frame_timestamp_ = media::kNoTimestamp(); | 522 last_texture_id_ = 0; |
| 523 } | |
| 601 } | 524 } |
| 602 | 525 |
| 603 } // namespace media | 526 } // namespace media |
| OLD | NEW |