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 "gpu/GLES2/gl2extchromium.h" | |
| 9 #include "gpu/command_buffer/client/gles2_interface.h" | |
| 10 #include "gpu/command_buffer/common/mailbox_holder.h" | |
| 8 #include "media/base/video_frame.h" | 11 #include "media/base/video_frame.h" |
| 9 #include "media/base/yuv_convert.h" | 12 #include "media/base/yuv_convert.h" |
| 13 #include "skia/ext/refptr.h" | |
| 10 #include "third_party/libyuv/include/libyuv.h" | 14 #include "third_party/libyuv/include/libyuv.h" |
| 11 #include "third_party/skia/include/core/SkCanvas.h" | 15 #include "third_party/skia/include/core/SkCanvas.h" |
| 12 #include "third_party/skia/include/core/SkImageGenerator.h" | 16 #include "third_party/skia/include/core/SkImageGenerator.h" |
| 17 #include "third_party/skia/include/gpu/GrContext.h" | |
| 18 #include "third_party/skia/include/gpu/SkGrPixelRef.h" | |
| 13 #include "ui/gfx/skbitmap_operations.h" | 19 #include "ui/gfx/skbitmap_operations.h" |
| 14 | 20 |
| 15 // Skia internal format depends on a platform. On Android it is ABGR, on others | 21 // Skia internal format depends on a platform. On Android it is ABGR, on others |
| 16 // it is ARGB. | 22 // it is ARGB. |
| 17 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ | 23 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ |
| 18 SK_A32_SHIFT == 24 | 24 SK_A32_SHIFT == 24 |
| 19 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB | 25 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB |
| 20 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB | 26 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB |
| 21 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ | 27 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ |
| 22 SK_A32_SHIFT == 24 | 28 SK_A32_SHIFT == 24 |
| 23 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR | 29 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR |
| 24 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR | 30 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR |
| 25 #else | 31 #else |
| 26 #error Unexpected Skia ARGB_8888 layout! | 32 #error Unexpected Skia ARGB_8888 layout! |
| 27 #endif | 33 #endif |
| 28 | 34 |
| 29 namespace media { | 35 namespace media { |
| 30 | 36 |
| 31 static bool IsYUV(media::VideoFrame::Format format) { | 37 namespace { |
| 38 | |
| 39 // This class keeps two temporary resources; software bitmap, hardware bitmap. | |
| 40 // If both bitmap are created and then only software bitmap is updated every | |
| 41 // frame, hardware bitmap outlives until the media player dies. So we delete | |
| 42 // a temporary resource if it is not used for 3 sec. | |
| 43 const int kTemporaryResourceDeletionDelay = 3; // Seconds; | |
|
scherkus (not reviewing)
2014/10/15 17:10:23
note this is in media time, which has little relat
| |
| 44 | |
| 45 bool IsYUV(media::VideoFrame::Format format) { | |
| 32 switch (format) { | 46 switch (format) { |
| 33 case VideoFrame::YV12: | 47 case VideoFrame::YV12: |
| 34 case VideoFrame::YV16: | 48 case VideoFrame::YV16: |
| 35 case VideoFrame::I420: | 49 case VideoFrame::I420: |
| 36 case VideoFrame::YV12A: | 50 case VideoFrame::YV12A: |
| 37 case VideoFrame::YV12J: | 51 case VideoFrame::YV12J: |
| 38 case VideoFrame::YV24: | 52 case VideoFrame::YV24: |
| 39 case VideoFrame::NV12: | 53 case VideoFrame::NV12: |
| 40 return true; | 54 return true; |
| 41 case VideoFrame::UNKNOWN: | 55 case VideoFrame::UNKNOWN: |
| 42 case VideoFrame::NATIVE_TEXTURE: | 56 case VideoFrame::NATIVE_TEXTURE: |
| 43 #if defined(VIDEO_HOLE) | 57 #if defined(VIDEO_HOLE) |
| 44 case VideoFrame::HOLE: | 58 case VideoFrame::HOLE: |
| 45 #endif // defined(VIDEO_HOLE) | 59 #endif // defined(VIDEO_HOLE) |
| 46 return false; | 60 return false; |
| 47 } | 61 } |
| 48 NOTREACHED() << "Invalid videoframe format provided: " << format; | 62 NOTREACHED() << "Invalid videoframe format provided: " << format; |
| 49 return false; | 63 return false; |
| 50 } | 64 } |
| 51 | 65 |
| 52 static bool IsJPEGColorSpace(media::VideoFrame::Format format) { | 66 bool IsJPEGColorSpace(media::VideoFrame::Format format) { |
| 53 switch (format) { | 67 switch (format) { |
| 54 case VideoFrame::YV12J: | 68 case VideoFrame::YV12J: |
| 55 return true; | 69 return true; |
| 56 case VideoFrame::YV12: | 70 case VideoFrame::YV12: |
| 57 case VideoFrame::YV16: | 71 case VideoFrame::YV16: |
| 58 case VideoFrame::I420: | 72 case VideoFrame::I420: |
| 59 case VideoFrame::YV12A: | 73 case VideoFrame::YV12A: |
| 60 case VideoFrame::YV24: | 74 case VideoFrame::YV24: |
| 61 case VideoFrame::NV12: | 75 case VideoFrame::NV12: |
| 62 case VideoFrame::UNKNOWN: | 76 case VideoFrame::UNKNOWN: |
| 63 case VideoFrame::NATIVE_TEXTURE: | 77 case VideoFrame::NATIVE_TEXTURE: |
| 64 #if defined(VIDEO_HOLE) | 78 #if defined(VIDEO_HOLE) |
| 65 case VideoFrame::HOLE: | 79 case VideoFrame::HOLE: |
| 66 #endif // defined(VIDEO_HOLE) | 80 #endif // defined(VIDEO_HOLE) |
| 67 return false; | 81 return false; |
| 68 } | 82 } |
| 69 NOTREACHED() << "Invalid videoframe format provided: " << format; | 83 NOTREACHED() << "Invalid videoframe format provided: " << format; |
| 70 return false; | 84 return false; |
| 71 } | 85 } |
| 72 | 86 |
| 73 static bool IsYUVOrNative(media::VideoFrame::Format format) { | 87 bool IsYUVOrNative(media::VideoFrame::Format format) { |
| 74 return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE; | 88 return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE; |
| 75 } | 89 } |
| 76 | 90 |
| 77 // Converts a |video_frame| to raw |rgb_pixels|. | 91 // Converts a |video_frame| to raw |rgb_pixels|. |
| 78 static void ConvertVideoFrameToRGBPixels( | 92 void ConvertVideoFrameToRGBPixels( |
| 79 const scoped_refptr<media::VideoFrame>& video_frame, | 93 const scoped_refptr<media::VideoFrame>& video_frame, |
| 80 void* rgb_pixels, | 94 void* rgb_pixels, |
| 81 size_t row_bytes) { | 95 size_t row_bytes) { |
| 82 DCHECK(IsYUVOrNative(video_frame->format())) | 96 DCHECK(IsYUVOrNative(video_frame->format())) |
| 83 << video_frame->format(); | 97 << video_frame->format(); |
| 84 if (IsYUV(video_frame->format())) { | 98 if (IsYUV(video_frame->format())) { |
| 85 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane), | 99 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane), |
| 86 video_frame->stride(media::VideoFrame::kVPlane)); | 100 video_frame->stride(media::VideoFrame::kVPlane)); |
| 87 } | 101 } |
| 88 | 102 |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 198 row_bytes); | 212 row_bytes); |
| 199 video_frame->ReadPixelsFromNativeTexture(tmp); | 213 video_frame->ReadPixelsFromNativeTexture(tmp); |
| 200 break; | 214 break; |
| 201 } | 215 } |
| 202 default: | 216 default: |
| 203 NOTREACHED(); | 217 NOTREACHED(); |
| 204 break; | 218 break; |
| 205 } | 219 } |
| 206 } | 220 } |
| 207 | 221 |
| 222 bool EnsureTextureBackedSkBitmap(GrContext* gr, | |
|
scherkus (not reviewing)
2014/10/15 17:10:23
I know you lifted this code from WebMediaPlayerAnd
| |
| 223 SkBitmap* bitmap, | |
| 224 const gfx::Size& size) { | |
| 225 if (!bitmap->getTexture() || bitmap->width() != size.width() || | |
| 226 bitmap->height() != size.height()) { | |
| 227 if (!gr) | |
| 228 return false; | |
| 229 GrTextureDesc desc; | |
| 230 // Use kRGBA_8888_GrPixelConfig, not kSkia8888_GrPixelConfig, because | |
| 231 // glCopyTextureChromium doesn't support GL_BGRA_EXT as internal format. | |
| 232 desc.fConfig = kRGBA_8888_GrPixelConfig; | |
| 233 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; | |
| 234 desc.fSampleCnt = 0; | |
| 235 desc.fOrigin = kTopLeft_GrSurfaceOrigin; | |
| 236 desc.fWidth = size.width(); | |
| 237 desc.fHeight = size.height(); | |
| 238 GrAutoScratchTexture scratch_texture( | |
| 239 gr, desc, GrContext::kExact_ScratchTexMatch); | |
| 240 skia::RefPtr<GrTexture> texture = skia::AdoptRef(scratch_texture.detach()); | |
| 241 if (!texture.get()) | |
| 242 return false; | |
| 243 | |
| 244 SkImageInfo info = SkImageInfo::MakeN32Premul(desc.fWidth, desc.fHeight); | |
| 245 SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (info, texture.get())); | |
| 246 if (!pixelRef) | |
| 247 return false; | |
| 248 bitmap->setInfo(info); | |
| 249 bitmap->setPixelRef(pixelRef)->unref(); | |
| 250 } | |
| 251 | |
| 252 return true; | |
| 253 } | |
| 254 | |
| 255 bool ConvertVideoFrameToTexture( | |
|
scherkus (not reviewing)
2014/10/15 17:10:23
considering all the various combinations of sw vs.
| |
| 256 VideoFrame* video_frame, | |
| 257 SkBitmap* bitmap, | |
| 258 SkCanvasVideoRenderer::Context3DProvider* context_provider) { | |
| 259 DCHECK(context_provider && context_provider->gl && | |
| 260 context_provider->gr_context && | |
| 261 video_frame->format() == VideoFrame::NATIVE_TEXTURE); | |
| 262 | |
| 263 // Check if we could reuse existing texture based bitmap. | |
| 264 // Otherwise, release existing texture based bitmap and allocate | |
| 265 // a new one based on video size. | |
| 266 if (!EnsureTextureBackedSkBitmap(context_provider->gr_context, | |
| 267 bitmap, | |
| 268 video_frame->visible_rect().size())) { | |
| 269 return false; | |
| 270 } | |
| 271 | |
| 272 unsigned textureId = | |
|
scherkus (not reviewing)
2014/10/15 17:10:24
textureId -> texture_id
| |
| 273 static_cast<unsigned>((bitmap->getTexture())->getTextureHandle()); | |
| 274 // If CopyVideoFrameToTexture() changes the state of the 'textureId', it's | |
| 275 // needed to invalidate the state cached in skia, but currently the state | |
| 276 // isn't changed. | |
| 277 SkCanvasVideoRenderer::CopyVideoFrameToTexture(context_provider->gl, | |
| 278 video_frame, | |
| 279 textureId, | |
| 280 0, | |
| 281 GL_RGBA, | |
| 282 GL_UNSIGNED_BYTE, | |
| 283 true, | |
| 284 false); | |
| 285 return true; | |
| 286 } | |
| 287 | |
| 288 class SyncPointClientImpl : public VideoFrame::SyncPointClient { | |
| 289 public: | |
| 290 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} | |
| 291 virtual ~SyncPointClientImpl() {} | |
| 292 virtual uint32 InsertSyncPoint() OVERRIDE { | |
| 293 return gl_->InsertSyncPointCHROMIUM(); | |
| 294 } | |
| 295 virtual void WaitSyncPoint(uint32 sync_point) OVERRIDE { | |
| 296 gl_->WaitSyncPointCHROMIUM(sync_point); | |
| 297 } | |
| 298 | |
| 299 private: | |
| 300 gpu::gles2::GLES2Interface* gl_; | |
| 301 }; | |
| 302 | |
| 303 bool IsNull(SkCanvasVideoRenderer::Context3DProvider* context_provider) { | |
| 304 return !context_provider || | |
| 305 !(context_provider->gl && context_provider->gr_context); | |
| 306 } | |
| 307 | |
| 308 } // anonymous namespace | |
| 309 | |
| 208 // Generates an RGB image from a VideoFrame. | 310 // Generates an RGB image from a VideoFrame. |
| 209 class VideoImageGenerator : public SkImageGenerator { | 311 class VideoImageGenerator : public SkImageGenerator { |
| 210 public: | 312 public: |
| 211 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { | 313 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) { |
| 212 DCHECK(frame_.get()); | 314 DCHECK(frame_.get()); |
| 213 } | 315 } |
| 214 virtual ~VideoImageGenerator() {} | 316 virtual ~VideoImageGenerator() {} |
| 215 | 317 |
| 216 void set_frame(const scoped_refptr<VideoFrame>& frame) { frame_ = frame; } | 318 void set_frame(const scoped_refptr<VideoFrame>& frame) { frame_ = frame; } |
| 217 | 319 |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 282 return true; | 384 return true; |
| 283 } | 385 } |
| 284 | 386 |
| 285 private: | 387 private: |
| 286 scoped_refptr<VideoFrame> frame_; | 388 scoped_refptr<VideoFrame> frame_; |
| 287 | 389 |
| 288 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); | 390 DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator); |
| 289 }; | 391 }; |
| 290 | 392 |
| 291 SkCanvasVideoRenderer::SkCanvasVideoRenderer() | 393 SkCanvasVideoRenderer::SkCanvasVideoRenderer() |
| 292 : generator_(NULL), last_frame_timestamp_(media::kNoTimestamp()) { | 394 : generator_(NULL), |
| 395 last_frame_timestamp_(media::kNoTimestamp()), | |
| 396 accelerated_last_frame_timestamp_(media::kNoTimestamp()) { | |
| 293 last_frame_.setIsVolatile(true); | 397 last_frame_.setIsVolatile(true); |
| 294 } | 398 } |
| 295 | 399 |
| 296 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} | 400 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} |
| 297 | 401 |
| 298 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, | 402 void SkCanvasVideoRenderer::Paint( |
| 299 SkCanvas* canvas, | 403 const scoped_refptr<VideoFrame>& video_frame, |
| 300 const gfx::RectF& dest_rect, | 404 SkCanvas* canvas, |
| 301 uint8 alpha, | 405 const gfx::RectF& dest_rect, |
| 302 SkXfermode::Mode mode, | 406 uint8 alpha, |
| 303 VideoRotation video_rotation) { | 407 SkXfermode::Mode mode, |
| 408 VideoRotation video_rotation, | |
| 409 SkCanvasVideoRenderer::Context3DProvider* context_provider) { | |
|
scherkus (not reviewing)
2014/10/15 17:10:23
we can drop SkCanvasVideoRenderer::, right?
| |
| 304 if (alpha == 0) { | 410 if (alpha == 0) { |
| 305 return; | 411 return; |
| 306 } | 412 } |
| 307 | 413 |
| 308 SkRect dest; | 414 SkRect dest; |
| 309 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); | 415 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); |
| 310 | 416 |
| 311 SkPaint paint; | 417 SkPaint paint; |
| 312 paint.setAlpha(alpha); | 418 paint.setAlpha(alpha); |
| 313 | 419 |
| 314 // Paint black rectangle if there isn't a frame available or the | 420 // Paint black rectangle if there isn't a frame available or the |
| 315 // frame has an unexpected format. | 421 // frame has an unexpected format. |
| 316 if (!video_frame.get() || !IsYUVOrNative(video_frame->format())) { | 422 if (!video_frame.get() || video_frame->natural_size().IsEmpty() || |
| 423 !IsYUVOrNative(video_frame->format())) { | |
| 317 canvas->drawRect(dest, paint); | 424 canvas->drawRect(dest, paint); |
| 318 canvas->flush(); | 425 canvas->flush(); |
| 319 return; | 426 return; |
| 320 } | 427 } |
| 321 | 428 |
| 429 bool accelerated = false; | |
| 430 if (!IsNull(context_provider) && | |
| 431 video_frame->format() == media::VideoFrame::NATIVE_TEXTURE && | |
| 432 canvas->getGrContext()) { | |
| 433 // TODO(dshwang): Android video decoder doesn't update the timestamp on a | |
| 434 // VideoFrame. To reduce redundant copy, Android should update the | |
| 435 // timestamp. | |
| 436 if (accelerated_last_frame_.isNull() || | |
| 437 video_frame->timestamp() != accelerated_last_frame_timestamp_ || | |
| 438 video_frame->timestamp() == base::TimeDelta()) { | |
| 439 accelerated = ConvertVideoFrameToTexture( | |
| 440 video_frame.get(), &accelerated_last_frame_, context_provider); | |
| 441 if (accelerated) | |
| 442 accelerated_last_frame_timestamp_ = video_frame->timestamp(); | |
| 443 } else { | |
| 444 DCHECK(accelerated_last_frame_.getTexture()); | |
| 445 accelerated = true; | |
| 446 } | |
| 447 } | |
| 448 | |
| 322 // Check if we should convert and update |last_frame_|. | 449 // Check if we should convert and update |last_frame_|. |
| 323 if (last_frame_.isNull() || | 450 if (!accelerated) { |
| 324 video_frame->timestamp() != last_frame_timestamp_) { | 451 if (last_frame_.isNull() || |
| 325 generator_ = new VideoImageGenerator(video_frame); | 452 video_frame->timestamp() != last_frame_timestamp_) { |
| 453 generator_ = new VideoImageGenerator(video_frame); | |
| 326 | 454 |
| 327 // Note: This takes ownership of |generator_|. | 455 // Note: This takes ownership of |generator_|. |
| 328 if (!SkInstallDiscardablePixelRef(generator_, &last_frame_)) { | 456 if (!SkInstallDiscardablePixelRef(generator_, &last_frame_)) { |
| 329 NOTREACHED(); | 457 NOTREACHED(); |
| 458 } | |
| 459 DCHECK(video_frame->visible_rect().width() == last_frame_.width() && | |
| 460 video_frame->visible_rect().height() == last_frame_.height()); | |
| 461 | |
| 462 last_frame_timestamp_ = video_frame->timestamp(); | |
| 463 } else if (generator_) { | |
| 464 generator_->set_frame(video_frame); | |
| 330 } | 465 } |
| 331 DCHECK(video_frame->visible_rect().width() == last_frame_.width() && | |
| 332 video_frame->visible_rect().height() == last_frame_.height()); | |
| 333 | |
| 334 last_frame_timestamp_ = video_frame->timestamp(); | |
| 335 } else if (generator_) { | |
| 336 generator_->set_frame(video_frame); | |
| 337 } | 466 } |
| 338 | 467 |
| 339 paint.setXfermodeMode(mode); | 468 paint.setXfermodeMode(mode); |
| 340 paint.setFilterLevel(SkPaint::kLow_FilterLevel); | 469 paint.setFilterLevel(SkPaint::kLow_FilterLevel); |
| 341 | 470 |
| 471 SkBitmap& target_frame = accelerated ? accelerated_last_frame_ : last_frame_; | |
| 342 bool need_transform = | 472 bool need_transform = |
| 343 video_rotation != VIDEO_ROTATION_0 || | 473 video_rotation != VIDEO_ROTATION_0 || |
| 344 dest_rect.size() != video_frame->visible_rect().size() || | 474 dest_rect.size() != video_frame->visible_rect().size() || |
| 345 !dest_rect.origin().IsOrigin(); | 475 !dest_rect.origin().IsOrigin(); |
| 346 if (need_transform) { | 476 if (need_transform) { |
| 347 canvas->save(); | 477 canvas->save(); |
| 348 canvas->translate( | 478 canvas->translate( |
| 349 SkFloatToScalar(dest_rect.x() + (dest_rect.width() * 0.5f)), | 479 SkFloatToScalar(dest_rect.x() + (dest_rect.width() * 0.5f)), |
| 350 SkFloatToScalar(dest_rect.y() + (dest_rect.height() * 0.5f))); | 480 SkFloatToScalar(dest_rect.y() + (dest_rect.height() * 0.5f))); |
| 351 SkScalar angle = SkFloatToScalar(0.0f); | 481 SkScalar angle = SkFloatToScalar(0.0f); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 364 } | 494 } |
| 365 canvas->rotate(angle); | 495 canvas->rotate(angle); |
| 366 | 496 |
| 367 gfx::SizeF rotated_dest_size = dest_rect.size(); | 497 gfx::SizeF rotated_dest_size = dest_rect.size(); |
| 368 if (video_rotation == VIDEO_ROTATION_90 || | 498 if (video_rotation == VIDEO_ROTATION_90 || |
| 369 video_rotation == VIDEO_ROTATION_270) { | 499 video_rotation == VIDEO_ROTATION_270) { |
| 370 rotated_dest_size = | 500 rotated_dest_size = |
| 371 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); | 501 gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); |
| 372 } | 502 } |
| 373 canvas->scale( | 503 canvas->scale( |
| 374 SkFloatToScalar(rotated_dest_size.width() / last_frame_.width()), | 504 SkFloatToScalar(rotated_dest_size.width() / target_frame.width()), |
| 375 SkFloatToScalar(rotated_dest_size.height() / last_frame_.height())); | 505 SkFloatToScalar(rotated_dest_size.height() / target_frame.height())); |
| 376 canvas->translate(-SkFloatToScalar(last_frame_.width() * 0.5f), | 506 canvas->translate(-SkFloatToScalar(target_frame.width() * 0.5f), |
| 377 -SkFloatToScalar(last_frame_.height() * 0.5f)); | 507 -SkFloatToScalar(target_frame.height() * 0.5f)); |
| 378 } | 508 } |
| 379 canvas->drawBitmap(last_frame_, 0, 0, &paint); | 509 canvas->drawBitmap(target_frame, 0, 0, &paint); |
| 380 if (need_transform) | 510 if (need_transform) |
| 381 canvas->restore(); | 511 canvas->restore(); |
| 382 canvas->flush(); | 512 canvas->flush(); |
| 383 // SkCanvas::flush() causes the generator to generate SkImage, so delete | 513 // SkCanvas::flush() causes the generator to generate SkImage, so delete |
| 384 // |video_frame| not to be outlived. | 514 // |video_frame| not to be outlived. |
| 385 if (generator_) | 515 if (generator_) |
| 386 generator_->set_frame(NULL); | 516 generator_->set_frame(NULL); |
| 517 CleanUpTemporaryBuffers(); | |
| 387 } | 518 } |
| 388 | 519 |
| 389 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, | 520 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, |
| 390 SkCanvas* canvas) { | 521 SkCanvas* canvas) { |
| 391 Paint(video_frame, | 522 Paint(video_frame, |
| 392 canvas, | 523 canvas, |
| 393 video_frame->visible_rect(), | 524 video_frame->visible_rect(), |
| 394 0xff, | 525 0xff, |
| 395 SkXfermode::kSrc_Mode, | 526 SkXfermode::kSrc_Mode, |
| 396 media::VIDEO_ROTATION_0); | 527 media::VIDEO_ROTATION_0, |
| 528 NULL); | |
| 529 } | |
| 530 | |
| 531 void SkCanvasVideoRenderer::CleanUpTemporaryBuffers() { | |
| 532 static const base::TimeDelta buffer_time = | |
| 533 base::TimeDelta::FromSeconds(kTemporaryResourceDeletionDelay); | |
| 534 // Find the latest time stamp. | |
| 535 base::TimeDelta last_timestamp = | |
| 536 accelerated_last_frame_timestamp_ > last_frame_timestamp_ | |
| 537 ? accelerated_last_frame_timestamp_ | |
| 538 : last_frame_timestamp_; | |
| 539 // Delete a too old resource. | |
| 540 if (last_timestamp > last_frame_timestamp_ + buffer_time && | |
| 541 !last_frame_.isNull()) { | |
| 542 last_frame_.reset(); | |
| 543 } | |
| 544 if (last_timestamp > accelerated_last_frame_timestamp_ + buffer_time && | |
| 545 !accelerated_last_frame_.isNull()) { | |
| 546 accelerated_last_frame_.reset(); | |
| 547 } | |
| 548 } | |
| 549 | |
| 550 // static | |
| 551 void SkCanvasVideoRenderer::CopyVideoFrameToTexture( | |
|
scherkus (not reviewing)
2014/10/15 17:10:23
ditto for precise naming
this is much more like C
| |
| 552 gpu::gles2::GLES2Interface* gl, | |
| 553 VideoFrame* video_frame, | |
| 554 unsigned int texture, | |
| 555 unsigned int level, | |
| 556 unsigned int internal_format, | |
| 557 unsigned int type, | |
| 558 bool premultiply_alpha, | |
| 559 bool flip_y) { | |
| 560 DCHECK(video_frame && video_frame->format() == VideoFrame::NATIVE_TEXTURE); | |
| 561 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder(); | |
| 562 DCHECK(mailbox_holder->texture_target == GL_TEXTURE_2D || | |
| 563 mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES); | |
| 564 | |
| 565 gl->WaitSyncPointCHROMIUM(mailbox_holder->sync_point); | |
| 566 uint32 source_texture = gl->CreateAndConsumeTextureCHROMIUM( | |
| 567 mailbox_holder->texture_target, mailbox_holder->mailbox.name); | |
| 568 | |
| 569 // The video is stored in a unmultiplied format, so premultiply | |
| 570 // if necessary. | |
| 571 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, premultiply_alpha); | |
| 572 // Application itself needs to take care of setting the right |flip_y| | |
| 573 // value down to get the expected result. | |
| 574 // "flip_y == true" means to reverse the video orientation while | |
| 575 // "flip_y == false" means to keep the intrinsic orientation. | |
| 576 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y); | |
| 577 gl->CopyTextureCHROMIUM( | |
| 578 GL_TEXTURE_2D, source_texture, texture, level, internal_format, type); | |
| 579 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false); | |
| 580 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false); | |
| 581 | |
| 582 gl->DeleteTextures(1, &source_texture); | |
| 583 gl->Flush(); | |
| 584 | |
| 585 SyncPointClientImpl client(gl); | |
| 586 video_frame->UpdateReleaseSyncPoint(&client); | |
| 397 } | 587 } |
| 398 | 588 |
| 399 } // namespace media | 589 } // namespace media |
| OLD | NEW |