LCOV - code coverage report
Current view: top level - media/webrtc/trunk/webrtc/modules/desktop_capture - screen_capturer_x11.cc (source / functions) Hit Total Coverage
Test: output.info Lines: 0 162 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 17 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
       3             :  *
       4             :  *  Use of this source code is governed by a BSD-style license
       5             :  *  that can be found in the LICENSE file in the root of the source
       6             :  *  tree. An additional intellectual property rights grant can be found
       7             :  *  in the file PATENTS.  All contributing project authors may
       8             :  *  be found in the AUTHORS file in the root of the source tree.
       9             :  */
      10             : 
      11             : #include <string.h>
      12             : 
      13             : #include <memory>
      14             : #include <set>
      15             : #include <utility>
      16             : 
      17             : #include <X11/extensions/Xdamage.h>
      18             : #include <X11/extensions/Xfixes.h>
      19             : #include <X11/Xlib.h>
      20             : #include <X11/Xutil.h>
      21             : 
      22             : #include "webrtc/base/checks.h"
      23             : #include "webrtc/base/constructormagic.h"
      24             : #include "webrtc/base/timeutils.h"
      25             : #include "webrtc/modules/desktop_capture/desktop_capturer.h"
      26             : #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
      27             : #include "webrtc/modules/desktop_capture/desktop_frame.h"
      28             : #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
      29             : #include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
      30             : #include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
      31             : #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
      32             : #include "webrtc/system_wrappers/include/logging.h"
      33             : 
      34             : namespace webrtc {
      35             : namespace {
      36             : 
      37             : // A class to perform video frame capturing for Linux.
      38             : //
      39             : // If XDamage is used, this class sets DesktopFrame::updated_region() according
      40             : // to the areas reported by XDamage. Otherwise this class does not detect
      41             : // DesktopFrame::updated_region(), the field is always set to the entire frame
      42             : // rectangle. ScreenCapturerDifferWrapper should be used if that functionality
      43             : // is necessary.
      44             : class ScreenCapturerLinux : public DesktopCapturer,
      45             :                             public SharedXDisplay::XEventHandler {
      46             :  public:
      47             :   ScreenCapturerLinux();
      48             :   ~ScreenCapturerLinux() override;
      49             : 
      50             :   // TODO(ajwong): Do we really want this to be synchronous?
      51             :   bool Init(const DesktopCaptureOptions& options);
      52             : 
      53             :   // DesktopCapturer interface.
      54             :   void Start(Callback* delegate) override;
      55             :   void Stop() override;
      56             :   void CaptureFrame() override;
      57             :   bool GetSourceList(SourceList* sources) override;
      58             :   bool SelectSource(SourceId id) override;
      59             : 
      60             :  private:
      61           0 :   Display* display() { return options_.x_display()->display(); }
      62             : 
      63             :   // SharedXDisplay::XEventHandler interface.
      64             :   bool HandleXEvent(const XEvent& event) override;
      65             : 
      66             :   void InitXDamage();
      67             : 
      68             :   // Capture screen pixels to the current buffer in the queue. In the DAMAGE
      69             :   // case, the ScreenCapturerHelper already holds the list of invalid rectangles
      70             :   // from HandleXEvent(). In the non-DAMAGE case, this captures the
      71             :   // whole screen, then calculates some invalid rectangles that include any
      72             :   // differences between this and the previous capture.
      73             :   std::unique_ptr<DesktopFrame> CaptureScreen();
      74             : 
      75             :   // Called when the screen configuration is changed.
      76             :   void ScreenConfigurationChanged();
      77             : 
      78             :   // Synchronize the current buffer with |last_buffer_|, by copying pixels from
      79             :   // the area of |last_invalid_rects|.
      80             :   // Note this only works on the assumption that kNumBuffers == 2, as
      81             :   // |last_invalid_rects| holds the differences from the previous buffer and
      82             :   // the one prior to that (which will then be the current buffer).
      83             :   void SynchronizeFrame();
      84             : 
      85             :   void DeinitXlib();
      86             : 
      87             :   DesktopCaptureOptions options_;
      88             : 
      89             :   Callback* callback_ = nullptr;
      90             : 
      91             :   // X11 graphics context.
      92             :   GC gc_ = nullptr;
      93             :   Window root_window_ = BadValue;
      94             : 
      95             :   // XFixes.
      96             :   bool has_xfixes_ = false;
      97             :   int xfixes_event_base_ = -1;
      98             :   int xfixes_error_base_ = -1;
      99             : 
     100             :   // XDamage information.
     101             :   bool use_damage_ = false;
     102             :   Damage damage_handle_ = 0;
     103             :   int damage_event_base_ = -1;
     104             :   int damage_error_base_ = -1;
     105             :   XserverRegion damage_region_ = 0;
     106             : 
     107             :   // Access to the X Server's pixel buffer.
     108             :   XServerPixelBuffer x_server_pixel_buffer_;
     109             : 
     110             :   // A thread-safe list of invalid rectangles, and the size of the most
     111             :   // recently captured screen.
     112             :   ScreenCapturerHelper helper_;
     113             : 
     114             :   // Queue of the frames buffers.
     115             :   ScreenCaptureFrameQueue<SharedDesktopFrame> queue_;
     116             : 
     117             :   // Invalid region from the previous capture. This is used to synchronize the
     118             :   // current with the last buffer used.
     119             :   DesktopRegion last_invalid_region_;
     120             : 
     121             :   RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerLinux);
     122             : };
     123             : 
     124           0 : ScreenCapturerLinux::ScreenCapturerLinux() {
     125           0 :   helper_.SetLogGridSize(4);
     126           0 : }
     127             : 
     128           0 : ScreenCapturerLinux::~ScreenCapturerLinux() {
     129           0 :   options_.x_display()->RemoveEventHandler(ConfigureNotify, this);
     130           0 :   if (use_damage_) {
     131           0 :     options_.x_display()->RemoveEventHandler(
     132           0 :         damage_event_base_ + XDamageNotify, this);
     133             :   }
     134           0 :   DeinitXlib();
     135           0 : }
     136             : 
     137           0 : bool ScreenCapturerLinux::Init(const DesktopCaptureOptions& options) {
     138           0 :   options_ = options;
     139             : 
     140           0 :   root_window_ = RootWindow(display(), DefaultScreen(display()));
     141           0 :   if (root_window_ == BadValue) {
     142           0 :     LOG(LS_ERROR) << "Unable to get the root window";
     143           0 :     DeinitXlib();
     144           0 :     return false;
     145             :   }
     146             : 
     147           0 :   gc_ = XCreateGC(display(), root_window_, 0, NULL);
     148           0 :   if (gc_ == NULL) {
     149           0 :     LOG(LS_ERROR) << "Unable to get graphics context";
     150           0 :     DeinitXlib();
     151           0 :     return false;
     152             :   }
     153             : 
     154           0 :   options_.x_display()->AddEventHandler(ConfigureNotify, this);
     155             : 
     156             :   // Check for XFixes extension. This is required for cursor shape
     157             :   // notifications, and for our use of XDamage.
     158           0 :   if (XFixesQueryExtension(display(), &xfixes_event_base_,
     159             :                            &xfixes_error_base_)) {
     160           0 :     has_xfixes_ = true;
     161             :   } else {
     162           0 :     LOG(LS_INFO) << "X server does not support XFixes.";
     163             :   }
     164             : 
     165             :   // Register for changes to the dimensions of the root window.
     166           0 :   XSelectInput(display(), root_window_, StructureNotifyMask);
     167             : 
     168           0 :   if (!x_server_pixel_buffer_.Init(display(), DefaultRootWindow(display()))) {
     169           0 :     LOG(LS_ERROR) << "Failed to initialize pixel buffer.";
     170           0 :     return false;
     171             :   }
     172             : 
     173           0 :   if (options_.use_update_notifications()) {
     174           0 :     InitXDamage();
     175             :   }
     176             : 
     177           0 :   return true;
     178             : }
     179             : 
     180           0 : void ScreenCapturerLinux::InitXDamage() {
     181             :   // Our use of XDamage requires XFixes.
     182           0 :   if (!has_xfixes_) {
     183           0 :     return;
     184             :   }
     185             : 
     186             :   // Check for XDamage extension.
     187           0 :   if (!XDamageQueryExtension(display(), &damage_event_base_,
     188             :                              &damage_error_base_)) {
     189           0 :     LOG(LS_INFO) << "X server does not support XDamage.";
     190           0 :     return;
     191             :   }
     192             : 
     193             :   // TODO(lambroslambrou): Disable DAMAGE in situations where it is known
     194             :   // to fail, such as when Desktop Effects are enabled, with graphics
     195             :   // drivers (nVidia, ATI) that fail to report DAMAGE notifications
     196             :   // properly.
     197             : 
     198             :   // Request notifications every time the screen becomes damaged.
     199           0 :   damage_handle_ = XDamageCreate(display(), root_window_,
     200             :                                  XDamageReportNonEmpty);
     201           0 :   if (!damage_handle_) {
     202           0 :     LOG(LS_ERROR) << "Unable to initialize XDamage.";
     203           0 :     return;
     204             :   }
     205             : 
     206             :   // Create an XFixes server-side region to collate damage into.
     207           0 :   damage_region_ = XFixesCreateRegion(display(), 0, 0);
     208           0 :   if (!damage_region_) {
     209           0 :     XDamageDestroy(display(), damage_handle_);
     210           0 :     LOG(LS_ERROR) << "Unable to create XFixes region.";
     211           0 :     return;
     212             :   }
     213             : 
     214           0 :   options_.x_display()->AddEventHandler(
     215           0 :       damage_event_base_ + XDamageNotify, this);
     216             : 
     217           0 :   use_damage_ = true;
     218           0 :   LOG(LS_INFO) << "Using XDamage extension.";
     219             : }
     220             : 
     221           0 : void ScreenCapturerLinux::Start(Callback* callback) {
     222           0 :   RTC_DCHECK(!callback_);
     223           0 :   RTC_DCHECK(callback);
     224             : 
     225           0 :   callback_ = callback;
     226           0 : }
     227             : 
     228           0 : void ScreenCapturerLinux::Stop() {
     229           0 :   callback_ = NULL;
     230           0 : }
     231             : 
     232           0 : void ScreenCapturerLinux::CaptureFrame() {
     233           0 :   int64_t capture_start_time_nanos = rtc::TimeNanos();
     234             : 
     235           0 :   queue_.MoveToNextFrame();
     236           0 :   RTC_DCHECK(!queue_.current_frame() || !queue_.current_frame()->IsShared());
     237             : 
     238             :   // Process XEvents for XDamage and cursor shape tracking.
     239           0 :   options_.x_display()->ProcessPendingXEvents();
     240             : 
     241             :   // ProcessPendingXEvents() may call ScreenConfigurationChanged() which
     242             :   // reinitializes |x_server_pixel_buffer_|. Check if the pixel buffer is still
     243             :   // in a good shape.
     244           0 :   if (!x_server_pixel_buffer_.is_initialized()) {
     245             :      // We failed to initialize pixel buffer.
     246           0 :      callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
     247           0 :      return;
     248             :   }
     249             : 
     250             :   // If the current frame is from an older generation then allocate a new one.
     251             :   // Note that we can't reallocate other buffers at this point, since the caller
     252             :   // may still be reading from them.
     253           0 :   if (!queue_.current_frame()) {
     254           0 :     queue_.ReplaceCurrentFrame(
     255           0 :         SharedDesktopFrame::Wrap(std::unique_ptr<DesktopFrame>(
     256           0 :             new BasicDesktopFrame(x_server_pixel_buffer_.window_size()))));
     257             :   }
     258             : 
     259           0 :   std::unique_ptr<DesktopFrame> result = CaptureScreen();
     260           0 :   if (!result) {
     261           0 :     callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
     262           0 :     return;
     263             :   }
     264             : 
     265           0 :   last_invalid_region_ = result->updated_region();
     266           0 :   result->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) /
     267           0 :                               rtc::kNumNanosecsPerMillisec);
     268           0 :   callback_->OnCaptureResult(Result::SUCCESS, std::move(result));
     269             : }
     270             : 
     271           0 : bool ScreenCapturerLinux::GetSourceList(SourceList* sources) {
     272           0 :   RTC_DCHECK(sources->size() == 0);
     273             :   // TODO(jiayl): implement screen enumeration.
     274           0 :   sources->push_back({0});
     275           0 :   return true;
     276             : }
     277             : 
     278           0 : bool ScreenCapturerLinux::SelectSource(SourceId id) {
     279             :   // TODO(jiayl): implement screen selection.
     280           0 :   return true;
     281             : }
     282             : 
     283           0 : bool ScreenCapturerLinux::HandleXEvent(const XEvent& event) {
     284           0 :   if (use_damage_ && (event.type == damage_event_base_ + XDamageNotify)) {
     285             :     const XDamageNotifyEvent* damage_event =
     286           0 :         reinterpret_cast<const XDamageNotifyEvent*>(&event);
     287           0 :     if (damage_event->damage != damage_handle_)
     288           0 :       return false;
     289           0 :     RTC_DCHECK(damage_event->level == XDamageReportNonEmpty);
     290           0 :     return true;
     291           0 :   } else if (event.type == ConfigureNotify) {
     292           0 :     ScreenConfigurationChanged();
     293           0 :     return true;
     294             :   }
     295           0 :   return false;
     296             : }
     297             : 
     298           0 : std::unique_ptr<DesktopFrame> ScreenCapturerLinux::CaptureScreen() {
     299           0 :   std::unique_ptr<SharedDesktopFrame> frame = queue_.current_frame()->Share();
     300           0 :   RTC_DCHECK(x_server_pixel_buffer_.window_size().equals(frame->size()));
     301             : 
     302             :   // Pass the screen size to the helper, so it can clip the invalid region if it
     303             :   // expands that region to a grid.
     304           0 :   helper_.set_size_most_recent(frame->size());
     305             : 
     306             :   // In the DAMAGE case, ensure the frame is up-to-date with the previous frame
     307             :   // if any.  If there isn't a previous frame, that means a screen-resolution
     308             :   // change occurred, and |invalid_rects| will be updated to include the whole
     309             :   // screen.
     310           0 :   if (use_damage_ && queue_.previous_frame())
     311           0 :     SynchronizeFrame();
     312             : 
     313           0 :   DesktopRegion* updated_region = frame->mutable_updated_region();
     314             : 
     315           0 :   x_server_pixel_buffer_.Synchronize();
     316           0 :   if (use_damage_ && queue_.previous_frame()) {
     317             :     // Atomically fetch and clear the damage region.
     318           0 :     XDamageSubtract(display(), damage_handle_, None, damage_region_);
     319           0 :     int rects_num = 0;
     320             :     XRectangle bounds;
     321           0 :     XRectangle* rects = XFixesFetchRegionAndBounds(display(), damage_region_,
     322           0 :                                                    &rects_num, &bounds);
     323           0 :     for (int i = 0; i < rects_num; ++i) {
     324           0 :       updated_region->AddRect(DesktopRect::MakeXYWH(
     325           0 :           rects[i].x, rects[i].y, rects[i].width, rects[i].height));
     326             :     }
     327           0 :     XFree(rects);
     328           0 :     helper_.InvalidateRegion(*updated_region);
     329             : 
     330             :     // Capture the damaged portions of the desktop.
     331           0 :     helper_.TakeInvalidRegion(updated_region);
     332             : 
     333             :     // Clip the damaged portions to the current screen size, just in case some
     334             :     // spurious XDamage notifications were received for a previous (larger)
     335             :     // screen size.
     336             :     updated_region->IntersectWith(
     337           0 :         DesktopRect::MakeSize(x_server_pixel_buffer_.window_size()));
     338             : 
     339           0 :     for (DesktopRegion::Iterator it(*updated_region);
     340           0 :          !it.IsAtEnd(); it.Advance()) {
     341           0 :       if (!x_server_pixel_buffer_.CaptureRect(it.rect(), frame.get()))
     342           0 :         return nullptr;
     343             :     }
     344             :   } else {
     345             :     // Doing full-screen polling, or this is the first capture after a
     346             :     // screen-resolution change.  In either case, need a full-screen capture.
     347           0 :     DesktopRect screen_rect = DesktopRect::MakeSize(frame->size());
     348           0 :     x_server_pixel_buffer_.CaptureRect(screen_rect, frame.get());
     349           0 :     updated_region->SetRect(screen_rect);
     350             :   }
     351             : 
     352           0 :   return std::move(frame);
     353             : }
     354             : 
     355           0 : void ScreenCapturerLinux::ScreenConfigurationChanged() {
     356             :   // Make sure the frame buffers will be reallocated.
     357           0 :   queue_.Reset();
     358             : 
     359           0 :   helper_.ClearInvalidRegion();
     360           0 :   if (!x_server_pixel_buffer_.Init(display(), DefaultRootWindow(display()))) {
     361           0 :     LOG(LS_ERROR) << "Failed to initialize pixel buffer after screen "
     362           0 :         "configuration change.";
     363             :   }
     364           0 : }
     365             : 
     366           0 : void ScreenCapturerLinux::SynchronizeFrame() {
     367             :   // Synchronize the current buffer with the previous one since we do not
     368             :   // capture the entire desktop. Note that encoder may be reading from the
     369             :   // previous buffer at this time so thread access complaints are false
     370             :   // positives.
     371             : 
     372             :   // TODO(hclam): We can reduce the amount of copying here by subtracting
     373             :   // |capturer_helper_|s region from |last_invalid_region_|.
     374             :   // http://crbug.com/92354
     375           0 :   RTC_DCHECK(queue_.previous_frame());
     376             : 
     377           0 :   DesktopFrame* current = queue_.current_frame();
     378           0 :   DesktopFrame* last = queue_.previous_frame();
     379           0 :   RTC_DCHECK(current != last);
     380           0 :   for (DesktopRegion::Iterator it(last_invalid_region_);
     381           0 :        !it.IsAtEnd(); it.Advance()) {
     382           0 :     current->CopyPixelsFrom(*last, it.rect().top_left(), it.rect());
     383             :   }
     384           0 : }
     385             : 
     386           0 : void ScreenCapturerLinux::DeinitXlib() {
     387           0 :   if (gc_) {
     388           0 :     XFreeGC(display(), gc_);
     389           0 :     gc_ = nullptr;
     390             :   }
     391             : 
     392           0 :   x_server_pixel_buffer_.Release();
     393             : 
     394           0 :   if (display()) {
     395           0 :     if (damage_handle_) {
     396           0 :       XDamageDestroy(display(), damage_handle_);
     397           0 :       damage_handle_ = 0;
     398             :     }
     399             : 
     400           0 :     if (damage_region_) {
     401           0 :       XFixesDestroyRegion(display(), damage_region_);
     402           0 :       damage_region_ = 0;
     403             :     }
     404             :   }
     405           0 : }
     406             : 
     407             : }  // namespace
     408             : 
     409             : // static
     410           0 : std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
     411             :     const DesktopCaptureOptions& options) {
     412           0 :   if (!options.x_display())
     413           0 :     return nullptr;
     414             : 
     415           0 :   std::unique_ptr<ScreenCapturerLinux> capturer(new ScreenCapturerLinux());
     416           0 :   if (!capturer.get()->Init(options)) {
     417           0 :     return nullptr;
     418             :   }
     419             : 
     420           0 :   return std::unique_ptr<DesktopCapturer>(capturer.release());
     421             : }
     422             : 
     423             : }  // namespace webrtc

Generated by: LCOV version 1.13