Index: media/filters/skcanvas_video_renderer.cc |
diff --git a/media/filters/skcanvas_video_renderer.cc b/media/filters/skcanvas_video_renderer.cc |
index 4675cca8ccdefa41edc4fa183d27fac649935e62..2243445d459e903f6d230604a757f6c36cc4c0cb 100644 |
--- a/media/filters/skcanvas_video_renderer.cc |
+++ b/media/filters/skcanvas_video_renderer.cc |
@@ -10,6 +10,7 @@ |
#include "third_party/libyuv/include/libyuv.h" |
#include "third_party/skia/include/core/SkCanvas.h" |
#include "third_party/skia/include/core/SkImageGenerator.h" |
+#include "third_party/skia/include/gpu/GrContext.h" |
#include "ui/gfx/skbitmap_operations.h" |
// Skia internal format depends on a platform. On Android it is ABGR, on others |
@@ -28,7 +29,15 @@ |
namespace media { |
-static bool IsYUV(media::VideoFrame::Format format) { |
+namespace { |
+ |
+// This class keeps two temporary resources; software bitmap, hardware bitmap. |
+// If both bitmap are created and then only software bitmap is updated every |
+// frame, hardware bitmap outlives until the media player dies. So we delete |
+// a temporary resource if it is not used for 3 sec. |
+const int kTemporaryResourceDeletionDelay = 3; // Seconds; |
+ |
+bool IsYUV(media::VideoFrame::Format format) { |
switch (format) { |
case VideoFrame::YV12: |
case VideoFrame::YV16: |
@@ -49,7 +58,7 @@ static bool IsYUV(media::VideoFrame::Format format) { |
return false; |
} |
-static bool IsJPEGColorSpace(media::VideoFrame::Format format) { |
+bool IsJPEGColorSpace(media::VideoFrame::Format format) { |
switch (format) { |
case VideoFrame::YV12J: |
return true; |
@@ -70,12 +79,12 @@ static bool IsJPEGColorSpace(media::VideoFrame::Format format) { |
return false; |
} |
-static bool IsYUVOrNative(media::VideoFrame::Format format) { |
+bool IsYUVOrNative(media::VideoFrame::Format format) { |
return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE; |
} |
// Converts a |video_frame| to raw |rgb_pixels|. |
-static void ConvertVideoFrameToRGBPixels( |
+void ConvertVideoFrameToRGBPixels( |
const scoped_refptr<media::VideoFrame>& video_frame, |
void* rgb_pixels, |
size_t row_bytes) { |
@@ -205,7 +214,9 @@ static void ConvertVideoFrameToRGBPixels( |
} |
} |
-// Generates an RGB image from a VideoFrame. |
+} // anonymous namespace |
+ |
+// Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU. |
class VideoImageGenerator : public SkImageGenerator { |
public: |
VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { |
@@ -233,7 +244,7 @@ class VideoImageGenerator : public SkImageGenerator { |
return false; |
if (!pixels) |
return false; |
- // If skia couldn't do the YUV conversion, we will. |
+ // If skia couldn't do the YUV conversion on GPU, we will on CPU. |
ConvertVideoFrameToRGBPixels(frame_, pixels, row_bytes); |
return true; |
} |
@@ -289,7 +300,19 @@ class VideoImageGenerator : public SkImageGenerator { |
}; |
SkCanvasVideoRenderer::SkCanvasVideoRenderer() |
- : generator_(NULL), last_frame_timestamp_(media::kNoTimestamp()) { |
+ : last_frame_timestamp_(media::kNoTimestamp()), |
+ frame_deleting_timer_( |
+ FROM_HERE, |
+ base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), |
+ this, |
+ &SkCanvasVideoRenderer::ResetLastFrame), |
+ accelerated_generator_(NULL), |
+ accelerated_last_frame_timestamp_(media::kNoTimestamp()), |
+ accelerated_frame_deleting_timer_( |
+ FROM_HERE, |
+ base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay), |
+ this, |
+ &SkCanvasVideoRenderer::ResetAcceleratedLastFrame) { |
last_frame_.setIsVolatile(true); |
} |
@@ -313,27 +336,56 @@ void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, |
// Paint black rectangle if there isn't a frame available or the |
// frame has an unexpected format. |
- if (!video_frame.get() || !IsYUVOrNative(video_frame->format())) { |
+ if (!video_frame.get() || video_frame->natural_size().IsEmpty() || |
+ !IsYUVOrNative(video_frame->format())) { |
canvas->drawRect(dest, paint); |
canvas->flush(); |
return; |
} |
- // Check if we should convert and update |last_frame_|. |
- if (last_frame_.isNull() || |
- video_frame->timestamp() != last_frame_timestamp_) { |
- generator_ = new VideoImageGenerator(video_frame); |
+ SkBitmap* target_frame = NULL; |
+ if (canvas->getGrContext()) { |
+ if (accelerated_last_frame_.isNull() || |
+ video_frame->timestamp() != accelerated_last_frame_timestamp_) { |
+ accelerated_generator_ = new VideoImageGenerator(video_frame); |
- // Note: This takes ownership of |generator_|. |
- if (!SkInstallDiscardablePixelRef(generator_, &last_frame_)) { |
- NOTREACHED(); |
+ // Note: This takes ownership of |accelerated_generator_|. |
+ if (!SkInstallDiscardablePixelRef(accelerated_generator_, |
+ &accelerated_last_frame_)) { |
+ NOTREACHED(); |
+ } |
+ DCHECK(video_frame->visible_rect().width() == |
+ accelerated_last_frame_.width() && |
+ video_frame->visible_rect().height() == |
+ accelerated_last_frame_.height()); |
+ |
+ accelerated_last_frame_timestamp_ = video_frame->timestamp(); |
+ } else { |
+ accelerated_generator_->set_frame(video_frame); |
} |
- DCHECK(video_frame->visible_rect().width() == last_frame_.width() && |
- video_frame->visible_rect().height() == last_frame_.height()); |
- |
- last_frame_timestamp_ = video_frame->timestamp(); |
- } else if (generator_) { |
- generator_->set_frame(video_frame); |
+ target_frame = &accelerated_last_frame_; |
+ accelerated_frame_deleting_timer_.Reset(); |
+ } else { |
+ // Check if we should convert and update |last_frame_|. |
+ if (last_frame_.isNull() || |
+ video_frame->timestamp() != last_frame_timestamp_) { |
+ // Check if |bitmap| needs to be (re)allocated. |
+ if (last_frame_.isNull() || |
+ last_frame_.width() != video_frame->visible_rect().width() || |
+ last_frame_.height() != video_frame->visible_rect().height()) { |
+ last_frame_.allocN32Pixels(video_frame->visible_rect().width(), |
+ video_frame->visible_rect().height()); |
+ last_frame_.setIsVolatile(true); |
+ } |
+ last_frame_.lockPixels(); |
+ ConvertVideoFrameToRGBPixels( |
+ video_frame, last_frame_.getPixels(), last_frame_.rowBytes()); |
+ last_frame_.notifyPixelsChanged(); |
+ last_frame_.unlockPixels(); |
+ last_frame_timestamp_ = video_frame->timestamp(); |
+ } |
+ target_frame = &last_frame_; |
+ frame_deleting_timer_.Reset(); |
} |
paint.setXfermodeMode(mode); |
@@ -371,19 +423,19 @@ void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, |
gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); |
} |
canvas->scale( |
- SkFloatToScalar(rotated_dest_size.width() / last_frame_.width()), |
- SkFloatToScalar(rotated_dest_size.height() / last_frame_.height())); |
- canvas->translate(-SkFloatToScalar(last_frame_.width() * 0.5f), |
- -SkFloatToScalar(last_frame_.height() * 0.5f)); |
+ SkFloatToScalar(rotated_dest_size.width() / target_frame->width()), |
+ SkFloatToScalar(rotated_dest_size.height() / target_frame->height())); |
+ canvas->translate(-SkFloatToScalar(target_frame->width() * 0.5f), |
+ -SkFloatToScalar(target_frame->height() * 0.5f)); |
} |
- canvas->drawBitmap(last_frame_, 0, 0, &paint); |
+ canvas->drawBitmap(*target_frame, 0, 0, &paint); |
if (need_transform) |
canvas->restore(); |
canvas->flush(); |
// SkCanvas::flush() causes the generator to generate SkImage, so delete |
// |video_frame| not to be outlived. |
- if (generator_) |
- generator_->set_frame(NULL); |
+ if (canvas->getGrContext()) |
+ accelerated_generator_->set_frame(NULL); |
} |
void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, |
@@ -396,4 +448,15 @@ void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, |
media::VIDEO_ROTATION_0); |
} |
+void SkCanvasVideoRenderer::ResetLastFrame() { |
+ last_frame_.reset(); |
+ last_frame_timestamp_ = media::kNoTimestamp(); |
+} |
+ |
+void SkCanvasVideoRenderer::ResetAcceleratedLastFrame() { |
+ accelerated_last_frame_.reset(); |
+ accelerated_generator_ = nullptr; |
+ accelerated_last_frame_timestamp_ = media::kNoTimestamp(); |
+} |
+ |
} // namespace media |