Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/filters/skcanvas_video_renderer.h" | 5 #include "media/filters/skcanvas_video_renderer.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "media/base/video_frame.h" | 8 #include "media/base/video_frame.h" |
| 9 #include "media/base/yuv_convert.h" | 9 #include "media/base/yuv_convert.h" |
| 10 #include "third_party/libyuv/include/libyuv.h" | 10 #include "third_party/libyuv/include/libyuv.h" |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 201 } | 201 } |
| 202 default: | 202 default: |
| 203 NOTREACHED(); | 203 NOTREACHED(); |
| 204 break; | 204 break; |
| 205 } | 205 } |
| 206 } | 206 } |
| 207 | 207 |
| 208 // Generates an RGB image from a VideoFrame. | 208 // Generates an RGB image from a VideoFrame. |
| 209 class VideoImageGenerator : public SkImageGenerator { | 209 class VideoImageGenerator : public SkImageGenerator { |
| 210 public: | 210 public: |
| 211 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) {} | 211 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { |
| 212 DCHECK(frame_.get()); | |
| 213 } | |
| 212 virtual ~VideoImageGenerator() {} | 214 virtual ~VideoImageGenerator() {} |
| 213 | 215 void discard_frame() { |
| 214 void set_frame(const scoped_refptr<VideoFrame>& frame) { frame_ = frame; } | 216 frame_ = NULL; |
| 217 } | |
| 215 | 218 |
| 216 protected: | 219 protected: |
| 217 virtual bool onGetInfo(SkImageInfo* info) OVERRIDE { | 220 virtual bool onGetInfo(SkImageInfo* info) OVERRIDE { |
| 218 info->fWidth = frame_->visible_rect().width(); | 221 info->fWidth = frame_->visible_rect().width(); |
| 219 info->fHeight = frame_->visible_rect().height(); | 222 info->fHeight = frame_->visible_rect().height(); |
| 220 info->fColorType = kN32_SkColorType; | 223 info->fColorType = kN32_SkColorType; |
| 221 info->fAlphaType = kPremul_SkAlphaType; | 224 info->fAlphaType = kPremul_SkAlphaType; |
| 222 return true; | 225 return true; |
| 223 } | 226 } |
| 224 | 227 |
| 225 virtual bool onGetPixels(const SkImageInfo& info, | 228 virtual bool onGetPixels(const SkImageInfo& info, |
| 226 void* pixels, | 229 void* pixels, |
| 227 size_t row_bytes, | 230 size_t row_bytes, |
| 228 SkPMColor ctable[], | 231 SkPMColor ctable[], |
| 229 int* ctable_count) OVERRIDE { | 232 int* ctable_count) OVERRIDE { |
| 230 if (!frame_.get()) | 233 // This method should be called only one time. |
| 234 DCHECK(frame_.get()); | |
| 235 if (!pixels) | |
| 231 return false; | 236 return false; |
| 232 if (!pixels) | |
| 233 return true; | |
| 234 // If skia couldn't do the YUV conversion, we will. | 237 // If skia couldn't do the YUV conversion, we will. |
| 235 ConvertVideoFrameToRGBPixels(frame_, pixels, row_bytes); | 238 ConvertVideoFrameToRGBPixels(frame_, pixels, row_bytes); |
| 236 frame_ = NULL; | |
| 237 return true; | 239 return true; |
| 238 } | 240 } |
| 239 | 241 |
| 240 virtual bool onGetYUV8Planes(SkISize sizes[3], | 242 virtual bool onGetYUV8Planes(SkISize sizes[3], |
| 241 void* planes[3], | 243 void* planes[3], |
| 242 size_t row_bytes[3], | 244 size_t row_bytes[3], |
| 243 SkYUVColorSpace* color_space) OVERRIDE { | 245 SkYUVColorSpace* color_space) OVERRIDE { |
| 244 if (!frame_.get() || !IsYUV(frame_->format())) | 246 if (!frame_.get() || !IsYUV(frame_->format())) |
| 245 return false; | 247 return false; |
| 248 #if DCHECK_IS_ON | |
| 249 // This method should be called to get a YUV plane only one time. | |
| 250 if (row_bytes && planes) | |
| 251 DCHECK(frame_.get()); | |
| 252 #endif | |
| 246 | 253 |
| 247 if (color_space) { | 254 if (color_space) { |
| 248 if (IsJPEGColorSpace(frame_->format())) | 255 if (IsJPEGColorSpace(frame_->format())) |
| 249 *color_space = kJPEG_SkYUVColorSpace; | 256 *color_space = kJPEG_SkYUVColorSpace; |
| 250 else | 257 else |
| 251 *color_space = kRec601_SkYUVColorSpace; | 258 *color_space = kRec601_SkYUVColorSpace; |
| 252 } | 259 } |
| 253 | 260 |
| 254 for (int plane = VideoFrame::kYPlane; plane <= VideoFrame::kVPlane; | 261 for (int plane = VideoFrame::kYPlane; plane <= VideoFrame::kVPlane; |
| 255 ++plane) { | 262 ++plane) { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 268 if (plane == media::VideoFrame::kYPlane) { | 275 if (plane == media::VideoFrame::kYPlane) { |
| 269 offset = (frame_->stride(media::VideoFrame::kYPlane) * | 276 offset = (frame_->stride(media::VideoFrame::kYPlane) * |
| 270 frame_->visible_rect().y()) + | 277 frame_->visible_rect().y()) + |
| 271 frame_->visible_rect().x(); | 278 frame_->visible_rect().x(); |
| 272 } else { | 279 } else { |
| 273 offset = (frame_->stride(media::VideoFrame::kUPlane) * | 280 offset = (frame_->stride(media::VideoFrame::kUPlane) * |
| 274 (frame_->visible_rect().y() >> y_shift)) + | 281 (frame_->visible_rect().y() >> y_shift)) + |
| 275 (frame_->visible_rect().x() >> 1); | 282 (frame_->visible_rect().x() >> 1); |
| 276 } | 283 } |
| 277 row_bytes[plane] = static_cast<size_t>(frame_->stride(plane)); | 284 row_bytes[plane] = static_cast<size_t>(frame_->stride(plane)); |
| 278 planes[plane] = frame_->data(plane) + offset; | 285 planes[plane] = frame_->data(plane) + offset; |
|
dshwang
2014/10/02 18:23:52
It exposes VideoFrame's data to caller
| |
| 279 } | 286 } |
| 280 } | 287 } |
| 281 if (planes && row_bytes) | |
| 282 frame_ = NULL; | |
|
dshwang
2014/10/02 18:23:52
Removing frame_ can cause seg fault. So I remove |
| |
| 283 return true; | 288 return true; |
| 284 } | 289 } |
| 285 | 290 |
| 286 private: | 291 private: |
| 287 scoped_refptr<VideoFrame> frame_; | 292 scoped_refptr<VideoFrame> frame_; |
| 288 }; | 293 }; |
| 289 | 294 |
| 290 SkCanvasVideoRenderer::SkCanvasVideoRenderer() | 295 SkCanvasVideoRenderer::SkCanvasVideoRenderer() |
| 291 : generator_(NULL), last_frame_timestamp_(media::kNoTimestamp()) { | 296 : last_frame_timestamp_(media::kNoTimestamp()) { |
| 292 last_frame_.setIsVolatile(true); | 297 last_frame_.setIsVolatile(true); |
| 293 } | 298 } |
| 294 | 299 |
| 295 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} | 300 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} |
| 296 | 301 |
| 297 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, | 302 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, |
| 298 SkCanvas* canvas, | 303 SkCanvas* canvas, |
| 299 const gfx::RectF& dest_rect, | 304 const gfx::RectF& dest_rect, |
| 300 uint8 alpha, | 305 uint8 alpha, |
| 301 SkXfermode::Mode mode, | 306 SkXfermode::Mode mode, |
| 302 VideoRotation video_rotation) { | 307 VideoRotation video_rotation) { |
| 303 if (alpha == 0) { | 308 if (alpha == 0) { |
| 304 return; | 309 return; |
| 305 } | 310 } |
| 306 | 311 |
| 307 SkRect dest; | 312 SkRect dest; |
| 308 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); | 313 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); |
| 309 | 314 |
| 310 SkPaint paint; | 315 SkPaint paint; |
| 311 paint.setAlpha(alpha); | 316 paint.setAlpha(alpha); |
| 312 | 317 |
| 313 // Paint black rectangle if there isn't a frame available or the | 318 // Paint black rectangle if there isn't a frame available or the |
| 314 // frame has an unexpected format. | 319 // frame has an unexpected format. |
| 315 if (!video_frame.get() || !IsYUVOrNative(video_frame->format())) { | 320 if (!video_frame.get() || !IsYUVOrNative(video_frame->format())) { |
| 316 canvas->drawRect(dest, paint); | 321 canvas->drawRect(dest, paint); |
| 317 canvas->flush(); | 322 canvas->flush(); |
| 318 return; | 323 return; |
| 319 } | 324 } |
| 320 | 325 |
| 326 VideoImageGenerator* generator = NULL; | |
| 321 // Check if we should convert and update |last_frame_|. | 327 // Check if we should convert and update |last_frame_|. |
| 322 if (last_frame_.isNull() || | 328 if (last_frame_.isNull() || |
| 323 video_frame->timestamp() != last_frame_timestamp_) { | 329 video_frame->timestamp() != last_frame_timestamp_) { |
| 324 generator_ = new VideoImageGenerator(video_frame); | 330 generator = new VideoImageGenerator(video_frame); |
| 325 | 331 // Note: This takes ownership of new VideoImageGenerator instance. |
| 326 // Note: This takes ownership of |generator_|. | 332 if (!SkInstallDiscardablePixelRef(generator, &last_frame_)) { |
| 327 if (!SkInstallDiscardablePixelRef(generator_, &last_frame_)) { | |
| 328 NOTREACHED(); | 333 NOTREACHED(); |
| 329 } | 334 } |
| 330 | 335 |
| 331 // TODO(rileya): Perform this rotation on the canvas, rather than allocating | 336 // TODO(rileya): Perform this rotation on the canvas, rather than allocating |
| 332 // a new bitmap and copying. | 337 // a new bitmap and copying. |
| 333 switch (video_rotation) { | 338 switch (video_rotation) { |
| 334 case VIDEO_ROTATION_0: | 339 case VIDEO_ROTATION_0: |
| 335 break; | 340 break; |
| 336 case VIDEO_ROTATION_90: | 341 case VIDEO_ROTATION_90: |
| 337 last_frame_ = SkBitmapOperations::Rotate( | 342 last_frame_ = SkBitmapOperations::Rotate( |
| 338 last_frame_, SkBitmapOperations::ROTATION_90_CW); | 343 last_frame_, SkBitmapOperations::ROTATION_90_CW); |
| 339 break; | 344 break; |
| 340 case VIDEO_ROTATION_180: | 345 case VIDEO_ROTATION_180: |
| 341 last_frame_ = SkBitmapOperations::Rotate( | 346 last_frame_ = SkBitmapOperations::Rotate( |
| 342 last_frame_, SkBitmapOperations::ROTATION_180_CW); | 347 last_frame_, SkBitmapOperations::ROTATION_180_CW); |
| 343 break; | 348 break; |
| 344 case VIDEO_ROTATION_270: | 349 case VIDEO_ROTATION_270: |
| 345 last_frame_ = SkBitmapOperations::Rotate( | 350 last_frame_ = SkBitmapOperations::Rotate( |
| 346 last_frame_, SkBitmapOperations::ROTATION_270_CW); | 351 last_frame_, SkBitmapOperations::ROTATION_270_CW); |
| 347 break; | 352 break; |
| 348 } | 353 } |
| 349 | 354 |
| 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) | |
| 354 generator_ = NULL; | |
| 355 | |
| 356 last_frame_timestamp_ = video_frame->timestamp(); | 355 last_frame_timestamp_ = video_frame->timestamp(); |
| 357 } else if (generator_) { | |
| 358 generator_->set_frame(video_frame); | |
| 359 } | 356 } |
| 360 | 357 |
| 361 paint.setXfermodeMode(mode); | 358 paint.setXfermodeMode(mode); |
| 362 | 359 |
| 363 // Paint using |last_frame_|. | 360 // Paint using |last_frame_|. |
| 364 paint.setFilterLevel(SkPaint::kLow_FilterLevel); | 361 paint.setFilterLevel(SkPaint::kLow_FilterLevel); |
| 365 canvas->drawBitmapRect(last_frame_, NULL, dest, &paint); | 362 canvas->drawBitmapRect(last_frame_, NULL, dest, &paint); |
| 366 canvas->flush(); | 363 canvas->flush(); |
| 364 // SkCanvas::flush() causes the generator to generate SkImage, so delete | |
| 365 // |video_frame| not to be outlived. | |
| 366 if (generator) | |
| 367 generator->discard_frame(); | |
|
dshwang
2014/10/09 19:59:57
This line caused use-after-free because SkCanvas::
dshwang
2014/10/10 14:13:55
It's wrong analysis. |generator| is deleted by rep
| |
| 367 } | 368 } |
| 368 | 369 |
| 369 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, | 370 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, |
| 370 SkCanvas* canvas) { | 371 SkCanvas* canvas) { |
| 371 Paint(video_frame, | 372 Paint(video_frame, |
| 372 canvas, | 373 canvas, |
| 373 video_frame->visible_rect(), | 374 video_frame->visible_rect(), |
| 374 0xff, | 375 0xff, |
| 375 SkXfermode::kSrc_Mode, | 376 SkXfermode::kSrc_Mode, |
| 376 media::VIDEO_ROTATION_0); | 377 media::VIDEO_ROTATION_0); |
| 377 } | 378 } |
| 378 | 379 |
| 379 } // namespace media | 380 } // namespace media |
| OLD | NEW |