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/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 Loading... |
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* pixel_ref = SkNEW_ARGS(SkGrPixelRef, (info, texture.get())); |
| 247 if (!pixel_ref) |
| 248 return false; |
| 249 bitmap->setInfo(info); |
| 250 bitmap->setPixelRef(pixel_ref)->unref(); |
| 251 return true; |
| 252 } |
| 253 |
| 254 bool CopyVideoFrameTextureToSkBitmapTexture( |
| 255 VideoFrame* video_frame, |
| 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, bitmap, |
| 264 video_frame->visible_rect().size())) { |
| 265 return false; |
| 266 } |
| 267 } |
| 268 |
| 269 unsigned texture_id = |
| 270 static_cast<unsigned>((bitmap->getTexture())->getTextureHandle()); |
| 271 // If CopyVideoFrameTextureToGLTexture() changes the state of the |
| 272 // |texture_id|, it's needed to invalidate the state cached in skia, |
| 273 // but currently the state isn't changed. |
| 274 SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture( |
| 275 context_provider.gl, video_frame, texture_id, 0, GL_RGBA, |
| 276 GL_UNSIGNED_BYTE, true, false); |
| 277 return true; |
| 278 } |
| 279 |
| 280 class SyncPointClientImpl : public VideoFrame::SyncPointClient { |
| 281 public: |
| 282 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} |
| 283 ~SyncPointClientImpl() override {} |
| 284 uint32 InsertSyncPoint() override { return gl_->InsertSyncPointCHROMIUM(); } |
| 285 void WaitSyncPoint(uint32 sync_point) override { |
| 286 gl_->WaitSyncPointCHROMIUM(sync_point); |
| 287 } |
| 288 |
| 289 private: |
| 290 gpu::gles2::GLES2Interface* gl_; |
| 291 |
| 292 DISALLOW_IMPLICIT_CONSTRUCTORS(SyncPointClientImpl); |
| 293 }; |
| 294 |
217 } // anonymous namespace | 295 } // anonymous namespace |
218 | 296 |
219 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. | 297 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. |
220 class VideoImageGenerator : public SkImageGenerator { | 298 class VideoImageGenerator : public SkImageGenerator { |
221 public: | 299 public: |
222 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { | 300 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { |
223 DCHECK(frame_.get()); | 301 DCHECK(frame_.get()); |
224 } | 302 } |
225 ~VideoImageGenerator() override {} | 303 ~VideoImageGenerator() override {} |
226 | 304 |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
299 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); | 377 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); |
300 }; | 378 }; |
301 | 379 |
302 SkCanvasVideoRenderer::SkCanvasVideoRenderer() | 380 SkCanvasVideoRenderer::SkCanvasVideoRenderer() |
303 : last_frame_timestamp_(media::kNoTimestamp()), | 381 : last_frame_timestamp_(media::kNoTimestamp()), |
304 frame_deleting_timer_( | 382 frame_deleting_timer_( |
305 FROM_HERE, | 383 FROM_HERE, |
306 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), | 384 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), |
307 this, | 385 this, |
308 &SkCanvasVideoRenderer::ResetLastFrame), | 386 &SkCanvasVideoRenderer::ResetLastFrame), |
309 accelerated_generator_(NULL), | 387 accelerated_generator_(nullptr), |
310 accelerated_last_frame_timestamp_(media::kNoTimestamp()), | 388 accelerated_last_frame_timestamp_(media::kNoTimestamp()), |
311 accelerated_frame_deleting_timer_( | 389 accelerated_frame_deleting_timer_( |
312 FROM_HERE, | 390 FROM_HERE, |
313 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), | 391 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), |
314 this, | 392 this, |
315 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) { | 393 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) { |
316 last_frame_.setIsVolatile(true); | 394 last_frame_.setIsVolatile(true); |
317 } | 395 } |
318 | 396 |
319 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} | 397 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} |
320 | 398 |
321 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, | 399 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, |
322 SkCanvas* canvas, | 400 SkCanvas* canvas, |
323 const gfx::RectF& dest_rect, | 401 const gfx::RectF& dest_rect, |
324 uint8 alpha, | 402 uint8 alpha, |
325 SkXfermode::Mode mode, | 403 SkXfermode::Mode mode, |
326 VideoRotation video_rotation) { | 404 VideoRotation video_rotation, |
| 405 const Context3DProvider& context_provider) { |
327 if (alpha == 0) { | 406 if (alpha == 0) { |
328 return; | 407 return; |
329 } | 408 } |
330 | 409 |
331 SkRect dest; | 410 SkRect dest; |
332 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); | 411 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); |
333 | 412 |
334 SkPaint paint; | 413 SkPaint paint; |
335 paint.setAlpha(alpha); | 414 paint.setAlpha(alpha); |
336 | 415 |
337 // Paint black rectangle if there isn't a frame available or the | 416 // Paint black rectangle if there isn't a frame available or the |
338 // frame has an unexpected format. | 417 // frame has an unexpected format. |
339 if (!video_frame.get() || video_frame->natural_size().IsEmpty() || | 418 if (!video_frame.get() || video_frame->natural_size().IsEmpty() || |
340 !IsYUVOrNative(video_frame->format())) { | 419 !IsYUVOrNative(video_frame->format())) { |
341 canvas->drawRect(dest, paint); | 420 canvas->drawRect(dest, paint); |
342 canvas->flush(); | 421 canvas->flush(); |
343 return; | 422 return; |
344 } | 423 } |
345 | 424 |
346 SkBitmap* target_frame = NULL; | 425 #if DCHECK_IS_ON |
| 426 if (canvas->getGrContext() || |
| 427 video_frame->format() == media::VideoFrame::NATIVE_TEXTURE) { |
| 428 DCHECK(context_provider.gl); |
| 429 DCHECK(context_provider.gr_context); |
| 430 } |
| 431 #endif |
| 432 |
| 433 SkBitmap* target_frame = nullptr; |
347 if (canvas->getGrContext()) { | 434 if (canvas->getGrContext()) { |
| 435 // TODO(dshwang): Android video decoder doesn't update the timestamp on a |
| 436 // VideoFrame. To reduce redundant copy, Android should update the |
| 437 // timestamp. |
348 if (accelerated_last_frame_.isNull() || | 438 if (accelerated_last_frame_.isNull() || |
| 439 #if defined(OS_ANDROID) |
| 440 video_frame->timestamp() == base::TimeDelta() || |
| 441 #endif |
349 video_frame->timestamp() != accelerated_last_frame_timestamp_) { | 442 video_frame->timestamp() != accelerated_last_frame_timestamp_) { |
350 accelerated_generator_ = new VideoImageGenerator(video_frame); | 443 if (video_frame->format() == VideoFrame::NATIVE_TEXTURE) { |
| 444 // Draw HW Video on HW Canvas. |
| 445 DCHECK(!accelerated_generator_); |
| 446 if (!CopyVideoFrameTextureToSkBitmapTexture(video_frame.get(), |
| 447 &accelerated_last_frame_, |
| 448 context_provider)) { |
| 449 NOTREACHED(); |
| 450 return; |
| 451 } |
| 452 } else { |
| 453 // Draw SW Video on HW Canvas. |
| 454 accelerated_generator_ = new VideoImageGenerator(video_frame); |
351 | 455 |
352 // Note: This takes ownership of |accelerated_generator_|. | 456 // Note: This takes ownership of |accelerated_generator_|. |
353 if (!SkInstallDiscardablePixelRef(accelerated_generator_, | 457 if (!SkInstallDiscardablePixelRef(accelerated_generator_, |
354 &accelerated_last_frame_)) { | 458 &accelerated_last_frame_)) { |
355 NOTREACHED(); | 459 NOTREACHED(); |
| 460 return; |
| 461 } |
356 } | 462 } |
357 DCHECK(video_frame->visible_rect().width() == | 463 DCHECK(video_frame->visible_rect().width() == |
358 accelerated_last_frame_.width() && | 464 accelerated_last_frame_.width() && |
359 video_frame->visible_rect().height() == | 465 video_frame->visible_rect().height() == |
360 accelerated_last_frame_.height()); | 466 accelerated_last_frame_.height()); |
361 | 467 |
362 accelerated_last_frame_timestamp_ = video_frame->timestamp(); | 468 accelerated_last_frame_timestamp_ = video_frame->timestamp(); |
363 } else { | 469 } else if (accelerated_generator_) { |
364 accelerated_generator_->set_frame(video_frame); | 470 accelerated_generator_->set_frame(video_frame); |
365 } | 471 } |
366 target_frame = &accelerated_last_frame_; | 472 target_frame = &accelerated_last_frame_; |
367 accelerated_frame_deleting_timer_.Reset(); | 473 accelerated_frame_deleting_timer_.Reset(); |
368 } else { | 474 } else { |
369 // Check if we should convert and update |last_frame_|. | 475 // Draw both SW and HW Video on SW Canvas. |
370 if (last_frame_.isNull() || | 476 if (last_frame_.isNull() || |
371 video_frame->timestamp() != last_frame_timestamp_) { | 477 video_frame->timestamp() != last_frame_timestamp_) { |
372 // Check if |bitmap| needs to be (re)allocated. | 478 // Check if |bitmap| needs to be (re)allocated. |
373 if (last_frame_.isNull() || | 479 if (last_frame_.isNull() || |
374 last_frame_.width() != video_frame->visible_rect().width() || | 480 last_frame_.width() != video_frame->visible_rect().width() || |
375 last_frame_.height() != video_frame->visible_rect().height()) { | 481 last_frame_.height() != video_frame->visible_rect().height()) { |
376 last_frame_.allocN32Pixels(video_frame->visible_rect().width(), | 482 last_frame_.allocN32Pixels(video_frame->visible_rect().width(), |
377 video_frame->visible_rect().height()); | 483 video_frame->visible_rect().height()); |
378 last_frame_.setIsVolatile(true); | 484 last_frame_.setIsVolatile(true); |
379 } | 485 } |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
427 SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); | 533 SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); |
428 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), | 534 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), |
429 -SkFloatToScalar(target_frame->height() * 0.5f)); | 535 -SkFloatToScalar(target_frame->height() * 0.5f)); |
430 } | 536 } |
431 canvas->drawBitmap(*target_frame, 0, 0, &paint); | 537 canvas->drawBitmap(*target_frame, 0, 0, &paint); |
432 if (need_transform) | 538 if (need_transform) |
433 canvas->restore(); | 539 canvas->restore(); |
434 canvas->flush(); | 540 canvas->flush(); |
435 // SkCanvas::flush() causes the generator to generate SkImage, so delete | 541 // SkCanvas::flush() causes the generator to generate SkImage, so delete |
436 // |video_frame| not to be outlived. | 542 // |video_frame| not to be outlived. |
437 if (canvas->getGrContext()) | 543 if (canvas->getGrContext() && accelerated_generator_) |
438 accelerated_generator_->set_frame(NULL); | 544 accelerated_generator_->set_frame(nullptr); |
439 } | 545 } |
440 | 546 |
441 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, | 547 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, |
442 SkCanvas* canvas) { | 548 SkCanvas* canvas) { |
443 Paint(video_frame, | 549 Paint(video_frame, canvas, video_frame->visible_rect(), 0xff, |
444 canvas, | 550 SkXfermode::kSrc_Mode, media::VIDEO_ROTATION_0, Context3DProvider()); |
445 video_frame->visible_rect(), | 551 } |
446 0xff, | 552 |
447 SkXfermode::kSrc_Mode, | 553 // static |
448 media::VIDEO_ROTATION_0); | 554 void SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture( |
| 555 gpu::gles2::GLES2Interface* gl, |
| 556 VideoFrame* video_frame, |
| 557 unsigned int texture, |
| 558 unsigned int level, |
| 559 unsigned int internal_format, |
| 560 unsigned int type, |
| 561 bool premultiply_alpha, |
| 562 bool flip_y) { |
| 563 DCHECK(video_frame && video_frame->format() == VideoFrame::NATIVE_TEXTURE); |
| 564 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder(); |
| 565 DCHECK(mailbox_holder->texture_target == GL_TEXTURE_2D || |
| 566 mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES); |
| 567 |
| 568 gl->WaitSyncPointCHROMIUM(mailbox_holder->sync_point); |
| 569 uint32 source_texture = gl->CreateAndConsumeTextureCHROMIUM( |
| 570 mailbox_holder->texture_target, mailbox_holder->mailbox.name); |
| 571 |
| 572 // The video is stored in a unmultiplied format, so premultiply |
| 573 // if necessary. |
| 574 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, premultiply_alpha); |
| 575 // Application itself needs to take care of setting the right |flip_y| |
| 576 // value down to get the expected result. |
| 577 // "flip_y == true" means to reverse the video orientation while |
| 578 // "flip_y == false" means to keep the intrinsic orientation. |
| 579 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y); |
| 580 gl->CopyTextureCHROMIUM(GL_TEXTURE_2D, source_texture, texture, level, |
| 581 internal_format, type); |
| 582 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false); |
| 583 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false); |
| 584 |
| 585 gl->DeleteTextures(1, &source_texture); |
| 586 gl->Flush(); |
| 587 |
| 588 SyncPointClientImpl client(gl); |
| 589 video_frame->UpdateReleaseSyncPoint(&client); |
449 } | 590 } |
450 | 591 |
451 void SkCanvasVideoRenderer::ResetLastFrame() { | 592 void SkCanvasVideoRenderer::ResetLastFrame() { |
452 last_frame_.reset(); | 593 last_frame_.reset(); |
453 last_frame_timestamp_ = media::kNoTimestamp(); | 594 last_frame_timestamp_ = media::kNoTimestamp(); |
454 } | 595 } |
455 | 596 |
456 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { | 597 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { |
457 accelerated_last_frame_.reset(); | 598 accelerated_last_frame_.reset(); |
458 accelerated_generator_ = nullptr; | 599 accelerated_generator_ = nullptr; |
459 accelerated_last_frame_timestamp_ = media::kNoTimestamp(); | 600 accelerated_last_frame_timestamp_ = media::kNoTimestamp(); |
460 } | 601 } |
461 | 602 |
462 } // namespace media | 603 } // namespace media |
OLD | NEW |