Ignore non-active fullscreen windows for shelf state.
[chromium-blink-merge.git] / content / browser / renderer_host / media / desktop_capture_device_aura.cc
blob5419488c88cd0fa4801b2af035e697efe471c9cc
1 // Copyright 2013 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.
5 #include "content/browser/renderer_host/media/desktop_capture_device_aura.h"
7 #include "base/logging.h"
8 #include "base/timer/timer.h"
9 #include "cc/output/copy_output_request.h"
10 #include "cc/output/copy_output_result.h"
11 #include "content/browser/aura/image_transport_factory.h"
12 #include "content/browser/renderer_host/media/video_capture_device_impl.h"
13 #include "content/common/gpu/client/gl_helper.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "media/base/video_util.h"
16 #include "media/video/capture/video_capture_types.h"
17 #include "third_party/skia/include/core/SkBitmap.h"
18 #include "ui/aura/window.h"
19 #include "ui/aura/window_observer.h"
20 #include "ui/compositor/compositor.h"
21 #include "ui/compositor/dip_util.h"
22 #include "ui/compositor/layer.h"
23 #include "ui/gfx/screen.h"
25 namespace content {
27 namespace {
29 class DesktopVideoCaptureMachine
30 : public VideoCaptureMachine,
31 public aura::WindowObserver,
32 public ui::CompositorObserver,
33 public base::SupportsWeakPtr<DesktopVideoCaptureMachine> {
34 public:
35 DesktopVideoCaptureMachine(const DesktopMediaID& source);
36 virtual ~DesktopVideoCaptureMachine();
38 // VideoCaptureFrameSource overrides.
39 virtual bool Start(
40 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) OVERRIDE;
41 virtual void Stop() OVERRIDE;
43 // Implements aura::WindowObserver.
44 virtual void OnWindowBoundsChanged(aura::Window* window,
45 const gfx::Rect& old_bounds,
46 const gfx::Rect& new_bounds) OVERRIDE;
47 virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
49 // Implements ui::CompositorObserver.
50 virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE {}
51 virtual void OnCompositingStarted(ui::Compositor* compositor,
52 base::TimeTicks start_time) OVERRIDE {}
53 virtual void OnCompositingEnded(ui::Compositor* compositor) OVERRIDE;
54 virtual void OnCompositingAborted(ui::Compositor* compositor) OVERRIDE {}
55 virtual void OnCompositingLockStateChanged(
56 ui::Compositor* compositor) OVERRIDE {}
57 virtual void OnUpdateVSyncParameters(ui::Compositor* compositor,
58 base::TimeTicks timebase,
59 base::TimeDelta interval) OVERRIDE {}
61 private:
62 // Captures a frame.
63 // |dirty| is false for timer polls and true for compositor updates.
64 void Capture(bool dirty);
66 // Update capture size. Must be called on the UI thread.
67 void UpdateCaptureSize();
69 // Response callback for cc::Layer::RequestCopyOfOutput().
70 void DidCopyOutput(
71 scoped_refptr<media::VideoFrame> video_frame,
72 base::Time start_time,
73 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
74 scoped_ptr<cc::CopyOutputResult> result);
76 // The window associated with the desktop.
77 aura::Window* desktop_window_;
79 // The layer associated with the desktop.
80 ui::Layer* desktop_layer_;
82 // The timer that kicks off period captures.
83 base::Timer timer_;
85 // The desktop id.
86 DesktopMediaID desktop_id_;
88 // Makes all the decisions about which frames to copy, and how.
89 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
91 // YUV readback pipeline.
92 scoped_ptr<content::ReadbackYUVInterface> yuv_readback_pipeline_;
94 DISALLOW_COPY_AND_ASSIGN(DesktopVideoCaptureMachine);
97 DesktopVideoCaptureMachine::DesktopVideoCaptureMachine(
98 const DesktopMediaID& source)
99 : desktop_window_(NULL),
100 desktop_layer_(NULL),
101 timer_(true, true),
102 desktop_id_(source) {}
104 DesktopVideoCaptureMachine::~DesktopVideoCaptureMachine() {}
106 bool DesktopVideoCaptureMachine::Start(
107 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) {
108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
110 // TODO(hshi): get the correct display specified by |desktop_id_|.
111 const gfx::Display& primary_display =
112 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
113 desktop_window_ = gfx::Screen::GetNativeScreen()->GetWindowAtScreenPoint(
114 primary_display.bounds().CenterPoint())->GetRootWindow();
115 if (!desktop_window_)
116 return false;
118 // If the desktop layer is already destroyed then return failure.
119 desktop_layer_ = desktop_window_->layer();
120 if (!desktop_layer_)
121 return false;
123 DCHECK(oracle_proxy.get());
124 oracle_proxy_ = oracle_proxy;
126 // Update capture size.
127 UpdateCaptureSize();
129 // Start observing window events.
130 desktop_window_->AddObserver(this);
132 // Start observing compositor updates.
133 ui::Compositor* compositor = desktop_layer_->GetCompositor();
134 if (!compositor)
135 return false;
137 compositor->AddObserver(this);
139 // Starts timer.
140 timer_.Start(FROM_HERE, oracle_proxy_->capture_period(),
141 base::Bind(&DesktopVideoCaptureMachine::Capture, AsWeakPtr(),
142 false));
144 started_ = true;
145 return true;
148 void DesktopVideoCaptureMachine::Stop() {
149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
151 // Stop observing window events.
152 if (desktop_window_)
153 desktop_window_->RemoveObserver(this);
155 // Stop observing compositor updates.
156 if (desktop_layer_) {
157 ui::Compositor* compositor = desktop_layer_->GetCompositor();
158 if (compositor)
159 compositor->RemoveObserver(this);
162 // Stop timer.
163 timer_.Stop();
165 started_ = false;
168 void DesktopVideoCaptureMachine::UpdateCaptureSize() {
169 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
170 if (oracle_proxy_ && desktop_layer_) {
171 oracle_proxy_->UpdateCaptureSize(ui::ConvertSizeToPixel(
172 desktop_layer_, desktop_layer_->bounds().size()));
176 void DesktopVideoCaptureMachine::Capture(bool dirty) {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
179 // Do not capture if the desktop layer is already destroyed.
180 if (!desktop_layer_)
181 return;
183 scoped_refptr<media::VideoFrame> frame;
184 ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
186 const base::Time start_time = base::Time::Now();
187 const VideoCaptureOracle::Event event =
188 dirty ? VideoCaptureOracle::kCompositorUpdate
189 : VideoCaptureOracle::kTimerPoll;
190 if (oracle_proxy_->ObserveEventAndDecideCapture(
191 event, start_time, &frame, &capture_frame_cb)) {
192 scoped_ptr<cc::CopyOutputRequest> request =
193 cc::CopyOutputRequest::CreateRequest(
194 base::Bind(&DesktopVideoCaptureMachine::DidCopyOutput,
195 AsWeakPtr(), frame, start_time, capture_frame_cb));
196 gfx::Rect desktop_size = ui::ConvertRectToPixel(
197 desktop_layer_, desktop_layer_->bounds());
198 request->set_area(desktop_size);
199 desktop_layer_->RequestCopyOfOutput(request.Pass());
203 static void CopyOutputFinishedForVideo(
204 base::Time start_time,
205 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
206 scoped_ptr<cc::SingleReleaseCallback> release_callback,
207 bool result) {
208 release_callback->Run(0, false);
209 capture_frame_cb.Run(start_time, result);
212 void DesktopVideoCaptureMachine::DidCopyOutput(
213 scoped_refptr<media::VideoFrame> video_frame,
214 base::Time start_time,
215 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb,
216 scoped_ptr<cc::CopyOutputResult> result) {
217 if (result->IsEmpty() || result->size().IsEmpty())
218 return;
220 // Compute the dest size we want after the letterboxing resize. Make the
221 // coordinates and sizes even because we letterbox in YUV space
222 // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
223 // line up correctly.
224 // The video frame's coded_size() and the result's size() are both physical
225 // pixels.
226 gfx::Rect region_in_frame =
227 media::ComputeLetterboxRegion(gfx::Rect(video_frame->coded_size()),
228 result->size());
229 region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
230 region_in_frame.y() & ~1,
231 region_in_frame.width() & ~1,
232 region_in_frame.height() & ~1);
233 if (region_in_frame.IsEmpty())
234 return;
236 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
237 GLHelper* gl_helper = factory->GetGLHelper();
238 if (!gl_helper)
239 return;
241 cc::TextureMailbox texture_mailbox;
242 scoped_ptr<cc::SingleReleaseCallback> release_callback;
243 result->TakeTexture(&texture_mailbox, &release_callback);
244 DCHECK(texture_mailbox.IsTexture());
245 if (!texture_mailbox.IsTexture())
246 return;
248 gfx::Rect result_rect(result->size());
249 if (!yuv_readback_pipeline_ ||
250 yuv_readback_pipeline_->scaler()->SrcSize() != result_rect.size() ||
251 yuv_readback_pipeline_->scaler()->SrcSubrect() != result_rect ||
252 yuv_readback_pipeline_->scaler()->DstSize() != region_in_frame.size()) {
253 yuv_readback_pipeline_.reset(
254 gl_helper->CreateReadbackPipelineYUV(GLHelper::SCALER_QUALITY_FAST,
255 result_rect.size(),
256 result_rect,
257 video_frame->coded_size(),
258 region_in_frame,
259 true,
260 true));
262 yuv_readback_pipeline_->ReadbackYUV(
263 texture_mailbox.name(), texture_mailbox.sync_point(), video_frame.get(),
264 base::Bind(&CopyOutputFinishedForVideo, start_time, capture_frame_cb,
265 base::Passed(&release_callback)));
268 void DesktopVideoCaptureMachine::OnWindowBoundsChanged(
269 aura::Window* window,
270 const gfx::Rect& old_bounds,
271 const gfx::Rect& new_bounds) {
272 DCHECK(desktop_window_ && window == desktop_window_);
274 // Post task to update capture size on UI thread.
275 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
276 &DesktopVideoCaptureMachine::UpdateCaptureSize, AsWeakPtr()));
279 void DesktopVideoCaptureMachine::OnWindowDestroyed(aura::Window* window) {
280 DCHECK(desktop_window_ && window == desktop_window_);
281 desktop_window_ = NULL;
282 desktop_layer_ = NULL;
284 // Post task to stop capture on UI thread.
285 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
286 &DesktopVideoCaptureMachine::Stop, AsWeakPtr()));
289 void DesktopVideoCaptureMachine::OnCompositingEnded(
290 ui::Compositor* compositor) {
291 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
292 &DesktopVideoCaptureMachine::Capture, AsWeakPtr(), true));
295 } // namespace
297 DesktopCaptureDeviceAura::DesktopCaptureDeviceAura(
298 const DesktopMediaID& source)
299 : impl_(new VideoCaptureDeviceImpl(scoped_ptr<VideoCaptureMachine>(
300 new DesktopVideoCaptureMachine(source)))) {}
302 DesktopCaptureDeviceAura::~DesktopCaptureDeviceAura() {
303 DVLOG(2) << "DesktopCaptureDeviceAura@" << this << " destroying.";
306 // static
307 media::VideoCaptureDevice* DesktopCaptureDeviceAura::Create(
308 const DesktopMediaID& source) {
309 // This implementation only supports screen capture.
310 if (source.type != DesktopMediaID::TYPE_SCREEN)
311 return NULL;
313 return new DesktopCaptureDeviceAura(source);
316 void DesktopCaptureDeviceAura::AllocateAndStart(
317 const media::VideoCaptureParams& params,
318 scoped_ptr<Client> client) {
319 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString();
320 impl_->AllocateAndStart(params, client.Pass());
323 void DesktopCaptureDeviceAura::StopAndDeAllocate() {
324 impl_->StopAndDeAllocate();
327 } // namespace content