| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |