OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "media/filters/skcanvas_video_renderer.h" | |
6 | |
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 | |
13 // Skia internal format depends on a platform. On Android it is ABGR, on others | |
14 // it is ARGB. | |
15 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ | |
16 SK_A32_SHIFT == 24 | |
17 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB | |
18 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB | |
19 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ | |
20 SK_A32_SHIFT == 24 | |
21 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR | |
22 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR | |
23 #else | |
24 #error Unexpected Skia ARGB_8888 layout! | |
25 #endif | |
26 | |
27 namespace media { | |
28 | |
29 static bool IsYUV(media::VideoFrame::Format format) { | |
30 return format == media::VideoFrame::YV12 || | |
31 format == media::VideoFrame::YV16 || | |
32 format == media::VideoFrame::I420 || | |
33 format == media::VideoFrame::YV12A || | |
34 format == media::VideoFrame::YV12J || | |
35 format == media::VideoFrame::YV24; | |
36 } | |
37 | |
38 static bool IsYUVOrNative(media::VideoFrame::Format format) { | |
39 return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE; | |
40 } | |
41 | |
42 // Converts a VideoFrame containing YUV data to a SkBitmap containing RGB data. | |
43 // | |
44 // |bitmap| will be (re)allocated to match the dimensions of |video_frame|. | |
45 static void ConvertVideoFrameToBitmap( | |
46 const scoped_refptr<media::VideoFrame>& video_frame, | |
47 SkBitmap* bitmap) { | |
48 DCHECK(IsYUVOrNative(video_frame->format())) | |
49 << video_frame->format(); | |
50 if (IsYUV(video_frame->format())) { | |
51 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane), | |
52 video_frame->stride(media::VideoFrame::kVPlane)); | |
53 } | |
54 | |
55 // Check if |bitmap| needs to be (re)allocated. | |
56 if (bitmap->isNull() || | |
57 bitmap->width() != video_frame->visible_rect().width() || | |
58 bitmap->height() != video_frame->visible_rect().height()) { | |
59 bitmap->allocN32Pixels(video_frame->visible_rect().width(), | |
60 video_frame->visible_rect().height()); | |
61 bitmap->setIsVolatile(true); | |
62 } | |
63 | |
64 bitmap->lockPixels(); | |
65 | |
66 size_t y_offset = 0; | |
67 size_t uv_offset = 0; | |
68 if (IsYUV(video_frame->format())) { | |
69 int y_shift = (video_frame->format() == media::VideoFrame::YV16) ? 0 : 1; | |
70 // Use the "left" and "top" of the destination rect to locate the offset | |
71 // in Y, U and V planes. | |
72 y_offset = (video_frame->stride(media::VideoFrame::kYPlane) * | |
73 video_frame->visible_rect().y()) + | |
74 video_frame->visible_rect().x(); | |
75 // For format YV12, there is one U, V value per 2x2 block. | |
76 // For format YV16, there is one U, V value per 2x1 block. | |
77 uv_offset = (video_frame->stride(media::VideoFrame::kUPlane) * | |
78 (video_frame->visible_rect().y() >> y_shift)) + | |
79 (video_frame->visible_rect().x() >> 1); | |
80 } | |
81 | |
82 switch (video_frame->format()) { | |
83 case media::VideoFrame::YV12: | |
84 case media::VideoFrame::I420: | |
85 LIBYUV_I420_TO_ARGB( | |
86 video_frame->data(media::VideoFrame::kYPlane) + y_offset, | |
87 video_frame->stride(media::VideoFrame::kYPlane), | |
88 video_frame->data(media::VideoFrame::kUPlane) + uv_offset, | |
89 video_frame->stride(media::VideoFrame::kUPlane), | |
90 video_frame->data(media::VideoFrame::kVPlane) + uv_offset, | |
91 video_frame->stride(media::VideoFrame::kVPlane), | |
92 static_cast<uint8*>(bitmap->getPixels()), | |
93 bitmap->rowBytes(), | |
94 video_frame->visible_rect().width(), | |
95 video_frame->visible_rect().height()); | |
96 break; | |
97 | |
98 case media::VideoFrame::YV12J: | |
99 media::ConvertYUVToRGB32( | |
100 video_frame->data(media::VideoFrame::kYPlane) + y_offset, | |
101 video_frame->data(media::VideoFrame::kUPlane) + uv_offset, | |
102 video_frame->data(media::VideoFrame::kVPlane) + uv_offset, | |
103 static_cast<uint8*>(bitmap->getPixels()), | |
104 video_frame->visible_rect().width(), | |
105 video_frame->visible_rect().height(), | |
106 video_frame->stride(media::VideoFrame::kYPlane), | |
107 video_frame->stride(media::VideoFrame::kUPlane), | |
108 bitmap->rowBytes(), | |
109 media::YV12J); | |
110 break; | |
111 | |
112 case media::VideoFrame::YV16: | |
113 LIBYUV_I422_TO_ARGB( | |
114 video_frame->data(media::VideoFrame::kYPlane) + y_offset, | |
115 video_frame->stride(media::VideoFrame::kYPlane), | |
116 video_frame->data(media::VideoFrame::kUPlane) + uv_offset, | |
117 video_frame->stride(media::VideoFrame::kUPlane), | |
118 video_frame->data(media::VideoFrame::kVPlane) + uv_offset, | |
119 video_frame->stride(media::VideoFrame::kVPlane), | |
120 static_cast<uint8*>(bitmap->getPixels()), | |
121 bitmap->rowBytes(), | |
122 video_frame->visible_rect().width(), | |
123 video_frame->visible_rect().height()); | |
124 break; | |
125 | |
126 case media::VideoFrame::YV12A: | |
127 // Since libyuv doesn't support YUVA, fallback to media, which is not ARM | |
128 // optimized. | |
129 // TODO(fbarchard, mtomasz): Use libyuv, then copy the alpha channel. | |
130 media::ConvertYUVAToARGB( | |
131 video_frame->data(media::VideoFrame::kYPlane) + y_offset, | |
132 video_frame->data(media::VideoFrame::kUPlane) + uv_offset, | |
133 video_frame->data(media::VideoFrame::kVPlane) + uv_offset, | |
134 video_frame->data(media::VideoFrame::kAPlane), | |
135 static_cast<uint8*>(bitmap->getPixels()), | |
136 video_frame->visible_rect().width(), | |
137 video_frame->visible_rect().height(), | |
138 video_frame->stride(media::VideoFrame::kYPlane), | |
139 video_frame->stride(media::VideoFrame::kUPlane), | |
140 video_frame->stride(media::VideoFrame::kAPlane), | |
141 bitmap->rowBytes(), | |
142 media::YV12); | |
143 break; | |
144 | |
145 case media::VideoFrame::YV24: | |
146 libyuv::I444ToARGB( | |
147 video_frame->data(media::VideoFrame::kYPlane) + y_offset, | |
148 video_frame->stride(media::VideoFrame::kYPlane), | |
149 video_frame->data(media::VideoFrame::kUPlane) + uv_offset, | |
150 video_frame->stride(media::VideoFrame::kUPlane), | |
151 video_frame->data(media::VideoFrame::kVPlane) + uv_offset, | |
152 video_frame->stride(media::VideoFrame::kVPlane), | |
153 static_cast<uint8*>(bitmap->getPixels()), | |
154 bitmap->rowBytes(), | |
155 video_frame->visible_rect().width(), | |
156 video_frame->visible_rect().height()); | |
157 #if SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ | |
158 SK_A32_SHIFT == 24 | |
159 libyuv::ARGBToABGR( | |
160 static_cast<uint8*>(bitmap->getPixels()), | |
161 bitmap->rowBytes(), | |
162 static_cast<uint8*>(bitmap->getPixels()), | |
163 bitmap->rowBytes(), | |
164 video_frame->visible_rect().width(), | |
165 video_frame->visible_rect().height()); | |
166 #endif | |
167 break; | |
168 | |
169 case media::VideoFrame::NATIVE_TEXTURE: | |
170 DCHECK_EQ(video_frame->format(), media::VideoFrame::NATIVE_TEXTURE); | |
171 video_frame->ReadPixelsFromNativeTexture(*bitmap); | |
172 break; | |
173 | |
174 default: | |
175 NOTREACHED(); | |
176 break; | |
177 } | |
178 bitmap->notifyPixelsChanged(); | |
179 bitmap->unlockPixels(); | |
180 } | |
181 | |
182 SkCanvasVideoRenderer::SkCanvasVideoRenderer() | |
183 : last_frame_timestamp_(media::kNoTimestamp()) { | |
184 } | |
185 | |
186 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} | |
187 | |
188 void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame, | |
189 SkCanvas* canvas, | |
190 const gfx::RectF& dest_rect, | |
191 uint8 alpha) { | |
192 if (alpha == 0) { | |
193 return; | |
194 } | |
195 | |
196 SkRect dest; | |
197 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); | |
198 | |
199 SkPaint paint; | |
200 paint.setAlpha(alpha); | |
201 | |
202 // Paint black rectangle if there isn't a frame available or the | |
203 // frame has an unexpected format. | |
204 if (!video_frame || !IsYUVOrNative(video_frame->format())) { | |
205 canvas->drawRect(dest, paint); | |
206 return; | |
207 } | |
208 | |
209 // Check if we should convert and update |last_frame_|. | |
210 if (last_frame_.isNull() || | |
211 video_frame->timestamp() != last_frame_timestamp_) { | |
212 ConvertVideoFrameToBitmap(video_frame, &last_frame_); | |
213 last_frame_timestamp_ = video_frame->timestamp(); | |
214 } | |
215 | |
216 // Use SRC mode so we completely overwrite the buffer (in case we have alpha) | |
217 // this means we don't need the extra cost of clearing the buffer first. | |
218 paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode)); | |
219 | |
220 // Paint using |last_frame_|. | |
221 paint.setFilterLevel(SkPaint::kLow_FilterLevel); | |
222 canvas->drawBitmapRect(last_frame_, NULL, dest, &paint); | |
223 } | |
224 | |
225 } // namespace media | |
OLD | NEW |