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 "gpu/GLES2/gl2extchromium.h" | 7 #include "base/logging.h" |
8 #include "gpu/command_buffer/client/gles2_interface.h" | |
9 #include "gpu/command_buffer/common/mailbox_holder.h" | |
10 #include "media/base/video_frame.h" | 8 #include "media/base/video_frame.h" |
11 #include "media/base/yuv_convert.h" | 9 #include "media/base/yuv_convert.h" |
12 #include "skia/ext/refptr.h" | |
13 #include "third_party/libyuv/include/libyuv.h" | 10 #include "third_party/libyuv/include/libyuv.h" |
14 #include "third_party/skia/include/core/SkCanvas.h" | 11 #include "third_party/skia/include/core/SkCanvas.h" |
15 #include "third_party/skia/include/core/SkImageGenerator.h" | 12 #include "third_party/skia/include/core/SkImageGenerator.h" |
16 #include "third_party/skia/include/gpu/GrContext.h" | 13 #include "third_party/skia/include/gpu/GrContext.h" |
17 #include "third_party/skia/include/gpu/SkGrPixelRef.h" | |
18 #include "ui/gfx/skbitmap_operations.h" | 14 #include "ui/gfx/skbitmap_operations.h" |
19 | 15 |
20 // Skia internal format depends on a platform. On Android it is ABGR, on others | 16 // Skia internal format depends on a platform. On Android it is ABGR, on others |
21 // it is ARGB. | 17 // it is ARGB. |
22 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ | 18 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ |
23 SK_A32_SHIFT == 24 | 19 SK_A32_SHIFT == 24 |
24 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB | 20 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB |
25 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB | 21 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB |
26 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ | 22 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ |
27 SK_A32_SHIFT == 24 | 23 SK_A32_SHIFT == 24 |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 row_bytes); | 207 row_bytes); |
212 video_frame->ReadPixelsFromNativeTexture(tmp); | 208 video_frame->ReadPixelsFromNativeTexture(tmp); |
213 break; | 209 break; |
214 } | 210 } |
215 default: | 211 default: |
216 NOTREACHED(); | 212 NOTREACHED(); |
217 break; | 213 break; |
218 } | 214 } |
219 } | 215 } |
220 | 216 |
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, to avoid | |
233 // RGBA to BGRA conversion. | |
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(VideoFrame* video_frame, | |
255 SkBitmap* bitmap, | |
256 const Context3D& context_3d) { | |
257 // Check if we could reuse existing texture based bitmap. | |
258 // Otherwise, release existing texture based bitmap and allocate | |
259 // a new one based on video size. | |
260 if (!IsSkBitmapProperlySizedTexture(bitmap, | |
261 video_frame->visible_rect().size())) { | |
262 if (!AllocateSkBitmapTexture(context_3d.gr_context, bitmap, | |
263 video_frame->visible_rect().size())) { | |
264 return false; | |
265 } | |
266 } | |
267 | |
268 unsigned texture_id = | |
269 static_cast<unsigned>((bitmap->getTexture())->getTextureHandle()); | |
270 // If CopyVideoFrameTextureToGLTexture() changes the state of the | |
271 // |texture_id|, it's needed to invalidate the state cached in skia, | |
272 // but currently the state isn't changed. | |
273 SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture( | |
274 context_3d.gl, video_frame, texture_id, 0, GL_RGBA, GL_UNSIGNED_BYTE, | |
275 true, false); | |
276 return true; | |
277 } | |
278 | |
279 class SyncPointClientImpl : public VideoFrame::SyncPointClient { | |
280 public: | |
281 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} | |
282 ~SyncPointClientImpl() override {} | |
283 uint32 InsertSyncPoint() override { return gl_->InsertSyncPointCHROMIUM(); } | |
284 void WaitSyncPoint(uint32 sync_point) override { | |
285 gl_->WaitSyncPointCHROMIUM(sync_point); | |
286 } | |
287 | |
288 private: | |
289 gpu::gles2::GLES2Interface* gl_; | |
290 | |
291 DISALLOW_IMPLICIT_CONSTRUCTORS(SyncPointClientImpl); | |
292 }; | |
293 | |
294 } // anonymous namespace | 217 } // anonymous namespace |
295 | 218 |
296 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. | 219 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. |
297 class VideoImageGenerator : public SkImageGenerator { | 220 class VideoImageGenerator : public SkImageGenerator { |
298 public: | 221 public: |
299 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { | 222 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { |
300 DCHECK(frame_.get()); | 223 DCHECK(frame_.get()); |
301 } | 224 } |
302 ~VideoImageGenerator() override {} | 225 ~VideoImageGenerator() override {} |
303 | 226 |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
376 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); | 299 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); |
377 }; | 300 }; |
378 | 301 |
379 SkCanvasVideoRenderer::SkCanvasVideoRenderer() | 302 SkCanvasVideoRenderer::SkCanvasVideoRenderer() |
380 : last_frame_timestamp_(media::kNoTimestamp()), | 303 : last_frame_timestamp_(media::kNoTimestamp()), |
381 frame_deleting_timer_( | 304 frame_deleting_timer_( |
382 FROM_HERE, | 305 FROM_HERE, |
383 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), | 306 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), |
384 this, | 307 this, |
385 &SkCanvasVideoRenderer::ResetLastFrame), | 308 &SkCanvasVideoRenderer::ResetLastFrame), |
386 accelerated_generator_(nullptr), | 309 accelerated_generator_(NULL), |
387 accelerated_last_frame_timestamp_(media::kNoTimestamp()), | 310 accelerated_last_frame_timestamp_(media::kNoTimestamp()), |
388 accelerated_frame_deleting_timer_( | 311 accelerated_frame_deleting_timer_( |
389 FROM_HERE, | 312 FROM_HERE, |
390 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), | 313 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), |
391 this, | 314 this, |
392 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) { | 315 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) { |
393 last_frame_.setIsVolatile(true); | 316 last_frame_.setIsVolatile(true); |
394 } | 317 } |
395 | 318 |
396 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} | 319 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} |
397 | 320 |
398 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, | 321 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, |
399 SkCanvas* canvas, | 322 SkCanvas* canvas, |
400 const gfx::RectF& dest_rect, | 323 const gfx::RectF& dest_rect, |
401 uint8 alpha, | 324 uint8 alpha, |
402 SkXfermode::Mode mode, | 325 SkXfermode::Mode mode, |
403 VideoRotation video_rotation, | 326 VideoRotation video_rotation) { |
404 const Context3D& context_3d) { | |
405 if (alpha == 0) { | 327 if (alpha == 0) { |
406 return; | 328 return; |
407 } | 329 } |
408 | 330 |
409 SkRect dest; | 331 SkRect dest; |
410 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); | 332 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); |
411 | 333 |
412 SkPaint paint; | 334 SkPaint paint; |
413 paint.setAlpha(alpha); | 335 paint.setAlpha(alpha); |
414 | 336 |
415 // Paint black rectangle if there isn't a frame available or the | 337 // Paint black rectangle if there isn't a frame available or the |
416 // frame has an unexpected format. | 338 // frame has an unexpected format. |
417 if (!video_frame.get() || video_frame->natural_size().IsEmpty() || | 339 if (!video_frame.get() || video_frame->natural_size().IsEmpty() || |
418 !IsYUVOrNative(video_frame->format())) { | 340 !IsYUVOrNative(video_frame->format())) { |
419 canvas->drawRect(dest, paint); | 341 canvas->drawRect(dest, paint); |
420 canvas->flush(); | 342 canvas->flush(); |
421 return; | 343 return; |
422 } | 344 } |
423 | 345 |
424 SkBitmap* target_frame = nullptr; | 346 SkBitmap* target_frame = NULL; |
425 if (canvas->getGrContext()) { | 347 if (canvas->getGrContext()) { |
426 if (accelerated_last_frame_.isNull() || | 348 if (accelerated_last_frame_.isNull() || |
427 video_frame->timestamp() != accelerated_last_frame_timestamp_) { | 349 video_frame->timestamp() != accelerated_last_frame_timestamp_) { |
428 if (video_frame->format() == VideoFrame::NATIVE_TEXTURE) { | 350 accelerated_generator_ = new VideoImageGenerator(video_frame); |
429 DCHECK(context_3d.gl); | |
430 DCHECK(context_3d.gr_context); | |
431 // Draw HW Video on HW Canvas. | |
432 DCHECK(!accelerated_generator_); | |
433 if (!CopyVideoFrameTextureToSkBitmapTexture( | |
434 video_frame.get(), &accelerated_last_frame_, context_3d)) { | |
435 NOTREACHED(); | |
436 return; | |
437 } | |
438 } else { | |
439 // Draw SW Video on HW Canvas. | |
440 accelerated_generator_ = new VideoImageGenerator(video_frame); | |
441 | 351 |
442 // Note: This takes ownership of |accelerated_generator_|. | 352 // Note: This takes ownership of |accelerated_generator_|. |
443 if (!SkInstallDiscardablePixelRef(accelerated_generator_, | 353 if (!SkInstallDiscardablePixelRef(accelerated_generator_, |
444 &accelerated_last_frame_)) { | 354 &accelerated_last_frame_)) { |
445 NOTREACHED(); | 355 NOTREACHED(); |
446 return; | |
447 } | |
448 } | 356 } |
449 DCHECK(video_frame->visible_rect().width() == | 357 DCHECK(video_frame->visible_rect().width() == |
450 accelerated_last_frame_.width() && | 358 accelerated_last_frame_.width() && |
451 video_frame->visible_rect().height() == | 359 video_frame->visible_rect().height() == |
452 accelerated_last_frame_.height()); | 360 accelerated_last_frame_.height()); |
453 | 361 |
454 accelerated_last_frame_timestamp_ = video_frame->timestamp(); | 362 accelerated_last_frame_timestamp_ = video_frame->timestamp(); |
455 } else if (accelerated_generator_) { | 363 } else { |
456 accelerated_generator_->set_frame(video_frame); | 364 accelerated_generator_->set_frame(video_frame); |
457 } | 365 } |
458 target_frame = &accelerated_last_frame_; | 366 target_frame = &accelerated_last_frame_; |
459 accelerated_frame_deleting_timer_.Reset(); | 367 accelerated_frame_deleting_timer_.Reset(); |
460 } else { | 368 } else { |
461 // Draw both SW and HW Video on SW Canvas. | 369 // Check if we should convert and update |last_frame_|. |
462 if (last_frame_.isNull() || | 370 if (last_frame_.isNull() || |
463 video_frame->timestamp() != last_frame_timestamp_) { | 371 video_frame->timestamp() != last_frame_timestamp_) { |
464 // Check if |bitmap| needs to be (re)allocated. | 372 // Check if |bitmap| needs to be (re)allocated. |
465 if (last_frame_.isNull() || | 373 if (last_frame_.isNull() || |
466 last_frame_.width() != video_frame->visible_rect().width() || | 374 last_frame_.width() != video_frame->visible_rect().width() || |
467 last_frame_.height() != video_frame->visible_rect().height()) { | 375 last_frame_.height() != video_frame->visible_rect().height()) { |
468 last_frame_.allocN32Pixels(video_frame->visible_rect().width(), | 376 last_frame_.allocN32Pixels(video_frame->visible_rect().width(), |
469 video_frame->visible_rect().height()); | 377 video_frame->visible_rect().height()); |
470 last_frame_.setIsVolatile(true); | 378 last_frame_.setIsVolatile(true); |
471 } | 379 } |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
519 SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); | 427 SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); |
520 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), | 428 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), |
521 -SkFloatToScalar(target_frame->height() * 0.5f)); | 429 -SkFloatToScalar(target_frame->height() * 0.5f)); |
522 } | 430 } |
523 canvas->drawBitmap(*target_frame, 0, 0, &paint); | 431 canvas->drawBitmap(*target_frame, 0, 0, &paint); |
524 if (need_transform) | 432 if (need_transform) |
525 canvas->restore(); | 433 canvas->restore(); |
526 canvas->flush(); | 434 canvas->flush(); |
527 // SkCanvas::flush() causes the generator to generate SkImage, so delete | 435 // SkCanvas::flush() causes the generator to generate SkImage, so delete |
528 // |video_frame| not to be outlived. | 436 // |video_frame| not to be outlived. |
529 if (canvas->getGrContext() && accelerated_generator_) | 437 if (canvas->getGrContext()) |
530 accelerated_generator_->set_frame(nullptr); | 438 accelerated_generator_->set_frame(NULL); |
531 } | 439 } |
532 | 440 |
533 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, | 441 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, |
534 SkCanvas* canvas) { | 442 SkCanvas* canvas) { |
535 Paint(video_frame, canvas, video_frame->visible_rect(), 0xff, | 443 Paint(video_frame, |
536 SkXfermode::kSrc_Mode, media::VIDEO_ROTATION_0, Context3D()); | 444 canvas, |
537 } | 445 video_frame->visible_rect(), |
538 | 446 0xff, |
539 // static | 447 SkXfermode::kSrc_Mode, |
540 void SkCanvasVideoRenderer::CopyVideoFrameTextureToGLTexture( | 448 media::VIDEO_ROTATION_0); |
541 gpu::gles2::GLES2Interface* gl, | |
542 VideoFrame* video_frame, | |
543 unsigned int texture, | |
544 unsigned int level, | |
545 unsigned int internal_format, | |
546 unsigned int type, | |
547 bool premultiply_alpha, | |
548 bool flip_y) { | |
549 DCHECK(video_frame && video_frame->format() == VideoFrame::NATIVE_TEXTURE); | |
550 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder(); | |
551 DCHECK(mailbox_holder->texture_target == GL_TEXTURE_2D || | |
552 mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES); | |
553 | |
554 gl->WaitSyncPointCHROMIUM(mailbox_holder->sync_point); | |
555 uint32 source_texture = gl->CreateAndConsumeTextureCHROMIUM( | |
556 mailbox_holder->texture_target, mailbox_holder->mailbox.name); | |
557 | |
558 // The video is stored in a unmultiplied format, so premultiply | |
559 // if necessary. | |
560 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, premultiply_alpha); | |
561 // Application itself needs to take care of setting the right |flip_y| | |
562 // value down to get the expected result. | |
563 // "flip_y == true" means to reverse the video orientation while | |
564 // "flip_y == false" means to keep the intrinsic orientation. | |
565 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y); | |
566 gl->CopyTextureCHROMIUM(GL_TEXTURE_2D, source_texture, texture, level, | |
567 internal_format, type); | |
568 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false); | |
569 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false); | |
570 | |
571 gl->DeleteTextures(1, &source_texture); | |
572 gl->Flush(); | |
573 | |
574 SyncPointClientImpl client(gl); | |
575 video_frame->UpdateReleaseSyncPoint(&client); | |
576 } | 449 } |
577 | 450 |
578 void SkCanvasVideoRenderer::ResetLastFrame() { | 451 void SkCanvasVideoRenderer::ResetLastFrame() { |
579 last_frame_.reset(); | 452 last_frame_.reset(); |
580 last_frame_timestamp_ = media::kNoTimestamp(); | 453 last_frame_timestamp_ = media::kNoTimestamp(); |
581 } | 454 } |
582 | 455 |
583 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { | 456 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { |
584 accelerated_last_frame_.reset(); | 457 accelerated_last_frame_.reset(); |
585 accelerated_generator_ = nullptr; | 458 accelerated_generator_ = nullptr; |
586 accelerated_last_frame_timestamp_ = media::kNoTimestamp(); | 459 accelerated_last_frame_timestamp_ = media::kNoTimestamp(); |
587 } | 460 } |
588 | 461 |
589 } // namespace media | 462 } // namespace media |
OLD | NEW |