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 "ui/gfx/geometry/rect_f.h"
24 // Skia internal format depends on a platform. On Android it is ABGR, on others
26 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \
28 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB
29 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB
30 #define LIBYUV_I420ALPHA_TO_ARGB libyuv::I420AlphaToARGB
31 #define LIBYUV_J420_TO_ARGB libyuv::J420ToARGB
32 #define LIBYUV_H420_TO_ARGB libyuv::H420ToARGB
33 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \
35 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR
36 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR
37 #define LIBYUV_I420ALPHA_TO_ARGB libyuv::I420AlphaToABGR
38 #define LIBYUV_J420_TO_ARGB libyuv::J420ToABGR
39 #define LIBYUV_H420_TO_ARGB libyuv::H420ToABGR
41 #error Unexpected Skia ARGB_8888 layout!
48 // This class keeps the last image drawn.
49 // We delete the temporary resource if it is not used for 3 seconds.
50 const int kTemporaryResourceDeletionDelay
= 3; // Seconds;
52 bool CheckColorSpace(const VideoFrame
* video_frame
, ColorSpace color_space
) {
54 return video_frame
->metadata()->GetInteger(
55 VideoFrameMetadata::COLOR_SPACE
, &result
) &&
56 result
== color_space
;
59 class SyncPointClientImpl
: public VideoFrame::SyncPointClient
{
61 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface
* gl
) : gl_(gl
) {}
62 ~SyncPointClientImpl() override
{}
63 uint32
InsertSyncPoint() override
{ return gl_
->InsertSyncPointCHROMIUM(); }
64 void WaitSyncPoint(uint32 sync_point
) override
{
65 gl_
->WaitSyncPointCHROMIUM(sync_point
);
69 gpu::gles2::GLES2Interface
* gl_
;
71 DISALLOW_IMPLICIT_CONSTRUCTORS(SyncPointClientImpl
);
74 skia::RefPtr
<SkImage
> NewSkImageFromVideoFrameYUVTextures(
75 const VideoFrame
* video_frame
,
76 const Context3D
& context_3d
) {
77 // Support only TEXTURE_YUV_420.
78 DCHECK(video_frame
->HasTextures());
79 DCHECK_EQ(media::PIXEL_FORMAT_I420
, video_frame
->format());
80 DCHECK_EQ(3u, media::VideoFrame::NumPlanes(video_frame
->format()));
82 gpu::gles2::GLES2Interface
* gl
= context_3d
.gl
;
84 gfx::Size ya_tex_size
= video_frame
->coded_size();
85 gfx::Size
uv_tex_size((ya_tex_size
.width() + 1) / 2,
86 (ya_tex_size
.height() + 1) / 2);
88 unsigned source_textures
[3] = {0};
89 for (size_t i
= 0; i
< media::VideoFrame::NumPlanes(video_frame
->format());
91 // Get the texture from the mailbox and wrap it in a GrTexture.
92 const gpu::MailboxHolder
& mailbox_holder
= video_frame
->mailbox_holder(i
);
93 DCHECK(mailbox_holder
.texture_target
== GL_TEXTURE_2D
||
94 mailbox_holder
.texture_target
== GL_TEXTURE_EXTERNAL_OES
||
95 mailbox_holder
.texture_target
== GL_TEXTURE_RECTANGLE_ARB
);
96 gl
->WaitSyncPointCHROMIUM(mailbox_holder
.sync_point
);
97 source_textures
[i
] = gl
->CreateAndConsumeTextureCHROMIUM(
98 mailbox_holder
.texture_target
, mailbox_holder
.mailbox
.name
);
100 // TODO(dcastagna): avoid this copy once Skia supports native textures
101 // with a texture target different than TEXTURE_2D.
103 if (mailbox_holder
.texture_target
!= GL_TEXTURE_2D
) {
104 unsigned texture_copy
= 0;
105 gl
->GenTextures(1, &texture_copy
);
106 DCHECK(texture_copy
);
107 gl
->BindTexture(GL_TEXTURE_2D
, texture_copy
);
108 gl
->CopyTextureCHROMIUM(GL_TEXTURE_2D
, source_textures
[i
], texture_copy
,
109 GL_RGB
, GL_UNSIGNED_BYTE
, false, true, false);
111 gl
->DeleteTextures(1, &source_textures
[i
]);
112 source_textures
[i
] = texture_copy
;
115 GrBackendObject handles
[3] = {
116 source_textures
[0], source_textures
[1], source_textures
[2]};
118 SkISize yuvSizes
[] = {
119 {ya_tex_size
.width(), ya_tex_size
.height()},
120 {uv_tex_size
.width(), uv_tex_size
.height()},
121 {uv_tex_size
.width(), uv_tex_size
.height()},
124 SkYUVColorSpace color_space
= kRec601_SkYUVColorSpace
;
125 if (CheckColorSpace(video_frame
, media::COLOR_SPACE_JPEG
))
126 color_space
= kJPEG_SkYUVColorSpace
;
127 else if (CheckColorSpace(video_frame
, media::COLOR_SPACE_HD_REC709
))
128 color_space
= kRec709_SkYUVColorSpace
;
130 SkImage
* img
= SkImage::NewFromYUVTexturesCopy(context_3d
.gr_context
,
131 color_space
, handles
, yuvSizes
,
132 kTopLeft_GrSurfaceOrigin
);
133 gl
->DeleteTextures(3, source_textures
);
134 return skia::AdoptRef(img
);
137 // Creates a SkImage from a |video_frame| backed by native resources.
138 // The SkImage will take ownership of the underlying resource.
139 skia::RefPtr
<SkImage
> NewSkImageFromVideoFrameNative(
140 VideoFrame
* video_frame
,
141 const Context3D
& context_3d
) {
142 DCHECK(PIXEL_FORMAT_ARGB
== video_frame
->format() ||
143 PIXEL_FORMAT_NV12
== video_frame
->format() ||
144 PIXEL_FORMAT_UYVY
== video_frame
->format());
146 const gpu::MailboxHolder
& mailbox_holder
= video_frame
->mailbox_holder(0);
147 DCHECK(mailbox_holder
.texture_target
== GL_TEXTURE_2D
||
148 mailbox_holder
.texture_target
== GL_TEXTURE_RECTANGLE_ARB
||
149 mailbox_holder
.texture_target
== GL_TEXTURE_EXTERNAL_OES
)
150 << mailbox_holder
.texture_target
;
152 gpu::gles2::GLES2Interface
* gl
= context_3d
.gl
;
153 unsigned source_texture
= 0;
154 if (mailbox_holder
.texture_target
!= GL_TEXTURE_2D
) {
155 // TODO(dcastagna): At the moment Skia doesn't support targets different
156 // than GL_TEXTURE_2D. Avoid this copy once
157 // https://code.google.com/p/skia/issues/detail?id=3868 is addressed.
158 gl
->GenTextures(1, &source_texture
);
159 DCHECK(source_texture
);
160 gl
->BindTexture(GL_TEXTURE_2D
, source_texture
);
161 SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
162 gl
, video_frame
, source_texture
, GL_RGBA
, GL_UNSIGNED_BYTE
, true,
165 gl
->WaitSyncPointCHROMIUM(mailbox_holder
.sync_point
);
166 source_texture
= gl
->CreateAndConsumeTextureCHROMIUM(
167 mailbox_holder
.texture_target
, mailbox_holder
.mailbox
.name
);
169 GrBackendTextureDesc desc
;
170 desc
.fFlags
= kRenderTarget_GrBackendTextureFlag
;
171 desc
.fOrigin
= kTopLeft_GrSurfaceOrigin
;
172 desc
.fWidth
= video_frame
->coded_size().width();
173 desc
.fHeight
= video_frame
->coded_size().height();
174 desc
.fConfig
= kRGBA_8888_GrPixelConfig
;
175 desc
.fTextureHandle
= source_texture
;
176 return skia::AdoptRef(
177 SkImage::NewFromAdoptedTexture(context_3d
.gr_context
, desc
));
180 } // anonymous namespace
182 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU.
183 class VideoImageGenerator
: public SkImageGenerator
{
185 VideoImageGenerator(const scoped_refptr
<VideoFrame
>& frame
)
187 SkImageInfo::MakeN32Premul(frame
->visible_rect().width(),
188 frame
->visible_rect().height())),
190 DCHECK(!frame_
->HasTextures());
192 ~VideoImageGenerator() override
{}
195 bool onGetPixels(const SkImageInfo
& info
,
199 int* ctable_count
) override
{
200 // If skia couldn't do the YUV conversion on GPU, we will on CPU.
201 SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(frame_
.get(), pixels
,
206 bool onGetYUV8Planes(SkISize sizes
[3],
209 SkYUVColorSpace
* color_space
) override
{
210 if (!media::IsYuvPlanar(frame_
->format()) ||
211 // TODO(rileya): Skia currently doesn't support YUVA conversion. Remove
212 // this case once it does. As-is we will fall back on the pure-software
213 // path in this case.
214 frame_
->format() == PIXEL_FORMAT_YV12A
) {
219 if (CheckColorSpace(frame_
.get(), COLOR_SPACE_JPEG
))
220 *color_space
= kJPEG_SkYUVColorSpace
;
221 else if (CheckColorSpace(frame_
.get(), COLOR_SPACE_HD_REC709
))
222 *color_space
= kRec709_SkYUVColorSpace
;
224 *color_space
= kRec601_SkYUVColorSpace
;
227 for (int plane
= VideoFrame::kYPlane
; plane
<= VideoFrame::kVPlane
;
230 const gfx::Size size
=
231 VideoFrame::PlaneSize(frame_
->format(), plane
,
232 gfx::Size(frame_
->visible_rect().width(),
233 frame_
->visible_rect().height()));
234 sizes
[plane
].set(size
.width(), size
.height());
236 if (row_bytes
&& planes
) {
239 (frame_
->format() == media::PIXEL_FORMAT_YV16
) ? 0 : 1;
240 if (plane
== VideoFrame::kYPlane
) {
241 offset
= (frame_
->stride(VideoFrame::kYPlane
) *
242 frame_
->visible_rect().y()) +
243 frame_
->visible_rect().x();
245 offset
= (frame_
->stride(VideoFrame::kUPlane
) *
246 (frame_
->visible_rect().y() >> y_shift
)) +
247 (frame_
->visible_rect().x() >> 1);
250 // Copy the frame to the supplied memory.
251 // TODO: Find a way (API change?) to avoid this copy.
252 char* out_line
= static_cast<char*>(planes
[plane
]);
253 int out_line_stride
= row_bytes
[plane
];
254 uint8
* in_line
= frame_
->data(plane
) + offset
;
255 int in_line_stride
= frame_
->stride(plane
);
256 int plane_height
= sizes
[plane
].height();
257 if (in_line_stride
== out_line_stride
) {
258 memcpy(out_line
, in_line
, plane_height
* in_line_stride
);
260 // Different line padding so need to copy one line at a time.
261 int bytes_to_copy_per_line
= out_line_stride
< in_line_stride
264 for (int line_no
= 0; line_no
< plane_height
; line_no
++) {
265 memcpy(out_line
, in_line
, bytes_to_copy_per_line
);
266 in_line
+= in_line_stride
;
267 out_line
+= out_line_stride
;
276 scoped_refptr
<VideoFrame
> frame_
;
278 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator
);
281 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
282 : last_image_deleting_timer_(
284 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay
),
286 &SkCanvasVideoRenderer::ResetCache
) {}
288 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {
292 void SkCanvasVideoRenderer::Paint(const scoped_refptr
<VideoFrame
>& video_frame
,
294 const gfx::RectF
& dest_rect
,
296 SkXfermode::Mode mode
,
297 VideoRotation video_rotation
,
298 const Context3D
& context_3d
) {
304 dest
.set(dest_rect
.x(), dest_rect
.y(), dest_rect
.right(), dest_rect
.bottom());
307 paint
.setAlpha(alpha
);
309 // Paint black rectangle if there isn't a frame available or the
310 // frame has an unexpected format.
311 if (!video_frame
.get() || video_frame
->natural_size().IsEmpty() ||
312 !(media::IsYuvPlanar(video_frame
->format()) ||
313 video_frame
->HasTextures())) {
314 canvas
->drawRect(dest
, paint
);
319 gpu::gles2::GLES2Interface
* gl
= context_3d
.gl
;
321 if (!last_image_
|| video_frame
->timestamp() != last_timestamp_
) {
323 // Generate a new image.
324 // Note: Skia will hold onto |video_frame| via |video_generator| only when
325 // |video_frame| is software.
326 // Holding |video_frame| longer than this call when using GPUVideoDecoder
327 // could cause problems since the pool of VideoFrames has a fixed size.
328 if (video_frame
->HasTextures()) {
329 DCHECK(context_3d
.gr_context
);
331 if (media::VideoFrame::NumPlanes(video_frame
->format()) == 1) {
333 NewSkImageFromVideoFrameNative(video_frame
.get(), context_3d
);
336 NewSkImageFromVideoFrameYUVTextures(video_frame
.get(), context_3d
);
339 auto video_generator
= new VideoImageGenerator(video_frame
);
340 last_image_
= skia::AdoptRef(SkImage::NewFromGenerator(video_generator
));
342 if (!last_image_
) // Couldn't create the SkImage.
344 last_timestamp_
= video_frame
->timestamp();
346 last_image_deleting_timer_
.Reset();
348 paint
.setXfermodeMode(mode
);
349 paint
.setFilterQuality(kLow_SkFilterQuality
);
351 const bool need_transform
=
352 video_rotation
!= VIDEO_ROTATION_0
||
353 dest_rect
.size() != video_frame
->visible_rect().size() ||
354 !dest_rect
.origin().IsOrigin();
355 if (need_transform
) {
358 SkFloatToScalar(dest_rect
.x() + (dest_rect
.width() * 0.5f
)),
359 SkFloatToScalar(dest_rect
.y() + (dest_rect
.height() * 0.5f
)));
360 SkScalar angle
= SkFloatToScalar(0.0f
);
361 switch (video_rotation
) {
362 case VIDEO_ROTATION_0
:
364 case VIDEO_ROTATION_90
:
365 angle
= SkFloatToScalar(90.0f
);
367 case VIDEO_ROTATION_180
:
368 angle
= SkFloatToScalar(180.0f
);
370 case VIDEO_ROTATION_270
:
371 angle
= SkFloatToScalar(270.0f
);
374 canvas
->rotate(angle
);
376 gfx::SizeF rotated_dest_size
= dest_rect
.size();
377 if (video_rotation
== VIDEO_ROTATION_90
||
378 video_rotation
== VIDEO_ROTATION_270
) {
380 gfx::SizeF(rotated_dest_size
.height(), rotated_dest_size
.width());
383 SkFloatToScalar(rotated_dest_size
.width() / last_image_
->width()),
384 SkFloatToScalar(rotated_dest_size
.height() / last_image_
->height()));
385 canvas
->translate(-SkFloatToScalar(last_image_
->width() * 0.5f
),
386 -SkFloatToScalar(last_image_
->height() * 0.5f
));
389 // This is a workaround for crbug.com/524717. SkBitmaps are read back before a
390 // SkPicture is sent to multiple threads while SkImages are not. The long term
391 // solution is for Skia to provide a SkPicture filter that makes a picture
392 // safe for multiple CPU raster threads (skbug.com/4321). We limit the
393 // workaround to cases where the src frame is a texture and the canvas is
395 if (last_image_
.get()->getTexture() &&
396 canvas
->imageInfo().colorType() == kUnknown_SkColorType
) {
398 GrWrapTextureInBitmap(last_image_
.get()->getTexture(),
399 last_image_
.get()->width(), last_image_
.get()->height(), true, &bmp
);
400 // Even though the bitmap is logically immutable we do not mark it as such
401 // because doing so would defer readback until rasterization, which will be
402 // on another thread and is therefore unsafe.
403 canvas
->drawBitmap(bmp
, 0, 0, &paint
);
405 canvas
->drawImage(last_image_
.get(), 0, 0, &paint
);
410 // Make sure to flush so we can remove the videoframe from the generator.
413 if (video_frame
->HasTextures()) {
415 SyncPointClientImpl
client(gl
);
416 video_frame
->UpdateReleaseSyncPoint(&client
);
420 void SkCanvasVideoRenderer::Copy(const scoped_refptr
<VideoFrame
>& video_frame
,
422 const Context3D
& context_3d
) {
423 Paint(video_frame
, canvas
, gfx::RectF(video_frame
->visible_rect()), 0xff,
424 SkXfermode::kSrc_Mode
, media::VIDEO_ROTATION_0
, context_3d
);
428 void SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
429 const VideoFrame
* video_frame
,
432 if (!video_frame
->IsMappable()) {
433 NOTREACHED() << "Cannot extract pixels from non-CPU frame formats.";
436 if (!media::IsYuvPlanar(video_frame
->format())) {
437 NOTREACHED() << "Non YUV formats are not supported";
440 DCHECK_EQ(video_frame
->stride(VideoFrame::kUPlane
),
441 video_frame
->stride(VideoFrame::kVPlane
));
443 switch (video_frame
->format()) {
444 case PIXEL_FORMAT_YV12
:
445 case PIXEL_FORMAT_I420
:
446 if (CheckColorSpace(video_frame
, COLOR_SPACE_JPEG
)) {
448 video_frame
->visible_data(VideoFrame::kYPlane
),
449 video_frame
->stride(VideoFrame::kYPlane
),
450 video_frame
->visible_data(VideoFrame::kUPlane
),
451 video_frame
->stride(VideoFrame::kUPlane
),
452 video_frame
->visible_data(VideoFrame::kVPlane
),
453 video_frame
->stride(VideoFrame::kVPlane
),
454 static_cast<uint8
*>(rgb_pixels
),
456 video_frame
->visible_rect().width(),
457 video_frame
->visible_rect().height());
458 } else if (CheckColorSpace(video_frame
, COLOR_SPACE_HD_REC709
)) {
460 video_frame
->visible_data(VideoFrame::kYPlane
),
461 video_frame
->stride(VideoFrame::kYPlane
),
462 video_frame
->visible_data(VideoFrame::kUPlane
),
463 video_frame
->stride(VideoFrame::kUPlane
),
464 video_frame
->visible_data(VideoFrame::kVPlane
),
465 video_frame
->stride(VideoFrame::kVPlane
),
466 static_cast<uint8
*>(rgb_pixels
),
468 video_frame
->visible_rect().width(),
469 video_frame
->visible_rect().height());
472 video_frame
->visible_data(VideoFrame::kYPlane
),
473 video_frame
->stride(VideoFrame::kYPlane
),
474 video_frame
->visible_data(VideoFrame::kUPlane
),
475 video_frame
->stride(VideoFrame::kUPlane
),
476 video_frame
->visible_data(VideoFrame::kVPlane
),
477 video_frame
->stride(VideoFrame::kVPlane
),
478 static_cast<uint8
*>(rgb_pixels
),
480 video_frame
->visible_rect().width(),
481 video_frame
->visible_rect().height());
484 case PIXEL_FORMAT_YV16
:
486 video_frame
->visible_data(VideoFrame::kYPlane
),
487 video_frame
->stride(VideoFrame::kYPlane
),
488 video_frame
->visible_data(VideoFrame::kUPlane
),
489 video_frame
->stride(VideoFrame::kUPlane
),
490 video_frame
->visible_data(VideoFrame::kVPlane
),
491 video_frame
->stride(VideoFrame::kVPlane
),
492 static_cast<uint8
*>(rgb_pixels
),
494 video_frame
->visible_rect().width(),
495 video_frame
->visible_rect().height());
498 case PIXEL_FORMAT_YV12A
:
499 LIBYUV_I420ALPHA_TO_ARGB(
500 video_frame
->visible_data(VideoFrame::kYPlane
),
501 video_frame
->stride(VideoFrame::kYPlane
),
502 video_frame
->visible_data(VideoFrame::kUPlane
),
503 video_frame
->stride(VideoFrame::kUPlane
),
504 video_frame
->visible_data(VideoFrame::kVPlane
),
505 video_frame
->stride(VideoFrame::kVPlane
),
506 video_frame
->visible_data(VideoFrame::kAPlane
),
507 video_frame
->stride(VideoFrame::kAPlane
),
508 static_cast<uint8
*>(rgb_pixels
),
510 video_frame
->visible_rect().width(),
511 video_frame
->visible_rect().height());
514 case PIXEL_FORMAT_YV24
:
516 video_frame
->visible_data(VideoFrame::kYPlane
),
517 video_frame
->stride(VideoFrame::kYPlane
),
518 video_frame
->visible_data(VideoFrame::kUPlane
),
519 video_frame
->stride(VideoFrame::kUPlane
),
520 video_frame
->visible_data(VideoFrame::kVPlane
),
521 video_frame
->stride(VideoFrame::kVPlane
),
522 static_cast<uint8
*>(rgb_pixels
),
524 video_frame
->visible_rect().width(),
525 video_frame
->visible_rect().height());
526 #if SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \
528 libyuv::ARGBToABGR(static_cast<uint8
*>(rgb_pixels
),
530 static_cast<uint8
*>(rgb_pixels
),
532 video_frame
->visible_rect().width(),
533 video_frame
->visible_rect().height());
536 case PIXEL_FORMAT_NV12
:
537 case PIXEL_FORMAT_NV21
:
538 case PIXEL_FORMAT_UYVY
:
539 case PIXEL_FORMAT_YUY2
:
540 case PIXEL_FORMAT_ARGB
:
541 case PIXEL_FORMAT_XRGB
:
542 case PIXEL_FORMAT_RGB24
:
543 case PIXEL_FORMAT_RGB32
:
544 case PIXEL_FORMAT_MJPEG
:
545 case PIXEL_FORMAT_MT21
:
546 case PIXEL_FORMAT_UNKNOWN
:
552 void SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
553 gpu::gles2::GLES2Interface
* gl
,
554 VideoFrame
* video_frame
,
555 unsigned int texture
,
556 unsigned int internal_format
,
558 bool premultiply_alpha
,
561 DCHECK(video_frame
->HasTextures());
562 DCHECK_EQ(1u, VideoFrame::NumPlanes(video_frame
->format()));
564 const gpu::MailboxHolder
& mailbox_holder
= video_frame
->mailbox_holder(0);
565 DCHECK(mailbox_holder
.texture_target
== GL_TEXTURE_2D
||
566 mailbox_holder
.texture_target
== GL_TEXTURE_RECTANGLE_ARB
||
567 mailbox_holder
.texture_target
== GL_TEXTURE_EXTERNAL_OES
)
568 << mailbox_holder
.texture_target
;
570 gl
->WaitSyncPointCHROMIUM(mailbox_holder
.sync_point
);
571 uint32 source_texture
= gl
->CreateAndConsumeTextureCHROMIUM(
572 mailbox_holder
.texture_target
, mailbox_holder
.mailbox
.name
);
574 // The video is stored in a unmultiplied format, so premultiply
576 // Application itself needs to take care of setting the right |flip_y|
577 // value down to get the expected result.
578 // "flip_y == true" means to reverse the video orientation while
579 // "flip_y == false" means to keep the intrinsic orientation.
580 gl
->CopyTextureCHROMIUM(GL_TEXTURE_2D
, source_texture
, texture
,
581 internal_format
, type
,
582 flip_y
, premultiply_alpha
, false);
584 gl
->DeleteTextures(1, &source_texture
);
587 SyncPointClientImpl
client(gl
);
588 video_frame
->UpdateReleaseSyncPoint(&client
);
591 void SkCanvasVideoRenderer::ResetCache() {
592 // Clear cached values.
593 last_image_
= nullptr;
594 last_timestamp_
= kNoTimestamp();