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