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