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

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

Issue 23726015: All together now (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@screencast_cap
Patch Set: Created 7 years, 3 months 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
OLDNEW
(Empty)
1 // Copyright (c) 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_aura.h"
6
7 #include <map>
8
9 #include "base/bind.h"
10 #include "base/debug/trace_event.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/synchronization/lock.h"
13 #include "content/browser/aura/image_transport_factory.h"
14 #include "content/browser/renderer_host/media/video_capture_oracle.h"
15 #include "content/common/gpu/surface_capturer.h"
16 #include "media/base/bitstream_buffer.h"
17 #include "media/base/video_decoder_config.h"
18 #include "ui/aura/root_window.h"
19 #include "ui/compositor/compositor.h"
20 #include "ui/compositor/compositor_retriever_delegate.h"
21 #include "ui/gfx/screen.h"
22 #include "ui/gfx/size.h"
23
24 namespace content {
25
26 namespace {
27
28 const int kMinFrameWidth = 2;
29 const int kMinFrameHeight = 2;
30
31 const int kDefaultBitrate = 64;
32
33 // Returns the nearest even integer closer to zero.
34 template<typename IntType>
35 IntType MakeEven(IntType x) {
36 return x & static_cast<IntType>(-2);
37 }
38
39 } // anonymous namespace
40
41 // The implementation class that actually handles screen capture. The capture
42 // callback for the output surface is bound with a scoped_refptr to this object
43 // to allow for thread-safe destruction.
44 //
45 // This class uses manual locking for thread-safety since it needs to be able
46 // to be used from thwo different threads:
47 // * The media manager thread (Allocate(), Start(), Stop(), DeAllocate())
48 // * The output surface thread (DoFrameCapture())
49
50 class DesktopCaptureDeviceAura::Impl
51 : public SurfaceCapturer::Client,
52 public ui::CompositorObserver,
53 public base::RefCountedThreadSafe<Impl> {
54 public:
55 Impl(const base::WeakPtr<DesktopCaptureDeviceAura>& weak_client,
56 const scoped_refptr<base::MessageLoopProxy>& client_loop,
57 scoped_ptr<VideoCaptureOracle> oracle,
58 media::VideoCaptureDevice::EventHandler* consumer,
59 const gfx::Size& requested_size,
60 int requested_frame_rate);
61 virtual ~Impl();
62
63 void CreateCapturerOnUIThread();
64
65 void Start();
66 void Stop();
67 void Destroy();
68
69 // Implements SurfaceCapturer::Client.
70 virtual void NotifyCaptureParameters(const gfx::Size& buffer_size,
71 const gfx::Rect& visible_rect) OVERRIDE;
72 virtual void NotifyCopyCaptureDone(
73 const scoped_refptr<media::VideoFrame>& frame) OVERRIDE;
74 virtual void NotifyError(SurfaceCapturer::Error error) OVERRIDE;
75
76 // Implements ui::CompositorObserver.
77 virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE {}
78 virtual void OnCompositingStarted(ui::Compositor* compositor,
79 base::TimeTicks start_time) OVERRIDE {}
80 virtual void OnCompositingEnded(ui::Compositor* compositor) OVERRIDE;
81 virtual void OnCompositingAborted(ui::Compositor* compositor) OVERRIDE {}
82 virtual void OnCompositingLockStateChanged(
83 ui::Compositor* compositor) OVERRIDE {}
84 virtual void OnUpdateVSyncParameters(ui::Compositor* compositor,
85 base::TimeTicks timebase,
86 base::TimeDelta interval) OVERRIDE {}
87 private:
88 static const size_t kMaxPendingFrames = 2;
89
90 void ReportError();
91 void DoFrameCapture(bool is_compositor_update);
92
93 // The timer should be started and stopped on UI thread because this is the
94 // thread in which the surface capturer is initialized.
95 void StartCaptureOnUIThread();
96 void StopCaptureOnUIThread();
97
98 static void DestroyCapturerOnUIThread(scoped_ptr<SurfaceCapturer> capturer);
99
100 const base::ThreadChecker thread_checker_;
101
102 const base::WeakPtr<DesktopCaptureDeviceAura> weak_client_;
103 const scoped_refptr<base::MessageLoopProxy> client_loop_;
104 const gfx::Size requested_size_;
105 const int requested_frame_rate_;
106 base::RepeatingTimer<DesktopCaptureDeviceAura::Impl> timer_;
107
108 // This lock protects everything under it.
109 base::Lock lock_;
110 scoped_ptr<VideoCaptureOracle> oracle_;
111 scoped_ptr<SurfaceCapturer> capturer_;
112 media::VideoCaptureDevice::EventHandler* consumer_;
113 ui::Compositor* compositor_;
114 bool is_started_;
115
116 // Map from media::VideoFrame* to frame number.
117 typedef std::map<media::VideoFrame*, int> FrameNumberMap;
118 FrameNumberMap pending_frames_;
119 };
120
121 DesktopCaptureDeviceAura::Impl::Impl(
122 const base::WeakPtr<DesktopCaptureDeviceAura>& weak_client,
123 const scoped_refptr<base::MessageLoopProxy>& client_loop,
124 scoped_ptr<VideoCaptureOracle> oracle,
125 media::VideoCaptureDevice::EventHandler* consumer,
126 const gfx::Size& requested_size,
127 int requested_frame_rate)
128 : thread_checker_(),
129 weak_client_(weak_client),
130 client_loop_(client_loop),
131 requested_size_(requested_size),
132 requested_frame_rate_(requested_frame_rate),
133 oracle_(oracle.Pass()),
134 consumer_(consumer),
135 compositor_(NULL),
136 is_started_(false) {
137 }
138
139 DesktopCaptureDeviceAura::Impl::~Impl() {
140 DCHECK(pending_frames_.empty());
141 }
142
143 void DesktopCaptureDeviceAura::Impl::CreateCapturerOnUIThread() {
144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
145 base::AutoLock guard(lock_);
146 DCHECK(!capturer_);
147
148 if (!oracle_ || !consumer_) {
149 // We were destroyed sometime in the interim.
150 return;
151 }
152
153 ui::CompositorRetrieverDelegate* delegate =
154 ui::CompositorRetrieverDelegate::GetInstance();
155 if (!delegate) {
156 NOTREACHED();
157 return;
158 }
159
160 // TODO(scottmg): Native is wrong http://crbug.com/133312.
161 // TODO(hshi): support multiple displays. The display identifier should be
162 // passed to DesktopCaptureDeviceAura::Create in the DesktopMediaID.
163 const gfx::Display& display =
164 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
165
166 ui::Compositor* compositor = delegate->GetCompositorForDisplay(display);
167 if (!compositor) {
168 ReportError();
169 return;
170 }
171
172 SurfaceCapturingContextFactory* factory =
173 ImageTransportFactory::GetInstance()->AsSurfaceCapturingContextFactory();
174 if (!factory) {
175 ReportError();
176 return;
177 }
178
179 scoped_ptr<SurfaceCapturer> capturer =
180 factory->CreateOutputSurfaceCapturer(compositor, this);
181 if (!capturer) {
182 ReportError();
183 return;
184 }
185
186 capturer->Initialize(media::VideoFrame::I420);
187 capturer_ = capturer.Pass();
188 compositor_ = compositor;
189 }
190
191 void DesktopCaptureDeviceAura::Impl::StartCaptureOnUIThread() {
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
193 const base::TimeDelta capture_period = base::TimeDelta::FromMicroseconds(
194 1000000.0 / requested_frame_rate_ + 0.5);
195 timer_.Start(
196 FROM_HERE, capture_period,
197 base::Bind(&DesktopCaptureDeviceAura::Impl::DoFrameCapture, this, false));
198
199 DCHECK(compositor_);
200 compositor_->AddObserver(this);
201 }
202
203 void DesktopCaptureDeviceAura::Impl::StopCaptureOnUIThread() {
204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
205 timer_.Stop();
206
207 DCHECK(compositor_);
208 compositor_->RemoveObserver(this);
209 }
210
211 void DesktopCaptureDeviceAura::Impl::Start() {
212 DCHECK(thread_checker_.CalledOnValidThread());
213 base::AutoLock guard(lock_);
214 is_started_ = true;
215 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
216 &DesktopCaptureDeviceAura::Impl::StartCaptureOnUIThread, this));
217 }
218
219 void DesktopCaptureDeviceAura::Impl::Stop() {
220 DCHECK(thread_checker_.CalledOnValidThread());
221 base::AutoLock guard(lock_);
222 is_started_ = false;
223 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
224 &DesktopCaptureDeviceAura::Impl::StopCaptureOnUIThread, this));
225 }
226
227 void DesktopCaptureDeviceAura::Impl::Destroy() {
228 DCHECK(thread_checker_.CalledOnValidThread());
229 base::AutoLock guard(lock_);
230 if (oracle_)
231 oracle_.reset();
232 if (capturer_) {
233 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
234 &DesktopCaptureDeviceAura::Impl::DestroyCapturerOnUIThread,
235 base::Passed(&capturer_)));
236 }
237 consumer_ = NULL;
238 is_started_ = false;
239 }
240
241 void DesktopCaptureDeviceAura::Impl::DoFrameCapture(bool is_compositor_update) {
242 base::AutoLock guard(lock_);
243 if (!oracle_ || !capturer_ || !consumer_)
244 return;
245
246 if (!is_started_)
247 return;
248
249 if (pending_frames_.size() >= kMaxPendingFrames)
250 return;
251
252 bool has_frame = true;
253 scoped_refptr<media::VideoFrame> frame = consumer_->ReserveOutputBuffer();
254 if (!frame) {
255 has_frame = false;
256 }
257
258 const base::Time now = base::Time::Now();
259 const VideoCaptureOracle::Event event =
260 is_compositor_update ? VideoCaptureOracle::kCompositorUpdate
261 : VideoCaptureOracle::kTimerPoll;
262 const bool should_capture = oracle_->ObserveEventAndDecideCapture(event, now);
263
264 // Consider the various reasons not to initiate a capture.
265 if (should_capture && !has_frame) {
266 TRACE_EVENT_INSTANT1("mirroring", "EncodeLimited",
267 TRACE_EVENT_SCOPE_THREAD,
268 "trigger", "gpu");
269 return;
270 } else if (!should_capture && has_frame) {
271 // This is a normal and acceptable way to drop a frame. We've hit our
272 // capture rate limit: for example, the content is animating at 60fps but
273 // we're capturing at 30fps.
274 TRACE_EVENT_INSTANT1("mirroring", "FpsRateLimited",
275 TRACE_EVENT_SCOPE_THREAD,
276 "trigger", "gpu");
277 return;
278 } else if (!should_capture && !has_frame) {
279 // We decided not to capture, but we wouldn't have been able to if we wanted
280 // to because no output buffer was available.
281 TRACE_EVENT_INSTANT1("mirroring", "NearlyEncodeLimited",
282 TRACE_EVENT_SCOPE_THREAD,
283 "trigger", "gpu");
284 return;
285 }
286
287 // Enqueue a video frame for capture output.
288 DCHECK(pending_frames_.find(frame.get()) == pending_frames_.end());
289 pending_frames_[frame.get()] = oracle_->RecordCapture();
290 capturer_->CopyCaptureToVideoFrame(frame);
291 }
292
293 void DesktopCaptureDeviceAura::Impl::ReportError() {
294 LOG(ERROR) << "DesktopCaptureDeviceAura::ReportError()";
295 client_loop_->PostTask(FROM_HERE, base::Bind(
296 &DesktopCaptureDeviceAura::ReportError,
297 weak_client_));
298 }
299
300 void DesktopCaptureDeviceAura::Impl::NotifyCaptureParameters(
301 const gfx::Size& buffer_size, const gfx::Rect& visible_rect) {
302 base::AutoLock guard(lock_);
303 if (!consumer_)
304 return;
305
306 // Initialize capture settings which will be consistent for the duration of
307 // the capture.
308 media::VideoCaptureCapability settings;
309
310 // We'll ignore requested_size_ and just use the size as reported by the
311 // SurfaceCapturer.
312 settings.width = buffer_size.width();
313 settings.height = buffer_size.height();
314 settings.frame_rate = requested_frame_rate_;
315 // Note: the value of |settings.color| doesn't matter if we use only the
316 // VideoFrame based methods on |consumer|.
317 settings.color = media::VideoCaptureCapability::kI420;
318 settings.expected_capture_delay = 0;
319 settings.interlaced = false;
320
321 consumer_->OnFrameInfo(settings);
322 }
323
324 void DesktopCaptureDeviceAura::Impl::NotifyCopyCaptureDone(
325 const scoped_refptr<media::VideoFrame>& frame) {
326 if (!oracle_ || !capturer_ || !consumer_)
327 return;
328
329 FrameNumberMap::const_iterator it = pending_frames_.find(frame.get());
330 if (it == pending_frames_.end()) {
331 NOTREACHED();
332 return;
333 }
334
335 // Notify oracle that capture is complete.
336 int frame_number = it->second;
337 const base::Time now = base::Time::Now();
338 if (oracle_->CompleteCapture(frame_number, now)) {
339 // Deliver the captured frame to the consumer.
340 consumer_->OnIncomingCapturedVideoFrame(frame, now);
341 }
342 pending_frames_.erase(frame.get());
343 }
344
345 void DesktopCaptureDeviceAura::Impl::NotifyError(
346 SurfaceCapturer::Error error) {
347 ReportError();
348 }
349
350 void DesktopCaptureDeviceAura::Impl::OnCompositingEnded(
351 ui::Compositor* compositor) {
352 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
353 &DesktopCaptureDeviceAura::Impl::DoFrameCapture, this, true));
354 }
355
356 // static
357 void DesktopCaptureDeviceAura::Impl::DestroyCapturerOnUIThread(
358 scoped_ptr<SurfaceCapturer> capturer) {
359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
360 capturer.release()->Destroy();
361 }
362
363 DesktopCaptureDeviceAura::DesktopCaptureDeviceAura(const Name& device_name)
364 : thread_checker_(),
365 device_name_(device_name),
366 state_(kIdle),
367 weak_ptr_factory_(this) {
368 }
369
370 DesktopCaptureDeviceAura::~DesktopCaptureDeviceAura() {
371 DCHECK(thread_checker_.CalledOnValidThread());
372 DCHECK(!impl_);
373 }
374
375 // static
376 media::VideoCaptureDevice* DesktopCaptureDeviceAura::Create(
377 const DesktopMediaID& source) {
378 media::VideoCaptureDevice::Name name("AuraScreen", source.ToString());
379 return new DesktopCaptureDeviceAura(name);
380 }
381
382 void DesktopCaptureDeviceAura::Allocate(
383 const media::VideoCaptureCapability& capture_format,
384 VideoCaptureDevice::EventHandler* consumer) {
385 DCHECK(thread_checker_.CalledOnValidThread());
386 DCHECK(!impl_);
387
388 if (state_ != kIdle) {
389 DVLOG(1) << "Allocate() invoked when not in state idle.";
390 return;
391 }
392
393 // Frame dimensions must each be a positive, even integer, since the consumer
394 // wants (or will convert to) YUV420.
395 int width = MakeEven(capture_format.width);
396 int height = MakeEven(capture_format.height);
397 if (width < kMinFrameWidth || height < kMinFrameHeight) {
398 DVLOG(1) << "invalid width (" << width << ") and/or height ("
399 << height << ")";
400 consumer->OnError();
401 return;
402 }
403
404 base::TimeDelta capture_period = base::TimeDelta::FromMicroseconds(
405 1000000.0 / capture_format.frame_rate + 0.5);
406
407 scoped_ptr<VideoCaptureOracle> oracle(new VideoCaptureOracle(
408 capture_period, true));
409
410 impl_ = new Impl(weak_ptr_factory_.GetWeakPtr(),
411 base::MessageLoopProxy::current(),
412 oracle.Pass(),
413 consumer,
414 gfx::Size(width, height),
415 capture_format.frame_rate);
416
417 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
418 &DesktopCaptureDeviceAura::Impl::CreateCapturerOnUIThread,
419 impl_));
420
421 state_ = kAllocated;
422 }
423
424 void DesktopCaptureDeviceAura::Start() {
425 DCHECK(thread_checker_.CalledOnValidThread());
426
427 if (state_ != kAllocated)
428 return;
429
430 impl_->Start();
431 state_ = kCapturing;
432 }
433
434 void DesktopCaptureDeviceAura::Stop() {
435 DCHECK(thread_checker_.CalledOnValidThread());
436
437 if (state_ != kCapturing)
438 return;
439
440 impl_->Stop();
441 state_ = kAllocated;
442 }
443
444 void DesktopCaptureDeviceAura::DeAllocate() {
445 DCHECK(thread_checker_.CalledOnValidThread());
446 Stop();
447
448 weak_ptr_factory_.InvalidateWeakPtrs();
449 if (impl_) {
450 impl_->Destroy();
451 impl_ = NULL;
452 }
453 state_ = kIdle;
454 }
455
456 const media::VideoCaptureDevice::Name&
457 DesktopCaptureDeviceAura::device_name() {
458 DCHECK(thread_checker_.CalledOnValidThread());
459 return device_name_;
460 }
461
462 void DesktopCaptureDeviceAura::ReportError() {
463 DCHECK(thread_checker_.CalledOnValidThread());
464 Stop();
465 state_ = kError;
466 }
467
468 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/renderer_host/media/desktop_capture_device_aura.h ('k') | content/content_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698