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/filters/skcanvas_video_renderer.h"
7 #include "base/logging.h"
8 #include "media/base/video_frame.h"
9 #include "media/base/yuv_convert.h"
10 #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"
13 #include "ui/gfx/skbitmap_operations.h"
15 // Skia internal format depends on a platform. On Android it is ABGR, on others
17 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \
19 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB
20 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB
21 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \
23 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR
24 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR
26 #error Unexpected Skia ARGB_8888 layout!
31 static bool IsYUV(media::VideoFrame::Format format
) {
33 case VideoFrame::YV12
:
34 case VideoFrame::YV16
:
35 case VideoFrame::I420
:
36 case VideoFrame::YV12A
:
37 case VideoFrame::YV12J
:
38 case VideoFrame::YV24
:
39 case VideoFrame::NV12
:
41 case VideoFrame::UNKNOWN
:
42 case VideoFrame::NATIVE_TEXTURE
:
43 #if defined(VIDEO_HOLE)
44 case VideoFrame::HOLE
:
45 #endif // defined(VIDEO_HOLE)
48 NOTREACHED() << "Invalid videoframe format provided: " << format
;
52 static bool IsJPEGColorSpace(media::VideoFrame::Format format
) {
54 case VideoFrame::YV12J
:
56 case VideoFrame::YV12
:
57 case VideoFrame::YV16
:
58 case VideoFrame::I420
:
59 case VideoFrame::YV12A
:
60 case VideoFrame::YV24
:
61 case VideoFrame::NV12
:
62 case VideoFrame::UNKNOWN
:
63 case VideoFrame::NATIVE_TEXTURE
:
64 #if defined(VIDEO_HOLE)
65 case VideoFrame::HOLE
:
66 #endif // defined(VIDEO_HOLE)
69 NOTREACHED() << "Invalid videoframe format provided: " << format
;
73 static bool IsYUVOrNative(media::VideoFrame::Format format
) {
74 return IsYUV(format
) || format
== media::VideoFrame::NATIVE_TEXTURE
;
77 // Converts a |video_frame| to raw |rgb_pixels|.
78 static void ConvertVideoFrameToRGBPixels(
79 const scoped_refptr
<media::VideoFrame
>& video_frame
,
82 DCHECK(IsYUVOrNative(video_frame
->format()))
83 << video_frame
->format();
84 if (IsYUV(video_frame
->format())) {
85 DCHECK_EQ(video_frame
->stride(media::VideoFrame::kUPlane
),
86 video_frame
->stride(media::VideoFrame::kVPlane
));
91 if (IsYUV(video_frame
->format())) {
92 int y_shift
= (video_frame
->format() == media::VideoFrame::YV16
) ? 0 : 1;
93 // Use the "left" and "top" of the destination rect to locate the offset
94 // in Y, U and V planes.
95 y_offset
= (video_frame
->stride(media::VideoFrame::kYPlane
) *
96 video_frame
->visible_rect().y()) +
97 video_frame
->visible_rect().x();
98 // For format YV12, there is one U, V value per 2x2 block.
99 // For format YV16, there is one U, V value per 2x1 block.
100 uv_offset
= (video_frame
->stride(media::VideoFrame::kUPlane
) *
101 (video_frame
->visible_rect().y() >> y_shift
)) +
102 (video_frame
->visible_rect().x() >> 1);
105 switch (video_frame
->format()) {
106 case media::VideoFrame::YV12
:
107 case media::VideoFrame::I420
:
109 video_frame
->data(media::VideoFrame::kYPlane
) + y_offset
,
110 video_frame
->stride(media::VideoFrame::kYPlane
),
111 video_frame
->data(media::VideoFrame::kUPlane
) + uv_offset
,
112 video_frame
->stride(media::VideoFrame::kUPlane
),
113 video_frame
->data(media::VideoFrame::kVPlane
) + uv_offset
,
114 video_frame
->stride(media::VideoFrame::kVPlane
),
115 static_cast<uint8
*>(rgb_pixels
),
117 video_frame
->visible_rect().width(),
118 video_frame
->visible_rect().height());
121 case media::VideoFrame::YV12J
:
122 media::ConvertYUVToRGB32(
123 video_frame
->data(media::VideoFrame::kYPlane
) + y_offset
,
124 video_frame
->data(media::VideoFrame::kUPlane
) + uv_offset
,
125 video_frame
->data(media::VideoFrame::kVPlane
) + uv_offset
,
126 static_cast<uint8
*>(rgb_pixels
),
127 video_frame
->visible_rect().width(),
128 video_frame
->visible_rect().height(),
129 video_frame
->stride(media::VideoFrame::kYPlane
),
130 video_frame
->stride(media::VideoFrame::kUPlane
),
135 case media::VideoFrame::YV16
:
137 video_frame
->data(media::VideoFrame::kYPlane
) + y_offset
,
138 video_frame
->stride(media::VideoFrame::kYPlane
),
139 video_frame
->data(media::VideoFrame::kUPlane
) + uv_offset
,
140 video_frame
->stride(media::VideoFrame::kUPlane
),
141 video_frame
->data(media::VideoFrame::kVPlane
) + uv_offset
,
142 video_frame
->stride(media::VideoFrame::kVPlane
),
143 static_cast<uint8
*>(rgb_pixels
),
145 video_frame
->visible_rect().width(),
146 video_frame
->visible_rect().height());
149 case media::VideoFrame::YV12A
:
150 // Since libyuv doesn't support YUVA, fallback to media, which is not ARM
152 // TODO(fbarchard, mtomasz): Use libyuv, then copy the alpha channel.
153 media::ConvertYUVAToARGB(
154 video_frame
->data(media::VideoFrame::kYPlane
) + y_offset
,
155 video_frame
->data(media::VideoFrame::kUPlane
) + uv_offset
,
156 video_frame
->data(media::VideoFrame::kVPlane
) + uv_offset
,
157 video_frame
->data(media::VideoFrame::kAPlane
),
158 static_cast<uint8
*>(rgb_pixels
),
159 video_frame
->visible_rect().width(),
160 video_frame
->visible_rect().height(),
161 video_frame
->stride(media::VideoFrame::kYPlane
),
162 video_frame
->stride(media::VideoFrame::kUPlane
),
163 video_frame
->stride(media::VideoFrame::kAPlane
),
168 case media::VideoFrame::YV24
:
170 video_frame
->data(media::VideoFrame::kYPlane
) + y_offset
,
171 video_frame
->stride(media::VideoFrame::kYPlane
),
172 video_frame
->data(media::VideoFrame::kUPlane
) + uv_offset
,
173 video_frame
->stride(media::VideoFrame::kUPlane
),
174 video_frame
->data(media::VideoFrame::kVPlane
) + uv_offset
,
175 video_frame
->stride(media::VideoFrame::kVPlane
),
176 static_cast<uint8
*>(rgb_pixels
),
178 video_frame
->visible_rect().width(),
179 video_frame
->visible_rect().height());
180 #if SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \
182 libyuv::ARGBToABGR(static_cast<uint8
*>(rgb_pixels
),
184 static_cast<uint8
*>(rgb_pixels
),
186 video_frame
->visible_rect().width(),
187 video_frame
->visible_rect().height());
191 case media::VideoFrame::NATIVE_TEXTURE
: {
192 DCHECK_EQ(video_frame
->format(), media::VideoFrame::NATIVE_TEXTURE
);
195 SkImageInfo::MakeN32Premul(video_frame
->visible_rect().width(),
196 video_frame
->visible_rect().height()),
199 video_frame
->ReadPixelsFromNativeTexture(tmp
);
208 // Generates an RGB image from a VideoFrame.
209 class VideoImageGenerator
: public SkImageGenerator
{
211 VideoImageGenerator(const scoped_refptr
<VideoFrame
>& frame
) : frame_(frame
) {}
212 virtual ~VideoImageGenerator() {}
214 void set_frame(const scoped_refptr
<VideoFrame
>& frame
) { frame_
= frame
; }
217 virtual bool onGetInfo(SkImageInfo
* info
) OVERRIDE
{
218 info
->fWidth
= frame_
->visible_rect().width();
219 info
->fHeight
= frame_
->visible_rect().height();
220 info
->fColorType
= kN32_SkColorType
;
221 info
->fAlphaType
= kPremul_SkAlphaType
;
225 virtual bool onGetPixels(const SkImageInfo
& info
,
229 int* ctable_count
) OVERRIDE
{
234 // If skia couldn't do the YUV conversion, we will.
235 ConvertVideoFrameToRGBPixels(frame_
, pixels
, row_bytes
);
240 virtual bool onGetYUV8Planes(SkISize sizes
[3],
243 SkYUVColorSpace
* color_space
) OVERRIDE
{
244 if (!frame_
.get() || !IsYUV(frame_
->format()))
248 if (IsJPEGColorSpace(frame_
->format()))
249 *color_space
= kJPEG_SkYUVColorSpace
;
251 *color_space
= kRec601_SkYUVColorSpace
;
254 for (int plane
= VideoFrame::kYPlane
; plane
<= VideoFrame::kVPlane
;
259 VideoFrame::PlaneSize(frame_
->format(),
261 gfx::Size(frame_
->visible_rect().width(),
262 frame_
->visible_rect().height()));
263 sizes
[plane
].set(size
.width(), size
.height());
265 if (row_bytes
&& planes
) {
267 int y_shift
= (frame_
->format() == media::VideoFrame::YV16
) ? 0 : 1;
268 if (plane
== media::VideoFrame::kYPlane
) {
269 offset
= (frame_
->stride(media::VideoFrame::kYPlane
) *
270 frame_
->visible_rect().y()) +
271 frame_
->visible_rect().x();
273 offset
= (frame_
->stride(media::VideoFrame::kUPlane
) *
274 (frame_
->visible_rect().y() >> y_shift
)) +
275 (frame_
->visible_rect().x() >> 1);
277 row_bytes
[plane
] = static_cast<size_t>(frame_
->stride(plane
));
278 planes
[plane
] = frame_
->data(plane
) + offset
;
281 if (planes
&& row_bytes
)
287 scoped_refptr
<VideoFrame
> frame_
;
290 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
291 : generator_(NULL
), last_frame_timestamp_(media::kNoTimestamp()) {
292 last_frame_
.setIsVolatile(true);
295 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
297 void SkCanvasVideoRenderer::Paint(const scoped_refptr
<VideoFrame
>& video_frame
,
299 const gfx::RectF
& dest_rect
,
301 SkXfermode::Mode mode
,
302 VideoRotation video_rotation
) {
308 dest
.set(dest_rect
.x(), dest_rect
.y(), dest_rect
.right(), dest_rect
.bottom());
311 paint
.setAlpha(alpha
);
313 // Paint black rectangle if there isn't a frame available or the
314 // frame has an unexpected format.
315 if (!video_frame
.get() || !IsYUVOrNative(video_frame
->format())) {
316 canvas
->drawRect(dest
, paint
);
321 // Check if we should convert and update |last_frame_|.
322 if (last_frame_
.isNull() ||
323 video_frame
->timestamp() != last_frame_timestamp_
) {
324 generator_
= new VideoImageGenerator(video_frame
);
326 // Note: This takes ownership of |generator_|.
327 if (!SkInstallDiscardablePixelRef(generator_
, &last_frame_
)) {
331 // TODO(rileya): Perform this rotation on the canvas, rather than allocating
332 // a new bitmap and copying.
333 switch (video_rotation
) {
334 case VIDEO_ROTATION_0
:
336 case VIDEO_ROTATION_90
:
337 last_frame_
= SkBitmapOperations::Rotate(
338 last_frame_
, SkBitmapOperations::ROTATION_90_CW
);
340 case VIDEO_ROTATION_180
:
341 last_frame_
= SkBitmapOperations::Rotate(
342 last_frame_
, SkBitmapOperations::ROTATION_180_CW
);
344 case VIDEO_ROTATION_270
:
345 last_frame_
= SkBitmapOperations::Rotate(
346 last_frame_
, SkBitmapOperations::ROTATION_270_CW
);
350 // We copied the frame into a new bitmap and threw out the old one, so we
351 // no longer have a |generator_| around. This should be removed when the
352 // above TODO is addressed.
353 if (video_rotation
!= VIDEO_ROTATION_0
)
356 last_frame_timestamp_
= video_frame
->timestamp();
357 } else if (generator_
) {
358 generator_
->set_frame(video_frame
);
361 paint
.setXfermodeMode(mode
);
363 // Paint using |last_frame_|.
364 paint
.setFilterLevel(SkPaint::kLow_FilterLevel
);
365 canvas
->drawBitmapRect(last_frame_
, NULL
, dest
, &paint
);
369 void SkCanvasVideoRenderer::Copy(const scoped_refptr
<VideoFrame
>& video_frame
,
373 video_frame
->visible_rect(),
375 SkXfermode::kSrc_Mode
,
376 media::VIDEO_ROTATION_0
);