| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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 "webkit/glue/media/video_renderer_impl.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/skia/include/core/SkCanvas.h" | |
| 11 #include "third_party/skia/include/core/SkDevice.h" | |
| 12 #include "webkit/glue/webmediaplayer_proxy.h" | |
| 13 | |
| 14 namespace webkit_glue { | |
| 15 | |
| 16 VideoRendererImpl::VideoRendererImpl(bool pts_logging) | |
| 17 : last_converted_frame_(NULL), | |
| 18 pts_logging_(pts_logging) { | |
| 19 } | |
| 20 | |
| 21 VideoRendererImpl::~VideoRendererImpl() {} | |
| 22 | |
| 23 bool VideoRendererImpl::OnInitialize(media::VideoDecoder* decoder) { | |
| 24 natural_size_ = decoder->natural_size(); | |
| 25 bitmap_.setConfig(SkBitmap::kARGB_8888_Config, | |
| 26 natural_size_.width(), natural_size_.height()); | |
| 27 bitmap_.allocPixels(); | |
| 28 bitmap_.eraseRGB(0x00, 0x00, 0x00); | |
| 29 bitmap_.setIsVolatile(true); | |
| 30 return true; | |
| 31 } | |
| 32 | |
| 33 void VideoRendererImpl::OnStop(const base::Closure& callback) { | |
| 34 if (!callback.is_null()) | |
| 35 callback.Run(); | |
| 36 } | |
| 37 | |
| 38 void VideoRendererImpl::OnFrameAvailable() { | |
| 39 proxy_->Repaint(); | |
| 40 } | |
| 41 | |
| 42 void VideoRendererImpl::SetWebMediaPlayerProxy(WebMediaPlayerProxy* proxy) { | |
| 43 proxy_ = proxy; | |
| 44 } | |
| 45 | |
| 46 void VideoRendererImpl::SetRect(const gfx::Rect& rect) {} | |
| 47 | |
| 48 // This method is always called on the renderer's thread. | |
| 49 void VideoRendererImpl::Paint(SkCanvas* canvas, const gfx::Rect& dest_rect) { | |
| 50 scoped_refptr<media::VideoFrame> video_frame; | |
| 51 GetCurrentFrame(&video_frame); | |
| 52 if (!video_frame) { | |
| 53 SkPaint paint; | |
| 54 paint.setColor(SK_ColorBLACK); | |
| 55 canvas->drawRectCoords( | |
| 56 static_cast<float>(dest_rect.x()), | |
| 57 static_cast<float>(dest_rect.y()), | |
| 58 static_cast<float>(dest_rect.right()), | |
| 59 static_cast<float>(dest_rect.bottom()), | |
| 60 paint); | |
| 61 } else { | |
| 62 if (CanFastPaint(canvas, dest_rect)) { | |
| 63 FastPaint(video_frame, canvas, dest_rect); | |
| 64 } else { | |
| 65 SlowPaint(video_frame, canvas, dest_rect); | |
| 66 } | |
| 67 | |
| 68 // Presentation timestamp logging is primarily used to measure performance | |
| 69 // on low-end devices. When profiled on an Intel Atom N280 @ 1.66GHz this | |
| 70 // code had a ~63 microsecond perf hit when logging to a file (not stdout), | |
| 71 // which is neglible enough for measuring playback performance. | |
| 72 if (pts_logging_) | |
| 73 VLOG(1) << "pts=" << video_frame->GetTimestamp().InMicroseconds(); | |
| 74 } | |
| 75 | |
| 76 PutCurrentFrame(video_frame); | |
| 77 } | |
| 78 | |
| 79 // CanFastPaint is a helper method to determine the conditions for fast | |
| 80 // painting. The conditions are: | |
| 81 // 1. No skew in canvas matrix. | |
| 82 // 2. No flipping nor mirroring. | |
| 83 // 3. Canvas has pixel format ARGB8888. | |
| 84 // 4. Canvas is opaque. | |
| 85 // TODO(hclam): The fast paint method should support flipping and mirroring. | |
| 86 // Disable the flipping and mirroring checks once we have it. | |
| 87 bool VideoRendererImpl::CanFastPaint(SkCanvas* canvas, | |
| 88 const gfx::Rect& dest_rect) { | |
| 89 // Fast paint does not handle opacity value other than 1.0. Hence use slow | |
| 90 // paint if opacity is not 1.0. Since alpha = opacity * 0xFF, we check that | |
| 91 // alpha != 0xFF. | |
| 92 // | |
| 93 // Additonal notes: If opacity = 0.0, the chrome display engine does not try | |
| 94 // to render the video. So, this method is never called. However, if the | |
| 95 // opacity = 0.0001, alpha is again 0, but the display engine tries to render | |
| 96 // the video. If we use Fast paint, the video shows up with opacity = 1.0. | |
| 97 // Hence we use slow paint also in the case where alpha = 0. It would be ideal | |
| 98 // if rendering was never called even for cases where alpha is 0. Created | |
| 99 // bug 48090 for this. | |
| 100 SkCanvas::LayerIter layer_iter(canvas, false); | |
| 101 SkColor sk_color = layer_iter.paint().getColor(); | |
| 102 SkAlpha sk_alpha = SkColorGetA(sk_color); | |
| 103 if (sk_alpha != 0xFF) { | |
| 104 return false; | |
| 105 } | |
| 106 | |
| 107 const SkMatrix& total_matrix = canvas->getTotalMatrix(); | |
| 108 // Perform the following checks here: | |
| 109 // 1. Check for skewing factors of the transformation matrix. They should be | |
| 110 // zero. | |
| 111 // 2. Check for mirroring and flipping. Make sure they are greater than zero. | |
| 112 if (SkScalarNearlyZero(total_matrix.getSkewX()) && | |
| 113 SkScalarNearlyZero(total_matrix.getSkewY()) && | |
| 114 total_matrix.getScaleX() > 0 && | |
| 115 total_matrix.getScaleY() > 0) { | |
| 116 SkDevice* device = canvas->getDevice(); | |
| 117 const SkBitmap::Config config = device->config(); | |
| 118 | |
| 119 if (config == SkBitmap::kARGB_8888_Config && device->isOpaque()) { | |
| 120 return true; | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 return false; | |
| 125 } | |
| 126 | |
| 127 void VideoRendererImpl::SlowPaint(media::VideoFrame* video_frame, | |
| 128 SkCanvas* canvas, | |
| 129 const gfx::Rect& dest_rect) { | |
| 130 // 1. Convert YUV frame to RGB. | |
| 131 base::TimeDelta timestamp = video_frame->GetTimestamp(); | |
| 132 if (video_frame != last_converted_frame_ || | |
| 133 timestamp != last_converted_timestamp_) { | |
| 134 last_converted_frame_ = video_frame; | |
| 135 last_converted_timestamp_ = timestamp; | |
| 136 DCHECK(video_frame->format() == media::VideoFrame::YV12 || | |
| 137 video_frame->format() == media::VideoFrame::YV16); | |
| 138 DCHECK(video_frame->stride(media::VideoFrame::kUPlane) == | |
| 139 video_frame->stride(media::VideoFrame::kVPlane)); | |
| 140 DCHECK(video_frame->planes() == media::VideoFrame::kNumYUVPlanes); | |
| 141 bitmap_.lockPixels(); | |
| 142 media::YUVType yuv_type = | |
| 143 (video_frame->format() == media::VideoFrame::YV12) ? | |
| 144 media::YV12 : media::YV16; | |
| 145 media::ConvertYUVToRGB32(video_frame->data(media::VideoFrame::kYPlane), | |
| 146 video_frame->data(media::VideoFrame::kUPlane), | |
| 147 video_frame->data(media::VideoFrame::kVPlane), | |
| 148 static_cast<uint8*>(bitmap_.getPixels()), | |
| 149 video_frame->width(), | |
| 150 video_frame->height(), | |
| 151 video_frame->stride(media::VideoFrame::kYPlane), | |
| 152 video_frame->stride(media::VideoFrame::kUPlane), | |
| 153 bitmap_.rowBytes(), | |
| 154 yuv_type); | |
| 155 bitmap_.notifyPixelsChanged(); | |
| 156 bitmap_.unlockPixels(); | |
| 157 } | |
| 158 | |
| 159 // 2. Paint the bitmap to canvas. | |
| 160 SkMatrix matrix; | |
| 161 matrix.setTranslate(static_cast<SkScalar>(dest_rect.x()), | |
| 162 static_cast<SkScalar>(dest_rect.y())); | |
| 163 if (dest_rect.width() != natural_size_.width() || | |
| 164 dest_rect.height() != natural_size_.height()) { | |
| 165 matrix.preScale(SkIntToScalar(dest_rect.width()) / | |
| 166 SkIntToScalar(natural_size_.width()), | |
| 167 SkIntToScalar(dest_rect.height()) / | |
| 168 SkIntToScalar(natural_size_.height())); | |
| 169 } | |
| 170 SkPaint paint; | |
| 171 paint.setFlags(SkPaint::kFilterBitmap_Flag); | |
| 172 canvas->drawBitmapMatrix(bitmap_, matrix, &paint); | |
| 173 } | |
| 174 | |
| 175 void VideoRendererImpl::FastPaint(media::VideoFrame* video_frame, | |
| 176 SkCanvas* canvas, | |
| 177 const gfx::Rect& dest_rect) { | |
| 178 DCHECK(video_frame->format() == media::VideoFrame::YV12 || | |
| 179 video_frame->format() == media::VideoFrame::YV16); | |
| 180 DCHECK(video_frame->stride(media::VideoFrame::kUPlane) == | |
| 181 video_frame->stride(media::VideoFrame::kVPlane)); | |
| 182 DCHECK(video_frame->planes() == media::VideoFrame::kNumYUVPlanes); | |
| 183 const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(true); | |
| 184 media::YUVType yuv_type = (video_frame->format() == media::VideoFrame::YV12) ? | |
| 185 media::YV12 : media::YV16; | |
| 186 int y_shift = yuv_type; // 1 for YV12, 0 for YV16. | |
| 187 | |
| 188 // Create a rectangle backed by SkScalar. | |
| 189 SkRect scalar_dest_rect; | |
| 190 scalar_dest_rect.iset(dest_rect.x(), dest_rect.y(), | |
| 191 dest_rect.right(), dest_rect.bottom()); | |
| 192 | |
| 193 // Transform the destination rectangle to local coordinates. | |
| 194 const SkMatrix& local_matrix = canvas->getTotalMatrix(); | |
| 195 SkRect local_dest_rect; | |
| 196 local_matrix.mapRect(&local_dest_rect, scalar_dest_rect); | |
| 197 | |
| 198 // After projecting the destination rectangle to local coordinates, round | |
| 199 // the projected rectangle to integer values, this will give us pixel values | |
| 200 // of the rectangle. | |
| 201 SkIRect local_dest_irect, local_dest_irect_saved; | |
| 202 local_dest_rect.round(&local_dest_irect); | |
| 203 local_dest_rect.round(&local_dest_irect_saved); | |
| 204 | |
| 205 // Only does the paint if the destination rect intersects with the clip | |
| 206 // rect. | |
| 207 if (local_dest_irect.intersect(canvas->getTotalClip().getBounds())) { | |
| 208 // At this point |local_dest_irect| contains the rect that we should draw | |
| 209 // to within the clipping rect. | |
| 210 | |
| 211 // Calculate the address for the top left corner of destination rect in | |
| 212 // the canvas that we will draw to. The address is obtained by the base | |
| 213 // address of the canvas shifted by "left" and "top" of the rect. | |
| 214 uint8* dest_rect_pointer = static_cast<uint8*>(bitmap.getPixels()) + | |
| 215 local_dest_irect.fTop * bitmap.rowBytes() + | |
| 216 local_dest_irect.fLeft * 4; | |
| 217 | |
| 218 // Project the clip rect to the original video frame, obtains the | |
| 219 // dimensions of the projected clip rect, "left" and "top" of the rect. | |
| 220 // The math here are all integer math so we won't have rounding error and | |
| 221 // write outside of the canvas. | |
| 222 // We have the assumptions of dest_rect.width() and dest_rect.height() | |
| 223 // being non-zero, these are valid assumptions since finding intersection | |
| 224 // above rejects empty rectangle so we just do a DCHECK here. | |
| 225 DCHECK_NE(0, dest_rect.width()); | |
| 226 DCHECK_NE(0, dest_rect.height()); | |
| 227 size_t frame_clip_width = local_dest_irect.width() * | |
| 228 video_frame->width() / local_dest_irect_saved.width(); | |
| 229 size_t frame_clip_height = local_dest_irect.height() * | |
| 230 video_frame->height() / local_dest_irect_saved.height(); | |
| 231 | |
| 232 // Project the "left" and "top" of the final destination rect to local | |
| 233 // coordinates of the video frame, use these values to find the offsets | |
| 234 // in the video frame to start reading. | |
| 235 size_t frame_clip_left = | |
| 236 (local_dest_irect.fLeft - local_dest_irect_saved.fLeft) * | |
| 237 video_frame->width() / local_dest_irect_saved.width(); | |
| 238 size_t frame_clip_top = | |
| 239 (local_dest_irect.fTop - local_dest_irect_saved.fTop) * | |
| 240 video_frame->height() / local_dest_irect_saved.height(); | |
| 241 | |
| 242 // Use the "left" and "top" of the destination rect to locate the offset | |
| 243 // in Y, U and V planes. | |
| 244 size_t y_offset = video_frame->stride(media::VideoFrame::kYPlane) * | |
| 245 frame_clip_top + frame_clip_left; | |
| 246 // For format YV12, there is one U, V value per 2x2 block. | |
| 247 // For format YV16, there is one u, V value per 2x1 block. | |
| 248 size_t uv_offset = (video_frame->stride(media::VideoFrame::kUPlane) * | |
| 249 (frame_clip_top >> y_shift)) + (frame_clip_left >> 1); | |
| 250 uint8* frame_clip_y = | |
| 251 video_frame->data(media::VideoFrame::kYPlane) + y_offset; | |
| 252 uint8* frame_clip_u = | |
| 253 video_frame->data(media::VideoFrame::kUPlane) + uv_offset; | |
| 254 uint8* frame_clip_v = | |
| 255 video_frame->data(media::VideoFrame::kVPlane) + uv_offset; | |
| 256 bitmap.lockPixels(); | |
| 257 | |
| 258 // TODO(hclam): do rotation and mirroring here. | |
| 259 // TODO(fbarchard): switch filtering based on performance. | |
| 260 media::ScaleYUVToRGB32(frame_clip_y, | |
| 261 frame_clip_u, | |
| 262 frame_clip_v, | |
| 263 dest_rect_pointer, | |
| 264 frame_clip_width, | |
| 265 frame_clip_height, | |
| 266 local_dest_irect.width(), | |
| 267 local_dest_irect.height(), | |
| 268 video_frame->stride(media::VideoFrame::kYPlane), | |
| 269 video_frame->stride(media::VideoFrame::kUPlane), | |
| 270 bitmap.rowBytes(), | |
| 271 yuv_type, | |
| 272 media::ROTATE_0, | |
| 273 media::FILTER_BILINEAR); | |
| 274 bitmap.unlockPixels(); | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 } // namespace webkit_glue | |
| OLD | NEW |