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

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

Issue 12090109: Tab Capture: Backing store readbacks to YV12 VideoFrames. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Style fix per wjia Created 7 years, 10 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 // 4 //
5 // Implementation notes: This needs to work on a variety of hardware 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 6 // configurations where the speed of the CPU and GPU greatly affect overall
7 // performance. Therefore, the process of capturing has been split up into a 7 // performance. Therefore, the process of capturing has been split up into a
8 // pipeline of three stages. Each stage executes on its own thread: 8 // pipeline of three stages. Each stage executes on its own thread:
9 // 9 //
10 // 1. Capture: A bitmap is snapshotted/copied from the RenderView's backing 10 // 1. Capture: A bitmap is snapshotted/copied from the RenderView's backing
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
50 #include "base/debug/trace_event.h" 50 #include "base/debug/trace_event.h"
51 #include "base/logging.h" 51 #include "base/logging.h"
52 #include "base/memory/scoped_ptr.h" 52 #include "base/memory/scoped_ptr.h"
53 #include "base/metrics/histogram.h" 53 #include "base/metrics/histogram.h"
54 #include "base/stringprintf.h" 54 #include "base/stringprintf.h"
55 #include "base/synchronization/lock.h" 55 #include "base/synchronization/lock.h"
56 #include "base/threading/thread.h" 56 #include "base/threading/thread.h"
57 #include "base/time.h" 57 #include "base/time.h"
58 #include "content/browser/renderer_host/media/web_contents_capture_util.h" 58 #include "content/browser/renderer_host/media/web_contents_capture_util.h"
59 #include "content/browser/web_contents/web_contents_impl.h" 59 #include "content/browser/web_contents/web_contents_impl.h"
60 #include "content/port/browser/render_widget_host_view_port.h"
60 #include "content/public/browser/browser_thread.h" 61 #include "content/public/browser/browser_thread.h"
61 #include "content/public/browser/render_process_host.h" 62 #include "content/public/browser/render_process_host.h"
62 #include "content/public/browser/render_view_host.h" 63 #include "content/public/browser/render_view_host.h"
63 #include "content/public/browser/render_widget_host_view.h" 64 #include "content/public/browser/render_widget_host_view.h"
64 #include "content/public/browser/web_contents.h" 65 #include "content/public/browser/web_contents.h"
65 #include "content/public/browser/web_contents_observer.h" 66 #include "content/public/browser/web_contents_observer.h"
66 #include "media/base/bind_to_loop.h" 67 #include "media/base/bind_to_loop.h"
68 #include "media/base/video_frame.h"
67 #include "media/video/capture/video_capture_types.h" 69 #include "media/video/capture/video_capture_types.h"
68 #include "skia/ext/image_operations.h" 70 #include "skia/ext/image_operations.h"
69 #include "third_party/skia/include/core/SkBitmap.h" 71 #include "third_party/skia/include/core/SkBitmap.h"
70 #include "third_party/skia/include/core/SkColor.h" 72 #include "third_party/skia/include/core/SkColor.h"
71 #include "ui/gfx/rect.h" 73 #include "ui/gfx/rect.h"
74 #include "ui/gfx/skia_util.h"
72 75
73 // Used to self-trampoline invocation of methods to the approprate thread. This 76 // Used to self-trampoline invocation of methods to the approprate thread. This
74 // should be used sparingly, only when it's not clear which thread is invoking a 77 // should be used sparingly, only when it's not clear which thread is invoking a
75 // method. 78 // method.
76 #define ENSURE_INVOKED_ON_THREAD(thread, ...) { \ 79 #define ENSURE_INVOKED_ON_THREAD(thread, ...) { \
77 DCHECK(thread.IsRunning()); \ 80 DCHECK(thread.IsRunning()); \
78 if (MessageLoop::current() != thread.message_loop()) { \ 81 if (MessageLoop::current() != thread.message_loop()) { \
79 thread.message_loop()->PostTask(FROM_HERE, base::Bind(__VA_ARGS__)); \ 82 thread.message_loop()->PostTask(FROM_HERE, base::Bind(__VA_ARGS__)); \
80 return; \ 83 return; \
81 } \ 84 } \
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
132 fitted_width = std::max(kMinFrameWidth, MakeEven(fitted_width)); 135 fitted_width = std::max(kMinFrameWidth, MakeEven(fitted_width));
133 fitted_height = std::max(kMinFrameHeight, MakeEven(fitted_height)); 136 fitted_height = std::max(kMinFrameHeight, MakeEven(fitted_height));
134 137
135 *fitted_size = gfx::Size(fitted_width, fitted_height); 138 *fitted_size = gfx::Size(fitted_width, fitted_height);
136 } 139 }
137 140
138 // Keeps track of the RenderView to be sourced, and executes copying of the 141 // Keeps track of the RenderView to be sourced, and executes copying of the
139 // backing store on the UI BrowserThread. 142 // backing store on the UI BrowserThread.
140 class BackingStoreCopier : public WebContentsObserver { 143 class BackingStoreCopier : public WebContentsObserver {
141 public: 144 public:
142 // Result status and done callback used with StartCopy().
143 enum Result {
144 OK,
145 TRANSIENT_ERROR,
146 NO_SOURCE,
147 };
148 typedef base::Callback<void(Result result,
149 const SkBitmap& capture,
150 const base::Time& capture_time)> DoneCB;
151
152 BackingStoreCopier(int render_process_id, int render_view_id); 145 BackingStoreCopier(int render_process_id, int render_view_id);
153 146
154 virtual ~BackingStoreCopier(); 147 virtual ~BackingStoreCopier();
155 148
156 // If non-NULL, use the given |override| to access the backing store. 149 // If non-NULL, use the given |override| to access the backing store.
157 // This is used for unit testing. 150 // This is used for unit testing.
158 void SetRenderWidgetHostForTesting(RenderWidgetHost* override); 151 void SetRenderWidgetHostForTesting(RenderWidgetHost* override);
159 152
160 // Starts the copy from the backing store. Must be run on the UI 153 // Starts the copy from the backing store. Must be run on the UI
161 // BrowserThread. |done_cb| is invoked with result status. When successful 154 // BrowserThread. Resulting frame is conveyed back to |consumer|.
162 // (OK), the bitmap of the capture is transferred to the callback along with 155 void StartCopy(const scoped_refptr<CaptureMachine>& consumer,
163 // the timestamp at which the capture was completed. 156 int frame_number,
164 void StartCopy(int frame_number, int desired_width, int desired_height, 157 int desired_width,
165 const DoneCB& done_cb); 158 int desired_height);
166 159
167 // Stops observing an existing WebContents instance, if any. This must be 160 // Stops observing an existing WebContents instance, if any. This must be
168 // called before BackingStoreCopier is destroyed. Must be run on the UI 161 // called before BackingStoreCopier is destroyed. Must be run on the UI
169 // BrowserThread. 162 // BrowserThread.
170 void StopObservingWebContents(); 163 void StopObservingWebContents();
171 164
165 // content::WebContentsObserver implementation.
172 virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE { 166 virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE {
173 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
174 fullscreen_widget_id_ = routing_id; 168 fullscreen_widget_id_ = routing_id;
175 } 169 }
176 170
177 virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE { 171 virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
179 DCHECK_EQ(fullscreen_widget_id_, routing_id); 173 DCHECK_EQ(fullscreen_widget_id_, routing_id);
180 fullscreen_widget_id_ = MSG_ROUTING_NONE; 174 fullscreen_widget_id_ = MSG_ROUTING_NONE;
181 } 175 }
182 176
183 private: 177 private:
184 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE; 178 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
185 179
186 void LookUpAndObserveWebContents(); 180 void LookUpAndObserveWebContents();
187 181
188 void CopyFromBackingStoreComplete(int frame_number, 182 // Response callback for RenderWidgetHost::CopyFromBackingStore.
189 const DoneCB& done_cb, 183 void DidCopyFromBackingStore(const scoped_refptr<CaptureMachine>& consumer,
190 bool success, 184 int frame_number,
191 const SkBitmap& result); 185 const base::Time& start_time,
186 bool success,
187 const SkBitmap& frame);
188
189 // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame().
190 void DidCopyFromBackingStoreToVideoFrame(
191 const scoped_refptr<CaptureMachine>& consumer,
192 int frame_number,
193 const base::Time& start_time,
194 const scoped_refptr<media::VideoFrame>& frame,
195 bool success);
192 196
193 // The "starting point" to find the capture source. 197 // The "starting point" to find the capture source.
194 const int render_process_id_; 198 const int render_process_id_;
195 const int render_view_id_; 199 const int render_view_id_;
196 200
197 // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE 201 // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE
198 // otherwise. 202 // otherwise.
199 int fullscreen_widget_id_; 203 int fullscreen_widget_id_;
200 204
201 // Last known RenderView size. 205 // Last known RenderView size.
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
251 class SynchronizedConsumer { 255 class SynchronizedConsumer {
252 public: 256 public:
253 SynchronizedConsumer(); 257 SynchronizedConsumer();
254 258
255 void SetConsumer(media::VideoCaptureDevice::EventHandler* consumer); 259 void SetConsumer(media::VideoCaptureDevice::EventHandler* consumer);
256 260
257 void OnFrameInfo(const media::VideoCaptureCapability& info); 261 void OnFrameInfo(const media::VideoCaptureCapability& info);
258 void OnError(); 262 void OnError();
259 void OnIncomingCapturedFrame(const uint8* pixels, int size, 263 void OnIncomingCapturedFrame(const uint8* pixels, int size,
260 const base::Time& timestamp); 264 const base::Time& timestamp);
265 void OnIncomingCapturedVideoFrame(
266 const scoped_refptr<media::VideoFrame>& video_frame,
267 const base::Time& timestamp);
261 268
262 private: 269 private:
263 base::Lock consumer_lock_; 270 base::Lock consumer_lock_;
264 media::VideoCaptureDevice::EventHandler* wrapped_consumer_; 271 media::VideoCaptureDevice::EventHandler* wrapped_consumer_;
265 272
266 DISALLOW_COPY_AND_ASSIGN(SynchronizedConsumer); 273 DISALLOW_COPY_AND_ASSIGN(SynchronizedConsumer);
267 }; 274 };
268 275
269 // Delivers rendered video frames to a consumer on a separate thread. Also 276 // Delivers rendered video frames to a consumer on a separate thread. Also
270 // responsible for logging the effective frame rate. 277 // responsible for logging the effective frame rate.
271 class VideoFrameDeliverer { 278 class VideoFrameDeliverer {
272 public: 279 public:
273 explicit VideoFrameDeliverer(SynchronizedConsumer* consumer); 280 explicit VideoFrameDeliverer(SynchronizedConsumer* consumer);
274 281
282 // Deliver a fully rendered ARGB frame, using SkBitmap as a container.
283 // |done_cb| will be invoked after delivery is complete.
275 void Deliver(int frame_number, 284 void Deliver(int frame_number,
276 const SkBitmap& frame_buffer, const base::Time& frame_timestamp, 285 const SkBitmap& frame_buffer,
286 const base::Time& frame_timestamp,
277 const base::Closure& done_cb); 287 const base::Closure& done_cb);
288 // Deliver a fully rendered frame YV12 frame, using VideoFrame as a container.
289 // A refcount is taken on |frame| until delivery is complete.
290 void Deliver(int frame_number,
291 const scoped_refptr<media::VideoFrame>& frame,
292 const base::Time& frame_timestamp);
278 293
279 private: 294 private:
280 void DeliverOnDeliverThread(int frame_number, 295 void DeliverOnDeliverThread(int frame_number,
281 const SkBitmap& frame_buffer, 296 const SkBitmap& frame,
282 const base::Time& frame_timestamp, 297 const base::Time& frame_timestamp,
283 const base::Closure& done_cb); 298 const base::Closure& done_cb);
299 void DeliverVideoFrameOnDeliverThread(
300 int frame_number,
301 const scoped_refptr<media::VideoFrame>& frame,
302 const base::Time& frame_timestamp);
303
304 // Treat |frame_number| as having been delivered, and update the
305 // frame rate statistics accordingly.
306 void ChronicleFrameDelivery(int frame_number);
284 307
285 base::Thread deliver_thread_; 308 base::Thread deliver_thread_;
286 SynchronizedConsumer* const consumer_; 309 SynchronizedConsumer* const consumer_;
287 310
288 // The following keep track of and log the effective frame rate (from the 311 // The following keep track of and log the effective frame rate (from the
289 // deliver stage) whenever verbose logging is turned on. 312 // deliver stage) whenever verbose logging is turned on.
290 base::Time last_frame_rate_log_time_; 313 base::Time last_frame_rate_log_time_;
291 int count_frames_rendered_; 314 int count_frames_rendered_;
292 int last_frame_number_; 315 int last_frame_number_;
293 316
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
344 } else { 367 } else {
345 DVLOG(1) << "WebContents::FromRenderViewHost(" << rvh << ") returned NULL."; 368 DVLOG(1) << "WebContents::FromRenderViewHost(" << rvh << ") returned NULL.";
346 } 369 }
347 } 370 }
348 371
349 void BackingStoreCopier::SetRenderWidgetHostForTesting( 372 void BackingStoreCopier::SetRenderWidgetHostForTesting(
350 RenderWidgetHost* override) { 373 RenderWidgetHost* override) {
351 rwh_for_testing_ = override; 374 rwh_for_testing_ = override;
352 } 375 }
353 376
354 void BackingStoreCopier::StartCopy(int frame_number,
355 int desired_width, int desired_height,
356 const DoneCB& done_cb) {
357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
358
359 TRACE_EVENT_ASYNC_BEGIN1("mirroring", "Capture", this,
360 "frame_number", frame_number);
361
362 RenderWidgetHost* rwh;
363 if (rwh_for_testing_) {
364 rwh = rwh_for_testing_;
365 } else {
366 if (!web_contents()) { // No source yet.
367 LookUpAndObserveWebContents();
368 if (!web_contents()) { // No source ever.
369 done_cb.Run(NO_SOURCE, SkBitmap(), base::Time());
370 return;
371 }
372 }
373
374 if (fullscreen_widget_id_ != MSG_ROUTING_NONE) {
375 RenderProcessHost* process = web_contents()->GetRenderProcessHost();
376 rwh = process ? process->GetRenderWidgetHostByID(fullscreen_widget_id_)
377 : NULL;
378 } else {
379 rwh = web_contents()->GetRenderViewHost();
380 }
381
382 if (!rwh) {
383 // Transient failure state (e.g., a RenderView is being replaced).
384 done_cb.Run(TRANSIENT_ERROR, SkBitmap(), base::Time());
385 return;
386 }
387 }
388
389 gfx::Size fitted_size;
390 if (RenderWidgetHostView* const view = rwh->GetView()) {
391 const gfx::Size& view_size = view->GetViewBounds().size();
392 if (!view_size.IsEmpty()) {
393 CalculateFittedSize(view_size.width(), view_size.height(),
394 desired_width, desired_height,
395 &fitted_size);
396 }
397 if (view_size != last_view_size_) {
398 last_view_size_ = view_size;
399
400 // Measure the number of kilopixels.
401 UMA_HISTOGRAM_COUNTS_10000(
402 "TabCapture.ViewChangeKiloPixels",
403 view_size.width() * view_size.height() / 1024);
404 }
405 }
406
407 rwh->CopyFromBackingStore(
408 gfx::Rect(),
409 fitted_size,
410 base::Bind(&BackingStoreCopier::CopyFromBackingStoreComplete,
411 base::Unretained(this),
412 frame_number, done_cb));
413
414 // TODO(miu): When a tab is not visible to the user, rendering stops. For
415 // mirroring, however, it's important that rendering continues to happen.
416 }
417
418 void BackingStoreCopier::CopyFromBackingStoreComplete(
419 int frame_number,
420 const DoneCB& done_cb,
421 bool success,
422 const SkBitmap& frame) {
423 // Note: No restriction on which thread invokes this method but, currently,
424 // it's always the UI BrowserThread.
425 TRACE_EVENT_ASYNC_END1("mirroring", "Capture", this,
426 "frame_number", frame_number);
427 if (success) {
428 done_cb.Run(OK, frame, base::Time::Now());
429 } else {
430 // Capture can fail due to transient issues, so just skip this frame.
431 DVLOG(1) << "CopyFromBackingStore was not successful; skipping frame.";
432 done_cb.Run(TRANSIENT_ERROR, SkBitmap(), base::Time());
433 }
434 }
435
436 VideoFrameRenderer::VideoFrameRenderer() 377 VideoFrameRenderer::VideoFrameRenderer()
437 : render_thread_("WebContentsVideo_RenderThread") { 378 : render_thread_("WebContentsVideo_RenderThread") {
438 output_[0].in_use = false; 379 output_[0].in_use = false;
439 output_[1].in_use = false; 380 output_[1].in_use = false;
440 render_thread_.Start(); 381 render_thread_.Start();
441 } 382 }
442 383
443 void VideoFrameRenderer::Render(int frame_number, 384 void VideoFrameRenderer::Render(int frame_number,
444 const SkBitmap& capture, 385 const SkBitmap& capture,
445 int frame_width, int frame_height, 386 int frame_width, int frame_height,
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
595 } 536 }
596 537
597 void SynchronizedConsumer::OnIncomingCapturedFrame( 538 void SynchronizedConsumer::OnIncomingCapturedFrame(
598 const uint8* pixels, int size, const base::Time& timestamp) { 539 const uint8* pixels, int size, const base::Time& timestamp) {
599 base::AutoLock guard(consumer_lock_); 540 base::AutoLock guard(consumer_lock_);
600 if (wrapped_consumer_) { 541 if (wrapped_consumer_) {
601 wrapped_consumer_->OnIncomingCapturedFrame(pixels, size, timestamp); 542 wrapped_consumer_->OnIncomingCapturedFrame(pixels, size, timestamp);
602 } 543 }
603 } 544 }
604 545
546 void SynchronizedConsumer::OnIncomingCapturedVideoFrame(
547 const scoped_refptr<media::VideoFrame>& video_frame,
548 const base::Time& timestamp) {
549 base::AutoLock guard(consumer_lock_);
550 if (wrapped_consumer_) {
551 wrapped_consumer_->OnIncomingCapturedVideoFrame(video_frame, timestamp);
552 }
553 }
554
605 VideoFrameDeliverer::VideoFrameDeliverer(SynchronizedConsumer* consumer) 555 VideoFrameDeliverer::VideoFrameDeliverer(SynchronizedConsumer* consumer)
606 : deliver_thread_("WebContentsVideo_DeliverThread"), 556 : deliver_thread_("WebContentsVideo_DeliverThread"),
607 consumer_(consumer), 557 consumer_(consumer),
608 last_frame_number_(0) { 558 last_frame_number_(0) {
609 DCHECK(consumer_); 559 DCHECK(consumer_);
610 deliver_thread_.Start(); 560 deliver_thread_.Start();
611 } 561 }
612 562
613 void VideoFrameDeliverer::Deliver( 563 void VideoFrameDeliverer::Deliver(
614 int frame_number, 564 int frame_number,
615 const SkBitmap& frame_buffer, const base::Time& frame_timestamp, 565 const SkBitmap& frame_buffer, const base::Time& frame_timestamp,
616 const base::Closure& done_cb) { 566 const base::Closure& done_cb) {
617 deliver_thread_.message_loop()->PostTask( 567 deliver_thread_.message_loop()->PostTask(
618 FROM_HERE, 568 FROM_HERE,
619 base::Bind(&VideoFrameDeliverer::DeliverOnDeliverThread, 569 base::Bind(&VideoFrameDeliverer::DeliverOnDeliverThread,
620 base::Unretained(this), 570 base::Unretained(this),
621 frame_number, base::ConstRef(frame_buffer), frame_timestamp, 571 frame_number, base::ConstRef(frame_buffer), frame_timestamp,
622 done_cb)); 572 done_cb));
623 } 573 }
624 574
575 void VideoFrameDeliverer::Deliver(
576 int frame_number,
577 const scoped_refptr<media::VideoFrame>& frame,
578 const base::Time& frame_timestamp) {
579 deliver_thread_.message_loop()->PostTask(
580 FROM_HERE,
581 base::Bind(&VideoFrameDeliverer::DeliverVideoFrameOnDeliverThread,
582 base::Unretained(this), frame_number, frame, frame_timestamp));
583 }
584
625 void VideoFrameDeliverer::DeliverOnDeliverThread( 585 void VideoFrameDeliverer::DeliverOnDeliverThread(
626 int frame_number, 586 int frame_number,
627 const SkBitmap& frame_buffer, const base::Time& frame_timestamp, 587 const SkBitmap& frame_buffer,
588 const base::Time& frame_timestamp,
628 const base::Closure& done_cb) { 589 const base::Closure& done_cb) {
629 DCHECK_EQ(deliver_thread_.message_loop(), MessageLoop::current()); 590 DCHECK_EQ(deliver_thread_.message_loop(), MessageLoop::current());
630 591
631 TRACE_EVENT1("mirroring", "DeliverFrame", "frame_number", frame_number); 592 TRACE_EVENT1("mirroring", "DeliverFrame", "frame_number", frame_number);
632 593
633 // Send the frame to the consumer. 594 // Send the frame to the consumer.
634 // Note: The consumer will do an ARGB-->YUV conversion in this callback, 595 // Note: The consumer will do an ARGB-->YUV conversion in this callback,
635 // blocking the current thread for a bit. 596 // blocking the current thread for a bit.
636 SkAutoLockPixels frame_buffer_locker(frame_buffer); 597 SkAutoLockPixels frame_buffer_locker(frame_buffer);
637 consumer_->OnIncomingCapturedFrame( 598 consumer_->OnIncomingCapturedFrame(
638 static_cast<const uint8*>(frame_buffer.getPixels()), 599 static_cast<const uint8*>(frame_buffer.getPixels()),
639 frame_buffer.getSize(), 600 frame_buffer.getSize(),
640 frame_timestamp); 601 frame_timestamp);
641 602
603 ChronicleFrameDelivery(frame_number);
604
605 // All done.
606 done_cb.Run();
607 }
608
609 void VideoFrameDeliverer::DeliverVideoFrameOnDeliverThread(
610 int frame_number,
611 const scoped_refptr<media::VideoFrame>& frame,
612 const base::Time& frame_timestamp) {
613 DCHECK_EQ(deliver_thread_.message_loop(), MessageLoop::current());
614
615 TRACE_EVENT1("mirroring", "DeliverFrame", "frame_number", frame_number);
616
617 // Send the frame to the consumer.
618 consumer_->OnIncomingCapturedVideoFrame(frame, frame_timestamp);
619
620 ChronicleFrameDelivery(frame_number);
621 }
622
623 void VideoFrameDeliverer::ChronicleFrameDelivery(int frame_number) {
642 // Log frame rate, if verbose logging is turned on. 624 // Log frame rate, if verbose logging is turned on.
643 static const base::TimeDelta kFrameRateLogInterval = 625 static const base::TimeDelta kFrameRateLogInterval =
644 base::TimeDelta::FromSeconds(10); 626 base::TimeDelta::FromSeconds(10);
645 const base::Time& now = base::Time::Now(); 627 const base::Time& now = base::Time::Now();
646 if (last_frame_rate_log_time_.is_null()) { 628 if (last_frame_rate_log_time_.is_null()) {
647 last_frame_rate_log_time_ = now; 629 last_frame_rate_log_time_ = now;
648 count_frames_rendered_ = 0; 630 count_frames_rendered_ = 0;
649 last_frame_number_ = frame_number; 631 last_frame_number_ = frame_number;
650 } else { 632 } else {
651 ++count_frames_rendered_; 633 ++count_frames_rendered_;
(...skipping 10 matching lines...) Expand all
662 UMA_HISTOGRAM_COUNTS( 644 UMA_HISTOGRAM_COUNTS(
663 "TabCapture.FrameRate", 645 "TabCapture.FrameRate",
664 static_cast<int>(measured_fps)); 646 static_cast<int>(measured_fps));
665 VLOG(1) << "Current measured frame rate for CaptureMachine@" << this 647 VLOG(1) << "Current measured frame rate for CaptureMachine@" << this
666 << " is " << measured_fps << " FPS."; 648 << " is " << measured_fps << " FPS.";
667 last_frame_rate_log_time_ = now; 649 last_frame_rate_log_time_ = now;
668 count_frames_rendered_ = 0; 650 count_frames_rendered_ = 0;
669 last_frame_number_ = frame_number; 651 last_frame_number_ = frame_number;
670 } 652 }
671 } 653 }
672
673 // All done.
674 done_cb.Run();
675 } 654 }
676 655
677 } // namespace 656 } // namespace
678 657
679 // The "meat" of the video capture implementation, which is a ref-counted class. 658 // The "meat" of the video capture implementation, which is a ref-counted class.
680 // Separating this from the "shell class" WebContentsVideoCaptureDevice allows 659 // Separating this from the "shell class" WebContentsVideoCaptureDevice allows
681 // safe destruction without needing to block any threads (e.g., the IO 660 // safe destruction without needing to block any threads (e.g., the IO
682 // BrowserThread). 661 // BrowserThread).
683 // 662 //
684 // CaptureMachine manages a simple state machine and the pipeline (see notes at 663 // CaptureMachine manages a simple state machine and the pipeline (see notes at
685 // top of this file). It times the start of successive captures and 664 // top of this file). It times the start of successive captures and
686 // facilitates the processing of each through the stages of the pipeline. 665 // facilitates the processing of each through the stages of the pipeline.
687 class CaptureMachine 666 class CaptureMachine
688 : public base::RefCountedThreadSafe<CaptureMachine, CaptureMachine> { 667 : public base::RefCountedThreadSafe<CaptureMachine, CaptureMachine> {
689 public: 668 public:
669 enum SnapshotError {
670 NO_SOURCE,
671 TRANSIENT_ERROR
672 };
673
690 CaptureMachine(int render_process_id, int render_view_id); 674 CaptureMachine(int render_process_id, int render_view_id);
691 675
692 // Sets the capture source to the given |override| for unit testing. 676 // Sets the capture source to the given |override| for unit testing.
693 // Also, |destroy_cb| will be invoked after CaptureMachine is fully destroyed 677 // Also, |destroy_cb| will be invoked after CaptureMachine is fully destroyed
694 // (to synchronize tear-down). 678 // (to synchronize tear-down).
695 void InitializeForTesting(RenderWidgetHost* override, 679 void InitializeForTesting(RenderWidgetHost* override,
696 const base::Closure& destroy_cb); 680 const base::Closure& destroy_cb);
697 681
698 // Synchronously sets/unsets the consumer. Pass |consumer| as NULL to remove 682 // Synchronously sets/unsets the consumer. Pass |consumer| as NULL to remove
699 // the reference to the consumer; then, once this method returns, 683 // the reference to the consumer; then, once this method returns,
700 // CaptureMachine will no longer invoke callbacks on the old consumer from any 684 // CaptureMachine will no longer invoke callbacks on the old consumer from any
701 // thread. 685 // thread.
702 void SetConsumer(media::VideoCaptureDevice::EventHandler* consumer); 686 void SetConsumer(media::VideoCaptureDevice::EventHandler* consumer);
703 687
704 // Asynchronous requests to change CaptureMachine state. 688 // Asynchronous requests to change CaptureMachine state.
705 void Allocate(int width, int height, int frame_rate); 689 void Allocate(int width, int height, int frame_rate);
706 void Start(); 690 void Start();
707 void Stop(); 691 void Stop();
708 void DeAllocate(); 692 void DeAllocate();
709 693
694 // Snapshot result events.
695 void OnSnapshotComplete(int frame_number,
696 const base::Time& start_time,
697 const base::TimeDelta& duration,
698 const SkBitmap& frame);
699 void OnSnapshotComplete(int frame_number,
700 const base::Time& start_time,
701 const base::TimeDelta& duration,
702 const scoped_refptr<media::VideoFrame>& frame);
703 void OnSnapshotFailed(SnapshotError error,
704 int frame_number);
705
710 private: 706 private:
711 friend class base::RefCountedThreadSafe<CaptureMachine, CaptureMachine>; 707 friend class base::RefCountedThreadSafe<CaptureMachine, CaptureMachine>;
712 708
713 // Flag indicating current state. 709 // Flag indicating current state.
714 enum State { 710 enum State {
715 kIdle, 711 kIdle,
716 kAllocated, 712 kAllocated,
717 kCapturing, 713 kCapturing,
718 kError, 714 kError,
719 kDestroyed 715 kDestroyed
720 }; 716 };
721 717
722 virtual ~CaptureMachine(); 718 virtual ~CaptureMachine();
723 719
724 void TransitionStateTo(State next_state); 720 void TransitionStateTo(State next_state);
725 721
726 // Stops capturing and notifies consumer_ of an error state. 722 // Stops capturing and notifies consumer_ of an error state.
727 void Error(); 723 void Error();
728 724
729 // Schedules the next frame capture off of the system clock, skipping frames 725 // Schedules the next frame capture off of the system clock, skipping frames
730 // to catch-up if necessary. 726 // to catch-up if necessary.
731 void ScheduleNextFrameCapture(); 727 void ScheduleNextFrameCapture();
732 728
733 // The glue between the pipeline stages. 729 // The glue between the pipeline stages.
734 void StartSnapshot(); 730 void StartSnapshot();
735 void SnapshotComplete(int frame_number, 731 bool FinishSnapshot();
736 const base::Time& start_time, 732 void SnapshotCompleteBitmap(int frame_number,
737 BackingStoreCopier::Result result, 733 const base::Time& start_time,
738 const SkBitmap& capture, 734 const base::TimeDelta& duration,
739 const base::Time& capture_time); 735 const SkBitmap& frame);
736 void SnapshotCompleteVideoFrame(
737 int frame_number,
738 const base::Time& start_time,
739 const base::TimeDelta& duration,
740 const scoped_refptr<media::VideoFrame>& frame);
741 void SnapshotFailed(SnapshotError error, int frame_number);
742
740 void RenderComplete(int frame_number, 743 void RenderComplete(int frame_number,
741 const base::Time& capture_time, 744 const base::Time& capture_time,
742 const SkBitmap* frame_buffer); 745 const SkBitmap* frame_buffer);
743 void DeliverComplete(const SkBitmap* frame_buffer); 746 void DeliverComplete(const SkBitmap* frame_buffer);
744 747
745 void DoShutdownTasksOnUIThread(); 748 void DoShutdownTasksOnUIThread();
746 749
747 // Specialized RefCounted traits for CaptureMachine, so that operator delete 750 // Specialized RefCounted traits for CaptureMachine, so that operator delete
748 // is called from an "outside" thread. See comments for "traits" in 751 // is called from an "outside" thread. See comments for "traits" in
749 // base/memory/ref_counted.h. 752 // base/memory/ref_counted.h.
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
819 if (width < kMinFrameWidth || height < kMinFrameHeight) { 822 if (width < kMinFrameWidth || height < kMinFrameHeight) {
820 DVLOG(1) << "invalid width (" << width << ") and/or height (" 823 DVLOG(1) << "invalid width (" << width << ") and/or height ("
821 << height << ")"; 824 << height << ")";
822 Error(); 825 Error();
823 return; 826 return;
824 } 827 }
825 828
826 settings_.width = width; 829 settings_.width = width;
827 settings_.height = height; 830 settings_.height = height;
828 settings_.frame_rate = frame_rate; 831 settings_.frame_rate = frame_rate;
832 // Sets the color format used by OnIncomingCapturedFrame().
833 // Does not apply to OnIncomingCapturedVideoFrame().
829 settings_.color = media::VideoCaptureCapability::kARGB; 834 settings_.color = media::VideoCaptureCapability::kARGB;
830 settings_.expected_capture_delay = 0; 835 settings_.expected_capture_delay = 0;
831 settings_.interlaced = false; 836 settings_.interlaced = false;
832 837
833 capture_period_ = base::TimeDelta::FromMicroseconds( 838 capture_period_ = base::TimeDelta::FromMicroseconds(
834 1000000.0 / settings_.frame_rate + 0.5); 839 1000000.0 / settings_.frame_rate + 0.5);
835 840
836 consumer_.OnFrameInfo(settings_); 841 consumer_.OnFrameInfo(settings_);
837 842
838 TransitionStateTo(kAllocated); 843 TransitionStateTo(kAllocated);
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
967 void CaptureMachine::StartSnapshot() { 972 void CaptureMachine::StartSnapshot() {
968 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); 973 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current());
969 974
970 if (state_ != kCapturing) { 975 if (state_ != kCapturing) {
971 return; 976 return;
972 } 977 }
973 978
974 if (!is_snapshotting_) { 979 if (!is_snapshotting_) {
975 is_snapshotting_ = true; 980 is_snapshotting_ = true;
976 981
977 const BackingStoreCopier::DoneCB& done_cb = 982 BrowserThread::PostTask(
978 media::BindToLoop(manager_thread_.message_loop_proxy(), 983 BrowserThread::UI, FROM_HERE,
979 base::Bind(&CaptureMachine::SnapshotComplete, this, 984 base::Bind(&BackingStoreCopier::StartCopy, base::Unretained(&copier_),
980 frame_number_, base::Time::Now())); 985 make_scoped_refptr(this), frame_number_, settings_.width,
981 const base::Closure& start_cb = 986 settings_.height));
982 base::Bind(&BackingStoreCopier::StartCopy,
983 base::Unretained(&copier_),
984 frame_number_, settings_.width, settings_.height, done_cb);
985 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, start_cb);
986 } 987 }
987 988
988 ScheduleNextFrameCapture(); 989 ScheduleNextFrameCapture();
989 } 990 }
990 991
991 void CaptureMachine::SnapshotComplete(int frame_number, 992 bool CaptureMachine::FinishSnapshot() {
992 const base::Time& start_time,
993 BackingStoreCopier::Result result,
994 const SkBitmap& capture,
995 const base::Time& capture_time) {
996 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); 993 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current());
997 994
998 DCHECK(is_snapshotting_); 995 DCHECK(is_snapshotting_);
999 is_snapshotting_ = false; 996 is_snapshotting_ = false;
1000 997
1001 if (state_ != kCapturing) { 998 return state_ == kCapturing;
999 }
1000
1001 void CaptureMachine::OnSnapshotComplete(int frame_number,
1002 const base::Time& start_time,
1003 const base::TimeDelta& duration,
1004 const SkBitmap& frame) {
1005 manager_thread_.message_loop()->PostTask(
1006 FROM_HERE,
1007 base::Bind(&CaptureMachine::SnapshotCompleteBitmap, this,
1008 frame_number, start_time, duration, frame));
1009 }
1010
1011 void CaptureMachine::SnapshotCompleteBitmap(int frame_number,
1012 const base::Time& start_time,
1013 const base::TimeDelta& duration,
1014 const SkBitmap& capture) {
1015 if (!FinishSnapshot())
1002 return; 1016 return;
1003 }
1004 1017
1005 switch (result) { 1018 UMA_HISTOGRAM_TIMES("TabCapture.SnapshotTime", duration);
1006 case BackingStoreCopier::OK: 1019 if (num_renders_pending_ <= 1) {
1007 UMA_HISTOGRAM_TIMES("TabCapture.SnapshotTime", 1020 ++num_renders_pending_;
1008 base::Time::Now() - start_time); 1021 renderer_.Render(
1009 if (num_renders_pending_ <= 1) { 1022 frame_number,
1010 ++num_renders_pending_; 1023 capture,
1011 DCHECK(!capture_time.is_null()); 1024 settings_.width, settings_.height,
1012 renderer_.Render( 1025 media::BindToLoop(manager_thread_.message_loop_proxy(),
1013 frame_number, 1026 base::Bind(&CaptureMachine::RenderComplete, this,
1014 capture, 1027 frame_number, start_time + duration)));
1015 settings_.width, settings_.height,
1016 media::BindToLoop(manager_thread_.message_loop_proxy(),
1017 base::Bind(&CaptureMachine::RenderComplete, this,
1018 frame_number, capture_time)));
1019 }
1020 break;
1021
1022 case BackingStoreCopier::TRANSIENT_ERROR:
1023 // Skip this frame.
1024 break;
1025
1026 case BackingStoreCopier::NO_SOURCE:
1027 DVLOG(1) << "no capture source";
1028 Error();
1029 break;
1030 } 1028 }
1031 } 1029 }
1032 1030
1031 void CaptureMachine::OnSnapshotComplete(
1032 int frame_number,
1033 const base::Time& start_time,
1034 const base::TimeDelta& duration,
1035 const scoped_refptr<media::VideoFrame>& frame) {
1036 manager_thread_.message_loop()->PostTask(
1037 FROM_HERE,
1038 base::Bind(&CaptureMachine::SnapshotCompleteVideoFrame, this,
1039 frame_number, start_time, duration, frame));
1040 }
1041
1042 void CaptureMachine::SnapshotCompleteVideoFrame(
1043 int frame_number,
1044 const base::Time& start_time,
1045 const base::TimeDelta& duration,
1046 const scoped_refptr<media::VideoFrame>& frame) {
1047 if (!FinishSnapshot())
1048 return;
1049
1050 UMA_HISTOGRAM_TIMES("TabCapture.SnapshotTime", duration);
1051
1052 deliverer_.Deliver(frame_number, frame, start_time + duration);
1053 }
1054
1055 void CaptureMachine::OnSnapshotFailed(CaptureMachine::SnapshotError error,
1056 int frame_number) {
1057 manager_thread_.message_loop()->PostTask(FROM_HERE,
1058 base::Bind(&CaptureMachine::SnapshotFailed, this, error, frame_number));
1059 }
1060
1061 void CaptureMachine::SnapshotFailed(CaptureMachine::SnapshotError error,
1062 int frame_number) {
1063 if (!FinishSnapshot())
1064 return;
1065
1066 if (error == NO_SOURCE)
1067 Error();
1068 }
1069
1033 void CaptureMachine::RenderComplete(int frame_number, 1070 void CaptureMachine::RenderComplete(int frame_number,
1034 const base::Time& capture_time, 1071 const base::Time& capture_time,
1035 const SkBitmap* frame_buffer) { 1072 const SkBitmap* frame_buffer) {
1036 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); 1073 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current());
1037 1074
1038 --num_renders_pending_; 1075 --num_renders_pending_;
1039 DCHECK_LE(0, num_renders_pending_); 1076 DCHECK_LE(0, num_renders_pending_);
1040 1077
1041 if (state_ != kCapturing || !frame_buffer) { 1078 if (state_ != kCapturing || !frame_buffer) {
1042 return; 1079 return;
1043 } 1080 }
1044 1081
1045 DCHECK(!capture_time.is_null()); 1082 DCHECK(!capture_time.is_null());
1046 DCHECK(frame_buffer); 1083 DCHECK(frame_buffer);
1047 deliverer_.Deliver( 1084 deliverer_.Deliver(
1048 frame_number, *frame_buffer, capture_time, 1085 frame_number, *frame_buffer, capture_time,
1049 base::Bind(&CaptureMachine::DeliverComplete, this, frame_buffer)); 1086 base::Bind(&CaptureMachine::DeliverComplete, this, frame_buffer));
1050 } 1087 }
1051 1088
1052 void CaptureMachine::DeliverComplete(const SkBitmap* frame_buffer) { 1089 void CaptureMachine::DeliverComplete(const SkBitmap* frame_buffer) {
1053 renderer_.Release(frame_buffer); 1090 renderer_.Release(frame_buffer);
1054 } 1091 }
1055 1092
1056 void CaptureMachine::DoShutdownTasksOnUIThread() { 1093 void CaptureMachine::DoShutdownTasksOnUIThread() {
1057 copier_.StopObservingWebContents(); 1094 copier_.StopObservingWebContents();
1058 } 1095 }
1059 1096
1097 void BackingStoreCopier::StartCopy(
1098 const scoped_refptr<CaptureMachine>& consumer,
1099 int frame_number,
1100 int desired_width,
1101 int desired_height) {
1102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1103
1104 RenderWidgetHost* rwh;
1105 if (rwh_for_testing_) {
1106 rwh = rwh_for_testing_;
1107 } else {
1108 if (!web_contents()) { // No source yet.
1109 LookUpAndObserveWebContents();
1110 if (!web_contents()) { // No source ever.
1111 consumer->OnSnapshotFailed(CaptureMachine::NO_SOURCE, frame_number);
1112 return;
1113 }
1114 }
1115
1116 if (fullscreen_widget_id_ != MSG_ROUTING_NONE) {
1117 RenderProcessHost* process = web_contents()->GetRenderProcessHost();
1118 rwh = process ? process->GetRenderWidgetHostByID(fullscreen_widget_id_)
1119 : NULL;
1120 } else {
1121 rwh = web_contents()->GetRenderViewHost();
1122 }
1123
1124 if (!rwh) {
1125 // Transient failure state (e.g., a RenderView is being replaced).
1126 consumer->OnSnapshotFailed(CaptureMachine::TRANSIENT_ERROR, frame_number);
1127 return;
1128 }
1129 }
1130
1131 RenderWidgetHostViewPort* view =
1132 RenderWidgetHostViewPort::FromRWHV(rwh->GetView());
1133
1134 gfx::Size fitted_size;
1135 if (view) {
1136 gfx::Size view_size = view->GetViewBounds().size();
1137 if (!view_size.IsEmpty()) {
1138 CalculateFittedSize(view_size.width(), view_size.height(),
1139 desired_width, desired_height,
1140 &fitted_size);
1141 }
1142 if (view_size != last_view_size_) {
1143 last_view_size_ = view_size;
1144
1145 // Measure the number of kilopixels.
1146 UMA_HISTOGRAM_COUNTS_10000(
1147 "TabCapture.ViewChangeKiloPixels",
1148 view_size.width() * view_size.height() / 1024);
1149 }
1150 }
1151
1152 TRACE_EVENT_ASYNC_BEGIN1("mirroring", "Capture", this,
1153 "frame_number", frame_number);
1154
1155 if (view && view->CanCopyToVideoFrame()) {
1156 gfx::Size view_size = view->GetViewBounds().size();
1157 gfx::Size dst_size(desired_width, desired_height);
1158 scoped_refptr<media::VideoFrame> video_frame(
1159 media::VideoFrame::CreateFrame(
1160 media::VideoFrame::YV12,
1161 dst_size,
1162 gfx::Rect(dst_size),
1163 dst_size,
1164 base::TimeDelta()));
1165
1166 view->CopyFromCompositingSurfaceToVideoFrame(
1167 gfx::Rect(view_size),
1168 video_frame,
1169 base::Bind(&BackingStoreCopier::DidCopyFromBackingStoreToVideoFrame,
1170 base::Unretained(this), consumer, frame_number,
1171 base::Time::Now(), video_frame));
1172 } else {
1173 rwh->CopyFromBackingStore(
1174 gfx::Rect(),
1175 fitted_size,
1176 base::Bind(&BackingStoreCopier::DidCopyFromBackingStore,
1177 base::Unretained(this), consumer, frame_number,
1178 base::Time::Now()));
1179 }
1180 // TODO(miu): When a tab is not visible to the user, rendering stops. For
1181 // mirroring, however, it's important that rendering continues to happen.
1182 }
1183
1184 void BackingStoreCopier::DidCopyFromBackingStore(
1185 const scoped_refptr<CaptureMachine>& consumer,
1186 int frame_number,
1187 const base::Time& start_time,
1188 bool success,
1189 const SkBitmap& frame) {
1190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1191 // Note: No restriction on which thread invokes this method but, currently,
1192 // it's always the UI BrowserThread.
1193 TRACE_EVENT_ASYNC_END1("mirroring", "Capture", this,
1194 "frame_number", frame_number);
1195
1196 if (success) {
1197 consumer->OnSnapshotComplete(
1198 frame_number, start_time, base::Time::Now() - start_time, frame);
1199 } else {
1200 // Capture can fail due to transient issues, so just skip this frame.
1201 DVLOG(1) << "CopyFromBackingStore was not successful; skipping frame.";
1202 consumer->OnSnapshotFailed(CaptureMachine::TRANSIENT_ERROR, frame_number);
1203 }
1204 }
1205
1206 void BackingStoreCopier::DidCopyFromBackingStoreToVideoFrame(
1207 const scoped_refptr<CaptureMachine>& consumer,
1208 int frame_number,
1209 const base::Time& start_time,
1210 const scoped_refptr<media::VideoFrame>& frame,
1211 bool success) {
1212 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1213 // Note: No restriction on which thread invokes this method but, currently,
1214 // it's always the UI BrowserThread.
1215 TRACE_EVENT_ASYNC_END1("mirroring", "Capture", this,
1216 "frame_number", frame_number);
1217
1218 if (success) {
1219 consumer->OnSnapshotComplete(
1220 frame_number, start_time, base::Time::Now() - start_time, frame);
1221 } else {
1222 // Capture can fail due to transient issues, so just skip this frame.
1223 DVLOG(1) << "CopyFromBackingStoreToVideoFrame failure; skipping frame.";
1224 consumer->OnSnapshotFailed(
1225 CaptureMachine::TRANSIENT_ERROR, frame_number);
1226 }
1227 }
1228
1060 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( 1229 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice(
1061 const media::VideoCaptureDevice::Name& name, 1230 const media::VideoCaptureDevice::Name& name,
1062 int render_process_id, int render_view_id) 1231 int render_process_id, int render_view_id)
1063 : device_name_(name), 1232 : device_name_(name),
1064 capturer_(new CaptureMachine(render_process_id, render_view_id)) {} 1233 capturer_(new CaptureMachine(render_process_id, render_view_id)) {}
1065 1234
1066 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( 1235 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice(
1067 RenderWidgetHost* test_source, const base::Closure& destroy_cb) 1236 RenderWidgetHost* test_source, const base::Closure& destroy_cb)
1068 : capturer_(new CaptureMachine(-1, -1)) { 1237 : capturer_(new CaptureMachine(-1, -1)) {
1069 device_name_.device_name = "WebContentsForTesting"; 1238 device_name_.device_name = "WebContentsForTesting";
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
1125 capturer_->SetConsumer(NULL); 1294 capturer_->SetConsumer(NULL);
1126 capturer_->DeAllocate(); 1295 capturer_->DeAllocate();
1127 } 1296 }
1128 1297
1129 const media::VideoCaptureDevice::Name& 1298 const media::VideoCaptureDevice::Name&
1130 WebContentsVideoCaptureDevice::device_name() { 1299 WebContentsVideoCaptureDevice::device_name() {
1131 return device_name_; 1300 return device_name_;
1132 } 1301 }
1133 1302
1134 } // namespace content 1303 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698