OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/host/capturer.h" | 5 #include "remoting/host/capturer.h" |
6 | 6 |
7 #include <X11/Xlib.h> | 7 #include <X11/Xlib.h> |
8 #include <X11/Xutil.h> | 8 #include <X11/Xutil.h> |
9 #include <X11/extensions/Xdamage.h> | 9 #include <X11/extensions/Xdamage.h> |
10 #include <X11/extensions/Xfixes.h> | |
10 | 11 |
11 #include <set> | 12 #include <set> |
12 | 13 |
13 #include "base/basictypes.h" | 14 #include "base/basictypes.h" |
14 #include "base/logging.h" | 15 #include "base/logging.h" |
15 #include "base/memory/scoped_ptr.h" | 16 #include "base/memory/scoped_ptr.h" |
17 #include "remoting/base/capture_data.h" | |
16 #include "remoting/host/capturer_helper.h" | 18 #include "remoting/host/capturer_helper.h" |
17 #include "remoting/host/differ.h" | 19 #include "remoting/host/differ.h" |
18 #include "remoting/host/x_server_pixel_buffer.h" | 20 #include "remoting/host/x_server_pixel_buffer.h" |
21 #include "remoting/proto/control.pb.h" | |
19 | 22 |
20 namespace remoting { | 23 namespace remoting { |
21 | 24 |
22 namespace { | 25 namespace { |
23 | 26 |
24 static const int kBytesPerPixel = 4; | 27 static const int kBytesPerPixel = 4; |
25 | 28 |
26 // Default to false, since many systems have broken XDamage support - see | 29 // Default to false, since many systems have broken XDamage support - see |
27 // http://crbug.com/73423. | 30 // http://crbug.com/73423. |
28 static bool g_should_use_x_damage = false; | 31 static bool g_should_use_x_damage = false; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
68 | 71 |
69 // A class to perform capturing for Linux. | 72 // A class to perform capturing for Linux. |
70 class CapturerLinux : public Capturer { | 73 class CapturerLinux : public Capturer { |
71 public: | 74 public: |
72 CapturerLinux(); | 75 CapturerLinux(); |
73 virtual ~CapturerLinux(); | 76 virtual ~CapturerLinux(); |
74 | 77 |
75 bool Init(); // TODO(ajwong): Do we really want this to be synchronous? | 78 bool Init(); // TODO(ajwong): Do we really want this to be synchronous? |
76 | 79 |
77 // Capturer interface. | 80 // Capturer interface. |
78 virtual void Start() OVERRIDE; | 81 virtual void Start(const CursorShapeChangedCallback& callback) OVERRIDE; |
79 virtual void Stop() OVERRIDE; | 82 virtual void Stop() OVERRIDE; |
80 virtual void ScreenConfigurationChanged() OVERRIDE; | 83 virtual void ScreenConfigurationChanged() OVERRIDE; |
81 virtual media::VideoFrame::Format pixel_format() const OVERRIDE; | 84 virtual media::VideoFrame::Format pixel_format() const OVERRIDE; |
82 virtual void ClearInvalidRegion() OVERRIDE; | 85 virtual void ClearInvalidRegion() OVERRIDE; |
83 virtual void InvalidateRegion(const SkRegion& invalid_region) OVERRIDE; | 86 virtual void InvalidateRegion(const SkRegion& invalid_region) OVERRIDE; |
84 virtual void InvalidateScreen(const SkISize& size) OVERRIDE; | 87 virtual void InvalidateScreen(const SkISize& size) OVERRIDE; |
85 virtual void InvalidateFullScreen() OVERRIDE; | 88 virtual void InvalidateFullScreen() OVERRIDE; |
86 virtual void CaptureInvalidRegion( | 89 virtual void CaptureInvalidRegion( |
87 const CaptureCompletedCallback& callback) OVERRIDE; | 90 const CaptureCompletedCallback& callback) OVERRIDE; |
88 virtual const SkISize& size_most_recent() const OVERRIDE; | 91 virtual const SkISize& size_most_recent() const OVERRIDE; |
(...skipping 10 matching lines...) Expand all Loading... | |
99 | 102 |
100 // Capture screen pixels, and return the data in a new CaptureData object, | 103 // Capture screen pixels, and return the data in a new CaptureData object, |
101 // to be freed by the caller. | 104 // to be freed by the caller. |
102 // In the DAMAGE case, the CapturerHelper already holds the list of invalid | 105 // In the DAMAGE case, the CapturerHelper already holds the list of invalid |
103 // rectangles from ProcessPendingXEvents(). | 106 // rectangles from ProcessPendingXEvents(). |
104 // In the non-DAMAGE case, this captures the whole screen, then calculates | 107 // In the non-DAMAGE case, this captures the whole screen, then calculates |
105 // some invalid rectangles that include any differences between this and the | 108 // some invalid rectangles that include any differences between this and the |
106 // previous capture. | 109 // previous capture. |
107 CaptureData* CaptureFrame(); | 110 CaptureData* CaptureFrame(); |
108 | 111 |
112 // Capture the cursor image and call the CursorShapeChangedCallback if it | |
113 // has been set (using SetCursorShapeChangedCallback). | |
114 void CaptureCursor(); | |
115 | |
109 // Synchronize the current buffer with |last_buffer_|, by copying pixels from | 116 // Synchronize the current buffer with |last_buffer_|, by copying pixels from |
110 // the area of |last_invalid_rects|. | 117 // the area of |last_invalid_rects|. |
111 // Note this only works on the assumption that kNumBuffers == 2, as | 118 // Note this only works on the assumption that kNumBuffers == 2, as |
112 // |last_invalid_rects| holds the differences from the previous buffer and | 119 // |last_invalid_rects| holds the differences from the previous buffer and |
113 // the one prior to that (which will then be the current buffer). | 120 // the one prior to that (which will then be the current buffer). |
114 void SynchronizeFrame(); | 121 void SynchronizeFrame(); |
115 | 122 |
116 void DeinitXlib(); | 123 void DeinitXlib(); |
117 | 124 |
118 // Capture a rectangle from |x_server_pixel_buffer_|, and copy the data into | 125 // Capture a rectangle from |x_server_pixel_buffer_|, and copy the data into |
119 // |capture_data|. | 126 // |capture_data|. |
120 void CaptureRect(const SkIRect& rect, CaptureData* capture_data); | 127 void CaptureRect(const SkIRect& rect, CaptureData* capture_data); |
121 | 128 |
122 // We expose two forms of blitting to handle variations in the pixel format. | 129 // We expose two forms of blitting to handle variations in the pixel format. |
123 // In FastBlit, the operation is effectively a memcpy. | 130 // In FastBlit, the operation is effectively a memcpy. |
124 void FastBlit(uint8* image, const SkIRect& rect, CaptureData* capture_data); | 131 void FastBlit(uint8* image, const SkIRect& rect, CaptureData* capture_data); |
125 void SlowBlit(uint8* image, const SkIRect& rect, CaptureData* capture_data); | 132 void SlowBlit(uint8* image, const SkIRect& rect, CaptureData* capture_data); |
126 | 133 |
127 // X11 graphics context. | 134 // X11 graphics context. |
128 Display* display_; | 135 Display* display_; |
129 GC gc_; | 136 GC gc_; |
130 Window root_window_; | 137 Window root_window_; |
131 | 138 |
139 // XFixes. | |
140 bool has_xfixes_; | |
141 int xfixes_event_base_; | |
142 int xfixes_error_base_; | |
143 | |
132 // XDamage information. | 144 // XDamage information. |
133 bool use_damage_; | 145 bool use_damage_; |
134 Damage damage_handle_; | 146 Damage damage_handle_; |
135 int damage_event_base_; | 147 int damage_event_base_; |
136 int damage_error_base_; | 148 int damage_error_base_; |
137 XserverRegion damage_region_; | 149 XserverRegion damage_region_; |
138 | 150 |
139 // Access to the X Server's pixel buffer. | 151 // Access to the X Server's pixel buffer. |
140 XServerPixelBuffer x_server_pixel_buffer_; | 152 XServerPixelBuffer x_server_pixel_buffer_; |
141 | 153 |
142 // A thread-safe list of invalid rectangles, and the size of the most | 154 // A thread-safe list of invalid rectangles, and the size of the most |
143 // recently captured screen. | 155 // recently captured screen. |
144 CapturerHelper helper_; | 156 CapturerHelper helper_; |
145 | 157 |
158 // Callback notified whenever the cursor shape is changed. | |
159 CursorShapeChangedCallback cursor_shape_changed_callback_; | |
160 | |
146 // Capture state. | 161 // Capture state. |
147 static const int kNumBuffers = 2; | 162 static const int kNumBuffers = 2; |
148 VideoFrameBuffer buffers_[kNumBuffers]; | 163 VideoFrameBuffer buffers_[kNumBuffers]; |
149 int current_buffer_; | 164 int current_buffer_; |
150 | 165 |
151 // Format of pixels returned in buffer. | 166 // Format of pixels returned in buffer. |
152 media::VideoFrame::Format pixel_format_; | 167 media::VideoFrame::Format pixel_format_; |
153 | 168 |
154 // Invalid region from the previous capture. This is used to synchronize the | 169 // Invalid region from the previous capture. This is used to synchronize the |
155 // current with the last buffer used. | 170 // current with the last buffer used. |
156 SkRegion last_invalid_region_; | 171 SkRegion last_invalid_region_; |
157 | 172 |
158 // Last capture buffer used. | 173 // Last capture buffer used. |
159 uint8* last_buffer_; | 174 uint8* last_buffer_; |
160 | 175 |
161 // |Differ| for use when polling for changes. | 176 // |Differ| for use when polling for changes. |
162 scoped_ptr<Differ> differ_; | 177 scoped_ptr<Differ> differ_; |
163 | 178 |
164 DISALLOW_COPY_AND_ASSIGN(CapturerLinux); | 179 DISALLOW_COPY_AND_ASSIGN(CapturerLinux); |
165 }; | 180 }; |
166 | 181 |
167 CapturerLinux::CapturerLinux() | 182 CapturerLinux::CapturerLinux() |
168 : display_(NULL), | 183 : display_(NULL), |
169 gc_(NULL), | 184 gc_(NULL), |
170 root_window_(BadValue), | 185 root_window_(BadValue), |
186 has_xfixes_(false), | |
187 xfixes_event_base_(-1), | |
188 xfixes_error_base_(-1), | |
171 use_damage_(false), | 189 use_damage_(false), |
172 damage_handle_(0), | 190 damage_handle_(0), |
173 damage_event_base_(-1), | 191 damage_event_base_(-1), |
174 damage_error_base_(-1), | 192 damage_error_base_(-1), |
175 damage_region_(0), | 193 damage_region_(0), |
176 current_buffer_(0), | 194 current_buffer_(0), |
177 pixel_format_(media::VideoFrame::RGB32), | 195 pixel_format_(media::VideoFrame::RGB32), |
178 last_buffer_(NULL) { | 196 last_buffer_(NULL) { |
179 helper_.SetLogGridSize(4); | 197 helper_.SetLogGridSize(4); |
180 } | 198 } |
(...skipping 20 matching lines...) Expand all Loading... | |
201 return false; | 219 return false; |
202 } | 220 } |
203 | 221 |
204 gc_ = XCreateGC(display_, root_window_, 0, NULL); | 222 gc_ = XCreateGC(display_, root_window_, 0, NULL); |
205 if (gc_ == NULL) { | 223 if (gc_ == NULL) { |
206 LOG(ERROR) << "Unable to get graphics context"; | 224 LOG(ERROR) << "Unable to get graphics context"; |
207 DeinitXlib(); | 225 DeinitXlib(); |
208 return false; | 226 return false; |
209 } | 227 } |
210 | 228 |
229 // Check for XFixes extension. This is required for cursor shape | |
230 // notifications, and for our use of XDamage. | |
231 if (XFixesQueryExtension(display_, &xfixes_event_base_, | |
232 &xfixes_error_base_)) { | |
233 has_xfixes_ = true; | |
234 } else { | |
235 LOG(INFO) << "X server does not support XFixes."; | |
236 } | |
237 | |
211 if (ShouldUseXDamage()) { | 238 if (ShouldUseXDamage()) { |
212 InitXDamage(); | 239 InitXDamage(); |
213 } | 240 } |
214 | 241 |
215 // Register for changes to the dimensions of the root window. | 242 // Register for changes to the dimensions of the root window. |
216 XSelectInput(display_, root_window_, StructureNotifyMask); | 243 XSelectInput(display_, root_window_, StructureNotifyMask); |
217 | 244 |
245 if (has_xfixes_) { | |
246 // Register for changes to the cursor shape. | |
247 XFixesSelectCursorInput(display_, root_window_, | |
248 XFixesDisplayCursorNotifyMask); | |
249 } | |
250 | |
218 return true; | 251 return true; |
219 } | 252 } |
220 | 253 |
221 void CapturerLinux::InitXDamage() { | 254 void CapturerLinux::InitXDamage() { |
222 // Check for XFixes and XDamage extensions. If both are found then use | 255 // Our use of XDamage requires XFixes. |
223 // XDamage to get explicit notifications of on-screen changes. | 256 if (!has_xfixes_) { |
224 int xfixes_event_base; | |
225 int xfixes_error_base; | |
226 if (!XFixesQueryExtension(display_, &xfixes_event_base, &xfixes_error_base)) { | |
227 LOG(INFO) << "X server does not support XFixes."; | |
228 return; | 257 return; |
229 } | 258 } |
259 | |
260 // Check for XDamage extension. | |
230 if (!XDamageQueryExtension(display_, &damage_event_base_, | 261 if (!XDamageQueryExtension(display_, &damage_event_base_, |
231 &damage_error_base_)) { | 262 &damage_error_base_)) { |
232 LOG(INFO) << "X server does not support XDamage."; | 263 LOG(INFO) << "X server does not support XDamage."; |
233 return; | 264 return; |
234 } | 265 } |
235 | 266 |
236 // TODO(lambroslambrou): Disable DAMAGE in situations where it is known | 267 // TODO(lambroslambrou): Disable DAMAGE in situations where it is known |
237 // to fail, such as when Desktop Effects are enabled, with graphics | 268 // to fail, such as when Desktop Effects are enabled, with graphics |
238 // drivers (nVidia, ATI) that fail to report DAMAGE notifications | 269 // drivers (nVidia, ATI) that fail to report DAMAGE notifications |
239 // properly. | 270 // properly. |
(...skipping 11 matching lines...) Expand all Loading... | |
251 if (!damage_region_) { | 282 if (!damage_region_) { |
252 XDamageDestroy(display_, damage_handle_); | 283 XDamageDestroy(display_, damage_handle_); |
253 LOG(ERROR) << "Unable to create XFixes region."; | 284 LOG(ERROR) << "Unable to create XFixes region."; |
254 return; | 285 return; |
255 } | 286 } |
256 | 287 |
257 use_damage_ = true; | 288 use_damage_ = true; |
258 LOG(INFO) << "Using XDamage extension."; | 289 LOG(INFO) << "Using XDamage extension."; |
259 } | 290 } |
260 | 291 |
261 void CapturerLinux::Start() { | 292 void CapturerLinux::Start( |
293 const CursorShapeChangedCallback& callback) { | |
294 cursor_shape_changed_callback_ = callback; | |
262 } | 295 } |
263 | 296 |
264 void CapturerLinux::Stop() { | 297 void CapturerLinux::Stop() { |
265 } | 298 } |
266 | 299 |
267 void CapturerLinux::ScreenConfigurationChanged() { | 300 void CapturerLinux::ScreenConfigurationChanged() { |
268 last_buffer_ = NULL; | 301 last_buffer_ = NULL; |
269 for (int i = 0; i < kNumBuffers; ++i) { | 302 for (int i = 0; i < kNumBuffers; ++i) { |
270 buffers_[i].set_needs_update(); | 303 buffers_[i].set_needs_update(); |
271 } | 304 } |
(...skipping 17 matching lines...) Expand all Loading... | |
289 helper_.InvalidateScreen(size); | 322 helper_.InvalidateScreen(size); |
290 } | 323 } |
291 | 324 |
292 void CapturerLinux::InvalidateFullScreen() { | 325 void CapturerLinux::InvalidateFullScreen() { |
293 helper_.InvalidateFullScreen(); | 326 helper_.InvalidateFullScreen(); |
294 last_buffer_ = NULL; | 327 last_buffer_ = NULL; |
295 } | 328 } |
296 | 329 |
297 void CapturerLinux::CaptureInvalidRegion( | 330 void CapturerLinux::CaptureInvalidRegion( |
298 const CaptureCompletedCallback& callback) { | 331 const CaptureCompletedCallback& callback) { |
299 // TODO(lambroslambrou): In the non-DAMAGE case, there should be no need | 332 // Process XEvents for XDamage and cursor shape tracking. |
300 // for any X event processing in this class. | |
301 ProcessPendingXEvents(); | 333 ProcessPendingXEvents(); |
302 | 334 |
303 // Resize the current buffer if there was a recent change of | 335 // Resize the current buffer if there was a recent change of |
304 // screen-resolution. | 336 // screen-resolution. |
305 VideoFrameBuffer ¤t = buffers_[current_buffer_]; | 337 VideoFrameBuffer ¤t = buffers_[current_buffer_]; |
306 current.Update(display_, root_window_); | 338 current.Update(display_, root_window_); |
307 // Also refresh the Differ helper used by CaptureFrame(), if needed. | 339 // Also refresh the Differ helper used by CaptureFrame(), if needed. |
308 if (!use_damage_ && !last_buffer_) { | 340 if (!use_damage_ && !last_buffer_) { |
309 differ_.reset(new Differ(current.size().width(), current.size().height(), | 341 differ_.reset(new Differ(current.size().width(), current.size().height(), |
310 kBytesPerPixel, current.bytes_per_row())); | 342 kBytesPerPixel, current.bytes_per_row())); |
311 } | 343 } |
312 | 344 |
313 scoped_refptr<CaptureData> capture_data(CaptureFrame()); | 345 scoped_refptr<CaptureData> capture_data(CaptureFrame()); |
314 | 346 |
315 current_buffer_ = (current_buffer_ + 1) % kNumBuffers; | 347 current_buffer_ = (current_buffer_ + 1) % kNumBuffers; |
316 | 348 |
317 callback.Run(capture_data); | 349 callback.Run(capture_data); |
318 } | 350 } |
319 | 351 |
320 void CapturerLinux::ProcessPendingXEvents() { | 352 void CapturerLinux::ProcessPendingXEvents() { |
321 // Find the number of events that are outstanding "now." We don't just loop | 353 // Find the number of events that are outstanding "now." We don't just loop |
322 // on XPending because we want to guarantee this terminates. | 354 // on XPending because we want to guarantee this terminates. |
323 int events_to_process = XPending(display_); | 355 int events_to_process = XPending(display_); |
324 XEvent e; | 356 XEvent e; |
325 | 357 |
326 for (int i = 0; i < events_to_process; i++) { | 358 for (int i = 0; i < events_to_process; i++) { |
327 XNextEvent(display_, &e); | 359 XNextEvent(display_, &e); |
328 if (use_damage_ && (e.type == damage_event_base_ + XDamageNotify)) { | 360 if (use_damage_ && (e.type == damage_event_base_ + XDamageNotify)) { |
329 XDamageNotifyEvent *event = reinterpret_cast<XDamageNotifyEvent*>(&e); | 361 XDamageNotifyEvent* event = reinterpret_cast<XDamageNotifyEvent*>(&e); |
330 DCHECK(event->level == XDamageReportNonEmpty); | 362 DCHECK(event->level == XDamageReportNonEmpty); |
331 } else if (e.type == ConfigureNotify) { | 363 } else if (e.type == ConfigureNotify) { |
332 ScreenConfigurationChanged(); | 364 ScreenConfigurationChanged(); |
365 } else if (has_xfixes_ && | |
366 e.type == xfixes_event_base_ + XFixesCursorNotify) { | |
367 XFixesCursorNotifyEvent* cne; | |
368 cne = reinterpret_cast<XFixesCursorNotifyEvent*>(&e); | |
369 if (cne->subtype == XFixesDisplayCursorNotify) { | |
370 CaptureCursor(); | |
371 } | |
333 } else { | 372 } else { |
334 LOG(WARNING) << "Got unknown event type: " << e.type; | 373 LOG(WARNING) << "Got unknown event type: " << e.type; |
335 } | 374 } |
336 } | 375 } |
337 } | 376 } |
338 | 377 |
378 void CapturerLinux::CaptureCursor() { | |
379 DCHECK(has_xfixes_); | |
380 if (cursor_shape_changed_callback_.is_null()) | |
381 return; | |
382 | |
383 XFixesCursorImage* img = XFixesGetCursorImage(display_); | |
384 if (!img) { | |
385 return; | |
386 } | |
387 | |
388 int width = img->width; | |
389 int height = img->height; | |
390 SkISize size = SkISize::Make(width, height); | |
391 SkISize hotspot = SkISize::Make(img->xhot, img->yhot); | |
392 int total_bytes = width * height * kBytesPerPixel; | |
393 | |
394 scoped_ptr<protocol::CursorShapeInfo> cursor_proto( | |
395 new protocol::CursorShapeInfo()); | |
396 cursor_proto->set_width(width); | |
397 cursor_proto->set_height(height); | |
398 cursor_proto->set_hotspot_x(img->xhot); | |
399 cursor_proto->set_hotspot_y(img->yhot); | |
400 | |
401 cursor_proto->mutable_data()->resize(total_bytes); | |
402 uint8* proto_data = const_cast<uint8*>(reinterpret_cast<const uint8*>( | |
403 cursor_proto->mutable_data()->data())); | |
404 | |
405 // Xlib stores 32-bit data in longs, even if longs are 64-bits long. | |
406 unsigned long* src = img->pixels; | |
407 uint32* dst = reinterpret_cast<uint32*>(proto_data); | |
408 uint32* dst_end = dst + (width * height); | |
409 while (dst < dst_end) { | |
410 *dst++ = *src++; | |
Wez
2012/05/30 23:42:42
Do you need an explicit down-cast to avoid compile
garykac
2012/05/31 00:29:41
There weren't any warnings, but I added a static_c
| |
411 } | |
412 XFree(img); | |
413 | |
414 cursor_shape_changed_callback_.Run(cursor_proto.Pass()); | |
415 } | |
416 | |
339 CaptureData* CapturerLinux::CaptureFrame() { | 417 CaptureData* CapturerLinux::CaptureFrame() { |
340 VideoFrameBuffer& buffer = buffers_[current_buffer_]; | 418 VideoFrameBuffer& buffer = buffers_[current_buffer_]; |
341 DataPlanes planes; | 419 DataPlanes planes; |
342 planes.data[0] = buffer.ptr(); | 420 planes.data[0] = buffer.ptr(); |
343 planes.strides[0] = buffer.bytes_per_row(); | 421 planes.strides[0] = buffer.bytes_per_row(); |
344 | 422 |
345 CaptureData* capture_data = new CaptureData(planes, buffer.size(), | 423 CaptureData* capture_data = new CaptureData(planes, buffer.size(), |
346 media::VideoFrame::RGB32); | 424 media::VideoFrame::RGB32); |
347 | 425 |
348 // Pass the screen size to the helper, so it can clip the invalid region if it | 426 // Pass the screen size to the helper, so it can clip the invalid region if it |
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
551 } | 629 } |
552 return capturer; | 630 return capturer; |
553 } | 631 } |
554 | 632 |
555 // static | 633 // static |
556 void Capturer::EnableXDamage(bool enable) { | 634 void Capturer::EnableXDamage(bool enable) { |
557 g_should_use_x_damage = enable; | 635 g_should_use_x_damage = enable; |
558 } | 636 } |
559 | 637 |
560 } // namespace remoting | 638 } // namespace remoting |
OLD | NEW |