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