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 |