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" |
11 #include "third_party/skia/include/core/SkCanvas.h" | 11 #include "third_party/skia/include/core/SkCanvas.h" |
12 #include "third_party/skia/include/core/SkImageGenerator.h" | 12 #include "third_party/skia/include/core/SkImageGenerator.h" |
13 #include "third_party/skia/include/gpu/GrContext.h" | |
13 #include "ui/gfx/skbitmap_operations.h" | 14 #include "ui/gfx/skbitmap_operations.h" |
14 | 15 |
15 // Skia internal format depends on a platform. On Android it is ABGR, on others | 16 // Skia internal format depends on a platform. On Android it is ABGR, on others |
16 // it is ARGB. | 17 // it is ARGB. |
17 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ | 18 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ |
18 SK_A32_SHIFT == 24 | 19 SK_A32_SHIFT == 24 |
19 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB | 20 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB |
20 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB | 21 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB |
21 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ | 22 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ |
22 SK_A32_SHIFT == 24 | 23 SK_A32_SHIFT == 24 |
23 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR | 24 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR |
24 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR | 25 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR |
25 #else | 26 #else |
26 #error Unexpected Skia ARGB_8888 layout! | 27 #error Unexpected Skia ARGB_8888 layout! |
27 #endif | 28 #endif |
28 | 29 |
29 namespace media { | 30 namespace media { |
30 | 31 |
31 static bool IsYUV(media::VideoFrame::Format format) { | 32 namespace { |
33 | |
34 // This class keeps two temporary resources; software bitmap, hardware bitmap. | |
35 // If both bitmap are created and then only software bitmap is updated every | |
36 // frame, hardware bitmap outlives until the media player dies. So we delete | |
37 // a temporary resource if it is not used for 3 sec. | |
38 const int kTemporaryResourceDeletionDelay = 3; // Seconds; | |
39 | |
40 bool IsYUV(media::VideoFrame::Format format) { | |
32 switch (format) { | 41 switch (format) { |
33 case VideoFrame::YV12: | 42 case VideoFrame::YV12: |
34 case VideoFrame::YV16: | 43 case VideoFrame::YV16: |
35 case VideoFrame::I420: | 44 case VideoFrame::I420: |
36 case VideoFrame::YV12A: | 45 case VideoFrame::YV12A: |
37 case VideoFrame::YV12J: | 46 case VideoFrame::YV12J: |
38 case VideoFrame::YV24: | 47 case VideoFrame::YV24: |
39 case VideoFrame::NV12: | 48 case VideoFrame::NV12: |
40 return true; | 49 return true; |
41 case VideoFrame::UNKNOWN: | 50 case VideoFrame::UNKNOWN: |
42 case VideoFrame::NATIVE_TEXTURE: | 51 case VideoFrame::NATIVE_TEXTURE: |
43 #if defined(VIDEO_HOLE) | 52 #if defined(VIDEO_HOLE) |
44 case VideoFrame::HOLE: | 53 case VideoFrame::HOLE: |
45 #endif // defined(VIDEO_HOLE) | 54 #endif // defined(VIDEO_HOLE) |
46 return false; | 55 return false; |
47 } | 56 } |
48 NOTREACHED() << "Invalid videoframe format provided: " << format; | 57 NOTREACHED() << "Invalid videoframe format provided: " << format; |
49 return false; | 58 return false; |
50 } | 59 } |
51 | 60 |
52 static bool IsJPEGColorSpace(media::VideoFrame::Format format) { | 61 bool IsJPEGColorSpace(media::VideoFrame::Format format) { |
53 switch (format) { | 62 switch (format) { |
54 case VideoFrame::YV12J: | 63 case VideoFrame::YV12J: |
55 return true; | 64 return true; |
56 case VideoFrame::YV12: | 65 case VideoFrame::YV12: |
57 case VideoFrame::YV16: | 66 case VideoFrame::YV16: |
58 case VideoFrame::I420: | 67 case VideoFrame::I420: |
59 case VideoFrame::YV12A: | 68 case VideoFrame::YV12A: |
60 case VideoFrame::YV24: | 69 case VideoFrame::YV24: |
61 case VideoFrame::NV12: | 70 case VideoFrame::NV12: |
62 case VideoFrame::UNKNOWN: | 71 case VideoFrame::UNKNOWN: |
63 case VideoFrame::NATIVE_TEXTURE: | 72 case VideoFrame::NATIVE_TEXTURE: |
64 #if defined(VIDEO_HOLE) | 73 #if defined(VIDEO_HOLE) |
65 case VideoFrame::HOLE: | 74 case VideoFrame::HOLE: |
66 #endif // defined(VIDEO_HOLE) | 75 #endif // defined(VIDEO_HOLE) |
67 return false; | 76 return false; |
68 } | 77 } |
69 NOTREACHED() << "Invalid videoframe format provided: " << format; | 78 NOTREACHED() << "Invalid videoframe format provided: " << format; |
70 return false; | 79 return false; |
71 } | 80 } |
72 | 81 |
73 static bool IsYUVOrNative(media::VideoFrame::Format format) { | 82 bool IsYUVOrNative(media::VideoFrame::Format format) { |
74 return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE; | 83 return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE; |
75 } | 84 } |
76 | 85 |
77 // Converts a |video_frame| to raw |rgb_pixels|. | 86 // Converts a |video_frame| to raw |rgb_pixels|. |
78 static void ConvertVideoFrameToRGBPixels( | 87 void ConvertVideoFrameToRGBPixels( |
79 const scoped_refptr<media::VideoFrame>& video_frame, | 88 const scoped_refptr<media::VideoFrame>& video_frame, |
80 void* rgb_pixels, | 89 void* rgb_pixels, |
81 size_t row_bytes) { | 90 size_t row_bytes) { |
82 DCHECK(IsYUVOrNative(video_frame->format())) | 91 DCHECK(IsYUVOrNative(video_frame->format())) |
83 << video_frame->format(); | 92 << video_frame->format(); |
84 if (IsYUV(video_frame->format())) { | 93 if (IsYUV(video_frame->format())) { |
85 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane), | 94 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane), |
86 video_frame->stride(media::VideoFrame::kVPlane)); | 95 video_frame->stride(media::VideoFrame::kVPlane)); |
87 } | 96 } |
88 | 97 |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
198 row_bytes); | 207 row_bytes); |
199 video_frame->ReadPixelsFromNativeTexture(tmp); | 208 video_frame->ReadPixelsFromNativeTexture(tmp); |
200 break; | 209 break; |
201 } | 210 } |
202 default: | 211 default: |
203 NOTREACHED(); | 212 NOTREACHED(); |
204 break; | 213 break; |
205 } | 214 } |
206 } | 215 } |
207 | 216 |
208 // Generates an RGB image from a VideoFrame. | 217 } // anonymous namespace |
218 | |
219 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. | |
209 class VideoImageGenerator : public SkImageGenerator { | 220 class VideoImageGenerator : public SkImageGenerator { |
210 public: | 221 public: |
211 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { | 222 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { |
212 DCHECK(frame_.get()); | 223 DCHECK(frame_.get()); |
213 } | 224 } |
214 ~VideoImageGenerator() override {} | 225 ~VideoImageGenerator() override {} |
215 | 226 |
216 void set_frame(const scoped_refptr<VideoFrame>& frame) { frame_ = frame; } | 227 void set_frame(const scoped_refptr<VideoFrame>& frame) { frame_ = frame; } |
217 | 228 |
218 protected: | 229 protected: |
219 bool onGetInfo(SkImageInfo* info) override { | 230 bool onGetInfo(SkImageInfo* info) override { |
220 info->fWidth = frame_->visible_rect().width(); | 231 info->fWidth = frame_->visible_rect().width(); |
221 info->fHeight = frame_->visible_rect().height(); | 232 info->fHeight = frame_->visible_rect().height(); |
222 info->fColorType = kN32_SkColorType; | 233 info->fColorType = kN32_SkColorType; |
223 info->fAlphaType = kPremul_SkAlphaType; | 234 info->fAlphaType = kPremul_SkAlphaType; |
224 return true; | 235 return true; |
225 } | 236 } |
226 | 237 |
227 bool onGetPixels(const SkImageInfo& info, | 238 bool onGetPixels(const SkImageInfo& info, |
228 void* pixels, | 239 void* pixels, |
229 size_t row_bytes, | 240 size_t row_bytes, |
230 SkPMColor ctable[], | 241 SkPMColor ctable[], |
231 int* ctable_count) override { | 242 int* ctable_count) override { |
232 if (!frame_.get()) | 243 if (!frame_.get()) |
233 return false; | 244 return false; |
234 if (!pixels) | 245 if (!pixels) |
235 return false; | 246 return false; |
236 // If skia couldn't do the YUV conversion, we will. | 247 // If skia couldn't do the YUV conversion on GPU, we will on CPU. |
237 ConvertVideoFrameToRGBPixels(frame_, pixels, row_bytes); | 248 ConvertVideoFrameToRGBPixels(frame_, pixels, row_bytes); |
238 return true; | 249 return true; |
239 } | 250 } |
240 | 251 |
241 bool onGetYUV8Planes(SkISize sizes[3], | 252 bool onGetYUV8Planes(SkISize sizes[3], |
242 void* planes[3], | 253 void* planes[3], |
243 size_t row_bytes[3], | 254 size_t row_bytes[3], |
244 SkYUVColorSpace* color_space) override { | 255 SkYUVColorSpace* color_space) override { |
245 if (!frame_.get() || !IsYUV(frame_->format())) | 256 if (!frame_.get() || !IsYUV(frame_->format())) |
246 return false; | 257 return false; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
282 return true; | 293 return true; |
283 } | 294 } |
284 | 295 |
285 private: | 296 private: |
286 scoped_refptr<VideoFrame> frame_; | 297 scoped_refptr<VideoFrame> frame_; |
287 | 298 |
288 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); | 299 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); |
289 }; | 300 }; |
290 | 301 |
291 SkCanvasVideoRenderer::SkCanvasVideoRenderer() | 302 SkCanvasVideoRenderer::SkCanvasVideoRenderer() |
292 : generator_(NULL), last_frame_timestamp_(media::kNoTimestamp()) { | 303 : last_frame_timestamp_(media::kNoTimestamp()), |
304 frame_deleting_timer_( | |
305 FROM_HERE, | |
306 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), | |
307 this, | |
308 &SkCanvasVideoRenderer::ResetLastFrame), | |
309 accelerated_generator_(NULL), | |
310 accelerated_last_frame_timestamp_(media::kNoTimestamp()), | |
311 accelerated_frame_deleting_timer_( | |
312 FROM_HERE, | |
313 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), | |
314 this, | |
315 &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) { | |
293 last_frame_.setIsVolatile(true); | 316 last_frame_.setIsVolatile(true); |
294 } | 317 } |
295 | 318 |
296 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} | 319 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} |
297 | 320 |
298 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, | 321 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, |
299 SkCanvas* canvas, | 322 SkCanvas* canvas, |
300 const gfx::RectF& dest_rect, | 323 const gfx::RectF& dest_rect, |
301 uint8 alpha, | 324 uint8 alpha, |
302 SkXfermode::Mode mode, | 325 SkXfermode::Mode mode, |
303 VideoRotation video_rotation) { | 326 VideoRotation video_rotation) { |
304 if (alpha == 0) { | 327 if (alpha == 0) { |
305 return; | 328 return; |
306 } | 329 } |
307 | 330 |
308 SkRect dest; | 331 SkRect dest; |
309 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); | 332 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); |
310 | 333 |
311 SkPaint paint; | 334 SkPaint paint; |
312 paint.setAlpha(alpha); | 335 paint.setAlpha(alpha); |
313 | 336 |
314 // Paint black rectangle if there isn't a frame available or the | 337 // Paint black rectangle if there isn't a frame available or the |
315 // frame has an unexpected format. | 338 // frame has an unexpected format. |
316 if (!video_frame.get() || !IsYUVOrNative(video_frame->format())) { | 339 if (!video_frame.get() || video_frame->natural_size().IsEmpty() || |
340 !IsYUVOrNative(video_frame->format())) { | |
317 canvas->drawRect(dest, paint); | 341 canvas->drawRect(dest, paint); |
318 canvas->flush(); | 342 canvas->flush(); |
319 return; | 343 return; |
320 } | 344 } |
321 | 345 |
322 // Check if we should convert and update |last_frame_|. | 346 SkBitmap* target_frame = NULL; |
323 if (last_frame_.isNull() || | 347 if (canvas->getGrContext()) { |
324 video_frame->timestamp() != last_frame_timestamp_) { | 348 if (accelerated_last_frame_.isNull() || |
325 generator_ = new VideoImageGenerator(video_frame); | 349 video_frame->timestamp() != accelerated_last_frame_timestamp_) { |
350 accelerated_generator_ = new VideoImageGenerator(video_frame); | |
326 | 351 |
327 // Note: This takes ownership of |generator_|. | 352 // Note: This takes ownership of |accelerated_generator_|. |
328 if (!SkInstallDiscardablePixelRef(generator_, &last_frame_)) { | 353 if (!SkInstallDiscardablePixelRef(accelerated_generator_, |
329 NOTREACHED(); | 354 &accelerated_last_frame_)) { |
355 NOTREACHED(); | |
356 } | |
357 DCHECK(video_frame->visible_rect().width() == | |
358 accelerated_last_frame_.width() && | |
359 video_frame->visible_rect().height() == | |
360 accelerated_last_frame_.height()); | |
361 | |
362 accelerated_last_frame_timestamp_ = video_frame->timestamp(); | |
363 } else { | |
364 accelerated_generator_->set_frame(video_frame); | |
330 } | 365 } |
331 DCHECK(video_frame->visible_rect().width() == last_frame_.width() && | 366 target_frame = &accelerated_last_frame_; |
332 video_frame->visible_rect().height() == last_frame_.height()); | 367 accelerated_frame_deleting_timer_.Reset(); |
333 | 368 } else { |
334 last_frame_timestamp_ = video_frame->timestamp(); | 369 // Check if we should convert and update |last_frame_|. |
335 } else if (generator_) { | 370 if (last_frame_.isNull() || |
336 generator_->set_frame(video_frame); | 371 video_frame->timestamp() != last_frame_timestamp_) { |
372 // Check if |bitmap| needs to be (re)allocated. | |
373 if (last_frame_.isNull() || | |
374 last_frame_.width() != video_frame->visible_rect().width() || | |
375 last_frame_.height() != video_frame->visible_rect().height()) { | |
376 last_frame_.allocN32Pixels(video_frame->visible_rect().width(), | |
377 video_frame->visible_rect().height()); | |
378 last_frame_.setIsVolatile(true); | |
379 } | |
380 last_frame_.lockPixels(); | |
381 ConvertVideoFrameToRGBPixels( | |
382 video_frame, last_frame_.getPixels(), last_frame_.rowBytes()); | |
383 last_frame_.notifyPixelsChanged(); | |
384 last_frame_.unlockPixels(); | |
385 last_frame_timestamp_ = video_frame->timestamp(); | |
386 } | |
387 target_frame = &last_frame_; | |
388 frame_deleting_timer_.Reset(); | |
337 } | 389 } |
338 | 390 |
339 paint.setXfermodeMode(mode); | 391 paint.setXfermodeMode(mode); |
340 paint.setFilterLevel(SkPaint::kLow_FilterLevel); | 392 paint.setFilterLevel(SkPaint::kLow_FilterLevel); |
341 | 393 |
342 bool need_transform = | 394 bool need_transform = |
343 video_rotation != VIDEO_ROTATION_0 || | 395 video_rotation != VIDEO_ROTATION_0 || |
344 dest_rect.size() != video_frame->visible_rect().size() || | 396 dest_rect.size() != video_frame->visible_rect().size() || |
345 !dest_rect.origin().IsOrigin(); | 397 !dest_rect.origin().IsOrigin(); |
346 if (need_transform) { | 398 if (need_transform) { |
(...skipping 17 matching lines...) Expand all Loading... | |
364 } | 416 } |
365 canvas->rotate(angle); | 417 canvas->rotate(angle); |
366 | 418 |
367 gfx::SizeF rotated_dest_size = dest_rect.size(); | 419 gfx::SizeF rotated_dest_size = dest_rect.size(); |
368 if (video_rotation == VIDEO_ROTATION_90 || | 420 if (video_rotation == VIDEO_ROTATION_90 || |
369 video_rotation == VIDEO_ROTATION_270) { | 421 video_rotation == VIDEO_ROTATION_270) { |
370 rotated_dest_size = | 422 rotated_dest_size = |
371 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); | 423 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); |
372 } | 424 } |
373 canvas->scale( | 425 canvas->scale( |
374 SkFloatToScalar(rotated_dest_size.width() / last_frame_.width()), | 426 SkFloatToScalar(rotated_dest_size.width() / target_frame->width()), |
375 SkFloatToScalar(rotated_dest_size.height() / last_frame_.height())); | 427 SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); |
376 canvas->translate(-SkFloatToScalar(last_frame_.width() * 0.5f), | 428 canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), |
377 -SkFloatToScalar(last_frame_.height() * 0.5f)); | 429 -SkFloatToScalar(target_frame->height() * 0.5f)); |
378 } | 430 } |
379 canvas->drawBitmap(last_frame_, 0, 0, &paint); | 431 canvas->drawBitmap(*target_frame, 0, 0, &paint); |
380 if (need_transform) | 432 if (need_transform) |
381 canvas->restore(); | 433 canvas->restore(); |
382 canvas->flush(); | 434 canvas->flush(); |
383 // SkCanvas::flush() causes the generator to generate SkImage, so delete | 435 // SkCanvas::flush() causes the generator to generate SkImage, so delete |
384 // |video_frame| not to be outlived. | 436 // |video_frame| not to be outlived. |
385 if (generator_) | 437 if (canvas->getGrContext()) |
386 generator_->set_frame(NULL); | 438 accelerated_generator_->set_frame(NULL); |
387 } | 439 } |
388 | 440 |
389 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, | 441 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, |
390 SkCanvas* canvas) { | 442 SkCanvas* canvas) { |
391 Paint(video_frame, | 443 Paint(video_frame, |
392 canvas, | 444 canvas, |
393 video_frame->visible_rect(), | 445 video_frame->visible_rect(), |
394 0xff, | 446 0xff, |
395 SkXfermode::kSrc_Mode, | 447 SkXfermode::kSrc_Mode, |
396 media::VIDEO_ROTATION_0); | 448 media::VIDEO_ROTATION_0); |
397 } | 449 } |
398 | 450 |
451 void SkCanvasVideoRenderer::ResetLastFrame() { | |
452 last_frame_.reset(); | |
453 last_frame_timestamp_ = base::TimeDelta(); | |
scherkus (not reviewing)
2014/10/29 17:24:09
use media::kNoTimestamp()
| |
454 } | |
455 | |
456 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { | |
457 accelerated_last_frame_.reset(); | |
458 accelerated_generator_ = nullptr; | |
459 accelerated_last_frame_timestamp_ = base::TimeDelta(); | |
scherkus (not reviewing)
2014/10/29 17:24:09
use media::kNoTimestamp()
| |
460 } | |
461 | |
399 } // namespace media | 462 } // namespace media |
OLD | NEW |