| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 #include "webrtc/modules/desktop_capture/screen_capturer.h" | 11 #include "webrtc/modules/desktop_capture/screen_capturer.h" |
| 12 | 12 |
| 13 #include <string.h> | 13 #include <string.h> |
| 14 | 14 |
| 15 #include <memory> | 15 #include <memory> |
| 16 #include <set> | 16 #include <set> |
| 17 | 17 |
| 18 #include <X11/extensions/Xdamage.h> | 18 #include <X11/extensions/Xdamage.h> |
| 19 #include <X11/extensions/Xfixes.h> | 19 #include <X11/extensions/Xfixes.h> |
| 20 #include <X11/Xlib.h> | 20 #include <X11/Xlib.h> |
| 21 #include <X11/Xutil.h> | 21 #include <X11/Xutil.h> |
| 22 | 22 |
| 23 #include "webrtc/base/checks.h" | 23 #include "webrtc/base/checks.h" |
| 24 #include "webrtc/base/thread_checker.h" |
| 24 #include "webrtc/modules/desktop_capture/desktop_capture_options.h" | 25 #include "webrtc/modules/desktop_capture/desktop_capture_options.h" |
| 25 #include "webrtc/modules/desktop_capture/desktop_frame.h" | 26 #include "webrtc/modules/desktop_capture/desktop_frame.h" |
| 26 #include "webrtc/modules/desktop_capture/differ.h" | 27 #include "webrtc/modules/desktop_capture/differ.h" |
| 27 #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" | 28 #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" |
| 28 #include "webrtc/modules/desktop_capture/screen_capturer_helper.h" | 29 #include "webrtc/modules/desktop_capture/screen_capturer_helper.h" |
| 29 #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h" | 30 #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h" |
| 30 #include "webrtc/system_wrappers/include/logging.h" | 31 #include "webrtc/system_wrappers/include/logging.h" |
| 31 #include "webrtc/system_wrappers/include/tick_util.h" | 32 #include "webrtc/system_wrappers/include/tick_util.h" |
| 32 | 33 |
| 33 namespace webrtc { | 34 namespace webrtc { |
| 34 namespace { | 35 namespace { |
| 35 | 36 |
| 36 // A class to perform video frame capturing for Linux. | 37 // A class to perform video frame capturing for Linux. |
| 37 class ScreenCapturerLinux : public ScreenCapturer, | 38 class ScreenCapturerLinux : public ScreenCapturer, |
| 38 public SharedXDisplay::XEventHandler { | 39 public SharedXDisplay::XEventHandler { |
| 39 public: | 40 public: |
| 40 ScreenCapturerLinux(); | 41 explicit ScreenCapturerLinux(const DesktopCaptureOptions& options); |
| 41 virtual ~ScreenCapturerLinux(); | 42 virtual ~ScreenCapturerLinux(); |
| 42 | 43 |
| 43 // TODO(ajwong): Do we really want this to be synchronous? | |
| 44 bool Init(const DesktopCaptureOptions& options); | |
| 45 | |
| 46 // DesktopCapturer interface. | 44 // DesktopCapturer interface. |
| 47 void Start(Callback* delegate) override; | 45 void Start(Callback* delegate) override; |
| 48 void Capture(const DesktopRegion& region) override; | 46 void Capture(const DesktopRegion& region) override; |
| 49 | 47 |
| 50 // ScreenCapturer interface. | 48 // ScreenCapturer interface. |
| 51 bool GetScreenList(ScreenList* screens) override; | 49 bool GetScreenList(ScreenList* screens) override; |
| 52 bool SelectScreen(ScreenId id) override; | 50 bool SelectScreen(ScreenId id) override; |
| 53 | 51 |
| 54 private: | 52 private: |
| 55 Display* display() { return options_.x_display()->display(); } | 53 Display* display() { return options_.x_display()->display(); } |
| 56 | 54 |
| 55 bool Initialize(); |
| 56 |
| 57 void InitXDamage(); |
| 58 |
| 57 // SharedXDisplay::XEventHandler interface. | 59 // SharedXDisplay::XEventHandler interface. |
| 58 bool HandleXEvent(const XEvent& event) override; | 60 bool HandleXEvent(const XEvent& event) override; |
| 59 | 61 |
| 60 void InitXDamage(); | |
| 61 | |
| 62 // Capture screen pixels to the current buffer in the queue. In the DAMAGE | 62 // Capture screen pixels to the current buffer in the queue. In the DAMAGE |
| 63 // case, the ScreenCapturerHelper already holds the list of invalid rectangles | 63 // case, the ScreenCapturerHelper already holds the list of invalid rectangles |
| 64 // from HandleXEvent(). In the non-DAMAGE case, this captures the | 64 // from HandleXEvent(). In the non-DAMAGE case, this captures the |
| 65 // whole screen, then calculates some invalid rectangles that include any | 65 // whole screen, then calculates some invalid rectangles that include any |
| 66 // differences between this and the previous capture. | 66 // differences between this and the previous capture. |
| 67 DesktopFrame* CaptureScreen(); | 67 DesktopFrame* CaptureScreen(); |
| 68 | 68 |
| 69 // Called when the screen configuration is changed. | 69 // Called when the screen configuration is changed. |
| 70 void ScreenConfigurationChanged(); | 70 void ScreenConfigurationChanged(); |
| 71 | 71 |
| 72 // Synchronize the current buffer with |last_buffer_|, by copying pixels from | 72 // Synchronize the current buffer with |last_buffer_|, by copying pixels from |
| 73 // the area of |last_invalid_rects|. | 73 // the area of |last_invalid_rects|. |
| 74 // Note this only works on the assumption that kNumBuffers == 2, as | 74 // Note this only works on the assumption that kNumBuffers == 2, as |
| 75 // |last_invalid_rects| holds the differences from the previous buffer and | 75 // |last_invalid_rects| holds the differences from the previous buffer and |
| 76 // the one prior to that (which will then be the current buffer). | 76 // the one prior to that (which will then be the current buffer). |
| 77 void SynchronizeFrame(); | 77 void SynchronizeFrame(); |
| 78 | 78 |
| 79 void DeinitXlib(); | 79 void DeinitXlib(); |
| 80 | 80 |
| 81 rtc::ThreadChecker thread_checker_; |
| 82 |
| 81 DesktopCaptureOptions options_; | 83 DesktopCaptureOptions options_; |
| 82 | 84 |
| 83 Callback* callback_; | 85 Callback* callback_ = nullptr; |
| 84 | 86 |
| 85 // X11 graphics context. | 87 // X11 graphics context. |
| 86 GC gc_; | 88 GC gc_ = nullptr; |
| 87 Window root_window_; | 89 Window root_window_ = BadValue; |
| 88 | 90 |
| 89 // XFixes. | 91 // XFixes. |
| 90 bool has_xfixes_; | 92 bool has_xfixes_ = false; |
| 91 int xfixes_event_base_; | 93 int xfixes_event_base_ = -1; |
| 92 int xfixes_error_base_; | 94 int xfixes_error_base_ = -1; |
| 93 | 95 |
| 94 // XDamage information. | 96 // XDamage information. |
| 95 bool use_damage_; | 97 bool use_damage_ = false; |
| 96 Damage damage_handle_; | 98 Damage damage_handle_ = 0; |
| 97 int damage_event_base_; | 99 int damage_event_base_ = -1; |
| 98 int damage_error_base_; | 100 int damage_error_base_ = -1; |
| 99 XserverRegion damage_region_; | 101 XserverRegion damage_region_ = 0; |
| 100 | 102 |
| 101 // Access to the X Server's pixel buffer. | 103 // Access to the X Server's pixel buffer. |
| 102 XServerPixelBuffer x_server_pixel_buffer_; | 104 XServerPixelBuffer x_server_pixel_buffer_; |
| 103 | 105 |
| 104 // A thread-safe list of invalid rectangles, and the size of the most | 106 // A thread-safe list of invalid rectangles, and the size of the most |
| 105 // recently captured screen. | 107 // recently captured screen. |
| 106 ScreenCapturerHelper helper_; | 108 ScreenCapturerHelper helper_; |
| 107 | 109 |
| 108 // Queue of the frames buffers. | 110 // Queue of the frames buffers. |
| 109 ScreenCaptureFrameQueue queue_; | 111 ScreenCaptureFrameQueue queue_; |
| 110 | 112 |
| 111 // Invalid region from the previous capture. This is used to synchronize the | 113 // Invalid region from the previous capture. This is used to synchronize the |
| 112 // current with the last buffer used. | 114 // current with the last buffer used. |
| 113 DesktopRegion last_invalid_region_; | 115 DesktopRegion last_invalid_region_; |
| 114 | 116 |
| 115 // |Differ| for use when polling for changes. | 117 // |Differ| for use when polling for changes. |
| 116 std::unique_ptr<Differ> differ_; | 118 std::unique_ptr<Differ> differ_; |
| 117 | 119 |
| 118 RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerLinux); | 120 RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerLinux); |
| 119 }; | 121 }; |
| 120 | 122 |
| 121 ScreenCapturerLinux::ScreenCapturerLinux() | 123 ScreenCapturerLinux::ScreenCapturerLinux(const DesktopCaptureOptions& options) |
| 122 : callback_(NULL), | 124 : options_(options) { |
| 123 gc_(NULL), | 125 // ScreenCapturer can be used on a thread different from the thread on which |
| 124 root_window_(BadValue), | 126 // it's created. |
| 125 has_xfixes_(false), | 127 thread_checker_.DetachFromThread(); |
| 126 xfixes_event_base_(-1), | |
| 127 xfixes_error_base_(-1), | |
| 128 use_damage_(false), | |
| 129 damage_handle_(0), | |
| 130 damage_event_base_(-1), | |
| 131 damage_error_base_(-1), | |
| 132 damage_region_(0) { | |
| 133 helper_.SetLogGridSize(4); | |
| 134 } | 128 } |
| 135 | 129 |
| 136 ScreenCapturerLinux::~ScreenCapturerLinux() { | 130 ScreenCapturerLinux::~ScreenCapturerLinux() { |
| 131 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 132 |
| 137 options_.x_display()->RemoveEventHandler(ConfigureNotify, this); | 133 options_.x_display()->RemoveEventHandler(ConfigureNotify, this); |
| 138 if (use_damage_) { | 134 if (use_damage_) { |
| 139 options_.x_display()->RemoveEventHandler( | 135 options_.x_display()->RemoveEventHandler( |
| 140 damage_event_base_ + XDamageNotify, this); | 136 damage_event_base_ + XDamageNotify, this); |
| 141 } | 137 } |
| 142 DeinitXlib(); | 138 DeinitXlib(); |
| 143 } | 139 } |
| 144 | 140 |
| 145 bool ScreenCapturerLinux::Init(const DesktopCaptureOptions& options) { | 141 bool ScreenCapturerLinux::Initialize() { |
| 146 options_ = options; | 142 helper_.SetLogGridSize(4); |
| 147 | 143 |
| 148 root_window_ = RootWindow(display(), DefaultScreen(display())); | 144 root_window_ = RootWindow(display(), DefaultScreen(display())); |
| 149 if (root_window_ == BadValue) { | 145 if (root_window_ == BadValue) { |
| 150 LOG(LS_ERROR) << "Unable to get the root window"; | 146 LOG(LS_ERROR) << "Unable to get the root window"; |
| 151 DeinitXlib(); | 147 DeinitXlib(); |
| 152 return false; | 148 return false; |
| 153 } | 149 } |
| 154 | 150 |
| 155 gc_ = XCreateGC(display(), root_window_, 0, NULL); | 151 gc_ = XCreateGC(display(), root_window_, 0, NULL); |
| 156 if (gc_ == NULL) { | 152 if (gc_ == NULL) { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 191 return; | 187 return; |
| 192 } | 188 } |
| 193 | 189 |
| 194 // Check for XDamage extension. | 190 // Check for XDamage extension. |
| 195 if (!XDamageQueryExtension(display(), &damage_event_base_, | 191 if (!XDamageQueryExtension(display(), &damage_event_base_, |
| 196 &damage_error_base_)) { | 192 &damage_error_base_)) { |
| 197 LOG(LS_INFO) << "X server does not support XDamage."; | 193 LOG(LS_INFO) << "X server does not support XDamage."; |
| 198 return; | 194 return; |
| 199 } | 195 } |
| 200 | 196 |
| 201 // TODO(lambroslambrou): Disable DAMAGE in situations where it is known | |
| 202 // to fail, such as when Desktop Effects are enabled, with graphics | |
| 203 // drivers (nVidia, ATI) that fail to report DAMAGE notifications | |
| 204 // properly. | |
| 205 | |
| 206 // Request notifications every time the screen becomes damaged. | 197 // Request notifications every time the screen becomes damaged. |
| 207 damage_handle_ = XDamageCreate(display(), root_window_, | 198 damage_handle_ = XDamageCreate(display(), root_window_, |
| 208 XDamageReportNonEmpty); | 199 XDamageReportNonEmpty); |
| 209 if (!damage_handle_) { | 200 if (!damage_handle_) { |
| 210 LOG(LS_ERROR) << "Unable to initialize XDamage."; | 201 LOG(LS_ERROR) << "Unable to initialize XDamage."; |
| 211 return; | 202 return; |
| 212 } | 203 } |
| 213 | 204 |
| 214 // Create an XFixes server-side region to collate damage into. | 205 // Create an XFixes server-side region to collate damage into. |
| 215 damage_region_ = XFixesCreateRegion(display(), 0, 0); | 206 damage_region_ = XFixesCreateRegion(display(), 0, 0); |
| 216 if (!damage_region_) { | 207 if (!damage_region_) { |
| 217 XDamageDestroy(display(), damage_handle_); | 208 XDamageDestroy(display(), damage_handle_); |
| 218 LOG(LS_ERROR) << "Unable to create XFixes region."; | 209 LOG(LS_ERROR) << "Unable to create XFixes region."; |
| 219 return; | 210 return; |
| 220 } | 211 } |
| 221 | 212 |
| 222 options_.x_display()->AddEventHandler( | 213 options_.x_display()->AddEventHandler( |
| 223 damage_event_base_ + XDamageNotify, this); | 214 damage_event_base_ + XDamageNotify, this); |
| 224 | 215 |
| 225 use_damage_ = true; | 216 use_damage_ = true; |
| 226 LOG(LS_INFO) << "Using XDamage extension."; | 217 LOG(LS_INFO) << "Using XDamage extension."; |
| 227 } | 218 } |
| 228 | 219 |
| 229 void ScreenCapturerLinux::Start(Callback* callback) { | 220 void ScreenCapturerLinux::Start(Callback* callback) { |
| 221 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 230 RTC_DCHECK(!callback_); | 222 RTC_DCHECK(!callback_); |
| 231 RTC_DCHECK(callback); | 223 RTC_DCHECK(callback); |
| 232 | 224 |
| 233 callback_ = callback; | 225 callback_ = callback; |
| 226 |
| 227 if (!Initialize()) { |
| 228 callback_->OnInitializationFailed(); |
| 229 } |
| 234 } | 230 } |
| 235 | 231 |
| 236 void ScreenCapturerLinux::Capture(const DesktopRegion& region) { | 232 void ScreenCapturerLinux::Capture(const DesktopRegion& region) { |
| 233 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 234 |
| 237 TickTime capture_start_time = TickTime::Now(); | 235 TickTime capture_start_time = TickTime::Now(); |
| 238 | 236 |
| 239 queue_.MoveToNextFrame(); | 237 queue_.MoveToNextFrame(); |
| 240 | 238 |
| 241 // Process XEvents for XDamage and cursor shape tracking. | 239 // Process XEvents for XDamage and cursor shape tracking. |
| 242 options_.x_display()->ProcessPendingXEvents(); | 240 options_.x_display()->ProcessPendingXEvents(); |
| 243 | 241 |
| 244 // ProcessPendingXEvents() may call ScreenConfigurationChanged() which | 242 // ProcessPendingXEvents() may call ScreenConfigurationChanged() which |
| 245 // reinitializes |x_server_pixel_buffer_|. Check if the pixel buffer is still | 243 // reinitializes |x_server_pixel_buffer_|. Check if the pixel buffer is still |
| 246 // in a good shape. | 244 // in a good shape. |
| (...skipping 25 matching lines...) Expand all Loading... |
| 272 } | 270 } |
| 273 | 271 |
| 274 DesktopFrame* result = CaptureScreen(); | 272 DesktopFrame* result = CaptureScreen(); |
| 275 last_invalid_region_ = result->updated_region(); | 273 last_invalid_region_ = result->updated_region(); |
| 276 result->set_capture_time_ms( | 274 result->set_capture_time_ms( |
| 277 (TickTime::Now() - capture_start_time).Milliseconds()); | 275 (TickTime::Now() - capture_start_time).Milliseconds()); |
| 278 callback_->OnCaptureCompleted(result); | 276 callback_->OnCaptureCompleted(result); |
| 279 } | 277 } |
| 280 | 278 |
| 281 bool ScreenCapturerLinux::GetScreenList(ScreenList* screens) { | 279 bool ScreenCapturerLinux::GetScreenList(ScreenList* screens) { |
| 280 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 282 RTC_DCHECK(screens->size() == 0); | 281 RTC_DCHECK(screens->size() == 0); |
| 282 |
| 283 // TODO(jiayl): implement screen enumeration. | 283 // TODO(jiayl): implement screen enumeration. |
| 284 Screen default_screen; | 284 Screen default_screen; |
| 285 default_screen.id = 0; | 285 default_screen.id = 0; |
| 286 screens->push_back(default_screen); | 286 screens->push_back(default_screen); |
| 287 return true; | 287 return true; |
| 288 } | 288 } |
| 289 | 289 |
| 290 bool ScreenCapturerLinux::SelectScreen(ScreenId id) { | 290 bool ScreenCapturerLinux::SelectScreen(ScreenId id) { |
| 291 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 291 // TODO(jiayl): implement screen selection. | 292 // TODO(jiayl): implement screen selection. |
| 292 return true; | 293 return true; |
| 293 } | 294 } |
| 294 | 295 |
| 295 bool ScreenCapturerLinux::HandleXEvent(const XEvent& event) { | 296 bool ScreenCapturerLinux::HandleXEvent(const XEvent& event) { |
| 297 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 298 |
| 296 if (use_damage_ && (event.type == damage_event_base_ + XDamageNotify)) { | 299 if (use_damage_ && (event.type == damage_event_base_ + XDamageNotify)) { |
| 297 const XDamageNotifyEvent* damage_event = | 300 const XDamageNotifyEvent* damage_event = |
| 298 reinterpret_cast<const XDamageNotifyEvent*>(&event); | 301 reinterpret_cast<const XDamageNotifyEvent*>(&event); |
| 299 if (damage_event->damage != damage_handle_) | 302 if (damage_event->damage != damage_handle_) |
| 300 return false; | 303 return false; |
| 301 RTC_DCHECK(damage_event->level == XDamageReportNonEmpty); | 304 RTC_DCHECK(damage_event->level == XDamageReportNonEmpty); |
| 302 return true; | 305 return true; |
| 303 } else if (event.type == ConfigureNotify) { | 306 } else if (event.type == ConfigureNotify) { |
| 304 ScreenConfigurationChanged(); | 307 ScreenConfigurationChanged(); |
| 305 return true; | 308 return true; |
| 306 } | 309 } |
| 307 return false; | 310 return false; |
| 308 } | 311 } |
| 309 | 312 |
| 310 DesktopFrame* ScreenCapturerLinux::CaptureScreen() { | 313 DesktopFrame* ScreenCapturerLinux::CaptureScreen() { |
| 311 DesktopFrame* frame = queue_.current_frame()->Share(); | 314 DesktopFrame* frame = queue_.current_frame()->Share(); |
| 312 assert(x_server_pixel_buffer_.window_size().equals(frame->size())); | 315 RTC_DCHECK(x_server_pixel_buffer_.window_size().equals(frame->size())); |
| 313 | 316 |
| 314 // Pass the screen size to the helper, so it can clip the invalid region if it | 317 // Pass the screen size to the helper, so it can clip the invalid region if it |
| 315 // expands that region to a grid. | 318 // expands that region to a grid. |
| 316 helper_.set_size_most_recent(frame->size()); | 319 helper_.set_size_most_recent(frame->size()); |
| 317 | 320 |
| 318 // In the DAMAGE case, ensure the frame is up-to-date with the previous frame | 321 // In the DAMAGE case, ensure the frame is up-to-date with the previous frame |
| 319 // if any. If there isn't a previous frame, that means a screen-resolution | 322 // if any. If there isn't a previous frame, that means a screen-resolution |
| 320 // change occurred, and |invalid_rects| will be updated to include the whole | 323 // change occurred, and |invalid_rects| will be updated to include the whole |
| 321 // screen. | 324 // screen. |
| 322 if (use_damage_ && queue_.previous_frame()) | 325 if (use_damage_ && queue_.previous_frame()) |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 429 } | 432 } |
| 430 } | 433 } |
| 431 | 434 |
| 432 } // namespace | 435 } // namespace |
| 433 | 436 |
| 434 // static | 437 // static |
| 435 ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) { | 438 ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) { |
| 436 if (!options.x_display()) | 439 if (!options.x_display()) |
| 437 return NULL; | 440 return NULL; |
| 438 | 441 |
| 439 std::unique_ptr<ScreenCapturerLinux> capturer(new ScreenCapturerLinux()); | 442 return new ScreenCapturerLinux(options); |
| 440 if (!capturer->Init(options)) | |
| 441 capturer.reset(); | |
| 442 return capturer.release(); | |
| 443 } | 443 } |
| 444 | 444 |
| 445 } // namespace webrtc | 445 } // namespace webrtc |
| OLD | NEW |