Save errno for logging before potentially overwriting it.
[chromium-blink-merge.git] / content / browser / renderer_host / media / web_contents_video_capture_device.cc
blob42868c0a85fa83fd1f4203bacbf6c4075bc5c273
1 // Copyright (c) 2012 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 // Implementation notes: This needs to work on a variety of hardware
6 // configurations where the speed of the CPU and GPU greatly affect overall
7 // performance. Spanning several threads, the process of capturing has been
8 // split up into four conceptual stages:
9 //
10 // 1. Reserve Buffer: Before a frame can be captured, a slot in the consumer's
11 // shared-memory IPC buffer is reserved. There are only a few of these;
12 // when they run out, it indicates that the downstream consumer -- likely a
13 // video encoder -- is the performance bottleneck, and that the rate of
14 // frame capture should be throttled back.
16 // 2. Capture: A bitmap is snapshotted/copied from the RenderView's backing
17 // store. This is initiated on the UI BrowserThread, and often occurs
18 // asynchronously. Where supported, the GPU scales and color converts
19 // frames to our desired size, and the readback happens directly into the
20 // shared-memory buffer. But this is not always possible, particularly when
21 // accelerated compositing is disabled.
23 // 3. Render (if needed): If the web contents cannot be captured directly into
24 // our target size and color format, scaling and colorspace conversion must
25 // be done on the CPU. A dedicated thread is used for this operation, to
26 // avoid blocking the UI thread. The Render stage always reads from a
27 // bitmap returned by Capture, and writes into the reserved slot in the
28 // shared-memory buffer.
30 // 4. Deliver: The rendered video frame is returned to the consumer (which
31 // implements the VideoCaptureDevice::EventHandler interface). Because
32 // all paths have written the frame into the IPC buffer, this step should
33 // never need to do an additional copy of the pixel data.
35 // In the best-performing case, the Render step is bypassed: Capture produces
36 // ready-to-Deliver frames. But when accelerated readback is not possible, the
37 // system is designed so that Capture and Render may run concurrently. A timing
38 // diagram helps illustrate this point (@30 FPS):
40 // Time: 0ms 33ms 66ms 99ms
41 // thread1: |-Capture-f1------v |-Capture-f2------v |-Capture-f3----v |-Capt
42 // thread2: |-Render-f1-----v |-Render-f2-----v |-Render-f3
44 // In the above example, both capturing and rendering *each* take almost the
45 // full 33 ms available between frames, yet we see that the required throughput
46 // is obtained.
48 // Turning on verbose logging will cause the effective frame rate to be logged
49 // at 5-second intervals.
51 #include "content/browser/renderer_host/media/web_contents_video_capture_device.h"
53 #include <algorithm>
54 #include <list>
55 #include <string>
57 #include "base/basictypes.h"
58 #include "base/bind.h"
59 #include "base/bind_helpers.h"
60 #include "base/callback_forward.h"
61 #include "base/debug/trace_event.h"
62 #include "base/logging.h"
63 #include "base/memory/scoped_ptr.h"
64 #include "base/memory/weak_ptr.h"
65 #include "base/message_loop/message_loop_proxy.h"
66 #include "base/metrics/histogram.h"
67 #include "base/sequenced_task_runner.h"
68 #include "base/strings/stringprintf.h"
69 #include "base/synchronization/lock.h"
70 #include "base/threading/thread.h"
71 #include "base/threading/thread_checker.h"
72 #include "base/time.h"
73 #include "content/browser/renderer_host/media/video_capture_oracle.h"
74 #include "content/browser/renderer_host/media/web_contents_capture_util.h"
75 #include "content/browser/renderer_host/render_widget_host_impl.h"
76 #include "content/browser/web_contents/web_contents_impl.h"
77 #include "content/port/browser/render_widget_host_view_frame_subscriber.h"
78 #include "content/port/browser/render_widget_host_view_port.h"
79 #include "content/public/browser/browser_thread.h"
80 #include "content/public/browser/notification_source.h"
81 #include "content/public/browser/notification_types.h"
82 #include "content/public/browser/render_process_host.h"
83 #include "content/public/browser/render_view_host.h"
84 #include "content/public/browser/render_widget_host_view.h"
85 #include "content/public/browser/web_contents.h"
86 #include "content/public/browser/web_contents_observer.h"
87 #include "media/base/bind_to_loop.h"
88 #include "media/base/video_frame.h"
89 #include "media/base/video_util.h"
90 #include "media/base/yuv_convert.h"
91 #include "media/video/capture/video_capture_types.h"
92 #include "skia/ext/image_operations.h"
93 #include "third_party/skia/include/core/SkBitmap.h"
94 #include "third_party/skia/include/core/SkColor.h"
95 #include "ui/gfx/rect.h"
96 #include "ui/gfx/skia_util.h"
98 namespace content {
100 namespace {
102 const int kMinFrameWidth = 2;
103 const int kMinFrameHeight = 2;
104 const int kMaxFramesInFlight = 2;
105 const int kMaxSnapshotsInFlight = 1;
107 // TODO(nick): Remove this once frame subscription is supported on Aura and
108 // Linux.
109 #if (defined(OS_WIN) || defined(OS_MACOSX)) || defined(USE_AURA)
110 const bool kAcceleratedSubscriberIsSupported = true;
111 #else
112 const bool kAcceleratedSubscriberIsSupported = false;
113 #endif
115 // Returns the nearest even integer closer to zero.
116 template<typename IntType>
117 IntType MakeEven(IntType x) {
118 return x & static_cast<IntType>(-2);
121 // Compute a letterbox region, aligned to even coordinates.
122 gfx::Rect ComputeYV12LetterboxRegion(const gfx::Size& frame_size,
123 const gfx::Size& content_size) {
125 gfx::Rect result = media::ComputeLetterboxRegion(gfx::Rect(frame_size),
126 content_size);
128 result.set_x(MakeEven(result.x()));
129 result.set_y(MakeEven(result.y()));
130 result.set_width(std::max(kMinFrameWidth, MakeEven(result.width())));
131 result.set_height(std::max(kMinFrameHeight, MakeEven(result.height())));
133 return result;
136 // Thread-safe, refcounted proxy to the VideoCaptureOracle. This proxy wraps
137 // the VideoCaptureOracle, which decides which frames to capture, and a
138 // VideoCaptureDevice::EventHandler, which allocates and receives the captured
139 // frames, in a lock to synchronize state between the two.
140 class ThreadSafeCaptureOracle
141 : public base::RefCountedThreadSafe<ThreadSafeCaptureOracle> {
142 public:
143 ThreadSafeCaptureOracle(media::VideoCaptureDevice::EventHandler* consumer,
144 scoped_ptr<VideoCaptureOracle> oracle);
146 bool ObserveEventAndDecideCapture(
147 VideoCaptureOracle::Event event,
148 base::Time event_time,
149 scoped_refptr<media::VideoFrame>* storage,
150 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback* callback);
152 base::TimeDelta capture_period() const {
153 return oracle_->capture_period();
156 // Allow new captures to start occurring.
157 void Start();
159 // Stop new captures from happening (but doesn't forget the consumer).
160 void Stop();
162 // Signal an error to the consumer.
163 void ReportError();
165 // Permanently stop capturing. Immediately cease all activity on the
166 // VCD::EventHandler.
167 void InvalidateConsumer();
169 private:
170 friend class base::RefCountedThreadSafe<ThreadSafeCaptureOracle>;
171 virtual ~ThreadSafeCaptureOracle() {}
173 // Callback invoked on completion of all captures.
174 void DidCaptureFrame(const scoped_refptr<media::VideoFrame>& frame,
175 int frame_number,
176 base::Time timestamp,
177 bool success);
179 // Protects everything below it.
180 base::Lock lock_;
182 // Recipient of our capture activity. Becomes null after it is invalidated.
183 media::VideoCaptureDevice::EventHandler* consumer_;
185 // Makes the decision to capture a frame.
186 const scoped_ptr<VideoCaptureOracle> oracle_;
188 // Whether capturing is currently allowed. Can toggle back and forth.
189 bool is_started_;
192 // FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible
193 // with RenderWidgetHostViewFrameSubscriber. We create one per event type.
194 class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
195 public:
196 FrameSubscriber(VideoCaptureOracle::Event event_type,
197 const scoped_refptr<ThreadSafeCaptureOracle>& oracle)
198 : event_type_(event_type),
199 oracle_proxy_(oracle) {}
201 virtual bool ShouldCaptureFrame(
202 base::Time present_time,
203 scoped_refptr<media::VideoFrame>* storage,
204 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback*
205 deliver_frame_cb) OVERRIDE;
207 private:
208 const VideoCaptureOracle::Event event_type_;
209 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
212 // ContentCaptureSubscription is the relationship between a RenderWidgetHost
213 // whose content is updating, a subscriber that is deciding which of these
214 // updates to capture (and where to deliver them to), and a callback that
215 // knows how to do the capture and prepare the result for delivery.
217 // In practice, this means (a) installing a RenderWidgetHostFrameSubscriber in
218 // the RenderWidgetHostView, to process updates that occur via accelerated
219 // compositing, (b) installing itself as an observer of updates to the
220 // RenderWidgetHost's backing store, to hook updates that occur via software
221 // rendering, and (c) running a timer to possibly initiate non-event-driven
222 // captures that the subscriber might request.
224 // All of this happens on the UI thread, although the
225 // RenderWidgetHostViewFrameSubscriber we install may be dispatching updates
226 // autonomously on some other thread.
227 class ContentCaptureSubscription : public content::NotificationObserver {
228 public:
229 typedef base::Callback<void(
230 const base::Time&,
231 const scoped_refptr<media::VideoFrame>&,
232 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&)>
233 CaptureCallback;
235 // Create a subscription. Whenever a manual capture is required, the
236 // subscription will invoke |capture_callback| on the UI thread to do the
237 // work.
238 ContentCaptureSubscription(
239 const RenderWidgetHost& source,
240 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
241 const CaptureCallback& capture_callback);
242 virtual ~ContentCaptureSubscription();
244 // content::NotificationObserver implementation.
245 virtual void Observe(int type,
246 const content::NotificationSource& source,
247 const content::NotificationDetails& details) OVERRIDE;
249 private:
250 void OnTimer();
252 const int render_process_id_;
253 const int render_view_id_;
255 FrameSubscriber paint_subscriber_;
256 FrameSubscriber timer_subscriber_;
257 content::NotificationRegistrar registrar_;
258 CaptureCallback capture_callback_;
259 base::Timer timer_;
261 DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription);
264 // Render the SkBitmap |input| into the given VideoFrame buffer |output|, then
265 // invoke |done_cb| to indicate success or failure. |input| is expected to be
266 // ARGB. |output| must be YV12 or I420. Colorspace conversion is always done.
267 // Scaling and letterboxing will be done as needed.
269 // This software implementation should be used only when GPU acceleration of
270 // these activities is not possible. This operation may be expensive (tens to
271 // hundreds of milliseconds), so the caller should ensure that it runs on a
272 // thread where such a pause would cause UI jank.
273 void RenderVideoFrame(const SkBitmap& input,
274 const scoped_refptr<media::VideoFrame>& output,
275 const base::Callback<void(bool)>& done_cb);
277 // Keeps track of the RenderView to be sourced, and executes copying of the
278 // backing store on the UI BrowserThread.
280 // TODO(nick): It would be nice to merge this with WebContentsTracker, but its
281 // implementation is currently asynchronous -- in our case, the "rvh changed"
282 // notification would get posted back to the UI thread and processed later, and
283 // this seems disadvantageous.
284 class CaptureMachine : public WebContentsObserver,
285 public base::SupportsWeakPtr<CaptureMachine> {
286 public:
287 virtual ~CaptureMachine();
289 // Creates a CaptureMachine. Must be run on the UI BrowserThread. Returns
290 // NULL if the indicated render view cannot be found.
291 static scoped_ptr<CaptureMachine> Create(
292 int render_process_id,
293 int render_view_id,
294 const scoped_refptr<base::SequencedTaskRunner>& render_task_runner,
295 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy);
297 // Starts a copy from the backing store or the composited surface. Must be run
298 // on the UI BrowserThread. |deliver_frame_cb| will be run when the operation
299 // completes. The copy will occur to |target|.
301 // This may be used as a ContentCaptureSubscription::CaptureCallback.
302 void Capture(
303 const base::Time& start_time,
304 const scoped_refptr<media::VideoFrame>& target,
305 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
306 deliver_frame_cb);
308 // content::WebContentsObserver implementation.
309 virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE {
310 fullscreen_widget_id_ = routing_id;
311 RenewFrameSubscription();
314 virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE {
315 DCHECK_EQ(fullscreen_widget_id_, routing_id);
316 fullscreen_widget_id_ = MSG_ROUTING_NONE;
317 RenewFrameSubscription();
320 virtual void RenderViewReady() OVERRIDE {
321 RenewFrameSubscription();
324 virtual void AboutToNavigateRenderView(RenderViewHost* rvh) OVERRIDE {
325 RenewFrameSubscription();
328 virtual void DidNavigateMainFrame(
329 const LoadCommittedDetails& details,
330 const FrameNavigateParams& params) OVERRIDE {
331 RenewFrameSubscription();
334 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
336 private:
337 CaptureMachine(
338 const scoped_refptr<base::SequencedTaskRunner>& render_task_runner,
339 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy);
341 // Starts observing the web contents, returning false if lookup fails.
342 bool StartObservingWebContents(int initial_render_process_id,
343 int initial_render_view_id);
345 // Helper function to determine the view that we are currently tracking.
346 RenderWidgetHost* GetTarget();
348 // Response callback for RenderWidgetHost::CopyFromBackingStore().
349 void DidCopyFromBackingStore(
350 const base::Time& start_time,
351 const scoped_refptr<media::VideoFrame>& target,
352 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
353 deliver_frame_cb,
354 bool success,
355 const SkBitmap& bitmap);
357 // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame().
358 void DidCopyFromCompositingSurfaceToVideoFrame(
359 const base::Time& start_time,
360 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
361 deliver_frame_cb,
362 bool success);
364 // Remove the old subscription, and start a new one. This should be called
365 // after any change to the WebContents that affects the RenderWidgetHost or
366 // attached views.
367 void RenewFrameSubscription();
369 // The task runner of the thread on which SkBitmap->VideoFrame conversion will
370 // occur. Only used when this activity cannot be done on the GPU.
371 const scoped_refptr<base::SequencedTaskRunner> render_task_runner_;
373 // Makes all the decisions about which frames to copy, and how.
374 const scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
376 // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE
377 // otherwise.
378 int fullscreen_widget_id_;
380 // Last known RenderView size.
381 gfx::Size last_view_size_;
383 // Responsible for forwarding events from the active RenderWidgetHost to the
384 // oracle, and initiating captures accordingly.
385 scoped_ptr<ContentCaptureSubscription> subscription_;
387 DISALLOW_COPY_AND_ASSIGN(CaptureMachine);
390 // Responsible for logging the effective frame rate.
391 // TODO(nick): Make this compatible with the push model and hook it back up.
392 class VideoFrameDeliveryLog {
393 public:
394 VideoFrameDeliveryLog();
396 // Treat |frame_number| as having been delivered, and update the
397 // frame rate statistics accordingly.
398 void ChronicleFrameDelivery(int frame_number);
400 private:
401 // The following keep track of and log the effective frame rate whenever
402 // verbose logging is turned on.
403 base::Time last_frame_rate_log_time_;
404 int count_frames_rendered_;
405 int last_frame_number_;
407 DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog);
410 ThreadSafeCaptureOracle::ThreadSafeCaptureOracle(
411 media::VideoCaptureDevice::EventHandler* consumer,
412 scoped_ptr<VideoCaptureOracle> oracle)
413 : consumer_(consumer),
414 oracle_(oracle.Pass()),
415 is_started_(false) {
418 bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
419 VideoCaptureOracle::Event event,
420 base::Time event_time,
421 scoped_refptr<media::VideoFrame>* storage,
422 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback* callback) {
423 base::AutoLock guard(lock_);
425 if (!consumer_ || !is_started_)
426 return false; // Capture is stopped.
428 scoped_refptr<media::VideoFrame> output_buffer =
429 consumer_->ReserveOutputBuffer();
430 const bool should_capture =
431 oracle_->ObserveEventAndDecideCapture(event, event_time);
432 const bool content_is_dirty =
433 (event == VideoCaptureOracle::kCompositorUpdate ||
434 event == VideoCaptureOracle::kSoftwarePaint);
435 const char* event_name =
436 (event == VideoCaptureOracle::kTimerPoll ? "poll" :
437 (event == VideoCaptureOracle::kCompositorUpdate ? "gpu" :
438 "paint"));
440 // Consider the various reasons not to initiate a capture.
441 if (should_capture && !output_buffer.get()) {
442 TRACE_EVENT_INSTANT1("mirroring",
443 "EncodeLimited",
444 TRACE_EVENT_SCOPE_THREAD,
445 "trigger",
446 event_name);
447 return false;
448 } else if (!should_capture && output_buffer.get()) {
449 if (content_is_dirty) {
450 // This is a normal and acceptable way to drop a frame. We've hit our
451 // capture rate limit: for example, the content is animating at 60fps but
452 // we're capturing at 30fps.
453 TRACE_EVENT_INSTANT1("mirroring", "FpsRateLimited",
454 TRACE_EVENT_SCOPE_THREAD,
455 "trigger", event_name);
457 return false;
458 } else if (!should_capture && !output_buffer.get()) {
459 // We decided not to capture, but we wouldn't have been able to if we wanted
460 // to because no output buffer was available.
461 TRACE_EVENT_INSTANT1("mirroring", "NearlyEncodeLimited",
462 TRACE_EVENT_SCOPE_THREAD,
463 "trigger", event_name);
464 return false;
466 int frame_number = oracle_->RecordCapture();
467 TRACE_EVENT_ASYNC_BEGIN2("mirroring", "Capture", output_buffer.get(),
468 "frame_number", frame_number,
469 "trigger", event_name);
470 *storage = output_buffer;
471 *callback = base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame,
472 this, output_buffer, frame_number);
473 return true;
476 void ThreadSafeCaptureOracle::Start() {
477 base::AutoLock guard(lock_);
478 is_started_ = true;
481 void ThreadSafeCaptureOracle::Stop() {
482 base::AutoLock guard(lock_);
483 is_started_ = false;
486 void ThreadSafeCaptureOracle::ReportError() {
487 base::AutoLock guard(lock_);
488 if (consumer_)
489 consumer_->OnError();
492 void ThreadSafeCaptureOracle::InvalidateConsumer() {
493 base::AutoLock guard(lock_);
495 TRACE_EVENT_INSTANT0("mirroring", "InvalidateConsumer",
496 TRACE_EVENT_SCOPE_THREAD);
498 is_started_ = false;
499 consumer_ = NULL;
502 void ThreadSafeCaptureOracle::DidCaptureFrame(
503 const scoped_refptr<media::VideoFrame>& frame,
504 int frame_number,
505 base::Time timestamp,
506 bool success) {
507 base::AutoLock guard(lock_);
509 TRACE_EVENT_ASYNC_END2("mirroring", "Capture", frame.get(),
510 "success", success,
511 "timestamp", timestamp.ToInternalValue());
513 if (!consumer_ || !is_started_)
514 return; // Capture is stopped.
516 if (success)
517 if (oracle_->CompleteCapture(frame_number, timestamp))
518 consumer_->OnIncomingCapturedVideoFrame(frame, timestamp);
521 bool FrameSubscriber::ShouldCaptureFrame(
522 base::Time present_time,
523 scoped_refptr<media::VideoFrame>* storage,
524 DeliverFrameCallback* deliver_frame_cb) {
525 TRACE_EVENT1("mirroring", "FrameSubscriber::ShouldCaptureFrame",
526 "instance", this);
528 return oracle_proxy_->ObserveEventAndDecideCapture(event_type_, present_time,
529 storage, deliver_frame_cb);
532 ContentCaptureSubscription::ContentCaptureSubscription(
533 const RenderWidgetHost& source,
534 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy,
535 const CaptureCallback& capture_callback)
536 : render_process_id_(source.GetProcess()->GetID()),
537 render_view_id_(source.GetRoutingID()),
538 paint_subscriber_(VideoCaptureOracle::kSoftwarePaint, oracle_proxy),
539 timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy),
540 capture_callback_(capture_callback),
541 timer_(true, true) {
542 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
544 RenderWidgetHostViewPort* view =
545 RenderWidgetHostViewPort::FromRWHV(source.GetView());
547 // Subscribe to accelerated presents. These will be serviced directly by the
548 // oracle.
549 if (view && kAcceleratedSubscriberIsSupported) {
550 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber(
551 new FrameSubscriber(VideoCaptureOracle::kCompositorUpdate,
552 oracle_proxy));
553 view->BeginFrameSubscription(subscriber.Pass());
556 // Subscribe to software paint events. This instance will service these by
557 // reflecting them back to the CaptureMachine via |capture_callback|.
558 registrar_.Add(
559 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
560 Source<RenderWidgetHost>(&source));
562 // Subscribe to timer events. This instance will service these as well.
563 timer_.Start(FROM_HERE, oracle_proxy->capture_period(),
564 base::Bind(&ContentCaptureSubscription::OnTimer,
565 base::Unretained(this)));
568 ContentCaptureSubscription::~ContentCaptureSubscription() {
569 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
570 if (kAcceleratedSubscriberIsSupported) {
571 RenderViewHost* source = RenderViewHost::FromID(render_process_id_,
572 render_view_id_);
573 if (source) {
574 RenderWidgetHostViewPort* view =
575 RenderWidgetHostViewPort::FromRWHV(source->GetView());
576 if (view)
577 view->EndFrameSubscription();
582 void ContentCaptureSubscription::Observe(
583 int type,
584 const content::NotificationSource& source,
585 const content::NotificationDetails& details) {
586 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
587 DCHECK_EQ(NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, type);
589 RenderWidgetHostImpl* rwh =
590 RenderWidgetHostImpl::From(Source<RenderWidgetHost>(source).ptr());
592 // This message occurs on window resizes and visibility changes even when
593 // accelerated compositing is active, so we need to filter out these cases.
594 if (!rwh || !rwh->GetView() || rwh->is_accelerated_compositing_active())
595 return;
597 TRACE_EVENT1("mirroring", "ContentCaptureSubscription::Observe",
598 "instance", this);
600 base::Closure copy_done_callback;
601 scoped_refptr<media::VideoFrame> frame;
602 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
603 const base::Time start_time = base::Time::Now();
604 if (paint_subscriber_.ShouldCaptureFrame(start_time,
605 &frame,
606 &deliver_frame_cb)) {
607 // This message happens just before paint. If we post a task to do the copy,
608 // it should run soon after the paint.
609 BrowserThread::PostTask(
610 BrowserThread::UI, FROM_HERE,
611 base::Bind(capture_callback_, start_time, frame, deliver_frame_cb));
615 void ContentCaptureSubscription::OnTimer() {
616 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
617 TRACE_EVENT0("mirroring", "ContentCaptureSubscription::OnTimer");
619 scoped_refptr<media::VideoFrame> frame;
620 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb;
622 const base::Time start_time = base::Time::Now();
623 if (timer_subscriber_.ShouldCaptureFrame(start_time,
624 &frame,
625 &deliver_frame_cb)) {
626 capture_callback_.Run(start_time, frame, deliver_frame_cb);
630 void RenderVideoFrame(const SkBitmap& input,
631 const scoped_refptr<media::VideoFrame>& output,
632 const base::Callback<void(bool)>& done_cb) {
633 base::ScopedClosureRunner failure_handler(base::Bind(done_cb, false));
635 SkAutoLockPixels locker(input);
637 // Sanity-check the captured bitmap.
638 if (input.empty() ||
639 !input.readyToDraw() ||
640 input.config() != SkBitmap::kARGB_8888_Config ||
641 input.width() < 2 || input.height() < 2) {
642 DVLOG(1) << "input unacceptable (size="
643 << input.getSize()
644 << ", ready=" << input.readyToDraw()
645 << ", config=" << input.config() << ')';
646 return;
649 // Sanity-check the output buffer.
650 if (output->format() != media::VideoFrame::YV12) {
651 NOTREACHED();
652 return;
655 // Calculate the width and height of the content region in the |output|, based
656 // on the aspect ratio of |input|.
657 gfx::Rect region_in_frame = ComputeYV12LetterboxRegion(
658 output->coded_size(), gfx::Size(input.width(), input.height()));
660 // Scale the bitmap to the required size, if necessary.
661 SkBitmap scaled_bitmap;
662 if (input.width() != region_in_frame.width() ||
663 input.height() != region_in_frame.height()) {
665 skia::ImageOperations::ResizeMethod method;
666 if (input.width() < region_in_frame.width() ||
667 input.height() < region_in_frame.height()) {
668 // Avoid box filtering when magnifying, because it's actually
669 // nearest-neighbor.
670 method = skia::ImageOperations::RESIZE_HAMMING1;
671 } else {
672 method = skia::ImageOperations::RESIZE_BOX;
675 TRACE_EVENT_ASYNC_STEP0("mirroring", "Capture", output.get(), "Scale");
676 scaled_bitmap = skia::ImageOperations::Resize(input, method,
677 region_in_frame.width(),
678 region_in_frame.height());
679 } else {
680 scaled_bitmap = input;
683 TRACE_EVENT_ASYNC_STEP0("mirroring", "Capture", output.get(), "YUV");
685 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap);
687 media::CopyRGBToVideoFrame(
688 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()),
689 scaled_bitmap.rowBytes(),
690 region_in_frame,
691 output.get());
694 // The result is now ready.
695 failure_handler.Release();
696 done_cb.Run(true);
699 VideoFrameDeliveryLog::VideoFrameDeliveryLog()
700 : last_frame_rate_log_time_(),
701 count_frames_rendered_(0),
702 last_frame_number_(0) {
705 void VideoFrameDeliveryLog::ChronicleFrameDelivery(int frame_number) {
706 // Log frame rate, if verbose logging is turned on.
707 static const base::TimeDelta kFrameRateLogInterval =
708 base::TimeDelta::FromSeconds(10);
709 const base::Time now = base::Time::Now();
710 if (last_frame_rate_log_time_.is_null()) {
711 last_frame_rate_log_time_ = now;
712 count_frames_rendered_ = 0;
713 last_frame_number_ = frame_number;
714 } else {
715 ++count_frames_rendered_;
716 const base::TimeDelta elapsed = now - last_frame_rate_log_time_;
717 if (elapsed >= kFrameRateLogInterval) {
718 const double measured_fps =
719 count_frames_rendered_ / elapsed.InSecondsF();
720 const int frames_elapsed = frame_number - last_frame_number_;
721 const int count_frames_dropped = frames_elapsed - count_frames_rendered_;
722 DCHECK_LE(0, count_frames_dropped);
723 UMA_HISTOGRAM_PERCENTAGE(
724 "TabCapture.FrameDropPercentage",
725 (count_frames_dropped * 100 + frames_elapsed / 2) / frames_elapsed);
726 UMA_HISTOGRAM_COUNTS(
727 "TabCapture.FrameRate",
728 static_cast<int>(measured_fps));
729 VLOG(1) << "Current measured frame rate for "
730 << "WebContentsVideoCaptureDevice is " << measured_fps << " FPS.";
731 last_frame_rate_log_time_ = now;
732 count_frames_rendered_ = 0;
733 last_frame_number_ = frame_number;
738 // static
739 scoped_ptr<CaptureMachine> CaptureMachine::Create(
740 int render_process_id,
741 int render_view_id,
742 const scoped_refptr<base::SequencedTaskRunner>& render_task_runner,
743 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) {
744 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
745 DCHECK(render_task_runner.get());
746 DCHECK(oracle_proxy.get());
747 scoped_ptr<CaptureMachine> machine(
748 new CaptureMachine(render_task_runner, oracle_proxy));
750 if (!machine->StartObservingWebContents(render_process_id, render_view_id))
751 machine.reset();
753 return machine.Pass();
756 CaptureMachine::CaptureMachine(
757 const scoped_refptr<base::SequencedTaskRunner>& render_task_runner,
758 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy)
759 : render_task_runner_(render_task_runner),
760 oracle_proxy_(oracle_proxy),
761 fullscreen_widget_id_(MSG_ROUTING_NONE) {}
763 CaptureMachine::~CaptureMachine() {
764 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
765 !BrowserThread::IsMessageLoopValid(BrowserThread::UI));
767 // Stop observing the web contents.
768 subscription_.reset();
769 if (web_contents()) {
770 web_contents()->DecrementCapturerCount();
771 Observe(NULL);
775 void CaptureMachine::Capture(
776 const base::Time& start_time,
777 const scoped_refptr<media::VideoFrame>& target,
778 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
779 deliver_frame_cb) {
780 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
782 RenderWidgetHost* rwh = GetTarget();
783 RenderWidgetHostViewPort* view =
784 rwh ? RenderWidgetHostViewPort::FromRWHV(rwh->GetView()) : NULL;
785 if (!view || !rwh) {
786 deliver_frame_cb.Run(base::Time(), false);
787 return;
790 gfx::Size video_size = target->coded_size();
791 gfx::Size view_size = view->GetViewBounds().size();
792 gfx::Size fitted_size;
793 if (!view_size.IsEmpty()) {
794 fitted_size = ComputeYV12LetterboxRegion(video_size, view_size).size();
796 if (view_size != last_view_size_) {
797 last_view_size_ = view_size;
799 // Measure the number of kilopixels.
800 UMA_HISTOGRAM_COUNTS_10000(
801 "TabCapture.ViewChangeKiloPixels",
802 view_size.width() * view_size.height() / 1024);
805 if (!view->IsSurfaceAvailableForCopy()) {
806 // Fallback to the more expensive renderer-side copy if the surface and
807 // backing store are not accessible.
808 rwh->GetSnapshotFromRenderer(
809 gfx::Rect(),
810 base::Bind(&CaptureMachine::DidCopyFromBackingStore, this->AsWeakPtr(),
811 start_time, target, deliver_frame_cb));
812 } else if (view->CanCopyToVideoFrame()) {
813 view->CopyFromCompositingSurfaceToVideoFrame(
814 gfx::Rect(view_size),
815 target,
816 base::Bind(&CaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame,
817 this->AsWeakPtr(), start_time, deliver_frame_cb));
818 } else {
819 rwh->CopyFromBackingStore(
820 gfx::Rect(),
821 fitted_size, // Size here is a request not always honored.
822 base::Bind(&CaptureMachine::DidCopyFromBackingStore, this->AsWeakPtr(),
823 start_time, target, deliver_frame_cb));
827 bool CaptureMachine::StartObservingWebContents(int initial_render_process_id,
828 int initial_render_view_id) {
829 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
831 // Look-up the RenderViewHost and, from that, the WebContents that wraps it.
832 // If successful, begin observing the WebContents instance.
834 // Why this can be unsuccessful: The request for mirroring originates in a
835 // render process, and this request is based on the current RenderView
836 // associated with a tab. However, by the time we get up-and-running here,
837 // there have been multiple back-and-forth IPCs between processes, as well as
838 // a bit of indirection across threads. It's easily possible that, in the
839 // meantime, the original RenderView may have gone away.
840 RenderViewHost* const rvh =
841 RenderViewHost::FromID(initial_render_process_id,
842 initial_render_view_id);
843 DVLOG_IF(1, !rvh) << "RenderViewHost::FromID("
844 << initial_render_process_id << ", "
845 << initial_render_view_id << ") returned NULL.";
846 Observe(rvh ? WebContents::FromRenderViewHost(rvh) : NULL);
848 WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents());
849 if (contents) {
850 contents->IncrementCapturerCount();
851 fullscreen_widget_id_ = contents->GetFullscreenWidgetRoutingID();
852 RenewFrameSubscription();
853 return true;
856 DVLOG(1) << "WebContents::FromRenderViewHost(" << rvh << ") returned NULL.";
857 return false;
860 void CaptureMachine::WebContentsDestroyed(WebContents* web_contents) {
861 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
863 subscription_.reset();
864 web_contents->DecrementCapturerCount();
865 oracle_proxy_->ReportError();
868 RenderWidgetHost* CaptureMachine::GetTarget() {
869 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
870 if (!web_contents())
871 return NULL;
873 RenderWidgetHost* rwh = NULL;
874 if (fullscreen_widget_id_ != MSG_ROUTING_NONE) {
875 RenderProcessHost* process = web_contents()->GetRenderProcessHost();
876 if (process)
877 rwh = RenderWidgetHost::FromID(process->GetID(), fullscreen_widget_id_);
878 } else {
879 rwh = web_contents()->GetRenderViewHost();
882 return rwh;
885 void CaptureMachine::DidCopyFromBackingStore(
886 const base::Time& start_time,
887 const scoped_refptr<media::VideoFrame>& target,
888 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
889 deliver_frame_cb,
890 bool success,
891 const SkBitmap& bitmap) {
892 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
894 base::Time now = base::Time::Now();
895 if (success) {
896 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeBitmap", now - start_time);
897 TRACE_EVENT_ASYNC_STEP0("mirroring", "Capture", target.get(), "Render");
898 render_task_runner_->PostTask(FROM_HERE, base::Bind(
899 &RenderVideoFrame, bitmap, target,
900 base::Bind(deliver_frame_cb, start_time)));
901 } else {
902 // Capture can fail due to transient issues, so just skip this frame.
903 DVLOG(1) << "CopyFromBackingStore failed; skipping frame.";
904 deliver_frame_cb.Run(start_time, false);
908 void CaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame(
909 const base::Time& start_time,
910 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&
911 deliver_frame_cb,
912 bool success) {
913 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
914 base::Time now = base::Time::Now();
916 if (success) {
917 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time);
918 } else {
919 // Capture can fail due to transient issues, so just skip this frame.
920 DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame.";
922 deliver_frame_cb.Run(start_time, success);
925 void CaptureMachine::RenewFrameSubscription() {
926 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
928 // Always destroy the old subscription before creating a new one.
929 subscription_.reset();
931 RenderWidgetHost* rwh = GetTarget();
932 if (!rwh || !rwh->GetView())
933 return;
935 subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_,
936 base::Bind(&CaptureMachine::Capture, this->AsWeakPtr())));
939 void DeleteCaptureMachineOnUIThread(
940 scoped_ptr<CaptureMachine> capture_machine) {
941 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
942 capture_machine.reset();
945 } // namespace
947 // The "meat" of the video capture implementation, which is a ref-counted class.
948 // Separating this from the "shell class" WebContentsVideoCaptureDevice allows
949 // safe destruction without needing to block any threads (e.g., the IO
950 // BrowserThread).
952 // WebContentsVideoCaptureDevice::Impl manages a simple state machine and the
953 // pipeline (see notes at top of this file). It times the start of successive
954 // captures and facilitates the processing of each through the stages of the
955 // pipeline.
956 class WebContentsVideoCaptureDevice::Impl : public base::SupportsWeakPtr<Impl> {
957 public:
959 Impl(int render_process_id, int render_view_id);
960 virtual ~Impl();
962 // Asynchronous requests to change WebContentsVideoCaptureDevice::Impl state.
963 void Allocate(int width,
964 int height,
965 int frame_rate,
966 media::VideoCaptureDevice::EventHandler* consumer);
967 void Start();
968 void Stop();
969 void DeAllocate();
971 private:
973 // Flag indicating current state.
974 enum State {
975 kIdle,
976 kAllocated,
977 kCapturing,
978 kError
981 void TransitionStateTo(State next_state);
983 // Stops capturing and notifies consumer_ of an error state.
984 void Error();
986 // Called in response to CaptureMachine::Create that runs on the UI thread.
987 // It will assign the capture machine to the Impl class if it still exists
988 // otherwise it will post a task to delete CaptureMachine on the UI thread.
989 static void AssignCaptureMachine(
990 base::WeakPtr<WebContentsVideoCaptureDevice::Impl> impl,
991 scoped_ptr<CaptureMachine> capture_machine);
993 // Tracks that all activity occurs on the media stream manager's thread.
994 base::ThreadChecker thread_checker_;
996 // These values identify the starting view that will be captured. After
997 // capture starts, the target view IDs will change as navigation occurs, and
998 // so these values are not relevant after the initial bootstrapping.
999 const int initial_render_process_id_;
1000 const int initial_render_view_id_;
1002 // Our event handler, which gobbles the frames we capture.
1003 VideoCaptureDevice::EventHandler* consumer_;
1005 // Current lifecycle state.
1006 State state_;
1008 // A dedicated worker thread for doing image operations. Started/joined here,
1009 // but used by the CaptureMachine.
1010 base::Thread render_thread_;
1012 // Tracks the CaptureMachine that's doing work on our behalf on the UI thread.
1013 // This value should never be dereferenced by this class, other than to
1014 // create and destroy it on the UI thread.
1015 scoped_ptr<CaptureMachine> capture_machine_;
1017 // Our thread-safe capture oracle which serves as the gateway to the video
1018 // capture pipeline. Besides the WCVCD itself, it is the only component of the
1019 // system with direct access to |consumer_|.
1020 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_;
1022 DISALLOW_COPY_AND_ASSIGN(Impl);
1025 WebContentsVideoCaptureDevice::Impl::Impl(int render_process_id,
1026 int render_view_id)
1027 : initial_render_process_id_(render_process_id),
1028 initial_render_view_id_(render_view_id),
1029 consumer_(NULL),
1030 state_(kIdle),
1031 render_thread_("WebContentsVideo_RenderThread") {
1034 void WebContentsVideoCaptureDevice::Impl::Allocate(
1035 int width,
1036 int height,
1037 int frame_rate,
1038 VideoCaptureDevice::EventHandler* consumer) {
1039 DCHECK(thread_checker_.CalledOnValidThread());
1041 if (state_ != kIdle) {
1042 DVLOG(1) << "Allocate() invoked when not in state Idle.";
1043 return;
1046 if (frame_rate <= 0) {
1047 DVLOG(1) << "invalid frame_rate: " << frame_rate;
1048 consumer->OnError();
1049 return;
1052 if (!render_thread_.Start()) {
1053 DVLOG(1) << "Failed to spawn render thread.";
1054 consumer->OnError();
1055 return;
1058 // Frame dimensions must each be a positive, even integer, since the consumer
1059 // wants (or will convert to) YUV420.
1060 width = MakeEven(width);
1061 height = MakeEven(height);
1062 if (width < kMinFrameWidth || height < kMinFrameHeight) {
1063 DVLOG(1) << "invalid width (" << width << ") and/or height ("
1064 << height << ")";
1065 consumer->OnError();
1066 return;
1069 // Initialize capture settings which will be consistent for the
1070 // duration of the capture.
1071 media::VideoCaptureCapability settings = { 0 };
1073 settings.width = width;
1074 settings.height = height;
1075 settings.frame_rate = frame_rate;
1076 // Note: the value of |settings.color| doesn't matter if we use only the
1077 // VideoFrame based methods on |consumer|.
1078 settings.color = media::VideoCaptureCapability::kI420;
1079 settings.expected_capture_delay = 0;
1080 settings.interlaced = false;
1082 base::TimeDelta capture_period = base::TimeDelta::FromMicroseconds(
1083 1000000.0 / settings.frame_rate + 0.5);
1085 consumer_ = consumer;
1086 consumer_->OnFrameInfo(settings);
1087 scoped_ptr<VideoCaptureOracle> oracle(
1088 new VideoCaptureOracle(capture_period,
1089 kAcceleratedSubscriberIsSupported));
1090 oracle_proxy_ = new ThreadSafeCaptureOracle(
1091 consumer_,
1092 oracle.Pass());
1094 // Allocates the CaptureMachine. The CaptureMachine will be tracking render
1095 // view swapping over its lifetime, and we don't want to lose our reference to
1096 // the current render view by starting over with the stale
1097 // |initial_render_view_id_|.
1098 DCHECK(!capture_machine_.get());
1099 BrowserThread::PostTaskAndReplyWithResult(
1100 BrowserThread::UI, FROM_HERE,
1101 base::Bind(&CaptureMachine::Create,
1102 initial_render_process_id_,
1103 initial_render_view_id_,
1104 render_thread_.message_loop_proxy(), oracle_proxy_),
1105 base::Bind(&Impl::AssignCaptureMachine, AsWeakPtr()));
1107 TransitionStateTo(kAllocated);
1110 void WebContentsVideoCaptureDevice::Impl::Start() {
1111 DCHECK(thread_checker_.CalledOnValidThread());
1113 if (state_ != kAllocated) {
1114 return;
1117 TransitionStateTo(kCapturing);
1119 oracle_proxy_->Start();
1122 // static
1123 void WebContentsVideoCaptureDevice::Impl::AssignCaptureMachine(
1124 base::WeakPtr<WebContentsVideoCaptureDevice::Impl> impl,
1125 scoped_ptr<CaptureMachine> capture_machine) {
1126 DCHECK(!impl.get() || impl->thread_checker_.CalledOnValidThread());
1128 if (!impl.get()) {
1129 // If WCVD::Impl was destroyed before we got back on it's thread and
1130 // capture_machine is not NULL, then we need to return to the UI thread to
1131 // safely cleanup the CaptureMachine.
1132 if (capture_machine) {
1133 BrowserThread::PostTask(
1134 BrowserThread::UI, FROM_HERE, base::Bind(
1135 &DeleteCaptureMachineOnUIThread, base::Passed(&capture_machine)));
1136 return;
1138 } else if (!capture_machine) {
1139 impl->Error();
1140 } else {
1141 impl->capture_machine_ = capture_machine.Pass();
1145 void WebContentsVideoCaptureDevice::Impl::Stop() {
1146 DCHECK(thread_checker_.CalledOnValidThread());
1148 if (state_ != kCapturing) {
1149 return;
1151 oracle_proxy_->Stop();
1153 TransitionStateTo(kAllocated);
1156 void WebContentsVideoCaptureDevice::Impl::DeAllocate() {
1157 DCHECK(thread_checker_.CalledOnValidThread());
1158 if (state_ == kCapturing) {
1159 Stop();
1161 if (state_ == kAllocated) {
1162 // |consumer_| is about to be deleted, so we mustn't use it anymore.
1163 oracle_proxy_->InvalidateConsumer();
1164 consumer_ = NULL;
1165 oracle_proxy_ = NULL;
1166 render_thread_.Stop();
1168 TransitionStateTo(kIdle);
1172 WebContentsVideoCaptureDevice::Impl::~Impl() {
1173 // There is still a capture pipeline running that is checking in with the
1174 // oracle, and processing captures that are already started in flight. That
1175 // pipeline must be shut down asynchronously, on the UI thread.
1176 if (capture_machine_) {
1177 // The task that is posted to the UI thread might not run if we are shutting
1178 // down, so we transfer ownership of CaptureMachine to the closure so that
1179 // it is still cleaned up when the closure is deleted.
1180 BrowserThread::PostTask(
1181 BrowserThread::UI, FROM_HERE, base::Bind(
1182 &DeleteCaptureMachineOnUIThread, base::Passed(&capture_machine_)));
1185 DCHECK(!capture_machine_) << "Cleanup on UI thread did not happen.";
1186 DCHECK(!consumer_) << "Device not DeAllocated -- possible data race.";
1187 DVLOG(1) << "WebContentsVideoCaptureDevice::Impl@" << this << " destroying.";
1190 void WebContentsVideoCaptureDevice::Impl::TransitionStateTo(State next_state) {
1191 DCHECK(thread_checker_.CalledOnValidThread());
1193 #ifndef NDEBUG
1194 static const char* kStateNames[] = {
1195 "Idle", "Allocated", "Capturing", "Error"
1197 DVLOG(1) << "State change: " << kStateNames[state_]
1198 << " --> " << kStateNames[next_state];
1199 #endif
1201 state_ = next_state;
1204 void WebContentsVideoCaptureDevice::Impl::Error() {
1205 DCHECK(thread_checker_.CalledOnValidThread());
1207 if (state_ == kIdle)
1208 return;
1210 if (consumer_)
1211 consumer_->OnError();
1213 DeAllocate();
1214 TransitionStateTo(kError);
1217 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice(
1218 const media::VideoCaptureDevice::Name& name,
1219 int render_process_id,
1220 int render_view_id)
1221 : device_name_(name),
1222 impl_(new WebContentsVideoCaptureDevice::Impl(render_process_id,
1223 render_view_id)) {}
1225 WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() {
1226 DVLOG(2) << "WebContentsVideoCaptureDevice@" << this << " destroying.";
1229 // static
1230 media::VideoCaptureDevice* WebContentsVideoCaptureDevice::Create(
1231 const std::string& device_id) {
1232 // Parse device_id into render_process_id and render_view_id.
1233 int render_process_id = -1;
1234 int render_view_id = -1;
1235 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(device_id,
1236 &render_process_id,
1237 &render_view_id))
1238 return NULL;
1240 media::VideoCaptureDevice::Name name;
1241 base::SStringPrintf(&name.device_name,
1242 "WebContents[%.*s]",
1243 static_cast<int>(device_id.size()), device_id.data());
1244 name.unique_id = device_id;
1246 return new WebContentsVideoCaptureDevice(
1247 name, render_process_id, render_view_id);
1250 void WebContentsVideoCaptureDevice::Allocate(
1251 int width, int height, int frame_rate,
1252 VideoCaptureDevice::EventHandler* consumer) {
1253 impl_->Allocate(width, height, frame_rate, consumer);
1256 void WebContentsVideoCaptureDevice::Start() {
1257 impl_->Start();
1260 void WebContentsVideoCaptureDevice::Stop() {
1261 impl_->Stop();
1264 void WebContentsVideoCaptureDevice::DeAllocate() {
1265 impl_->DeAllocate();
1268 const media::VideoCaptureDevice::Name&
1269 WebContentsVideoCaptureDevice::device_name() {
1270 return device_name_;
1273 } // namespace content