1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/compositor/delegated_frame_host.h" 6 7 #include "base/callback_helpers.h" 8 #include "base/command_line.h" 9 #include "cc/output/compositor_frame.h" 10 #include "cc/output/compositor_frame_ack.h" 11 #include "cc/output/copy_output_request.h" 12 #include "cc/resources/single_release_callback.h" 13 #include "cc/resources/texture_mailbox.h" 14 #include "content/browser/compositor/resize_lock.h" 15 #include "content/common/gpu/client/gl_helper.h" 16 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" 17 #include "content/public/common/content_switches.h" 18 #include "media/base/video_frame.h" 19 #include "media/base/video_util.h" 20 #include "skia/ext/image_operations.h" 21 22 namespace content { 23 24 //////////////////////////////////////////////////////////////////////////////// 25 // DelegatedFrameHostClient 26 27 bool DelegatedFrameHostClient::ShouldCreateResizeLock() { 28 // On Windows and Linux, holding pointer moves will not help throttling 29 // resizes. 30 // TODO(piman): on Windows we need to block (nested message loop?) the 31 // WM_SIZE event. On Linux we need to throttle at the WM level using 32 // _NET_WM_SYNC_REQUEST. 33 // TODO(ccameron): Mac browser window resizing is incompletely implemented. 34 #if !defined(OS_CHROMEOS) 35 return false; 36 #else 37 return GetDelegatedFrameHost()->ShouldCreateResizeLock(); 38 #endif 39 } 40 41 void DelegatedFrameHostClient::RequestCopyOfOutput( 42 scoped_ptr<cc::CopyOutputRequest> request) { 43 GetDelegatedFrameHost()->RequestCopyOfOutput(request.Pass()); 44 } 45 46 //////////////////////////////////////////////////////////////////////////////// 47 // DelegatedFrameHost 48 49 DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client) 50 : client_(client), 51 last_output_surface_id_(0), 52 pending_delegated_ack_count_(0), 53 skipped_frames_(false), 54 can_lock_compositor_(YES_CAN_LOCK), 55 delegated_frame_evictor_(new DelegatedFrameEvictor(this)) { 56 ImageTransportFactory::GetInstance()->AddObserver(this); 57 } 58 59 void DelegatedFrameHost::WasShown() { 60 delegated_frame_evictor_->SetVisible(true); 61 62 if (!released_front_lock_.get()) { 63 ui::Compositor* compositor = client_->GetCompositor(); 64 if (compositor) 65 released_front_lock_ = compositor->GetCompositorLock(); 66 } 67 } 68 69 void DelegatedFrameHost::WasHidden() { 70 delegated_frame_evictor_->SetVisible(false); 71 released_front_lock_ = NULL; 72 } 73 74 void DelegatedFrameHost::MaybeCreateResizeLock() { 75 if (!client_->ShouldCreateResizeLock()) 76 return; 77 DCHECK(client_->GetCompositor()); 78 79 // Listen to changes in the compositor lock state. 80 ui::Compositor* compositor = client_->GetCompositor(); 81 if (!compositor->HasObserver(this)) 82 compositor->AddObserver(this); 83 84 bool defer_compositor_lock = 85 can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || 86 can_lock_compositor_ == NO_PENDING_COMMIT; 87 88 if (can_lock_compositor_ == YES_CAN_LOCK) 89 can_lock_compositor_ = YES_DID_LOCK; 90 91 resize_lock_ = client_->CreateResizeLock(defer_compositor_lock); 92 } 93 94 bool DelegatedFrameHost::ShouldCreateResizeLock() { 95 RenderWidgetHostImpl* host = client_->GetHost(); 96 97 if (resize_lock_) 98 return false; 99 100 if (host->should_auto_resize()) 101 return false; 102 103 gfx::Size desired_size = client_->DesiredFrameSize(); 104 if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty()) 105 return false; 106 107 ui::Compositor* compositor = client_->GetCompositor(); 108 if (!compositor) 109 return false; 110 111 return true; 112 } 113 114 void DelegatedFrameHost::RequestCopyOfOutput( 115 scoped_ptr<cc::CopyOutputRequest> request) { 116 client_->GetLayer()->RequestCopyOfOutput(request.Pass()); 117 } 118 119 void DelegatedFrameHost::CopyFromCompositingSurface( 120 const gfx::Rect& src_subrect, 121 const gfx::Size& dst_size, 122 const base::Callback<void(bool, const SkBitmap&)>& callback, 123 const SkBitmap::Config config) { 124 // Only ARGB888 and RGB565 supported as of now. 125 bool format_support = ((config == SkBitmap::kRGB_565_Config) || 126 (config == SkBitmap::kARGB_8888_Config)); 127 DCHECK(format_support); 128 if (!CanCopyToBitmap()) { 129 callback.Run(false, SkBitmap()); 130 return; 131 } 132 133 const gfx::Size& dst_size_in_pixel = 134 client_->ConvertViewSizeToPixel(dst_size); 135 scoped_ptr<cc::CopyOutputRequest> request = 136 cc::CopyOutputRequest::CreateRequest(base::Bind( 137 &DelegatedFrameHost::CopyFromCompositingSurfaceHasResult, 138 dst_size_in_pixel, 139 config, 140 callback)); 141 gfx::Rect src_subrect_in_pixel = 142 ConvertRectToPixel(client_->CurrentDeviceScaleFactor(), src_subrect); 143 request->set_area(src_subrect_in_pixel); 144 client_->RequestCopyOfOutput(request.Pass()); 145 } 146 147 void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame( 148 const gfx::Rect& src_subrect, 149 const scoped_refptr<media::VideoFrame>& target, 150 const base::Callback<void(bool)>& callback) { 151 if (!CanCopyToVideoFrame()) { 152 callback.Run(false); 153 return; 154 } 155 156 // Try get a texture to reuse. 157 scoped_refptr<OwnedMailbox> subscriber_texture; 158 if (frame_subscriber_) { 159 if (!idle_frame_subscriber_textures_.empty()) { 160 subscriber_texture = idle_frame_subscriber_textures_.back(); 161 idle_frame_subscriber_textures_.pop_back(); 162 } else if (GLHelper* helper = 163 ImageTransportFactory::GetInstance()->GetGLHelper()) { 164 subscriber_texture = new OwnedMailbox(helper); 165 } 166 } 167 168 scoped_ptr<cc::CopyOutputRequest> request = 169 cc::CopyOutputRequest::CreateRequest(base::Bind( 170 &DelegatedFrameHost:: 171 CopyFromCompositingSurfaceHasResultForVideo, 172 AsWeakPtr(), // For caching the ReadbackYUVInterface on this class. 173 subscriber_texture, 174 target, 175 callback)); 176 gfx::Rect src_subrect_in_pixel = 177 ConvertRectToPixel(client_->CurrentDeviceScaleFactor(), src_subrect); 178 request->set_area(src_subrect_in_pixel); 179 if (subscriber_texture.get()) { 180 request->SetTextureMailbox( 181 cc::TextureMailbox(subscriber_texture->mailbox(), 182 subscriber_texture->target(), 183 subscriber_texture->sync_point())); 184 } 185 client_->RequestCopyOfOutput(request.Pass()); 186 } 187 188 bool DelegatedFrameHost::CanCopyToBitmap() const { 189 return client_->GetCompositor() && 190 client_->GetLayer()->has_external_content(); 191 } 192 193 bool DelegatedFrameHost::CanCopyToVideoFrame() const { 194 return client_->GetCompositor() && 195 client_->GetLayer()->has_external_content(); 196 } 197 198 bool DelegatedFrameHost::CanSubscribeFrame() const { 199 return true; 200 } 201 202 void DelegatedFrameHost::BeginFrameSubscription( 203 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) { 204 frame_subscriber_ = subscriber.Pass(); 205 } 206 207 void DelegatedFrameHost::EndFrameSubscription() { 208 idle_frame_subscriber_textures_.clear(); 209 frame_subscriber_.reset(); 210 } 211 212 bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip) const { 213 // Should skip a frame only when another frame from the renderer is guaranteed 214 // to replace it. Otherwise may cause hangs when the renderer is waiting for 215 // the completion of latency infos (such as when taking a Snapshot.) 216 if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || 217 can_lock_compositor_ == NO_PENDING_COMMIT || 218 !resize_lock_.get()) 219 return false; 220 221 return size_in_dip != resize_lock_->expected_size(); 222 } 223 224 void DelegatedFrameHost::WasResized() { 225 MaybeCreateResizeLock(); 226 } 227 228 gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const { 229 if (resize_lock_) 230 return resize_lock_->expected_size(); 231 else 232 return client_->DesiredFrameSize(); 233 } 234 235 void DelegatedFrameHost::CheckResizeLock() { 236 if (!resize_lock_ || 237 resize_lock_->expected_size() != current_frame_size_in_dip_) 238 return; 239 240 // Since we got the size we were looking for, unlock the compositor. But delay 241 // the release of the lock until we've kicked a frame with the new texture, to 242 // avoid resizing the UI before we have a chance to draw a "good" frame. 243 resize_lock_->UnlockCompositor(); 244 ui::Compositor* compositor = client_->GetCompositor(); 245 if (compositor) { 246 if (!compositor->HasObserver(this)) 247 compositor->AddObserver(this); 248 } 249 } 250 251 void DelegatedFrameHost::DidReceiveFrameFromRenderer() { 252 if (frame_subscriber() && CanCopyToVideoFrame()) { 253 const base::TimeTicks present_time = base::TimeTicks::Now(); 254 scoped_refptr<media::VideoFrame> frame; 255 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback; 256 if (frame_subscriber()->ShouldCaptureFrame(present_time, 257 &frame, &callback)) { 258 CopyFromCompositingSurfaceToVideoFrame( 259 gfx::Rect(current_frame_size_in_dip_), 260 frame, 261 base::Bind(callback, present_time)); 262 } 263 } 264 } 265 266 void DelegatedFrameHost::SwapDelegatedFrame( 267 uint32 output_surface_id, 268 scoped_ptr<cc::DelegatedFrameData> frame_data, 269 float frame_device_scale_factor, 270 const std::vector<ui::LatencyInfo>& latency_info) { 271 RenderWidgetHostImpl* host = client_->GetHost(); 272 DCHECK(!frame_data->render_pass_list.empty()); 273 274 cc::RenderPass* root_pass = frame_data->render_pass_list.back(); 275 276 gfx::Size frame_size = root_pass->output_rect.size(); 277 gfx::Size frame_size_in_dip = 278 ConvertSizeToDIP(frame_device_scale_factor, frame_size); 279 280 gfx::Rect damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect); 281 damage_rect.Intersect(gfx::Rect(frame_size)); 282 gfx::Rect damage_rect_in_dip = 283 ConvertRectToDIP(frame_device_scale_factor, damage_rect); 284 285 if (ShouldSkipFrame(frame_size_in_dip)) { 286 cc::CompositorFrameAck ack; 287 cc::TransferableResource::ReturnResources(frame_data->resource_list, 288 &ack.resources); 289 290 skipped_latency_info_list_.insert(skipped_latency_info_list_.end(), 291 latency_info.begin(), latency_info.end()); 292 293 RenderWidgetHostImpl::SendSwapCompositorFrameAck( 294 host->GetRoutingID(), output_surface_id, 295 host->GetProcess()->GetID(), ack); 296 skipped_frames_ = true; 297 return; 298 } 299 300 if (skipped_frames_) { 301 skipped_frames_ = false; 302 damage_rect = gfx::Rect(frame_size); 303 damage_rect_in_dip = gfx::Rect(frame_size_in_dip); 304 305 // Give the same damage rect to the compositor. 306 cc::RenderPass* root_pass = frame_data->render_pass_list.back(); 307 root_pass->damage_rect = damage_rect; 308 } 309 310 if (output_surface_id != last_output_surface_id_) { 311 // Resource ids are scoped by the output surface. 312 // If the originating output surface doesn't match the last one, it 313 // indicates the renderer's output surface may have been recreated, in which 314 // case we should recreate the DelegatedRendererLayer, to avoid matching 315 // resources from the old one with resources from the new one which would 316 // have the same id. Changing the layer to showing painted content destroys 317 // the DelegatedRendererLayer. 318 EvictDelegatedFrame(); 319 320 // Drop the cc::DelegatedFrameResourceCollection so that we will not return 321 // any resources from the old output surface with the new output surface id. 322 if (resource_collection_.get()) { 323 resource_collection_->SetClient(NULL); 324 325 if (resource_collection_->LoseAllResources()) 326 SendReturnedDelegatedResources(last_output_surface_id_); 327 328 resource_collection_ = NULL; 329 } 330 last_output_surface_id_ = output_surface_id; 331 } 332 if (frame_size.IsEmpty()) { 333 DCHECK(frame_data->resource_list.empty()); 334 EvictDelegatedFrame(); 335 } else { 336 if (!resource_collection_) { 337 resource_collection_ = new cc::DelegatedFrameResourceCollection; 338 resource_collection_->SetClient(this); 339 } 340 // If the physical frame size changes, we need a new |frame_provider_|. If 341 // the physical frame size is the same, but the size in DIP changed, we 342 // need to adjust the scale at which the frames will be drawn, and we do 343 // this by making a new |frame_provider_| also to ensure the scale change 344 // is presented in sync with the new frame content. 345 if (!frame_provider_.get() || frame_size != frame_provider_->frame_size() || 346 frame_size_in_dip != current_frame_size_in_dip_) { 347 frame_provider_ = new cc::DelegatedFrameProvider( 348 resource_collection_.get(), frame_data.Pass()); 349 client_->GetLayer()->SetShowDelegatedContent(frame_provider_.get(), 350 frame_size_in_dip); 351 } else { 352 frame_provider_->SetFrameData(frame_data.Pass()); 353 } 354 } 355 released_front_lock_ = NULL; 356 current_frame_size_in_dip_ = frame_size_in_dip; 357 CheckResizeLock(); 358 359 client_->SchedulePaintInRect(damage_rect_in_dip); 360 361 pending_delegated_ack_count_++; 362 363 ui::Compositor* compositor = client_->GetCompositor(); 364 if (!compositor) { 365 SendDelegatedFrameAck(output_surface_id); 366 } else { 367 std::vector<ui::LatencyInfo>::const_iterator it; 368 for (it = latency_info.begin(); it != latency_info.end(); ++it) 369 compositor->SetLatencyInfo(*it); 370 // If we've previously skipped any latency infos add them. 371 for (it = skipped_latency_info_list_.begin(); 372 it != skipped_latency_info_list_.end(); 373 ++it) 374 compositor->SetLatencyInfo(*it); 375 skipped_latency_info_list_.clear(); 376 AddOnCommitCallbackAndDisableLocks( 377 base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck, 378 AsWeakPtr(), 379 output_surface_id)); 380 } 381 DidReceiveFrameFromRenderer(); 382 if (frame_provider_.get()) 383 delegated_frame_evictor_->SwappedFrame(!host->is_hidden()); 384 // Note: the frame may have been evicted immediately. 385 } 386 387 void DelegatedFrameHost::SendDelegatedFrameAck(uint32 output_surface_id) { 388 RenderWidgetHostImpl* host = client_->GetHost(); 389 cc::CompositorFrameAck ack; 390 if (resource_collection_) 391 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources); 392 RenderWidgetHostImpl::SendSwapCompositorFrameAck(host->GetRoutingID(), 393 output_surface_id, 394 host->GetProcess()->GetID(), 395 ack); 396 DCHECK_GT(pending_delegated_ack_count_, 0); 397 pending_delegated_ack_count_--; 398 } 399 400 void DelegatedFrameHost::UnusedResourcesAreAvailable() { 401 if (pending_delegated_ack_count_) 402 return; 403 404 SendReturnedDelegatedResources(last_output_surface_id_); 405 } 406 407 void DelegatedFrameHost::SendReturnedDelegatedResources( 408 uint32 output_surface_id) { 409 RenderWidgetHostImpl* host = client_->GetHost(); 410 DCHECK(resource_collection_); 411 412 cc::CompositorFrameAck ack; 413 resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources); 414 DCHECK(!ack.resources.empty()); 415 416 RenderWidgetHostImpl::SendReclaimCompositorResources( 417 host->GetRoutingID(), 418 output_surface_id, 419 host->GetProcess()->GetID(), 420 ack); 421 } 422 423 void DelegatedFrameHost::EvictDelegatedFrame() { 424 client_->GetLayer()->SetShowPaintedContent(); 425 frame_provider_ = NULL; 426 delegated_frame_evictor_->DiscardedFrame(); 427 } 428 429 // static 430 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult( 431 const gfx::Size& dst_size_in_pixel, 432 const SkBitmap::Config config, 433 const base::Callback<void(bool, const SkBitmap&)>& callback, 434 scoped_ptr<cc::CopyOutputResult> result) { 435 if (result->IsEmpty() || result->size().IsEmpty()) { 436 callback.Run(false, SkBitmap()); 437 return; 438 } 439 440 if (result->HasTexture()) { 441 PrepareTextureCopyOutputResult(dst_size_in_pixel, config, 442 callback, 443 result.Pass()); 444 return; 445 } 446 447 DCHECK(result->HasBitmap()); 448 PrepareBitmapCopyOutputResult(dst_size_in_pixel, config, callback, 449 result.Pass()); 450 } 451 452 static void CopyFromCompositingSurfaceFinished( 453 const base::Callback<void(bool, const SkBitmap&)>& callback, 454 scoped_ptr<cc::SingleReleaseCallback> release_callback, 455 scoped_ptr<SkBitmap> bitmap, 456 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock, 457 bool result) { 458 bitmap_pixels_lock.reset(); 459 460 uint32 sync_point = 0; 461 if (result) { 462 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper(); 463 sync_point = gl_helper->InsertSyncPoint(); 464 } 465 bool lost_resource = sync_point == 0; 466 release_callback->Run(sync_point, lost_resource); 467 468 callback.Run(result, *bitmap); 469 } 470 471 // static 472 void DelegatedFrameHost::PrepareTextureCopyOutputResult( 473 const gfx::Size& dst_size_in_pixel, 474 const SkBitmap::Config config, 475 const base::Callback<void(bool, const SkBitmap&)>& callback, 476 scoped_ptr<cc::CopyOutputResult> result) { 477 DCHECK(result->HasTexture()); 478 base::ScopedClosureRunner scoped_callback_runner( 479 base::Bind(callback, false, SkBitmap())); 480 481 scoped_ptr<SkBitmap> bitmap(new SkBitmap); 482 bitmap->setConfig(config, 483 dst_size_in_pixel.width(), dst_size_in_pixel.height(), 484 0, kOpaque_SkAlphaType); 485 if (!bitmap->allocPixels()) 486 return; 487 488 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); 489 GLHelper* gl_helper = factory->GetGLHelper(); 490 if (!gl_helper) 491 return; 492 493 scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock( 494 new SkAutoLockPixels(*bitmap)); 495 uint8* pixels = static_cast<uint8*>(bitmap->getPixels()); 496 497 cc::TextureMailbox texture_mailbox; 498 scoped_ptr<cc::SingleReleaseCallback> release_callback; 499 result->TakeTexture(&texture_mailbox, &release_callback); 500 DCHECK(texture_mailbox.IsTexture()); 501 502 ignore_result(scoped_callback_runner.Release()); 503 504 gl_helper->CropScaleReadbackAndCleanMailbox( 505 texture_mailbox.mailbox(), 506 texture_mailbox.sync_point(), 507 result->size(), 508 gfx::Rect(result->size()), 509 dst_size_in_pixel, 510 pixels, 511 config, 512 base::Bind(&CopyFromCompositingSurfaceFinished, 513 callback, 514 base::Passed(&release_callback), 515 base::Passed(&bitmap), 516 base::Passed(&bitmap_pixels_lock)), 517 GLHelper::SCALER_QUALITY_FAST); 518 } 519 520 // static 521 void DelegatedFrameHost::PrepareBitmapCopyOutputResult( 522 const gfx::Size& dst_size_in_pixel, 523 const SkBitmap::Config config, 524 const base::Callback<void(bool, const SkBitmap&)>& callback, 525 scoped_ptr<cc::CopyOutputResult> result) { 526 if (config != SkBitmap::kARGB_8888_Config) { 527 NOTIMPLEMENTED(); 528 callback.Run(false, SkBitmap()); 529 return; 530 } 531 DCHECK(result->HasBitmap()); 532 scoped_ptr<SkBitmap> source = result->TakeBitmap(); 533 DCHECK(source); 534 SkBitmap bitmap = skia::ImageOperations::Resize( 535 *source, 536 skia::ImageOperations::RESIZE_BEST, 537 dst_size_in_pixel.width(), 538 dst_size_in_pixel.height()); 539 callback.Run(true, bitmap); 540 } 541 542 // static 543 void DelegatedFrameHost::ReturnSubscriberTexture( 544 base::WeakPtr<DelegatedFrameHost> dfh, 545 scoped_refptr<OwnedMailbox> subscriber_texture, 546 uint32 sync_point) { 547 if (!subscriber_texture.get()) 548 return; 549 if (!dfh) 550 return; 551 552 subscriber_texture->UpdateSyncPoint(sync_point); 553 554 if (dfh->frame_subscriber_ && subscriber_texture->texture_id()) 555 dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture); 556 } 557 558 void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo( 559 base::WeakPtr<DelegatedFrameHost> dfh, 560 const base::Callback<void(bool)>& callback, 561 scoped_refptr<OwnedMailbox> subscriber_texture, 562 scoped_ptr<cc::SingleReleaseCallback> release_callback, 563 bool result) { 564 callback.Run(result); 565 566 uint32 sync_point = 0; 567 if (result) { 568 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper(); 569 sync_point = gl_helper->InsertSyncPoint(); 570 } 571 if (release_callback) { 572 // A release callback means the texture came from the compositor, so there 573 // should be no |subscriber_texture|. 574 DCHECK(!subscriber_texture); 575 bool lost_resource = sync_point == 0; 576 release_callback->Run(sync_point, lost_resource); 577 } 578 ReturnSubscriberTexture(dfh, subscriber_texture, sync_point); 579 } 580 581 // static 582 void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo( 583 base::WeakPtr<DelegatedFrameHost> dfh, 584 scoped_refptr<OwnedMailbox> subscriber_texture, 585 scoped_refptr<media::VideoFrame> video_frame, 586 const base::Callback<void(bool)>& callback, 587 scoped_ptr<cc::CopyOutputResult> result) { 588 base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false)); 589 base::ScopedClosureRunner scoped_return_subscriber_texture( 590 base::Bind(&ReturnSubscriberTexture, dfh, subscriber_texture, 0)); 591 592 if (!dfh) 593 return; 594 if (result->IsEmpty()) 595 return; 596 if (result->size().IsEmpty()) 597 return; 598 599 // Compute the dest size we want after the letterboxing resize. Make the 600 // coordinates and sizes even because we letterbox in YUV space 601 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to 602 // line up correctly. 603 // The video frame's coded_size() and the result's size() are both physical 604 // pixels. 605 gfx::Rect region_in_frame = 606 media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_size()), 607 result->size()); 608 region_in_frame = gfx::Rect(region_in_frame.x() & ~1, 609 region_in_frame.y() & ~1, 610 region_in_frame.width() & ~1, 611 region_in_frame.height() & ~1); 612 if (region_in_frame.IsEmpty()) 613 return; 614 615 if (!result->HasTexture()) { 616 DCHECK(result->HasBitmap()); 617 scoped_ptr<SkBitmap> bitmap = result->TakeBitmap(); 618 // Scale the bitmap to the required size, if necessary. 619 SkBitmap scaled_bitmap; 620 if (result->size().width() != region_in_frame.width() || 621 result->size().height() != region_in_frame.height()) { 622 skia::ImageOperations::ResizeMethod method = 623 skia::ImageOperations::RESIZE_GOOD; 624 scaled_bitmap = skia::ImageOperations::Resize(*bitmap.get(), method, 625 region_in_frame.width(), 626 region_in_frame.height()); 627 } else { 628 scaled_bitmap = *bitmap.get(); 629 } 630 631 { 632 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap); 633 634 media::CopyRGBToVideoFrame( 635 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()), 636 scaled_bitmap.rowBytes(), 637 region_in_frame, 638 video_frame.get()); 639 } 640 ignore_result(scoped_callback_runner.Release()); 641 callback.Run(true); 642 return; 643 } 644 645 ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); 646 GLHelper* gl_helper = factory->GetGLHelper(); 647 if (!gl_helper) 648 return; 649 if (subscriber_texture.get() && !subscriber_texture->texture_id()) 650 return; 651 652 cc::TextureMailbox texture_mailbox; 653 scoped_ptr<cc::SingleReleaseCallback> release_callback; 654 result->TakeTexture(&texture_mailbox, &release_callback); 655 DCHECK(texture_mailbox.IsTexture()); 656 657 gfx::Rect result_rect(result->size()); 658 659 content::ReadbackYUVInterface* yuv_readback_pipeline = 660 dfh->yuv_readback_pipeline_.get(); 661 if (yuv_readback_pipeline == NULL || 662 yuv_readback_pipeline->scaler()->SrcSize() != result_rect.size() || 663 yuv_readback_pipeline->scaler()->SrcSubrect() != result_rect || 664 yuv_readback_pipeline->scaler()->DstSize() != region_in_frame.size()) { 665 GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST; 666 std::string quality_switch = switches::kTabCaptureDownscaleQuality; 667 // If we're scaling up, we can use the "best" quality. 668 if (result_rect.size().width() < region_in_frame.size().width() && 669 result_rect.size().height() < region_in_frame.size().height()) 670 quality_switch = switches::kTabCaptureUpscaleQuality; 671 672 std::string switch_value = 673 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(quality_switch); 674 if (switch_value == "fast") 675 quality = GLHelper::SCALER_QUALITY_FAST; 676 else if (switch_value == "good") 677 quality = GLHelper::SCALER_QUALITY_GOOD; 678 else if (switch_value == "best") 679 quality = GLHelper::SCALER_QUALITY_BEST; 680 681 dfh->yuv_readback_pipeline_.reset( 682 gl_helper->CreateReadbackPipelineYUV(quality, 683 result_rect.size(), 684 result_rect, 685 video_frame->coded_size(), 686 region_in_frame, 687 true, 688 true)); 689 yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get(); 690 } 691 692 ignore_result(scoped_callback_runner.Release()); 693 ignore_result(scoped_return_subscriber_texture.Release()); 694 base::Callback<void(bool result)> finished_callback = base::Bind( 695 &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo, 696 dfh->AsWeakPtr(), 697 callback, 698 subscriber_texture, 699 base::Passed(&release_callback)); 700 yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(), 701 texture_mailbox.sync_point(), 702 video_frame.get(), 703 finished_callback); 704 } 705 706 //////////////////////////////////////////////////////////////////////////////// 707 // DelegatedFrameHost, ui::CompositorObserver implementation: 708 709 void DelegatedFrameHost::OnCompositingDidCommit( 710 ui::Compositor* compositor) { 711 RenderWidgetHostImpl* host = client_->GetHost(); 712 if (can_lock_compositor_ == NO_PENDING_COMMIT) { 713 can_lock_compositor_ = YES_CAN_LOCK; 714 if (resize_lock_.get() && resize_lock_->GrabDeferredLock()) 715 can_lock_compositor_ = YES_DID_LOCK; 716 } 717 RunOnCommitCallbacks(); 718 if (resize_lock_ && 719 resize_lock_->expected_size() == current_frame_size_in_dip_) { 720 resize_lock_.reset(); 721 host->WasResized(); 722 // We may have had a resize while we had the lock (e.g. if the lock expired, 723 // or if the UI still gave us some resizes), so make sure we grab a new lock 724 // if necessary. 725 MaybeCreateResizeLock(); 726 } 727 } 728 729 void DelegatedFrameHost::OnCompositingStarted( 730 ui::Compositor* compositor, base::TimeTicks start_time) { 731 last_draw_ended_ = start_time; 732 } 733 734 void DelegatedFrameHost::OnCompositingEnded( 735 ui::Compositor* compositor) { 736 } 737 738 void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) { 739 } 740 741 void DelegatedFrameHost::OnCompositingLockStateChanged( 742 ui::Compositor* compositor) { 743 // A compositor lock that is part of a resize lock timed out. We 744 // should display a renderer frame. 745 if (!compositor->IsLocked() && can_lock_compositor_ == YES_DID_LOCK) { 746 can_lock_compositor_ = NO_PENDING_RENDERER_FRAME; 747 } 748 } 749 750 void DelegatedFrameHost::OnUpdateVSyncParameters( 751 base::TimeTicks timebase, 752 base::TimeDelta interval) { 753 RenderWidgetHostImpl* host = client_->GetHost(); 754 if (client_->IsVisible()) 755 host->UpdateVSyncParameters(timebase, interval); 756 } 757 758 //////////////////////////////////////////////////////////////////////////////// 759 // RenderWidgetHostViewAura, ImageTransportFactoryObserver implementation: 760 761 void DelegatedFrameHost::OnLostResources() { 762 RenderWidgetHostImpl* host = client_->GetHost(); 763 if (frame_provider_.get()) 764 EvictDelegatedFrame(); 765 idle_frame_subscriber_textures_.clear(); 766 yuv_readback_pipeline_.reset(); 767 768 host->ScheduleComposite(); 769 } 770 771 //////////////////////////////////////////////////////////////////////////////// 772 // DelegatedFrameHost, private: 773 774 DelegatedFrameHost::~DelegatedFrameHost() { 775 ImageTransportFactory::GetInstance()->RemoveObserver(this); 776 777 if (resource_collection_.get()) 778 resource_collection_->SetClient(NULL); 779 780 DCHECK(!vsync_manager_); 781 } 782 783 void DelegatedFrameHost::RunOnCommitCallbacks() { 784 for (std::vector<base::Closure>::const_iterator 785 it = on_compositing_did_commit_callbacks_.begin(); 786 it != on_compositing_did_commit_callbacks_.end(); ++it) { 787 it->Run(); 788 } 789 on_compositing_did_commit_callbacks_.clear(); 790 } 791 792 void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks( 793 const base::Closure& callback) { 794 ui::Compositor* compositor = client_->GetCompositor(); 795 DCHECK(compositor); 796 797 if (!compositor->HasObserver(this)) 798 compositor->AddObserver(this); 799 800 can_lock_compositor_ = NO_PENDING_COMMIT; 801 on_compositing_did_commit_callbacks_.push_back(callback); 802 } 803 804 void DelegatedFrameHost::AddedToWindow() { 805 ui::Compositor* compositor = client_->GetCompositor(); 806 if (compositor) { 807 DCHECK(!vsync_manager_); 808 vsync_manager_ = compositor->vsync_manager(); 809 vsync_manager_->AddObserver(this); 810 } 811 } 812 813 void DelegatedFrameHost::RemovingFromWindow() { 814 RunOnCommitCallbacks(); 815 resize_lock_.reset(); 816 client_->GetHost()->WasResized(); 817 ui::Compositor* compositor = client_->GetCompositor(); 818 if (compositor && compositor->HasObserver(this)) 819 compositor->RemoveObserver(this); 820 821 if (vsync_manager_) { 822 vsync_manager_->RemoveObserver(this); 823 vsync_manager_ = NULL; 824 } 825 } 826 827 void DelegatedFrameHost::LockResources() { 828 DCHECK(frame_provider_); 829 delegated_frame_evictor_->LockFrame(); 830 } 831 832 void DelegatedFrameHost::UnlockResources() { 833 DCHECK(frame_provider_); 834 delegated_frame_evictor_->UnlockFrame(); 835 } 836 837 //////////////////////////////////////////////////////////////////////////////// 838 // DelegatedFrameHost, ui::LayerOwnerDelegate implementation: 839 840 void DelegatedFrameHost::OnLayerRecreated(ui::Layer* old_layer, 841 ui::Layer* new_layer) { 842 // The new_layer is the one that will be used by our Window, so that's the one 843 // that should keep our frame. old_layer will be returned to the 844 // RecreateLayer caller, and should have a copy. 845 if (frame_provider_.get()) { 846 new_layer->SetShowDelegatedContent(frame_provider_.get(), 847 current_frame_size_in_dip_); 848 } 849 } 850 851 } // namespace content 852 853