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
3 // found in the LICENSE file.
5 #include "media/blink/skcanvas_video_renderer.h"
7 #include "gpu/GLES2/gl2extchromium.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"
11 #include "media/base/yuv_convert.h"
12 #include "skia/ext/refptr.h"
13 #include "third_party/libyuv/include/libyuv.h"
14 #include "third_party/skia/include/core/SkCanvas.h"
15 #include "third_party/skia/include/core/SkImage.h"
16 #include "third_party/skia/include/core/SkImageGenerator.h"
17 #include "third_party/skia/include/gpu/GrContext.h"
18 #include "third_party/skia/include/gpu/GrPaint.h"
19 #include "third_party/skia/include/gpu/GrTexture.h"
20 #include "third_party/skia/include/gpu/GrTextureProvider.h"
21 #include "third_party/skia/include/gpu/SkGr.h"
22 #include "third_party/skia/include/gpu/SkGrPixelRef.h"
23 #include "ui/gfx/skbitmap_operations.h"
25 // Skia internal format depends on a platform. On Android it is ABGR, on others
27 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \
29 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB
30 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB
31 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \
33 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR
34 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR
36 #error Unexpected Skia ARGB_8888 layout!
43 // This class keeps two temporary resources; software bitmap, hardware bitmap.
44 // If both bitmap are created and then only software bitmap is updated every
45 // frame, hardware bitmap outlives until the media player dies. So we delete
46 // a temporary resource if it is not used for 3 sec.
47 const int kTemporaryResourceDeletionDelay
= 3; // Seconds;
49 bool CheckColorSpace(const scoped_refptr
<VideoFrame
>& video_frame
,
50 VideoFrame::ColorSpace color_space
) {
52 return video_frame
->metadata()->GetInteger(
53 VideoFrameMetadata::COLOR_SPACE
, &result
) &&
54 result
== color_space
;
57 bool IsSkBitmapProperlySizedTexture(const SkBitmap
* bitmap
,
58 const gfx::Size
& size
) {
59 return bitmap
->getTexture() && bitmap
->width() == size
.width() &&
60 bitmap
->height() == size
.height();
63 bool AllocateSkBitmapTexture(GrContext
* gr
,
65 const gfx::Size
& size
) {
68 // Use kRGBA_8888_GrPixelConfig, not kSkia8888_GrPixelConfig, to avoid
69 // RGBA to BGRA conversion.
70 desc
.fConfig
= kRGBA_8888_GrPixelConfig
;
71 desc
.fFlags
= kRenderTarget_GrSurfaceFlag
;
73 desc
.fOrigin
= kTopLeft_GrSurfaceOrigin
;
74 desc
.fWidth
= size
.width();
75 desc
.fHeight
= size
.height();
76 skia::RefPtr
<GrTexture
> texture
= skia::AdoptRef(
77 gr
->textureProvider()->refScratchTexture(
78 desc
, GrTextureProvider::kExact_ScratchTexMatch
));
82 SkImageInfo info
= SkImageInfo::MakeN32Premul(desc
.fWidth
, desc
.fHeight
);
83 SkGrPixelRef
* pixel_ref
= SkNEW_ARGS(SkGrPixelRef
, (info
, texture
.get()));
86 bitmap
->setInfo(info
);
87 bitmap
->setPixelRef(pixel_ref
)->unref();
91 class SyncPointClientImpl
: public VideoFrame::SyncPointClient
{
93 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface
* gl
) : gl_(gl
) {}
94 ~SyncPointClientImpl() override
{}
95 uint32
InsertSyncPoint() override
{ return gl_
->InsertSyncPointCHROMIUM(); }
96 void WaitSyncPoint(uint32 sync_point
) override
{
97 gl_
->WaitSyncPointCHROMIUM(sync_point
);
101 gpu::gles2::GLES2Interface
* gl_
;
103 DISALLOW_IMPLICIT_CONSTRUCTORS(SyncPointClientImpl
);
106 scoped_ptr
<SkImage
> CreateSkImageFromVideoFrameYUVTextures(
107 VideoFrame
* video_frame
,
108 const Context3D
& context_3d
) {
109 // Support only TEXTURE_YUV_420.
110 DCHECK(video_frame
->HasTextures());
111 DCHECK_EQ(media::VideoFrame::I420
, video_frame
->format());
112 DCHECK_EQ(3u, media::VideoFrame::NumPlanes(video_frame
->format()));
114 gpu::gles2::GLES2Interface
* gl
= context_3d
.gl
;
116 gfx::Size ya_tex_size
= video_frame
->coded_size();
117 gfx::Size
uv_tex_size((ya_tex_size
.width() + 1) / 2,
118 (ya_tex_size
.height() + 1) / 2);
120 unsigned source_textures
[3] = {0};
121 for (size_t i
= 0; i
< media::VideoFrame::NumPlanes(video_frame
->format());
123 // Get the texture from the mailbox and wrap it in a GrTexture.
124 const gpu::MailboxHolder
& mailbox_holder
= video_frame
->mailbox_holder(i
);
125 DCHECK(mailbox_holder
.texture_target
== GL_TEXTURE_2D
||
126 mailbox_holder
.texture_target
== GL_TEXTURE_EXTERNAL_OES
||
127 mailbox_holder
.texture_target
== GL_TEXTURE_RECTANGLE_ARB
);
128 gl
->WaitSyncPointCHROMIUM(mailbox_holder
.sync_point
);
129 source_textures
[i
] = gl
->CreateAndConsumeTextureCHROMIUM(
130 mailbox_holder
.texture_target
, mailbox_holder
.mailbox
.name
);
132 GrBackendObject handles
[3] = {
133 source_textures
[0], source_textures
[1], source_textures
[2]};
135 SkISize yuvSizes
[] = {
136 {ya_tex_size
.width(), ya_tex_size
.height()},
137 {uv_tex_size
.width(), uv_tex_size
.height()},
138 {uv_tex_size
.width(), uv_tex_size
.height()},
141 // TODO(dcastagna): Skia currently doesn't support Rec709 YUV conversion.
142 DCHECK(!CheckColorSpace(video_frame
, VideoFrame::COLOR_SPACE_HD_REC709
));
143 SkYUVColorSpace color_space
= kRec601_SkYUVColorSpace
;
144 if (CheckColorSpace(video_frame
, VideoFrame::COLOR_SPACE_JPEG
))
145 color_space
= kJPEG_SkYUVColorSpace
;
147 SkImage
* img
= SkImage::NewFromYUVTexturesCopy(context_3d
.gr_context
,
148 color_space
, handles
, yuvSizes
,
149 kTopLeft_GrSurfaceOrigin
);
151 gl
->DeleteTextures(3, source_textures
);
152 SyncPointClientImpl
client(gl
);
153 video_frame
->UpdateReleaseSyncPoint(&client
);
154 return make_scoped_ptr(img
);
157 bool CopyVideoFrameSingleTextureToSkBitmap(VideoFrame
* video_frame
,
159 const Context3D
& context_3d
) {
160 // Check if we could reuse existing texture based bitmap.
161 // Otherwise, release existing texture based bitmap and allocate
162 // a new one based on video size.
163 if (!IsSkBitmapProperlySizedTexture(bitmap
,
164 video_frame
->visible_rect().size())) {
165 if (!AllocateSkBitmapTexture(context_3d
.gr_context
, bitmap
,
166 video_frame
->visible_rect().size())) {
171 unsigned texture_id
=
172 static_cast<unsigned>((bitmap
->getTexture())->getTextureHandle());
173 // If CopyVideoFrameSingleTextureToGLTexture() changes the state of the
174 // |texture_id|, it's needed to invalidate the state cached in skia,
175 // but currently the state isn't changed.
177 SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
178 context_3d
.gl
, video_frame
, texture_id
, GL_RGBA
, GL_UNSIGNED_BYTE
, true,
180 bitmap
->notifyPixelsChanged();
184 } // anonymous namespace
186 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU.
187 class VideoImageGenerator
: public SkImageGenerator
{
189 VideoImageGenerator(const scoped_refptr
<VideoFrame
>& frame
)
191 SkImageInfo::MakeN32Premul(frame
->visible_rect().width(),
192 frame
->visible_rect().height()))
194 DCHECK(frame_
.get());
196 ~VideoImageGenerator() override
{}
198 void set_frame(const scoped_refptr
<VideoFrame
>& frame
) { frame_
= frame
; }
201 Result
onGetPixels(const SkImageInfo
& info
,
205 int* ctable_count
) override
{
207 return kInvalidInput
;
208 // If skia couldn't do the YUV conversion on GPU, we will on CPU.
209 SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
210 frame_
, pixels
, row_bytes
);
214 bool onGetYUV8Planes(SkISize sizes
[3],
217 SkYUVColorSpace
* color_space
) override
{
218 if (!frame_
.get() || !VideoFrame::IsYuvPlanar(frame_
->format()) ||
219 // TODO(rileya): Skia currently doesn't support Rec709 YUV conversion,
220 // or YUVA conversion. Remove this case once it does. As-is we will
221 // fall back on the pure-software path in this case.
222 CheckColorSpace(frame_
, VideoFrame::COLOR_SPACE_HD_REC709
) ||
223 frame_
->format() == VideoFrame::YV12A
) {
228 if (CheckColorSpace(frame_
, VideoFrame::COLOR_SPACE_JPEG
))
229 *color_space
= kJPEG_SkYUVColorSpace
;
231 *color_space
= kRec601_SkYUVColorSpace
;
234 for (int plane
= VideoFrame::kYPlane
; plane
<= VideoFrame::kVPlane
;
237 const gfx::Size size
=
238 VideoFrame::PlaneSize(frame_
->format(), plane
,
239 gfx::Size(frame_
->visible_rect().width(),
240 frame_
->visible_rect().height()));
241 sizes
[plane
].set(size
.width(), size
.height());
243 if (row_bytes
&& planes
) {
245 const int y_shift
= (frame_
->format() == VideoFrame::YV16
) ? 0 : 1;
246 if (plane
== VideoFrame::kYPlane
) {
247 offset
= (frame_
->stride(VideoFrame::kYPlane
) *
248 frame_
->visible_rect().y()) +
249 frame_
->visible_rect().x();
251 offset
= (frame_
->stride(VideoFrame::kUPlane
) *
252 (frame_
->visible_rect().y() >> y_shift
)) +
253 (frame_
->visible_rect().x() >> 1);
256 // Copy the frame to the supplied memory.
257 // TODO: Find a way (API change?) to avoid this copy.
258 char* out_line
= static_cast<char*>(planes
[plane
]);
259 int out_line_stride
= row_bytes
[plane
];
260 uint8
* in_line
= frame_
->data(plane
) + offset
;
261 int in_line_stride
= frame_
->stride(plane
);
262 int plane_height
= sizes
[plane
].height();
263 if (in_line_stride
== out_line_stride
) {
264 memcpy(out_line
, in_line
, plane_height
* in_line_stride
);
266 // Different line padding so need to copy one line at a time.
267 int bytes_to_copy_per_line
= out_line_stride
< in_line_stride
270 for (int line_no
= 0; line_no
< plane_height
; line_no
++) {
271 memcpy(out_line
, in_line
, bytes_to_copy_per_line
);
272 in_line
+= in_line_stride
;
273 out_line
+= out_line_stride
;
282 scoped_refptr
<VideoFrame
> frame_
;
284 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator
);
287 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
288 : last_frame_timestamp_(media::kNoTimestamp()),
289 frame_deleting_timer_(
291 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay
),
293 &SkCanvasVideoRenderer::ResetLastFrame
),
294 accelerated_generator_(nullptr),
295 accelerated_last_frame_timestamp_(media::kNoTimestamp()),
296 accelerated_frame_deleting_timer_(
298 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay
),
300 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame
) {
301 last_frame_
.setIsVolatile(true);
304 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
306 void SkCanvasVideoRenderer::Paint(const scoped_refptr
<VideoFrame
>& video_frame
,
308 const gfx::RectF
& dest_rect
,
310 SkXfermode::Mode mode
,
311 VideoRotation video_rotation
,
312 const Context3D
& context_3d
) {
318 dest
.set(dest_rect
.x(), dest_rect
.y(), dest_rect
.right(), dest_rect
.bottom());
321 paint
.setAlpha(alpha
);
323 // Paint black rectangle if there isn't a frame available or the
324 // frame has an unexpected format.
325 if (!video_frame
.get() || video_frame
->natural_size().IsEmpty() ||
326 !(VideoFrame::IsYuvPlanar(video_frame
->format()) ||
327 video_frame
->HasTextures())) {
328 canvas
->drawRect(dest
, paint
);
333 SkBitmap
* target_frame
= nullptr;
335 if (video_frame
->HasTextures()) {
336 // Draw HW Video on both SW and HW Canvas.
337 // In SW Canvas case, rely on skia drawing Ganesh SkBitmap on SW SkCanvas.
338 if (accelerated_last_frame_
.isNull() ||
339 video_frame
->timestamp() != accelerated_last_frame_timestamp_
) {
340 DCHECK(context_3d
.gl
);
341 DCHECK(context_3d
.gr_context
);
342 if (accelerated_generator_
) {
343 // Reset SkBitmap used in SWVideo-to-HWCanvas path.
344 accelerated_last_frame_
.reset();
345 accelerated_generator_
= nullptr;
348 if (media::VideoFrame::NumPlanes(video_frame
->format()) == 1) {
349 accelerated_last_image_
.reset();
350 if (!CopyVideoFrameSingleTextureToSkBitmap(
351 video_frame
.get(), &accelerated_last_frame_
, context_3d
)) {
355 DCHECK(video_frame
->visible_rect().width() ==
356 accelerated_last_frame_
.width() &&
357 video_frame
->visible_rect().height() ==
358 accelerated_last_frame_
.height());
360 accelerated_last_image_
= CreateSkImageFromVideoFrameYUVTextures(
361 video_frame
.get(), context_3d
);
362 DCHECK(accelerated_last_image_
);
364 accelerated_last_frame_timestamp_
= video_frame
->timestamp();
366 target_frame
= &accelerated_last_frame_
;
367 accelerated_frame_deleting_timer_
.Reset();
368 } else if (canvas
->getGrContext()) {
369 if (accelerated_last_frame_
.isNull() ||
370 video_frame
->timestamp() != accelerated_last_frame_timestamp_
) {
371 // Draw SW Video on HW Canvas.
372 if (!accelerated_generator_
&& !accelerated_last_frame_
.isNull()) {
373 // Reset SkBitmap used in HWVideo-to-HWCanvas path.
374 accelerated_last_frame_
.reset();
376 accelerated_generator_
= new VideoImageGenerator(video_frame
);
378 // Note: This takes ownership of |accelerated_generator_|.
379 if (!SkInstallDiscardablePixelRef(accelerated_generator_
,
380 &accelerated_last_frame_
)) {
384 DCHECK(video_frame
->visible_rect().width() ==
385 accelerated_last_frame_
.width() &&
386 video_frame
->visible_rect().height() ==
387 accelerated_last_frame_
.height());
389 accelerated_last_frame_timestamp_
= video_frame
->timestamp();
390 } else if (accelerated_generator_
) {
391 accelerated_generator_
->set_frame(video_frame
);
393 target_frame
= &accelerated_last_frame_
;
394 accelerated_frame_deleting_timer_
.Reset();
396 // Draw SW Video on SW Canvas.
397 DCHECK(video_frame
->IsMappable());
398 if (last_frame_
.isNull() ||
399 video_frame
->timestamp() != last_frame_timestamp_
) {
400 // Check if |bitmap| needs to be (re)allocated.
401 if (last_frame_
.isNull() ||
402 last_frame_
.width() != video_frame
->visible_rect().width() ||
403 last_frame_
.height() != video_frame
->visible_rect().height()) {
404 last_frame_
.allocN32Pixels(video_frame
->visible_rect().width(),
405 video_frame
->visible_rect().height());
406 last_frame_
.setIsVolatile(true);
408 last_frame_
.lockPixels();
409 ConvertVideoFrameToRGBPixels(
410 video_frame
, last_frame_
.getPixels(), last_frame_
.rowBytes());
411 last_frame_
.notifyPixelsChanged();
412 last_frame_
.unlockPixels();
413 last_frame_timestamp_
= video_frame
->timestamp();
415 target_frame
= &last_frame_
;
416 frame_deleting_timer_
.Reset();
419 paint
.setXfermodeMode(mode
);
420 paint
.setFilterQuality(kLow_SkFilterQuality
);
422 const bool need_transform
=
423 video_rotation
!= VIDEO_ROTATION_0
||
424 dest_rect
.size() != video_frame
->visible_rect().size() ||
425 !dest_rect
.origin().IsOrigin();
426 if (need_transform
) {
429 SkFloatToScalar(dest_rect
.x() + (dest_rect
.width() * 0.5f
)),
430 SkFloatToScalar(dest_rect
.y() + (dest_rect
.height() * 0.5f
)));
431 SkScalar angle
= SkFloatToScalar(0.0f
);
432 switch (video_rotation
) {
433 case VIDEO_ROTATION_0
:
435 case VIDEO_ROTATION_90
:
436 angle
= SkFloatToScalar(90.0f
);
438 case VIDEO_ROTATION_180
:
439 angle
= SkFloatToScalar(180.0f
);
441 case VIDEO_ROTATION_270
:
442 angle
= SkFloatToScalar(270.0f
);
445 canvas
->rotate(angle
);
447 gfx::SizeF rotated_dest_size
= dest_rect
.size();
448 if (video_rotation
== VIDEO_ROTATION_90
||
449 video_rotation
== VIDEO_ROTATION_270
) {
451 gfx::SizeF(rotated_dest_size
.height(), rotated_dest_size
.width());
454 SkFloatToScalar(rotated_dest_size
.width() / target_frame
->width()),
455 SkFloatToScalar(rotated_dest_size
.height() / target_frame
->height()));
456 canvas
->translate(-SkFloatToScalar(target_frame
->width() * 0.5f
),
457 -SkFloatToScalar(target_frame
->height() * 0.5f
));
459 if (accelerated_last_image_
) {
460 canvas
->drawImage(accelerated_last_image_
.get(), 0, 0, &paint
);
462 canvas
->drawBitmap(*target_frame
, 0, 0, &paint
);
467 // SkCanvas::flush() causes the generator to generate SkImage, so delete
468 // |video_frame| not to be outlived.
469 if (canvas
->getGrContext() && accelerated_generator_
)
470 accelerated_generator_
->set_frame(nullptr);
473 void SkCanvasVideoRenderer::Copy(const scoped_refptr
<VideoFrame
>& video_frame
,
475 const Context3D
& context_3d
) {
476 Paint(video_frame
, canvas
, video_frame
->visible_rect(), 0xff,
477 SkXfermode::kSrc_Mode
, media::VIDEO_ROTATION_0
, context_3d
);
481 void SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
482 const scoped_refptr
<VideoFrame
>& video_frame
,
485 if (!video_frame
->IsMappable()) {
486 NOTREACHED() << "Cannot extract pixels from non-CPU frame formats.";
489 if (!VideoFrame::IsYuvPlanar(video_frame
->format())) {
490 NOTREACHED() << "Non YUV formats are not supported";
494 DCHECK_EQ(video_frame
->stride(VideoFrame::kUPlane
),
495 video_frame
->stride(VideoFrame::kVPlane
));
498 (video_frame
->format() == VideoFrame::YV16
) ? 0 : 1;
499 // Use the "left" and "top" of the destination rect to locate the offset
500 // in Y, U and V planes.
501 const size_t y_offset
= (video_frame
->stride(VideoFrame::kYPlane
) *
502 video_frame
->visible_rect().y()) +
503 video_frame
->visible_rect().x();
504 // For format YV12, there is one U, V value per 2x2 block.
505 // For format YV16, there is one U, V value per 2x1 block.
506 const size_t uv_offset
= (video_frame
->stride(VideoFrame::kUPlane
) *
507 (video_frame
->visible_rect().y() >> y_shift
)) +
508 (video_frame
->visible_rect().x() >> 1);
510 switch (video_frame
->format()) {
511 case VideoFrame::YV12
:
512 case VideoFrame::I420
:
513 if (CheckColorSpace(video_frame
, VideoFrame::COLOR_SPACE_JPEG
)) {
515 video_frame
->data(VideoFrame::kYPlane
) + y_offset
,
516 video_frame
->data(VideoFrame::kUPlane
) + uv_offset
,
517 video_frame
->data(VideoFrame::kVPlane
) + uv_offset
,
518 static_cast<uint8
*>(rgb_pixels
),
519 video_frame
->visible_rect().width(),
520 video_frame
->visible_rect().height(),
521 video_frame
->stride(VideoFrame::kYPlane
),
522 video_frame
->stride(VideoFrame::kUPlane
),
525 } else if (CheckColorSpace(video_frame
,
526 VideoFrame::COLOR_SPACE_HD_REC709
)) {
527 ConvertYUVToRGB32(video_frame
->data(VideoFrame::kYPlane
) + y_offset
,
528 video_frame
->data(VideoFrame::kUPlane
) + uv_offset
,
529 video_frame
->data(VideoFrame::kVPlane
) + uv_offset
,
530 static_cast<uint8
*>(rgb_pixels
),
531 video_frame
->visible_rect().width(),
532 video_frame
->visible_rect().height(),
533 video_frame
->stride(VideoFrame::kYPlane
),
534 video_frame
->stride(VideoFrame::kUPlane
), row_bytes
,
538 video_frame
->data(VideoFrame::kYPlane
) + y_offset
,
539 video_frame
->stride(VideoFrame::kYPlane
),
540 video_frame
->data(VideoFrame::kUPlane
) + uv_offset
,
541 video_frame
->stride(VideoFrame::kUPlane
),
542 video_frame
->data(VideoFrame::kVPlane
) + uv_offset
,
543 video_frame
->stride(VideoFrame::kVPlane
),
544 static_cast<uint8
*>(rgb_pixels
),
546 video_frame
->visible_rect().width(),
547 video_frame
->visible_rect().height());
550 case VideoFrame::YV16
:
552 video_frame
->data(VideoFrame::kYPlane
) + y_offset
,
553 video_frame
->stride(VideoFrame::kYPlane
),
554 video_frame
->data(VideoFrame::kUPlane
) + uv_offset
,
555 video_frame
->stride(VideoFrame::kUPlane
),
556 video_frame
->data(VideoFrame::kVPlane
) + uv_offset
,
557 video_frame
->stride(VideoFrame::kVPlane
),
558 static_cast<uint8
*>(rgb_pixels
),
560 video_frame
->visible_rect().width(),
561 video_frame
->visible_rect().height());
564 case VideoFrame::YV12A
:
565 // Since libyuv doesn't support YUVA, fallback to media, which is not ARM
567 // TODO(fbarchard, mtomasz): Use libyuv, then copy the alpha channel.
569 video_frame
->data(VideoFrame::kYPlane
) + y_offset
,
570 video_frame
->data(VideoFrame::kUPlane
) + uv_offset
,
571 video_frame
->data(VideoFrame::kVPlane
) + uv_offset
,
572 video_frame
->data(VideoFrame::kAPlane
),
573 static_cast<uint8
*>(rgb_pixels
),
574 video_frame
->visible_rect().width(),
575 video_frame
->visible_rect().height(),
576 video_frame
->stride(VideoFrame::kYPlane
),
577 video_frame
->stride(VideoFrame::kUPlane
),
578 video_frame
->stride(VideoFrame::kAPlane
),
583 case VideoFrame::YV24
:
585 video_frame
->data(VideoFrame::kYPlane
) + y_offset
,
586 video_frame
->stride(VideoFrame::kYPlane
),
587 video_frame
->data(VideoFrame::kUPlane
) + uv_offset
,
588 video_frame
->stride(VideoFrame::kUPlane
),
589 video_frame
->data(VideoFrame::kVPlane
) + uv_offset
,
590 video_frame
->stride(VideoFrame::kVPlane
),
591 static_cast<uint8
*>(rgb_pixels
),
593 video_frame
->visible_rect().width(),
594 video_frame
->visible_rect().height());
595 #if SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \
597 libyuv::ARGBToABGR(static_cast<uint8
*>(rgb_pixels
),
599 static_cast<uint8
*>(rgb_pixels
),
601 video_frame
->visible_rect().width(),
602 video_frame
->visible_rect().height());
605 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
606 case VideoFrame::NV12
:
608 case VideoFrame::ARGB
:
609 case VideoFrame::XRGB
:
610 case VideoFrame::UNKNOWN
:
616 void SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
617 gpu::gles2::GLES2Interface
* gl
,
618 VideoFrame
* video_frame
,
619 unsigned int texture
,
620 unsigned int internal_format
,
622 bool premultiply_alpha
,
625 DCHECK(video_frame
->HasTextures());
626 DCHECK_EQ(1u, VideoFrame::NumPlanes(video_frame
->format()));
628 const gpu::MailboxHolder
& mailbox_holder
= video_frame
->mailbox_holder(0);
629 DCHECK(mailbox_holder
.texture_target
== GL_TEXTURE_2D
||
630 mailbox_holder
.texture_target
== GL_TEXTURE_RECTANGLE_ARB
||
631 mailbox_holder
.texture_target
== GL_TEXTURE_EXTERNAL_OES
);
633 gl
->WaitSyncPointCHROMIUM(mailbox_holder
.sync_point
);
634 uint32 source_texture
= gl
->CreateAndConsumeTextureCHROMIUM(
635 mailbox_holder
.texture_target
, mailbox_holder
.mailbox
.name
);
637 // The video is stored in a unmultiplied format, so premultiply
639 gl
->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM
, premultiply_alpha
);
640 // Application itself needs to take care of setting the right |flip_y|
641 // value down to get the expected result.
642 // "flip_y == true" means to reverse the video orientation while
643 // "flip_y == false" means to keep the intrinsic orientation.
644 gl
->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM
, flip_y
);
645 gl
->CopyTextureCHROMIUM(GL_TEXTURE_2D
, source_texture
, texture
,
646 internal_format
, type
);
647 gl
->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM
, false);
648 gl
->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM
, false);
650 gl
->DeleteTextures(1, &source_texture
);
653 SyncPointClientImpl
client(gl
);
654 video_frame
->UpdateReleaseSyncPoint(&client
);
657 void SkCanvasVideoRenderer::ResetLastFrame() {
659 last_frame_timestamp_
= media::kNoTimestamp();
662 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() {
663 accelerated_last_image_
.reset();
664 accelerated_last_frame_
.reset();
665 accelerated_generator_
= nullptr;
666 accelerated_last_frame_timestamp_
= media::kNoTimestamp();