downloads.ItemView -> downloads.Item
[chromium-blink-merge.git] / media / blink / skcanvas_video_renderer.cc
blob16c7d87b4c341bce711b083936c8f3a8c4da04e7
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
26 // it is ARGB.
27 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \
28 SK_A32_SHIFT == 24
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 && \
32 SK_A32_SHIFT == 24
33 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR
34 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR
35 #else
36 #error Unexpected Skia ARGB_8888 layout!
37 #endif
39 namespace media {
41 namespace {
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 ColorSpace color_space) {
51 int result;
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,
64 SkBitmap* bitmap,
65 const gfx::Size& size) {
66 DCHECK(gr);
67 GrTextureDesc desc;
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;
72 desc.fSampleCnt = 0;
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));
79 if (!texture.get())
80 return false;
82 SkImageInfo info = SkImageInfo::MakeN32Premul(desc.fWidth, desc.fHeight);
83 SkGrPixelRef* pixel_ref = SkNEW_ARGS(SkGrPixelRef, (info, texture.get()));
84 if (!pixel_ref)
85 return false;
86 bitmap->setInfo(info);
87 bitmap->setPixelRef(pixel_ref)->unref();
88 return true;
91 class SyncPointClientImpl : public VideoFrame::SyncPointClient {
92 public:
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);
100 private:
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::PIXEL_FORMAT_I420, video_frame->format());
112 DCHECK_EQ(3u, media::VideoFrame::NumPlanes(video_frame->format()));
114 gpu::gles2::GLES2Interface* gl = context_3d.gl;
115 DCHECK(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());
122 ++i) {
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 // TODO(dcastagna): avoid this copy once Skia supports native textures
133 // with a texture target different than TEXTURE_2D.
134 // crbug.com/505026
135 if (mailbox_holder.texture_target != GL_TEXTURE_2D) {
136 unsigned texture_copy = 0;
137 gl->GenTextures(1, &texture_copy);
138 DCHECK(texture_copy);
139 gl->BindTexture(GL_TEXTURE_2D, texture_copy);
140 gl->CopyTextureCHROMIUM(GL_TEXTURE_2D, source_textures[i], texture_copy,
141 GL_RGB, GL_UNSIGNED_BYTE, false, true, false);
143 gl->DeleteTextures(1, &source_textures[i]);
144 source_textures[i] = texture_copy;
147 GrBackendObject handles[3] = {
148 source_textures[0], source_textures[1], source_textures[2]};
150 SkISize yuvSizes[] = {
151 {ya_tex_size.width(), ya_tex_size.height()},
152 {uv_tex_size.width(), uv_tex_size.height()},
153 {uv_tex_size.width(), uv_tex_size.height()},
156 // TODO(dcastagna): Skia currently doesn't support Rec709 YUV conversion.
157 DCHECK(!CheckColorSpace(video_frame, media::COLOR_SPACE_HD_REC709));
158 SkYUVColorSpace color_space = kRec601_SkYUVColorSpace;
159 if (CheckColorSpace(video_frame, media::COLOR_SPACE_JPEG))
160 color_space = kJPEG_SkYUVColorSpace;
162 SkImage* img = SkImage::NewFromYUVTexturesCopy(context_3d.gr_context,
163 color_space, handles, yuvSizes,
164 kTopLeft_GrSurfaceOrigin);
165 DCHECK(img);
166 gl->DeleteTextures(3, source_textures);
167 SyncPointClientImpl client(gl);
168 video_frame->UpdateReleaseSyncPoint(&client);
169 return make_scoped_ptr(img);
172 bool CopyVideoFrameSingleTextureToSkBitmap(VideoFrame* video_frame,
173 SkBitmap* bitmap,
174 const Context3D& context_3d) {
175 // Check if we could reuse existing texture based bitmap.
176 // Otherwise, release existing texture based bitmap and allocate
177 // a new one based on video size.
178 if (!IsSkBitmapProperlySizedTexture(bitmap,
179 video_frame->visible_rect().size())) {
180 if (!AllocateSkBitmapTexture(context_3d.gr_context, bitmap,
181 video_frame->visible_rect().size())) {
182 return false;
186 unsigned texture_id =
187 static_cast<unsigned>((bitmap->getTexture())->getTextureHandle());
188 // If CopyVideoFrameSingleTextureToGLTexture() changes the state of the
189 // |texture_id|, it's needed to invalidate the state cached in skia,
190 // but currently the state isn't changed.
192 SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
193 context_3d.gl, video_frame, texture_id, GL_RGBA, GL_UNSIGNED_BYTE, true,
194 false);
195 bitmap->notifyPixelsChanged();
196 return true;
199 } // anonymous namespace
201 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU.
202 class VideoImageGenerator : public SkImageGenerator {
203 public:
204 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame)
205 : SkImageGenerator(
206 SkImageInfo::MakeN32Premul(frame->visible_rect().width(),
207 frame->visible_rect().height()))
208 , frame_(frame) {
209 DCHECK(frame_.get());
211 ~VideoImageGenerator() override {}
213 void set_frame(const scoped_refptr<VideoFrame>& frame) { frame_ = frame; }
215 protected:
216 Result onGetPixels(const SkImageInfo& info,
217 void* pixels,
218 size_t row_bytes,
219 const Options&,
220 SkPMColor ctable[],
221 int* ctable_count) override {
222 if (!frame_.get())
223 return kInvalidInput;
224 // If skia couldn't do the YUV conversion on GPU, we will on CPU.
225 SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
226 frame_, pixels, row_bytes);
227 return kSuccess;
230 bool onGetYUV8Planes(SkISize sizes[3],
231 void* planes[3],
232 size_t row_bytes[3],
233 SkYUVColorSpace* color_space) override {
234 if (!frame_.get() || !media::IsYuvPlanar(frame_->format()) ||
235 // TODO(rileya): Skia currently doesn't support Rec709 YUV conversion,
236 // or YUVA conversion. Remove this case once it does. As-is we will
237 // fall back on the pure-software path in this case.
238 CheckColorSpace(frame_, COLOR_SPACE_HD_REC709) ||
239 frame_->format() == PIXEL_FORMAT_YV12A) {
240 return false;
243 if (color_space) {
244 if (CheckColorSpace(frame_, COLOR_SPACE_JPEG))
245 *color_space = kJPEG_SkYUVColorSpace;
246 else
247 *color_space = kRec601_SkYUVColorSpace;
250 for (int plane = VideoFrame::kYPlane; plane <= VideoFrame::kVPlane;
251 ++plane) {
252 if (sizes) {
253 const gfx::Size size =
254 VideoFrame::PlaneSize(frame_->format(), plane,
255 gfx::Size(frame_->visible_rect().width(),
256 frame_->visible_rect().height()));
257 sizes[plane].set(size.width(), size.height());
259 if (row_bytes && planes) {
260 size_t offset;
261 const int y_shift =
262 (frame_->format() == media::PIXEL_FORMAT_YV16) ? 0 : 1;
263 if (plane == VideoFrame::kYPlane) {
264 offset = (frame_->stride(VideoFrame::kYPlane) *
265 frame_->visible_rect().y()) +
266 frame_->visible_rect().x();
267 } else {
268 offset = (frame_->stride(VideoFrame::kUPlane) *
269 (frame_->visible_rect().y() >> y_shift)) +
270 (frame_->visible_rect().x() >> 1);
273 // Copy the frame to the supplied memory.
274 // TODO: Find a way (API change?) to avoid this copy.
275 char* out_line = static_cast<char*>(planes[plane]);
276 int out_line_stride = row_bytes[plane];
277 uint8* in_line = frame_->data(plane) + offset;
278 int in_line_stride = frame_->stride(plane);
279 int plane_height = sizes[plane].height();
280 if (in_line_stride == out_line_stride) {
281 memcpy(out_line, in_line, plane_height * in_line_stride);
282 } else {
283 // Different line padding so need to copy one line at a time.
284 int bytes_to_copy_per_line = out_line_stride < in_line_stride
285 ? out_line_stride
286 : in_line_stride;
287 for (int line_no = 0; line_no < plane_height; line_no++) {
288 memcpy(out_line, in_line, bytes_to_copy_per_line);
289 in_line += in_line_stride;
290 out_line += out_line_stride;
295 return true;
298 private:
299 scoped_refptr<VideoFrame> frame_;
301 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator);
304 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
305 : last_frame_timestamp_(media::kNoTimestamp()),
306 frame_deleting_timer_(
307 FROM_HERE,
308 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay),
309 this,
310 &SkCanvasVideoRenderer::ResetLastFrame),
311 accelerated_generator_(nullptr),
312 accelerated_last_frame_timestamp_(media::kNoTimestamp()),
313 accelerated_frame_deleting_timer_(
314 FROM_HERE,
315 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay),
316 this,
317 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) {
318 last_frame_.setIsVolatile(true);
321 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
323 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
324 SkCanvas* canvas,
325 const gfx::RectF& dest_rect,
326 uint8 alpha,
327 SkXfermode::Mode mode,
328 VideoRotation video_rotation,
329 const Context3D& context_3d) {
330 if (alpha == 0) {
331 return;
334 SkRect dest;
335 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom());
337 SkPaint paint;
338 paint.setAlpha(alpha);
340 // Paint black rectangle if there isn't a frame available or the
341 // frame has an unexpected format.
342 if (!video_frame.get() || video_frame->natural_size().IsEmpty() ||
343 !(media::IsYuvPlanar(video_frame->format()) ||
344 video_frame->HasTextures())) {
345 canvas->drawRect(dest, paint);
346 canvas->flush();
347 return;
350 SkBitmap* target_frame = nullptr;
352 if (video_frame->HasTextures()) {
353 // Draw HW Video on both SW and HW Canvas.
354 // In SW Canvas case, rely on skia drawing Ganesh SkBitmap on SW SkCanvas.
355 if (accelerated_last_frame_.isNull() ||
356 video_frame->timestamp() != accelerated_last_frame_timestamp_) {
357 DCHECK(context_3d.gl);
358 DCHECK(context_3d.gr_context);
359 if (accelerated_generator_) {
360 // Reset SkBitmap used in SWVideo-to-HWCanvas path.
361 accelerated_last_frame_.reset();
362 accelerated_generator_ = nullptr;
365 if (media::VideoFrame::NumPlanes(video_frame->format()) == 1) {
366 accelerated_last_image_.reset();
367 if (!CopyVideoFrameSingleTextureToSkBitmap(
368 video_frame.get(), &accelerated_last_frame_, context_3d)) {
369 NOTREACHED();
370 return;
372 DCHECK(video_frame->visible_rect().width() ==
373 accelerated_last_frame_.width() &&
374 video_frame->visible_rect().height() ==
375 accelerated_last_frame_.height());
376 } else {
377 accelerated_last_image_ = CreateSkImageFromVideoFrameYUVTextures(
378 video_frame.get(), context_3d);
379 DCHECK(accelerated_last_image_);
381 accelerated_last_frame_timestamp_ = video_frame->timestamp();
383 target_frame = &accelerated_last_frame_;
384 accelerated_frame_deleting_timer_.Reset();
385 } else if (canvas->getGrContext()) {
386 if (accelerated_last_frame_.isNull() ||
387 video_frame->timestamp() != accelerated_last_frame_timestamp_) {
388 // Draw SW Video on HW Canvas.
389 if (!accelerated_generator_ && !accelerated_last_frame_.isNull()) {
390 // Reset SkBitmap used in HWVideo-to-HWCanvas path.
391 accelerated_last_frame_.reset();
393 accelerated_generator_ = new VideoImageGenerator(video_frame);
395 // Note: This takes ownership of |accelerated_generator_|.
396 if (!SkInstallDiscardablePixelRef(accelerated_generator_,
397 &accelerated_last_frame_)) {
398 NOTREACHED();
399 return;
401 DCHECK(video_frame->visible_rect().width() ==
402 accelerated_last_frame_.width() &&
403 video_frame->visible_rect().height() ==
404 accelerated_last_frame_.height());
406 accelerated_last_frame_timestamp_ = video_frame->timestamp();
407 } else if (accelerated_generator_) {
408 accelerated_generator_->set_frame(video_frame);
410 target_frame = &accelerated_last_frame_;
411 accelerated_frame_deleting_timer_.Reset();
412 } else {
413 // Draw SW Video on SW Canvas.
414 DCHECK(video_frame->IsMappable());
415 if (last_frame_.isNull() ||
416 video_frame->timestamp() != last_frame_timestamp_) {
417 // Check if |bitmap| needs to be (re)allocated.
418 if (last_frame_.isNull() ||
419 last_frame_.width() != video_frame->visible_rect().width() ||
420 last_frame_.height() != video_frame->visible_rect().height()) {
421 last_frame_.allocN32Pixels(video_frame->visible_rect().width(),
422 video_frame->visible_rect().height());
423 last_frame_.setIsVolatile(true);
425 last_frame_.lockPixels();
426 ConvertVideoFrameToRGBPixels(
427 video_frame, last_frame_.getPixels(), last_frame_.rowBytes());
428 last_frame_.notifyPixelsChanged();
429 last_frame_.unlockPixels();
430 last_frame_timestamp_ = video_frame->timestamp();
432 target_frame = &last_frame_;
433 frame_deleting_timer_.Reset();
436 paint.setXfermodeMode(mode);
437 paint.setFilterQuality(kLow_SkFilterQuality);
439 const bool need_transform =
440 video_rotation != VIDEO_ROTATION_0 ||
441 dest_rect.size() != video_frame->visible_rect().size() ||
442 !dest_rect.origin().IsOrigin();
443 if (need_transform) {
444 canvas->save();
445 canvas->translate(
446 SkFloatToScalar(dest_rect.x() + (dest_rect.width() * 0.5f)),
447 SkFloatToScalar(dest_rect.y() + (dest_rect.height() * 0.5f)));
448 SkScalar angle = SkFloatToScalar(0.0f);
449 switch (video_rotation) {
450 case VIDEO_ROTATION_0:
451 break;
452 case VIDEO_ROTATION_90:
453 angle = SkFloatToScalar(90.0f);
454 break;
455 case VIDEO_ROTATION_180:
456 angle = SkFloatToScalar(180.0f);
457 break;
458 case VIDEO_ROTATION_270:
459 angle = SkFloatToScalar(270.0f);
460 break;
462 canvas->rotate(angle);
464 gfx::SizeF rotated_dest_size = dest_rect.size();
465 if (video_rotation == VIDEO_ROTATION_90 ||
466 video_rotation == VIDEO_ROTATION_270) {
467 rotated_dest_size =
468 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width());
470 canvas->scale(
471 SkFloatToScalar(rotated_dest_size.width() / target_frame->width()),
472 SkFloatToScalar(rotated_dest_size.height() / target_frame->height()));
473 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f),
474 -SkFloatToScalar(target_frame->height() * 0.5f));
476 if (accelerated_last_image_) {
477 canvas->drawImage(accelerated_last_image_.get(), 0, 0, &paint);
478 } else {
479 canvas->drawBitmap(*target_frame, 0, 0, &paint);
481 if (need_transform)
482 canvas->restore();
483 canvas->flush();
484 // SkCanvas::flush() causes the generator to generate SkImage, so delete
485 // |video_frame| not to be outlived.
486 if (canvas->getGrContext() && accelerated_generator_)
487 accelerated_generator_->set_frame(nullptr);
490 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame,
491 SkCanvas* canvas,
492 const Context3D& context_3d) {
493 Paint(video_frame, canvas, video_frame->visible_rect(), 0xff,
494 SkXfermode::kSrc_Mode, media::VIDEO_ROTATION_0, context_3d);
497 // static
498 void SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
499 const scoped_refptr<VideoFrame>& video_frame,
500 void* rgb_pixels,
501 size_t row_bytes) {
502 if (!video_frame->IsMappable()) {
503 NOTREACHED() << "Cannot extract pixels from non-CPU frame formats.";
504 return;
506 if (!media::IsYuvPlanar(video_frame->format())) {
507 NOTREACHED() << "Non YUV formats are not supported";
508 return;
511 DCHECK_EQ(video_frame->stride(VideoFrame::kUPlane),
512 video_frame->stride(VideoFrame::kVPlane));
514 const int y_shift =
515 (video_frame->format() == media::PIXEL_FORMAT_YV16) ? 0 : 1;
516 // Use the "left" and "top" of the destination rect to locate the offset
517 // in Y, U and V planes.
518 const size_t y_offset = (video_frame->stride(VideoFrame::kYPlane) *
519 video_frame->visible_rect().y()) +
520 video_frame->visible_rect().x();
521 // For format YV12, there is one U, V value per 2x2 block.
522 // For format YV16, there is one U, V value per 2x1 block.
523 const size_t uv_offset = (video_frame->stride(VideoFrame::kUPlane) *
524 (video_frame->visible_rect().y() >> y_shift)) +
525 (video_frame->visible_rect().x() >> 1);
527 switch (video_frame->format()) {
528 case PIXEL_FORMAT_YV12:
529 case PIXEL_FORMAT_I420:
530 if (CheckColorSpace(video_frame, COLOR_SPACE_JPEG)) {
531 ConvertYUVToRGB32(
532 video_frame->data(VideoFrame::kYPlane) + y_offset,
533 video_frame->data(VideoFrame::kUPlane) + uv_offset,
534 video_frame->data(VideoFrame::kVPlane) + uv_offset,
535 static_cast<uint8*>(rgb_pixels),
536 video_frame->visible_rect().width(),
537 video_frame->visible_rect().height(),
538 video_frame->stride(VideoFrame::kYPlane),
539 video_frame->stride(VideoFrame::kUPlane),
540 row_bytes,
541 YV12J);
542 } else if (CheckColorSpace(video_frame, COLOR_SPACE_HD_REC709)) {
543 ConvertYUVToRGB32(video_frame->data(VideoFrame::kYPlane) + y_offset,
544 video_frame->data(VideoFrame::kUPlane) + uv_offset,
545 video_frame->data(VideoFrame::kVPlane) + uv_offset,
546 static_cast<uint8*>(rgb_pixels),
547 video_frame->visible_rect().width(),
548 video_frame->visible_rect().height(),
549 video_frame->stride(VideoFrame::kYPlane),
550 video_frame->stride(VideoFrame::kUPlane), row_bytes,
551 YV12HD);
552 } else {
553 LIBYUV_I420_TO_ARGB(
554 video_frame->data(VideoFrame::kYPlane) + y_offset,
555 video_frame->stride(VideoFrame::kYPlane),
556 video_frame->data(VideoFrame::kUPlane) + uv_offset,
557 video_frame->stride(VideoFrame::kUPlane),
558 video_frame->data(VideoFrame::kVPlane) + uv_offset,
559 video_frame->stride(VideoFrame::kVPlane),
560 static_cast<uint8*>(rgb_pixels),
561 row_bytes,
562 video_frame->visible_rect().width(),
563 video_frame->visible_rect().height());
565 break;
566 case PIXEL_FORMAT_YV16:
567 LIBYUV_I422_TO_ARGB(
568 video_frame->data(VideoFrame::kYPlane) + y_offset,
569 video_frame->stride(VideoFrame::kYPlane),
570 video_frame->data(VideoFrame::kUPlane) + uv_offset,
571 video_frame->stride(VideoFrame::kUPlane),
572 video_frame->data(VideoFrame::kVPlane) + uv_offset,
573 video_frame->stride(VideoFrame::kVPlane),
574 static_cast<uint8*>(rgb_pixels),
575 row_bytes,
576 video_frame->visible_rect().width(),
577 video_frame->visible_rect().height());
578 break;
580 case PIXEL_FORMAT_YV12A:
581 // Since libyuv doesn't support YUVA, fallback to media, which is not ARM
582 // optimized.
583 // TODO(fbarchard, mtomasz): Use libyuv, then copy the alpha channel.
584 ConvertYUVAToARGB(
585 video_frame->data(VideoFrame::kYPlane) + y_offset,
586 video_frame->data(VideoFrame::kUPlane) + uv_offset,
587 video_frame->data(VideoFrame::kVPlane) + uv_offset,
588 video_frame->data(VideoFrame::kAPlane),
589 static_cast<uint8*>(rgb_pixels),
590 video_frame->visible_rect().width(),
591 video_frame->visible_rect().height(),
592 video_frame->stride(VideoFrame::kYPlane),
593 video_frame->stride(VideoFrame::kUPlane),
594 video_frame->stride(VideoFrame::kAPlane),
595 row_bytes,
596 YV12);
597 break;
599 case PIXEL_FORMAT_YV24:
600 libyuv::I444ToARGB(
601 video_frame->data(VideoFrame::kYPlane) + y_offset,
602 video_frame->stride(VideoFrame::kYPlane),
603 video_frame->data(VideoFrame::kUPlane) + uv_offset,
604 video_frame->stride(VideoFrame::kUPlane),
605 video_frame->data(VideoFrame::kVPlane) + uv_offset,
606 video_frame->stride(VideoFrame::kVPlane),
607 static_cast<uint8*>(rgb_pixels),
608 row_bytes,
609 video_frame->visible_rect().width(),
610 video_frame->visible_rect().height());
611 #if SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \
612 SK_A32_SHIFT == 24
613 libyuv::ARGBToABGR(static_cast<uint8*>(rgb_pixels),
614 row_bytes,
615 static_cast<uint8*>(rgb_pixels),
616 row_bytes,
617 video_frame->visible_rect().width(),
618 video_frame->visible_rect().height());
619 #endif
620 break;
621 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
622 case PIXEL_FORMAT_NV12:
623 #endif
624 case PIXEL_FORMAT_ARGB:
625 case PIXEL_FORMAT_XRGB:
626 case PIXEL_FORMAT_UNKNOWN:
627 NOTREACHED();
631 // static
632 void SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
633 gpu::gles2::GLES2Interface* gl,
634 VideoFrame* video_frame,
635 unsigned int texture,
636 unsigned int internal_format,
637 unsigned int type,
638 bool premultiply_alpha,
639 bool flip_y) {
640 DCHECK(video_frame);
641 DCHECK(video_frame->HasTextures());
642 DCHECK_EQ(1u, VideoFrame::NumPlanes(video_frame->format()));
644 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0);
645 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D ||
646 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB ||
647 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES);
649 gl->WaitSyncPointCHROMIUM(mailbox_holder.sync_point);
650 uint32 source_texture = gl->CreateAndConsumeTextureCHROMIUM(
651 mailbox_holder.texture_target, mailbox_holder.mailbox.name);
653 // The video is stored in a unmultiplied format, so premultiply
654 // if necessary.
655 // Application itself needs to take care of setting the right |flip_y|
656 // value down to get the expected result.
657 // "flip_y == true" means to reverse the video orientation while
658 // "flip_y == false" means to keep the intrinsic orientation.
659 gl->CopyTextureCHROMIUM(GL_TEXTURE_2D, source_texture, texture,
660 internal_format, type,
661 flip_y, premultiply_alpha, false);
663 gl->DeleteTextures(1, &source_texture);
664 gl->Flush();
666 SyncPointClientImpl client(gl);
667 video_frame->UpdateReleaseSyncPoint(&client);
670 void SkCanvasVideoRenderer::ResetLastFrame() {
671 last_frame_.reset();
672 last_frame_timestamp_ = media::kNoTimestamp();
675 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() {
676 accelerated_last_image_.reset();
677 accelerated_last_frame_.reset();
678 accelerated_generator_ = nullptr;
679 accelerated_last_frame_timestamp_ = media::kNoTimestamp();
682 } // namespace media