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_ = media::kNoTimestamp(); |
| 454 } |
| 455 |
| 456 void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { |
| 457 accelerated_last_frame_.reset(); |
| 458 accelerated_generator_ = nullptr; |
| 459 accelerated_last_frame_timestamp_ = media::kNoTimestamp(); |
| 460 } |
| 461 |
399 } // namespace media | 462 } // namespace media |
OLD | NEW |