Home | History | Annotate | Download | only in media
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/renderer_host/media/desktop_capture_device.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/location.h"
      9 #include "base/logging.h"
     10 #include "base/sequenced_task_runner.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "base/synchronization/lock.h"
     13 #include "base/threading/sequenced_worker_pool.h"
     14 #include "content/public/browser/browser_thread.h"
     15 #include "content/public/common/desktop_media_id.h"
     16 #include "media/base/video_util.h"
     17 #include "third_party/libyuv/include/libyuv/scale_argb.h"
     18 #include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
     19 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
     20 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
     21 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
     22 
     23 namespace content {
     24 
     25 namespace {
     26 const int kBytesPerPixel = 4;
     27 }  // namespace
     28 
     29 class DesktopCaptureDevice::Core
     30     : public base::RefCountedThreadSafe<Core>,
     31       public webrtc::DesktopCapturer::Callback {
     32  public:
     33   Core(scoped_refptr<base::SequencedTaskRunner> task_runner,
     34        scoped_ptr<webrtc::DesktopCapturer> capturer);
     35 
     36   // Implementation of VideoCaptureDevice methods.
     37   void Allocate(int width, int height,
     38                 int frame_rate,
     39                 EventHandler* event_handler);
     40   void Start();
     41   void Stop();
     42   void DeAllocate();
     43 
     44  private:
     45   friend class base::RefCountedThreadSafe<Core>;
     46   virtual ~Core();
     47 
     48   // webrtc::DesktopCapturer::Callback interface
     49   virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE;
     50   virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE;
     51 
     52   // Helper methods that run on the |task_runner_|. Posted from the
     53   // corresponding public methods.
     54   void DoAllocate(int width, int height, int frame_rate);
     55   void DoStart();
     56   void DoStop();
     57   void DoDeAllocate();
     58 
     59   // Helper to schedule capture tasks.
     60   void ScheduleCaptureTimer();
     61 
     62   // Method that is scheduled on |task_runner_| to be called on regular interval
     63   // to capture a frame.
     64   void OnCaptureTimer();
     65 
     66   // Captures a single frame.
     67   void DoCapture();
     68 
     69   // Task runner used for capturing operations.
     70   scoped_refptr<base::SequencedTaskRunner> task_runner_;
     71 
     72   // The underlying DesktopCapturer instance used to capture frames.
     73   scoped_ptr<webrtc::DesktopCapturer> desktop_capturer_;
     74 
     75   // |event_handler_lock_| must be locked whenever |event_handler_| is used.
     76   // It's necessary because DeAllocate() needs to reset it on the calling thread
     77   // to ensure that the event handler is not called once DeAllocate() returns.
     78   base::Lock event_handler_lock_;
     79   EventHandler* event_handler_;
     80 
     81   // Requested size specified to Allocate().
     82   webrtc::DesktopSize requested_size_;
     83 
     84   // Frame rate specified to Allocate().
     85   int frame_rate_;
     86 
     87   // Empty until the first frame has been captured, and the output dimensions
     88   // chosen based on the capture frame's size, and any caller-supplied
     89   // size constraints.
     90   webrtc::DesktopSize output_size_;
     91 
     92   // Size of the most recently received frame.
     93   webrtc::DesktopSize previous_frame_size_;
     94 
     95   // DesktopFrame into which captured frames are scaled, if the source size does
     96   // not match |output_size_|. If the source and output have the same dimensions
     97   // then this is NULL.
     98   scoped_ptr<webrtc::DesktopFrame> scaled_frame_;
     99 
    100   // True between DoStart() and DoStop(). Can't just check |event_handler_|
    101   // because |event_handler_| is used on the caller thread.
    102   bool started_;
    103 
    104   // True when we have delayed OnCaptureTimer() task posted on
    105   // |task_runner_|.
    106   bool capture_task_posted_;
    107 
    108   // True when waiting for |desktop_capturer_| to capture current frame.
    109   bool capture_in_progress_;
    110 
    111   DISALLOW_COPY_AND_ASSIGN(Core);
    112 };
    113 
    114 DesktopCaptureDevice::Core::Core(
    115     scoped_refptr<base::SequencedTaskRunner> task_runner,
    116     scoped_ptr<webrtc::DesktopCapturer> capturer)
    117     : task_runner_(task_runner),
    118       desktop_capturer_(capturer.Pass()),
    119       event_handler_(NULL),
    120       started_(false),
    121       capture_task_posted_(false),
    122       capture_in_progress_(false) {
    123 }
    124 
    125 DesktopCaptureDevice::Core::~Core() {
    126 }
    127 
    128 void DesktopCaptureDevice::Core::Allocate(int width, int height,
    129                                          int frame_rate,
    130                                          EventHandler* event_handler) {
    131   DCHECK_GT(width, 0);
    132   DCHECK_GT(height, 0);
    133   DCHECK_GT(frame_rate, 0);
    134 
    135   {
    136     base::AutoLock auto_lock(event_handler_lock_);
    137     event_handler_ = event_handler;
    138   }
    139 
    140   task_runner_->PostTask(
    141       FROM_HERE,
    142       base::Bind(&Core::DoAllocate, this, width, height, frame_rate));
    143 }
    144 
    145 void DesktopCaptureDevice::Core::Start() {
    146   task_runner_->PostTask(
    147       FROM_HERE, base::Bind(&Core::DoStart, this));
    148 }
    149 
    150 void DesktopCaptureDevice::Core::Stop() {
    151   task_runner_->PostTask(
    152       FROM_HERE, base::Bind(&Core::DoStop, this));
    153 }
    154 
    155 void DesktopCaptureDevice::Core::DeAllocate() {
    156   {
    157     base::AutoLock auto_lock(event_handler_lock_);
    158     event_handler_ = NULL;
    159   }
    160   task_runner_->PostTask(FROM_HERE, base::Bind(&Core::DoDeAllocate, this));
    161 }
    162 
    163 webrtc::SharedMemory*
    164 DesktopCaptureDevice::Core::CreateSharedMemory(size_t size) {
    165   return NULL;
    166 }
    167 
    168 void DesktopCaptureDevice::Core::OnCaptureCompleted(
    169     webrtc::DesktopFrame* frame) {
    170   DCHECK(task_runner_->RunsTasksOnCurrentThread());
    171   DCHECK(capture_in_progress_);
    172 
    173   capture_in_progress_ = false;
    174 
    175   if (!frame) {
    176     LOG(ERROR) << "Failed to capture a frame.";
    177     event_handler_->OnError();
    178     return;
    179   }
    180 
    181   scoped_ptr<webrtc::DesktopFrame> owned_frame(frame);
    182 
    183   // If an |output_size_| hasn't yet been chosen then choose one, based upon
    184   // the source frame size and the requested size supplied to Allocate().
    185   if (output_size_.is_empty()) {
    186     // Treat the requested size as upper bounds on width & height.
    187     // TODO(wez): Constraints should be passed from getUserMedia to Allocate.
    188     output_size_.set(
    189         std::min(frame->size().width(), requested_size_.width()),
    190         std::min(frame->size().height(), requested_size_.height()));
    191 
    192     // Inform the EventHandler of the output dimensions, format and frame rate.
    193     media::VideoCaptureCapability caps;
    194     caps.width = output_size_.width();
    195     caps.height = output_size_.height();
    196     caps.frame_rate = frame_rate_;
    197     caps.color = media::VideoCaptureCapability::kARGB;
    198     caps.expected_capture_delay =
    199         base::Time::kMillisecondsPerSecond / frame_rate_;
    200     caps.interlaced = false;
    201 
    202     base::AutoLock auto_lock(event_handler_lock_);
    203     if (event_handler_)
    204       event_handler_->OnFrameInfo(caps);
    205   }
    206 
    207   if (!started_)
    208     return;
    209 
    210   size_t output_bytes = output_size_.width() * output_size_.height() *
    211       webrtc::DesktopFrame::kBytesPerPixel;
    212 
    213   if (frame->size().equals(output_size_)) {
    214     // If the captured frame matches the output size, we can return the pixel
    215     // data directly, without scaling.
    216     scaled_frame_.reset();
    217 
    218     base::AutoLock auto_lock(event_handler_lock_);
    219     if (event_handler_) {
    220       event_handler_->OnIncomingCapturedFrame(
    221           frame->data(), output_bytes, base::Time::Now(), 0, false, false);
    222     }
    223     return;
    224   }
    225 
    226   // If the output size differs from the frame size (e.g. the source has changed
    227   // from its original dimensions, or the caller specified size constraints)
    228   // then we need to scale the image.
    229   if (!scaled_frame_)
    230     scaled_frame_.reset(new webrtc::BasicDesktopFrame(output_size_));
    231   DCHECK(scaled_frame_->size().equals(output_size_));
    232 
    233   // If the source frame size changed then clear |scaled_frame_|'s pixels.
    234   if (!previous_frame_size_.equals(frame->size())) {
    235     previous_frame_size_ = frame->size();
    236     memset(scaled_frame_->data(), 0, output_bytes);
    237   }
    238 
    239   // Determine the output size preserving aspect, and center in output buffer.
    240   gfx::Rect scaled_rect = media::ComputeLetterboxRegion(
    241       gfx::Rect(0, 0, output_size_.width(), output_size_.height()),
    242       gfx::Size(frame->size().width(), frame->size().height()));
    243   uint8* scaled_data = scaled_frame_->data() +
    244       scaled_frame_->stride() * scaled_rect.y() +
    245       webrtc::DesktopFrame::kBytesPerPixel * scaled_rect.x();
    246 
    247   // TODO(wez): Optimize this to scale only changed portions of the output,
    248   // using ARGBScaleClip().
    249   libyuv::ARGBScale(frame->data(), frame->stride(),
    250                     frame->size().width(), frame->size().height(),
    251                     scaled_data, scaled_frame_->stride(),
    252                     scaled_rect.width(), scaled_rect.height(),
    253                     libyuv::kFilterBilinear);
    254 
    255   base::AutoLock auto_lock(event_handler_lock_);
    256   if (event_handler_) {
    257     event_handler_->OnIncomingCapturedFrame(
    258         scaled_frame_->data(), output_bytes,
    259         base::Time::Now(), 0, false, false);
    260   }
    261 }
    262 
    263 void DesktopCaptureDevice::Core::DoAllocate(int width,
    264                                             int height,
    265                                             int frame_rate) {
    266   DCHECK(task_runner_->RunsTasksOnCurrentThread());
    267   DCHECK(desktop_capturer_);
    268 
    269   requested_size_.set(width, height);
    270   output_size_.set(0, 0);
    271   frame_rate_ = frame_rate;
    272 
    273   desktop_capturer_->Start(this);
    274 
    275   // Capture first frame, so that we can call OnFrameInfo() callback.
    276   DoCapture();
    277 }
    278 
    279 void DesktopCaptureDevice::Core::DoStart() {
    280   DCHECK(task_runner_->RunsTasksOnCurrentThread());
    281   started_ = true;
    282   if (!capture_task_posted_) {
    283     ScheduleCaptureTimer();
    284     DoCapture();
    285   }
    286 }
    287 
    288 void DesktopCaptureDevice::Core::DoStop() {
    289   DCHECK(task_runner_->RunsTasksOnCurrentThread());
    290   started_ = false;
    291   scaled_frame_.reset();
    292 }
    293 
    294 void DesktopCaptureDevice::Core::DoDeAllocate() {
    295   DCHECK(task_runner_->RunsTasksOnCurrentThread());
    296   DoStop();
    297   desktop_capturer_.reset();
    298   output_size_.set(0, 0);
    299 }
    300 
    301 void DesktopCaptureDevice::Core::ScheduleCaptureTimer() {
    302   DCHECK(!capture_task_posted_);
    303   capture_task_posted_ = true;
    304   task_runner_->PostDelayedTask(
    305       FROM_HERE, base::Bind(&Core::OnCaptureTimer, this),
    306       base::TimeDelta::FromSeconds(1) / frame_rate_);
    307 }
    308 
    309 void DesktopCaptureDevice::Core::OnCaptureTimer() {
    310   DCHECK(capture_task_posted_);
    311   capture_task_posted_ = false;
    312 
    313   if (!started_)
    314     return;
    315 
    316   // Schedule a task for the next frame.
    317   ScheduleCaptureTimer();
    318   DoCapture();
    319 }
    320 
    321 void DesktopCaptureDevice::Core::DoCapture() {
    322   DCHECK(task_runner_->RunsTasksOnCurrentThread());
    323   DCHECK(!capture_in_progress_);
    324 
    325   capture_in_progress_ = true;
    326   desktop_capturer_->Capture(webrtc::DesktopRegion());
    327 
    328   // Currently only synchronous implementations of DesktopCapturer are
    329   // supported.
    330   DCHECK(!capture_in_progress_);
    331 }
    332 
    333 // static
    334 scoped_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
    335     const DesktopMediaID& source) {
    336   scoped_refptr<base::SequencedWorkerPool> blocking_pool =
    337       BrowserThread::GetBlockingPool();
    338   scoped_refptr<base::SequencedTaskRunner> task_runner =
    339       blocking_pool->GetSequencedTaskRunner(
    340           blocking_pool->GetSequenceToken());
    341 
    342   switch (source.type) {
    343     case DesktopMediaID::TYPE_SCREEN: {
    344       scoped_ptr<webrtc::DesktopCapturer> capturer;
    345 
    346 #if defined(OS_CHROMEOS) && !defined(ARCH_CPU_ARMEL) && defined(USE_X11)
    347       // ScreenCapturerX11 polls by default, due to poor driver support for
    348       // DAMAGE. ChromeOS' drivers [can be patched to] support DAMAGE properly,
    349       // so use it. However ARM driver seems to not support this properly, so
    350       // disable it for ARM. See http://crbug.com/230105 .
    351       capturer.reset(webrtc::ScreenCapturer::CreateWithXDamage(true));
    352 #elif defined(OS_WIN)
    353       // ScreenCapturerWin disables Aero by default. We don't want it disabled
    354       // for WebRTC screen capture, though.
    355       capturer.reset(
    356           webrtc::ScreenCapturer::CreateWithDisableAero(false));
    357 #else
    358       capturer.reset(webrtc::ScreenCapturer::Create());
    359 #endif
    360 
    361       return scoped_ptr<media::VideoCaptureDevice>(new DesktopCaptureDevice(
    362           task_runner, capturer.Pass()));
    363     }
    364 
    365     case DesktopMediaID::TYPE_WINDOW: {
    366       scoped_ptr<webrtc::WindowCapturer> capturer(
    367           webrtc::WindowCapturer::Create());
    368 
    369       if (!capturer || !capturer->SelectWindow(source.id)) {
    370         return scoped_ptr<media::VideoCaptureDevice>();
    371       }
    372 
    373       return scoped_ptr<media::VideoCaptureDevice>(new DesktopCaptureDevice(
    374           task_runner, capturer.PassAs<webrtc::DesktopCapturer>()));
    375     }
    376 
    377     default: {
    378       NOTREACHED();
    379       return scoped_ptr<media::VideoCaptureDevice>();
    380     }
    381   }
    382 }
    383 
    384 DesktopCaptureDevice::DesktopCaptureDevice(
    385     scoped_refptr<base::SequencedTaskRunner> task_runner,
    386     scoped_ptr<webrtc::DesktopCapturer> capturer)
    387     : core_(new Core(task_runner, capturer.Pass())),
    388       name_("", "") {
    389 }
    390 
    391 DesktopCaptureDevice::~DesktopCaptureDevice() {
    392   DeAllocate();
    393 }
    394 
    395 void DesktopCaptureDevice::Allocate(
    396     const media::VideoCaptureCapability& capture_format,
    397     EventHandler* observer) {
    398   core_->Allocate(capture_format.width,
    399                   capture_format.height,
    400                   capture_format.frame_rate,
    401                   observer);
    402 }
    403 
    404 void DesktopCaptureDevice::Start() {
    405   core_->Start();
    406 }
    407 
    408 void DesktopCaptureDevice::Stop() {
    409   core_->Stop();
    410 }
    411 
    412 void DesktopCaptureDevice::DeAllocate() {
    413   core_->DeAllocate();
    414 }
    415 
    416 const media::VideoCaptureDevice::Name& DesktopCaptureDevice::device_name() {
    417   return name_;
    418 }
    419 
    420 }  // namespace content
    421