OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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_linux.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 | 10 |
11 #include <set> | 11 #include <set> |
12 | 12 |
13 #include "base/basictypes.h" | |
13 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/memory/scoped_ptr.h" | |
14 #include "remoting/base/types.h" | 16 #include "remoting/base/types.h" |
15 #include "remoting/host/capturer_helper.h" | 17 #include "remoting/host/capturer_helper.h" |
16 #include "remoting/host/x_server_pixel_buffer.h" | 18 #include "remoting/host/x_server_pixel_buffer.h" |
17 | 19 |
18 namespace remoting { | 20 namespace remoting { |
19 | 21 |
20 // Private Implementation pattern to avoid leaking the X11 types into the header | 22 namespace { |
21 // file. | 23 |
22 class CapturerLinuxPimpl : public Capturer { | 24 // A class to perform capturing for Linux. |
25 class CapturerLinux : public Capturer { | |
23 public: | 26 public: |
24 CapturerLinuxPimpl(); | 27 CapturerLinux(); |
25 virtual ~CapturerLinuxPimpl(); | 28 virtual ~CapturerLinux(); |
26 | 29 |
27 bool Init(); // TODO(ajwong): Do we really want this to be synchronous? | |
28 | 30 |
29 // Capturer interface. | 31 // Capturer interface. |
30 virtual void ScreenConfigurationChanged(); | 32 virtual void ScreenConfigurationChanged() OVERRIDE; |
31 virtual media::VideoFrame::Format pixel_format() const; | 33 virtual media::VideoFrame::Format pixel_format() const OVERRIDE; |
32 virtual void ClearInvalidRects(); | 34 virtual void ClearInvalidRects() OVERRIDE; |
33 virtual void InvalidateRects(const InvalidRects& inval_rects); | 35 virtual void InvalidateRects(const InvalidRects& inval_rects) OVERRIDE; |
34 virtual void InvalidateScreen(const gfx::Size& size); | 36 virtual void InvalidateScreen(const gfx::Size& size) OVERRIDE; |
35 virtual void InvalidateFullScreen(); | 37 virtual void InvalidateFullScreen() OVERRIDE; |
36 virtual void CaptureInvalidRects(CaptureCompletedCallback* callback); | 38 virtual void CaptureInvalidRects(CaptureCompletedCallback* callback) OVERRIDE; |
37 virtual const gfx::Size& size_most_recent() const; | 39 virtual const gfx::Size& size_most_recent() const OVERRIDE; |
38 | 40 |
39 private: | 41 private: |
42 bool Init(); // TODO(ajwong): Do we really want this to be synchronous? | |
40 void CalculateInvalidRects(); | 43 void CalculateInvalidRects(); |
41 void CaptureRects(const InvalidRects& rects, | 44 void CaptureRects(const InvalidRects& rects, |
42 Capturer::CaptureCompletedCallback* callback); | 45 Capturer::CaptureCompletedCallback* callback); |
43 | 46 |
44 void DeinitXlib(); | 47 void DeinitXlib(); |
45 // We expose two forms of blitting to handle variations in the pixel format. | 48 // We expose two forms of blitting to handle variations in the pixel format. |
46 // In FastBlit, the operation is effectively a memcpy. | 49 // In FastBlit, the operation is effectively a memcpy. |
47 void FastBlit(uint8* image, const gfx::Rect& rect, CaptureData* capture_data); | 50 void FastBlit(uint8* image, const gfx::Rect& rect, CaptureData* capture_data); |
48 void SlowBlit(uint8* image, const gfx::Rect& rect, CaptureData* capture_data); | 51 void SlowBlit(uint8* image, const gfx::Rect& rect, CaptureData* capture_data); |
49 | 52 |
(...skipping 26 matching lines...) Expand all Loading... | |
76 bool capture_fullscreen_; | 79 bool capture_fullscreen_; |
77 | 80 |
78 // Format of pixels returned in buffer. | 81 // Format of pixels returned in buffer. |
79 media::VideoFrame::Format pixel_format_; | 82 media::VideoFrame::Format pixel_format_; |
80 | 83 |
81 // Invalid rects in the last capture. This is used to synchronize current with | 84 // Invalid rects in the last capture. This is used to synchronize current with |
82 // the previous buffer used. | 85 // the previous buffer used. |
83 InvalidRects last_invalid_rects_; | 86 InvalidRects last_invalid_rects_; |
84 | 87 |
85 // Last capture buffer used. | 88 // Last capture buffer used. |
86 uint8* last_buffer_; | 89 uint8* last_buffer_; |
Lambros
2011/04/01 15:54:00
Nit: Maybe a bit too pedantic, but do we want DISA
dmac
2011/04/01 21:15:07
Done.
| |
87 }; | 90 }; |
88 | 91 |
89 CapturerLinux::CapturerLinux() | 92 CapturerLinux::CapturerLinux() |
90 : pimpl_(new CapturerLinuxPimpl()) { | |
91 // TODO(ajwong): This should be moved into an Init() method on Capturer | |
92 // itself. Then we can remove the CHECK. | |
93 CHECK(pimpl_->Init()); | |
94 } | |
95 | |
96 CapturerLinux::~CapturerLinux() { | |
97 } | |
98 | |
99 void CapturerLinux::ScreenConfigurationChanged() { | |
100 pimpl_->ScreenConfigurationChanged(); | |
101 } | |
102 | |
103 media::VideoFrame::Format CapturerLinux::pixel_format() const { | |
104 return pimpl_->pixel_format(); | |
105 } | |
106 | |
107 void CapturerLinux::ClearInvalidRects() { | |
108 pimpl_->ClearInvalidRects(); | |
109 } | |
110 | |
111 void CapturerLinux::InvalidateRects(const InvalidRects& inval_rects) { | |
112 pimpl_->InvalidateRects(inval_rects); | |
113 } | |
114 | |
115 void CapturerLinux::InvalidateScreen(const gfx::Size& size) { | |
116 pimpl_->InvalidateScreen(size); | |
117 } | |
118 | |
119 void CapturerLinux::InvalidateFullScreen() { | |
120 pimpl_->InvalidateFullScreen(); | |
121 } | |
122 | |
123 void CapturerLinux::CaptureInvalidRects(CaptureCompletedCallback* callback) { | |
124 pimpl_->CaptureInvalidRects(callback); | |
125 } | |
126 | |
127 const gfx::Size& CapturerLinux::size_most_recent() const { | |
128 return pimpl_->size_most_recent(); | |
129 } | |
130 | |
131 CapturerLinuxPimpl::CapturerLinuxPimpl() | |
132 : display_(NULL), | 93 : display_(NULL), |
133 gc_(NULL), | 94 gc_(NULL), |
134 root_window_(BadValue), | 95 root_window_(BadValue), |
135 width_(0), | 96 width_(0), |
136 height_(0), | 97 height_(0), |
137 damage_handle_(BadValue), | 98 damage_handle_(BadValue), |
138 damage_event_base_(-1), | 99 damage_event_base_(-1), |
139 damage_error_base_(-1), | 100 damage_error_base_(-1), |
140 current_buffer_(0), | 101 current_buffer_(0), |
141 stride_(0), | 102 stride_(0), |
142 capture_fullscreen_(true), | 103 capture_fullscreen_(true), |
143 pixel_format_(media::VideoFrame::RGB32), | 104 pixel_format_(media::VideoFrame::RGB32), |
144 last_buffer_(NULL) { | 105 last_buffer_(NULL) { |
145 for (int i = 0; i < kNumBuffers; i++) { | 106 for (int i = 0; i < kNumBuffers; i++) { |
146 buffers_[i] = NULL; | 107 buffers_[i] = NULL; |
147 } | 108 } |
109 CHECK(Init()); | |
148 } | 110 } |
149 | 111 |
150 CapturerLinuxPimpl::~CapturerLinuxPimpl() { | 112 CapturerLinux::~CapturerLinux() { |
151 DeinitXlib(); | 113 DeinitXlib(); |
152 | 114 |
153 for (int i = 0; i < kNumBuffers; i++) { | 115 for (int i = 0; i < kNumBuffers; i++) { |
154 delete [] buffers_[i]; | 116 delete [] buffers_[i]; |
155 buffers_[i] = NULL; | 117 buffers_[i] = NULL; |
156 } | 118 } |
157 } | 119 } |
158 | 120 |
159 bool CapturerLinuxPimpl::Init() { | 121 bool CapturerLinux::Init() { |
160 // TODO(ajwong): We should specify the display string we are attaching to | 122 // TODO(ajwong): We should specify the display string we are attaching to |
161 // in the constructor. | 123 // in the constructor. |
162 display_ = XOpenDisplay(NULL); | 124 display_ = XOpenDisplay(NULL); |
163 if (!display_) { | 125 if (!display_) { |
164 LOG(ERROR) << "Unable to open display"; | 126 LOG(ERROR) << "Unable to open display"; |
165 return false; | 127 return false; |
166 } | 128 } |
167 | 129 |
168 x_server_pixel_buffer_.Init(display_); | 130 x_server_pixel_buffer_.Init(display_); |
169 | 131 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
209 VLOG(1) << "Initialized with Geometry: " << width_ << "x" << height_; | 171 VLOG(1) << "Initialized with Geometry: " << width_ << "x" << height_; |
210 | 172 |
211 // Allocate the screen buffers. | 173 // Allocate the screen buffers. |
212 for (int i = 0; i < kNumBuffers; i++) { | 174 for (int i = 0; i < kNumBuffers; i++) { |
213 buffers_[i] = new uint8[width_ * height_ * kBytesPerPixel]; | 175 buffers_[i] = new uint8[width_ * height_ * kBytesPerPixel]; |
214 } | 176 } |
215 | 177 |
216 return true; | 178 return true; |
217 } | 179 } |
218 | 180 |
219 void CapturerLinuxPimpl::ScreenConfigurationChanged() { | 181 void CapturerLinux::ScreenConfigurationChanged() { |
220 // TODO(ajwong): Support resolution changes. | 182 // TODO(ajwong): Support resolution changes. |
221 NOTIMPLEMENTED(); | 183 NOTIMPLEMENTED(); |
222 } | 184 } |
223 | 185 |
224 media::VideoFrame::Format CapturerLinuxPimpl::pixel_format() const { | 186 media::VideoFrame::Format CapturerLinux::pixel_format() const { |
225 return pixel_format_; | 187 return pixel_format_; |
226 } | 188 } |
227 | 189 |
228 void CapturerLinuxPimpl::ClearInvalidRects() { | 190 void CapturerLinux::ClearInvalidRects() { |
229 helper_.ClearInvalidRects(); | 191 helper_.ClearInvalidRects(); |
230 } | 192 } |
231 | 193 |
232 void CapturerLinuxPimpl::InvalidateRects(const InvalidRects& inval_rects) { | 194 void CapturerLinux::InvalidateRects(const InvalidRects& inval_rects) { |
233 helper_.InvalidateRects(inval_rects); | 195 helper_.InvalidateRects(inval_rects); |
234 } | 196 } |
235 | 197 |
236 void CapturerLinuxPimpl::InvalidateScreen(const gfx::Size& size) { | 198 void CapturerLinux::InvalidateScreen(const gfx::Size& size) { |
237 helper_.InvalidateScreen(size); | 199 helper_.InvalidateScreen(size); |
238 } | 200 } |
239 | 201 |
240 void CapturerLinuxPimpl::InvalidateFullScreen() { | 202 void CapturerLinux::InvalidateFullScreen() { |
241 helper_.InvalidateFullScreen(); | 203 helper_.InvalidateFullScreen(); |
242 } | 204 } |
243 | 205 |
244 void CapturerLinuxPimpl::CaptureInvalidRects( | 206 void CapturerLinux::CaptureInvalidRects( |
245 CaptureCompletedCallback* callback) { | 207 CaptureCompletedCallback* callback) { |
246 CalculateInvalidRects(); | 208 CalculateInvalidRects(); |
247 | 209 |
248 InvalidRects rects; | 210 InvalidRects rects; |
249 helper_.SwapInvalidRects(rects); | 211 helper_.SwapInvalidRects(rects); |
250 | 212 |
251 CaptureRects(rects, callback); | 213 CaptureRects(rects, callback); |
252 } | 214 } |
253 | 215 |
254 void CapturerLinuxPimpl::CalculateInvalidRects() { | 216 void CapturerLinux::CalculateInvalidRects() { |
255 if (helper_.IsCaptureFullScreen(gfx::Size(width_, height_))) | 217 if (helper_.IsCaptureFullScreen(gfx::Size(width_, height_))) |
256 capture_fullscreen_ = true; | 218 capture_fullscreen_ = true; |
257 | 219 |
258 // TODO(ajwong): The capture_fullscreen_ logic here is very ugly. Refactor. | 220 // TODO(ajwong): The capture_fullscreen_ logic here is very ugly. Refactor. |
259 | 221 |
260 // Find the number of events that are outstanding "now." We don't just loop | 222 // Find the number of events that are outstanding "now." We don't just loop |
261 // on XPending because we want to guarantee this terminates. | 223 // on XPending because we want to guarantee this terminates. |
262 int events_to_process = XPending(display_); | 224 int events_to_process = XPending(display_); |
263 XEvent e; | 225 XEvent e; |
264 InvalidRects invalid_rects; | 226 InvalidRects invalid_rects; |
(...skipping 22 matching lines...) Expand all Loading... | |
287 | 249 |
288 if (capture_fullscreen_) { | 250 if (capture_fullscreen_) { |
289 // TODO(hclam): Check the new dimension again. | 251 // TODO(hclam): Check the new dimension again. |
290 helper_.InvalidateScreen(gfx::Size(width_, height_)); | 252 helper_.InvalidateScreen(gfx::Size(width_, height_)); |
291 capture_fullscreen_ = false; | 253 capture_fullscreen_ = false; |
292 } else { | 254 } else { |
293 helper_.InvalidateRects(invalid_rects); | 255 helper_.InvalidateRects(invalid_rects); |
294 } | 256 } |
295 } | 257 } |
296 | 258 |
297 void CapturerLinuxPimpl::CaptureRects( | 259 void CapturerLinux::CaptureRects( |
298 const InvalidRects& rects, | 260 const InvalidRects& rects, |
299 Capturer::CaptureCompletedCallback* callback) { | 261 Capturer::CaptureCompletedCallback* callback) { |
300 scoped_ptr<CaptureCompletedCallback> callback_deleter(callback); | 262 scoped_ptr<CaptureCompletedCallback> callback_deleter(callback); |
301 | 263 |
302 uint8* buffer = buffers_[current_buffer_]; | 264 uint8* buffer = buffers_[current_buffer_]; |
303 DataPlanes planes; | 265 DataPlanes planes; |
304 planes.data[0] = buffer; | 266 planes.data[0] = buffer; |
305 planes.strides[0] = stride_; | 267 planes.strides[0] = stride_; |
306 | 268 |
307 scoped_refptr<CaptureData> capture_data(new CaptureData( | 269 scoped_refptr<CaptureData> capture_data(new CaptureData( |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
347 capture_data->mutable_dirty_rects() = rects; | 309 capture_data->mutable_dirty_rects() = rects; |
348 last_invalid_rects_ = rects; | 310 last_invalid_rects_ = rects; |
349 last_buffer_ = buffer; | 311 last_buffer_ = buffer; |
350 | 312 |
351 current_buffer_ = (current_buffer_ + 1) % kNumBuffers; | 313 current_buffer_ = (current_buffer_ + 1) % kNumBuffers; |
352 helper_.set_size_most_recent(capture_data->size()); | 314 helper_.set_size_most_recent(capture_data->size()); |
353 | 315 |
354 callback->Run(capture_data); | 316 callback->Run(capture_data); |
355 } | 317 } |
356 | 318 |
357 void CapturerLinuxPimpl::DeinitXlib() { | 319 void CapturerLinux::DeinitXlib() { |
358 if (gc_) { | 320 if (gc_) { |
359 XFreeGC(display_, gc_); | 321 XFreeGC(display_, gc_); |
360 gc_ = NULL; | 322 gc_ = NULL; |
361 } | 323 } |
362 | 324 |
363 if (display_) { | 325 if (display_) { |
364 XCloseDisplay(display_); | 326 XCloseDisplay(display_); |
365 display_ = NULL; | 327 display_ = NULL; |
366 } | 328 } |
367 } | 329 } |
368 | 330 |
369 void CapturerLinuxPimpl::FastBlit(uint8* image, const gfx::Rect& rect, | 331 void CapturerLinux::FastBlit(uint8* image, const gfx::Rect& rect, |
370 CaptureData* capture_data) { | 332 CaptureData* capture_data) { |
371 uint8* src_pos = image; | 333 uint8* src_pos = image; |
372 int src_stride = x_server_pixel_buffer_.GetStride(); | 334 int src_stride = x_server_pixel_buffer_.GetStride(); |
373 int dst_x = rect.x(), dst_y = rect.y(); | 335 int dst_x = rect.x(), dst_y = rect.y(); |
374 | 336 |
375 DataPlanes planes = capture_data->data_planes(); | 337 DataPlanes planes = capture_data->data_planes(); |
376 uint8* dst_buffer = planes.data[0]; | 338 uint8* dst_buffer = planes.data[0]; |
377 | 339 |
378 const int dst_stride = planes.strides[0]; | 340 const int dst_stride = planes.strides[0]; |
379 | 341 |
380 uint8* dst_pos = dst_buffer + dst_stride * dst_y; | 342 uint8* dst_pos = dst_buffer + dst_stride * dst_y; |
381 dst_pos += dst_x * kBytesPerPixel; | 343 dst_pos += dst_x * kBytesPerPixel; |
382 | 344 |
383 int height = rect.height(), row_bytes = rect.width() * kBytesPerPixel; | 345 int height = rect.height(), row_bytes = rect.width() * kBytesPerPixel; |
384 for (int y = 0; y < height; ++y) { | 346 for (int y = 0; y < height; ++y) { |
385 memcpy(dst_pos, src_pos, row_bytes); | 347 memcpy(dst_pos, src_pos, row_bytes); |
386 src_pos += src_stride; | 348 src_pos += src_stride; |
387 dst_pos += dst_stride; | 349 dst_pos += dst_stride; |
388 } | 350 } |
389 } | 351 } |
390 | 352 |
391 void CapturerLinuxPimpl::SlowBlit(uint8* image, const gfx::Rect& rect, | 353 void CapturerLinux::SlowBlit(uint8* image, const gfx::Rect& rect, |
392 CaptureData* capture_data) { | 354 CaptureData* capture_data) { |
393 DataPlanes planes = capture_data->data_planes(); | 355 DataPlanes planes = capture_data->data_planes(); |
394 uint8* dst_buffer = planes.data[0]; | 356 uint8* dst_buffer = planes.data[0]; |
395 const int dst_stride = planes.strides[0]; | 357 const int dst_stride = planes.strides[0]; |
396 int src_stride = x_server_pixel_buffer_.GetStride(); | 358 int src_stride = x_server_pixel_buffer_.GetStride(); |
397 int dst_x = rect.x(), dst_y = rect.y(); | 359 int dst_x = rect.x(), dst_y = rect.y(); |
398 int width = rect.width(), height = rect.height(); | 360 int width = rect.width(), height = rect.height(); |
399 | 361 |
400 unsigned int red_mask = x_server_pixel_buffer_.GetRedMask(); | 362 unsigned int red_mask = x_server_pixel_buffer_.GetRedMask(); |
401 unsigned int blue_mask = x_server_pixel_buffer_.GetBlueMask(); | 363 unsigned int blue_mask = x_server_pixel_buffer_.GetBlueMask(); |
(...skipping 30 matching lines...) Expand all Loading... | |
432 uint32_t b = (((pixel & blue_mask) >> blue_shift) * 255) / max_blue; | 394 uint32_t b = (((pixel & blue_mask) >> blue_shift) * 255) / max_blue; |
433 uint32_t g = (((pixel & green_mask) >> green_shift) * 255) / max_green; | 395 uint32_t g = (((pixel & green_mask) >> green_shift) * 255) / max_green; |
434 // Write as 32-bit RGB. | 396 // Write as 32-bit RGB. |
435 dst_pos_32[x] = r << 16 | g << 8 | b; | 397 dst_pos_32[x] = r << 16 | g << 8 | b; |
436 } | 398 } |
437 dst_pos += dst_stride; | 399 dst_pos += dst_stride; |
438 src_pos += src_stride; | 400 src_pos += src_stride; |
439 } | 401 } |
440 } | 402 } |
441 | 403 |
442 const gfx::Size& CapturerLinuxPimpl::size_most_recent() const { | 404 const gfx::Size& CapturerLinux::size_most_recent() const { |
443 return helper_.size_most_recent(); | 405 return helper_.size_most_recent(); |
444 } | 406 } |
445 | 407 |
408 } // namespace | |
409 | |
446 // static | 410 // static |
447 Capturer* Capturer::Create() { | 411 Capturer* Capturer::Create() { |
448 return new CapturerLinux(); | 412 return new CapturerLinux(); |
449 } | 413 } |
450 | 414 |
451 } // namespace remoting | 415 } // namespace remoting |
OLD | NEW |