Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(363)

Side by Side Diff: media/filters/skcanvas_video_renderer.cc

Issue 445013002: media: Optimize HW Video to 2D Canvas copy. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Resolve comments, rebase to ToT Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/filters/skcanvas_video_renderer.h" 5 #include "media/filters/skcanvas_video_renderer.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "gpu/GLES2/gl2extchromium.h"
9 #include "gpu/command_buffer/client/gles2_interface.h"
10 #include "gpu/command_buffer/common/mailbox_holder.h"
8 #include "media/base/video_frame.h" 11 #include "media/base/video_frame.h"
9 #include "media/base/yuv_convert.h" 12 #include "media/base/yuv_convert.h"
13 #include "skia/ext/refptr.h"
10 #include "third_party/libyuv/include/libyuv.h" 14 #include "third_party/libyuv/include/libyuv.h"
11 #include "third_party/skia/include/core/SkCanvas.h"
12 #include "third_party/skia/include/core/SkImageGenerator.h" 15 #include "third_party/skia/include/core/SkImageGenerator.h"
13 #include "third_party/skia/include/gpu/GrContext.h" 16 #include "third_party/skia/include/gpu/GrContext.h"
17 #include "third_party/skia/include/gpu/SkGrPixelRef.h"
14 #include "ui/gfx/skbitmap_operations.h" 18 #include "ui/gfx/skbitmap_operations.h"
15 19
16 // 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
17 // it is ARGB. 21 // it is ARGB.
18 #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 && \
19 SK_A32_SHIFT == 24 23 SK_A32_SHIFT == 24
20 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB 24 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB
21 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB 25 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB
22 #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 && \
23 SK_A32_SHIFT == 24 27 SK_A32_SHIFT == 24
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
207 row_bytes); 211 row_bytes);
208 video_frame->ReadPixelsFromNativeTexture(tmp); 212 video_frame->ReadPixelsFromNativeTexture(tmp);
209 break; 213 break;
210 } 214 }
211 default: 215 default:
212 NOTREACHED(); 216 NOTREACHED();
213 break; 217 break;
214 } 218 }
215 } 219 }
216 220
221 bool IsSkBitmapProperlySizedTexture(const SkBitmap* bitmap,
222 const gfx::Size& size) {
223 return bitmap->getTexture() && bitmap->width() == size.width() &&
224 bitmap->height() == size.height();
225 }
226
227 bool AllocateSkBitmapTexture(GrContext* gr,
228 SkBitmap* bitmap,
229 const gfx::Size& size) {
230 DCHECK(gr);
231 GrTextureDesc desc;
232 // Use kRGBA_8888_GrPixelConfig, not kSkia8888_GrPixelConfig, because
233 // glCopyTextureChromium doesn't support GL_BGRA_EXT as internal format.
234 desc.fConfig = kRGBA_8888_GrPixelConfig;
235 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
236 desc.fSampleCnt = 0;
237 desc.fOrigin = kTopLeft_GrSurfaceOrigin;
238 desc.fWidth = size.width();
239 desc.fHeight = size.height();
240 skia::RefPtr<GrTexture> texture = skia::AdoptRef(
241 gr->refScratchTexture(desc, GrContext::kExact_ScratchTexMatch));
242 if (!texture.get())
243 return false;
244
245 SkImageInfo info = SkImageInfo::MakeN32Premul(desc.fWidth, desc.fHeight);
246 SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (info, texture.get()));
scherkus (not reviewing) 2014/10/30 20:40:12 pixelRef -> pixel_ref
dshwang 2014/10/31 09:29:17 oops, Thank you.
247 if (!pixelRef)
248 return false;
249 bitmap->setInfo(info);
250 bitmap->setPixelRef(pixelRef)->unref();
251 return true;
252 }
253
254 bool CopyVideoFrameTextureToSkBitmapTexture(
255 VideoFrame* video_frame,
scherkus (not reviewing) 2014/10/30 20:40:13 const scoped_refptr<VideoFrame>&
dshwang 2014/10/31 09:29:17 not needed because it doesn't take a ref
256 SkBitmap* bitmap,
257 const Context3DProvider& context_provider) {
258 // Check if we could reuse existing texture based bitmap.
259 // Otherwise, release existing texture based bitmap and allocate
260 // a new one based on video size.
261 if (!IsSkBitmapProperlySizedTexture(bitmap,
262 video_frame->visible_rect().size())) {
263 if (!AllocateSkBitmapTexture(context_provider.gr_context,
264 bitmap,
265 video_frame->visible_rect().size())) {
266 return false;
267 }
268 }
269
270 unsigned texture_id =
271 static_cast<unsigned>((bitmap->getTexture())->getTextureHandle());
272 // If CopyVideoFrameToTexture() changes the state of the |texture_id|, it's
scherkus (not reviewing) 2014/10/30 20:40:12 "CopyVideoFrameToTexture" doesn't refer to a funct
dshwang 2014/10/31 09:29:17 Thank you!
273 // needed to invalidate the state cached in skia, but currently the state
274 // isn't changed.
275 SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture(context_provider.gl,
276 video_frame,
277 texture_id,
278 0,
279 GL_RGBA,
280 GL_UNSIGNED_BYTE,
281 true,
282 false);
283 return true;
284 }
285
286 class SyncPointClientImpl : public VideoFrame::SyncPointClient {
287 public:
288 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
289 virtual ~SyncPointClientImpl() {}
scherkus (not reviewing) 2014/10/30 20:40:12 s/virtual/override/ everywhere
dshwang 2014/10/31 09:29:17 interesting. Done.
290 virtual uint32 InsertSyncPoint() override {
291 return gl_->InsertSyncPointCHROMIUM();
292 }
293 virtual void WaitSyncPoint(uint32 sync_point) override {
294 gl_->WaitSyncPointCHROMIUM(sync_point);
295 }
296
297 private:
298 gpu::gles2::GLES2Interface* gl_;
299 };
scherkus (not reviewing) 2014/10/30 20:40:12 DISALLOW_COPY_AND_ASSIGN()
dshwang 2014/10/31 09:29:17 Done.
300
217 } // anonymous namespace 301 } // anonymous namespace
218 302
219 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. 303 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU.
220 class VideoImageGenerator : public SkImageGenerator { 304 class VideoImageGenerator : public SkImageGenerator {
221 public: 305 public:
222 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { 306 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) {
223 DCHECK(frame_.get()); 307 DCHECK(frame_.get());
224 } 308 }
225 ~VideoImageGenerator() override {} 309 ~VideoImageGenerator() override {}
226 310
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
299 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); 383 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator);
300 }; 384 };
301 385
302 SkCanvasVideoRenderer::SkCanvasVideoRenderer() 386 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
303 : last_frame_timestamp_(media::kNoTimestamp()), 387 : last_frame_timestamp_(media::kNoTimestamp()),
304 frame_deleting_timer_( 388 frame_deleting_timer_(
305 FROM_HERE, 389 FROM_HERE,
306 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), 390 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay),
307 this, 391 this,
308 &SkCanvasVideoRenderer::ResetLastFrame), 392 &SkCanvasVideoRenderer::ResetLastFrame),
309 accelerated_generator_(NULL), 393 accelerated_generator_(nullptr),
310 accelerated_last_frame_timestamp_(media::kNoTimestamp()), 394 accelerated_last_frame_timestamp_(media::kNoTimestamp()),
311 accelerated_frame_deleting_timer_( 395 accelerated_frame_deleting_timer_(
312 FROM_HERE, 396 FROM_HERE,
313 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), 397 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay),
314 this, 398 this,
315 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) { 399 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) {
316 last_frame_.setIsVolatile(true); 400 last_frame_.setIsVolatile(true);
317 } 401 }
318 402
319 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} 403 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
320 404
321 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, 405 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
322 SkCanvas* canvas, 406 SkCanvas* canvas,
323 const gfx::RectF& dest_rect, 407 const gfx::RectF& dest_rect,
324 uint8 alpha, 408 uint8 alpha,
325 SkXfermode::Mode mode, 409 SkXfermode::Mode mode,
326 VideoRotation video_rotation) { 410 VideoRotation video_rotation,
411 const Context3DProvider& context_provider) {
327 if (alpha == 0) { 412 if (alpha == 0) {
328 return; 413 return;
329 } 414 }
330 415
331 SkRect dest; 416 SkRect dest;
332 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); 417 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom());
333 418
334 SkPaint paint; 419 SkPaint paint;
335 paint.setAlpha(alpha); 420 paint.setAlpha(alpha);
336 421
337 // Paint black rectangle if there isn't a frame available or the 422 // Paint black rectangle if there isn't a frame available or the
338 // frame has an unexpected format. 423 // frame has an unexpected format.
339 if (!video_frame.get() || video_frame->natural_size().IsEmpty() || 424 if (!video_frame.get() || video_frame->natural_size().IsEmpty() ||
340 !IsYUVOrNative(video_frame->format())) { 425 !IsYUVOrNative(video_frame->format())) {
341 canvas->drawRect(dest, paint); 426 canvas->drawRect(dest, paint);
342 canvas->flush(); 427 canvas->flush();
343 return; 428 return;
344 } 429 }
345 430
346 SkBitmap* target_frame = NULL; 431 SkBitmap* target_frame = nullptr;
347 if (canvas->getGrContext()) { 432 if (canvas->getGrContext()) {
433 // TODO(dshwang): Android video decoder doesn't update the timestamp on a
434 // VideoFrame. To reduce redundant copy, Android should update the
435 // timestamp.
348 if (accelerated_last_frame_.isNull() || 436 if (accelerated_last_frame_.isNull() ||
437 #if defined(OS_ANDROID)
438 video_frame->timestamp() == base::TimeDelta() ||
439 #endif
349 video_frame->timestamp() != accelerated_last_frame_timestamp_) { 440 video_frame->timestamp() != accelerated_last_frame_timestamp_) {
350 accelerated_generator_ = new VideoImageGenerator(video_frame); 441 if (video_frame->format() == media::VideoFrame::NATIVE_TEXTURE) {
442 // Draw HW Video on HW Canvas.
443 DCHECK(context_provider.gl && context_provider.gr_context);
scherkus (not reviewing) 2014/10/30 20:40:13 nit: break out into two DCHECKs so we know which o
dshwang 2014/10/31 09:29:17 Done.
444 DCHECK(!accelerated_generator_);
445 if (!CopyVideoFrameTextureToSkBitmapTexture(video_frame.get(),
scherkus (not reviewing) 2014/10/30 20:40:13 you can drop the .get() if you switch to using con
dshwang 2014/10/31 09:29:17 CopyVideoFrameTextureToSkBitmapTexture doesn't tak
446 &accelerated_last_frame_,
447 context_provider)) {
448 NOTREACHED();
449 return;
450 }
451 } else {
452 // Draw SW Video on HW Canvas.
453 accelerated_generator_ = new VideoImageGenerator(video_frame);
351 454
352 // Note: This takes ownership of |accelerated_generator_|. 455 // Note: This takes ownership of |accelerated_generator_|.
353 if (!SkInstallDiscardablePixelRef(accelerated_generator_, 456 if (!SkInstallDiscardablePixelRef(accelerated_generator_,
354 &accelerated_last_frame_)) { 457 &accelerated_last_frame_)) {
355 NOTREACHED(); 458 NOTREACHED();
459 return;
460 }
356 } 461 }
357 DCHECK(video_frame->visible_rect().width() == 462 DCHECK(video_frame->visible_rect().width() ==
358 accelerated_last_frame_.width() && 463 accelerated_last_frame_.width() &&
359 video_frame->visible_rect().height() == 464 video_frame->visible_rect().height() ==
360 accelerated_last_frame_.height()); 465 accelerated_last_frame_.height());
361 466
362 accelerated_last_frame_timestamp_ = video_frame->timestamp(); 467 accelerated_last_frame_timestamp_ = video_frame->timestamp();
363 } else { 468 } else if (accelerated_generator_) {
364 accelerated_generator_->set_frame(video_frame); 469 accelerated_generator_->set_frame(video_frame);
365 } 470 }
366 target_frame = &accelerated_last_frame_; 471 target_frame = &accelerated_last_frame_;
367 accelerated_frame_deleting_timer_.Reset(); 472 accelerated_frame_deleting_timer_.Reset();
368 } else { 473 } else {
369 // Check if we should convert and update |last_frame_|. 474 // Draw both SW and HW Video on SW Canvas.
370 if (last_frame_.isNull() || 475 if (last_frame_.isNull() ||
371 video_frame->timestamp() != last_frame_timestamp_) { 476 video_frame->timestamp() != last_frame_timestamp_) {
372 // Check if |bitmap| needs to be (re)allocated. 477 // Check if |bitmap| needs to be (re)allocated.
373 if (last_frame_.isNull() || 478 if (last_frame_.isNull() ||
374 last_frame_.width() != video_frame->visible_rect().width() || 479 last_frame_.width() != video_frame->visible_rect().width() ||
375 last_frame_.height() != video_frame->visible_rect().height()) { 480 last_frame_.height() != video_frame->visible_rect().height()) {
376 last_frame_.allocN32Pixels(video_frame->visible_rect().width(), 481 last_frame_.allocN32Pixels(video_frame->visible_rect().width(),
377 video_frame->visible_rect().height()); 482 video_frame->visible_rect().height());
378 last_frame_.setIsVolatile(true); 483 last_frame_.setIsVolatile(true);
379 } 484 }
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
427 SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); 532 SkFloatToScalar(rotated_dest_size.height() / target_frame->height()));
428 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), 533 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f),
429 -SkFloatToScalar(target_frame->height() * 0.5f)); 534 -SkFloatToScalar(target_frame->height() * 0.5f));
430 } 535 }
431 canvas->drawBitmap(*target_frame, 0, 0, &paint); 536 canvas->drawBitmap(*target_frame, 0, 0, &paint);
432 if (need_transform) 537 if (need_transform)
433 canvas->restore(); 538 canvas->restore();
434 canvas->flush(); 539 canvas->flush();
435 // SkCanvas::flush() causes the generator to generate SkImage, so delete 540 // SkCanvas::flush() causes the generator to generate SkImage, so delete
436 // |video_frame| not to be outlived. 541 // |video_frame| not to be outlived.
437 if (canvas->getGrContext()) 542 if (canvas->getGrContext() && accelerated_generator_)
438 accelerated_generator_->set_frame(NULL); 543 accelerated_generator_->set_frame(nullptr);
439 } 544 }
440 545
441 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, 546 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame,
442 SkCanvas* canvas) { 547 SkCanvas* canvas) {
443 Paint(video_frame, 548 Paint(video_frame,
444 canvas, 549 canvas,
445 video_frame->visible_rect(), 550 video_frame->visible_rect(),
446 0xff, 551 0xff,
447 SkXfermode::kSrc_Mode, 552 SkXfermode::kSrc_Mode,
448 media::VIDEO_ROTATION_0); 553 media::VIDEO_ROTATION_0,
554 media::Context3DProvider());
555 }
556
557 // static
558 void SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture(
559 gpu::gles2::GLES2Interface* gl,
560 VideoFrame* video_frame,
561 unsigned int texture,
562 unsigned int level,
563 unsigned int internal_format,
564 unsigned int type,
565 bool premultiply_alpha,
566 bool flip_y) {
567 DCHECK(video_frame && video_frame->format() == VideoFrame::NATIVE_TEXTURE);
568 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
569 DCHECK(mailbox_holder->texture_target == GL_TEXTURE_2D ||
570 mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES);
571
572 gl->WaitSyncPointCHROMIUM(mailbox_holder->sync_point);
573 uint32 source_texture = gl->CreateAndConsumeTextureCHROMIUM(
574 mailbox_holder->texture_target, mailbox_holder->mailbox.name);
575
576 // The video is stored in a unmultiplied format, so premultiply
577 // if necessary.
578 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, premultiply_alpha);
579 // Application itself needs to take care of setting the right |flip_y|
580 // value down to get the expected result.
581 // "flip_y == true" means to reverse the video orientation while
582 // "flip_y == false" means to keep the intrinsic orientation.
583 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
584 gl->CopyTextureCHROMIUM(
585 GL_TEXTURE_2D, source_texture, texture, level, internal_format, type);
586 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
587 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false);
588
589 gl->DeleteTextures(1, &source_texture);
590 gl->Flush();
591
592 SyncPointClientImpl client(gl);
593 video_frame->UpdateReleaseSyncPoint(&client);
449 } 594 }
450 595
451 void SkCanvasVideoRenderer::ResetLastFrame() { 596 void SkCanvasVideoRenderer::ResetLastFrame() {
452 last_frame_.reset(); 597 last_frame_.reset();
453 last_frame_timestamp_ = media::kNoTimestamp(); 598 last_frame_timestamp_ = media::kNoTimestamp();
454 } 599 }
455 600
456 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { 601 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() {
457 accelerated_last_frame_.reset(); 602 accelerated_last_frame_.reset();
458 accelerated_generator_ = nullptr; 603 accelerated_generator_ = nullptr;
459 accelerated_last_frame_timestamp_ = media::kNoTimestamp(); 604 accelerated_last_frame_timestamp_ = media::kNoTimestamp();
460 } 605 }
461 606
462 } // namespace media 607 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698