Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(339)

Side by Side Diff: content/browser/renderer_host/media/desktop_capture_device_ash.cc

Issue 100743003: Extend content::DesktopMediaID to allow Aura windows as capture sources. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4 //
5 #include "content/browser/renderer_host/media/desktop_capture_device_ash.h"
6
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"
24
25 namespace content {
26
27 namespace {
28
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();
37
38 // VideoCaptureFrameSource overrides.
39 virtual bool Start(
40 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) OVERRIDE;
41 virtual void Stop() OVERRIDE;
42
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;
48
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 {}
60
61 private:
62 // Captures a frame.
63 // |dirty| is false for timer polls and true for compositor updates.
64 void Capture(bool dirty);
65
66 // Update capture size. Must be called on the UI thread.
67 void UpdateCaptureSize();
68
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);
75
76 // The window associated with the desktop.
77 aura::Window* desktop_window_;
78
79 // The layer associated with the desktop.
80 ui::Layer* desktop_layer_;
81
82 // The timer that kicks off period captures.
83 base::Timer timer_;
84
85 // The desktop id.
86 DesktopMediaID desktop_id_;
87
88 // Makes all the decisions about which frames to copy, and how.
89 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
90
91 // YUV readback pipeline.
92 scoped_ptr<content::ReadbackYUVInterface> yuv_readback_pipeline_;
93
94 DISALLOW_COPY_AND_ASSIGN(DesktopVideoCaptureMachine);
95 };
96
97 DesktopVideoCaptureMachine::DesktopVideoCaptureMachine(
98 const DesktopMediaID& source)
99 : desktop_window_(NULL),
100 desktop_layer_(NULL),
101 timer_(true, true),
102 desktop_id_(source) {}
103
104 DesktopVideoCaptureMachine::~DesktopVideoCaptureMachine() {}
105
106 bool DesktopVideoCaptureMachine::Start(
107 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) {
108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
109
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;
117
118 // If the desktop layer is already destroyed then return failure.
119 desktop_layer_ = desktop_window_->layer();
120 if (!desktop_layer_)
121 return false;
122
123 DCHECK(oracle_proxy.get());
124 oracle_proxy_ = oracle_proxy;
125
126 // Update capture size.
127 UpdateCaptureSize();
128
129 // Start observing window events.
130 desktop_window_->AddObserver(this);
131
132 // Start observing compositor updates.
133 ui::Compositor* compositor = desktop_layer_->GetCompositor();
134 if (!compositor)
135 return false;
136
137 compositor->AddObserver(this);
138
139 // Starts timer.
140 timer_.Start(FROM_HERE, oracle_proxy_->capture_period(),
141 base::Bind(&DesktopVideoCaptureMachine::Capture, AsWeakPtr(),
142 false));
143
144 started_ = true;
145 return true;
146 }
147
148 void DesktopVideoCaptureMachine::Stop() {
149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
150
151 // Stop observing window events.
152 if (desktop_window_)
153 desktop_window_->RemoveObserver(this);
154
155 // Stop observing compositor updates.
156 if (desktop_layer_) {
157 ui::Compositor* compositor = desktop_layer_->GetCompositor();
158 if (compositor)
159 compositor->RemoveObserver(this);
160 }
161
162 // Stop timer.
163 timer_.Stop();
164
165 started_ = false;
166 }
167
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()));
173 }
174 }
175
176 void DesktopVideoCaptureMachine::Capture(bool dirty) {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
178
179 // Do not capture if the desktop layer is already destroyed.
180 if (!desktop_layer_)
181 return;
182
183 scoped_refptr<media::VideoFrame> frame;
184 ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
185
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());
200 }
201 }
202
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);
210 }
211
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;
219
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;
235
236 ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
237 GLHelper* gl_helper = factory->GetGLHelper();
238 if (!gl_helper)
239 return;
240
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;
247
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));
261 }
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)));
266 }
267
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_);
273
274 // Post task to update capture size on UI thread.
275 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
276 &DesktopVideoCaptureMachine::UpdateCaptureSize, AsWeakPtr()));
277 }
278
279 void DesktopVideoCaptureMachine::OnWindowDestroyed(aura::Window* window) {
280 DCHECK(desktop_window_ && window == desktop_window_);
281 desktop_window_ = NULL;
282 desktop_layer_ = NULL;
283
284 // Post task to stop capture on UI thread.
285 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
286 &DesktopVideoCaptureMachine::Stop, AsWeakPtr()));
287 }
288
289 void DesktopVideoCaptureMachine::OnCompositingEnded(
290 ui::Compositor* compositor) {
291 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
292 &DesktopVideoCaptureMachine::Capture, AsWeakPtr(), true));
293 }
294
295 } // namespace
296
297 DesktopCaptureDeviceAsh::DesktopCaptureDeviceAsh(
298 const DesktopMediaID& source)
299 : impl_(new VideoCaptureDeviceImpl(scoped_ptr<VideoCaptureMachine>(
300 new DesktopVideoCaptureMachine(source)))) {}
301
302 DesktopCaptureDeviceAsh::~DesktopCaptureDeviceAsh() {
303 DVLOG(2) << "DesktopCaptureDeviceAsh@" << this << " destroying.";
304 }
305
306 // static
307 media::VideoCaptureDevice* DesktopCaptureDeviceAsh::Create(
308 const DesktopMediaID& source) {
309 // This implementation only supports screen capture.
310 if (source.type != DesktopMediaID::TYPE_SCREEN)
311 return NULL;
312
313 return new DesktopCaptureDeviceAsh(source);
314 }
315
316 void DesktopCaptureDeviceAsh::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());
321 }
322
323 void DesktopCaptureDeviceAsh::StopAndDeAllocate() {
324 impl_->StopAndDeAllocate();
325 }
326
327 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698