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