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 27 matching lines...) Expand all Loading... |
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_; |
| 90 |
| 91 DISALLOW_COPY_AND_ASSIGN(CapturerLinux); |
87 }; | 92 }; |
88 | 93 |
89 CapturerLinux::CapturerLinux() | 94 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), | 95 : display_(NULL), |
133 gc_(NULL), | 96 gc_(NULL), |
134 root_window_(BadValue), | 97 root_window_(BadValue), |
135 width_(0), | 98 width_(0), |
136 height_(0), | 99 height_(0), |
137 damage_handle_(BadValue), | 100 damage_handle_(BadValue), |
138 damage_event_base_(-1), | 101 damage_event_base_(-1), |
139 damage_error_base_(-1), | 102 damage_error_base_(-1), |
140 current_buffer_(0), | 103 current_buffer_(0), |
141 stride_(0), | 104 stride_(0), |
142 capture_fullscreen_(true), | 105 capture_fullscreen_(true), |
143 pixel_format_(media::VideoFrame::RGB32), | 106 pixel_format_(media::VideoFrame::RGB32), |
144 last_buffer_(NULL) { | 107 last_buffer_(NULL) { |
145 for (int i = 0; i < kNumBuffers; i++) { | 108 for (int i = 0; i < kNumBuffers; i++) { |
146 buffers_[i] = NULL; | 109 buffers_[i] = NULL; |
147 } | 110 } |
| 111 CHECK(Init()); |
148 } | 112 } |
149 | 113 |
150 CapturerLinuxPimpl::~CapturerLinuxPimpl() { | 114 CapturerLinux::~CapturerLinux() { |
151 DeinitXlib(); | 115 DeinitXlib(); |
152 | 116 |
153 for (int i = 0; i < kNumBuffers; i++) { | 117 for (int i = 0; i < kNumBuffers; i++) { |
154 delete [] buffers_[i]; | 118 delete [] buffers_[i]; |
155 buffers_[i] = NULL; | 119 buffers_[i] = NULL; |
156 } | 120 } |
157 } | 121 } |
158 | 122 |
159 bool CapturerLinuxPimpl::Init() { | 123 bool CapturerLinux::Init() { |
160 // TODO(ajwong): We should specify the display string we are attaching to | 124 // TODO(ajwong): We should specify the display string we are attaching to |
161 // in the constructor. | 125 // in the constructor. |
162 display_ = XOpenDisplay(NULL); | 126 display_ = XOpenDisplay(NULL); |
163 if (!display_) { | 127 if (!display_) { |
164 LOG(ERROR) << "Unable to open display"; | 128 LOG(ERROR) << "Unable to open display"; |
165 return false; | 129 return false; |
166 } | 130 } |
167 | 131 |
168 x_server_pixel_buffer_.Init(display_); | 132 x_server_pixel_buffer_.Init(display_); |
169 | 133 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
209 VLOG(1) << "Initialized with Geometry: " << width_ << "x" << height_; | 173 VLOG(1) << "Initialized with Geometry: " << width_ << "x" << height_; |
210 | 174 |
211 // Allocate the screen buffers. | 175 // Allocate the screen buffers. |
212 for (int i = 0; i < kNumBuffers; i++) { | 176 for (int i = 0; i < kNumBuffers; i++) { |
213 buffers_[i] = new uint8[width_ * height_ * kBytesPerPixel]; | 177 buffers_[i] = new uint8[width_ * height_ * kBytesPerPixel]; |
214 } | 178 } |
215 | 179 |
216 return true; | 180 return true; |
217 } | 181 } |
218 | 182 |
219 void CapturerLinuxPimpl::ScreenConfigurationChanged() { | 183 void CapturerLinux::ScreenConfigurationChanged() { |
220 // TODO(ajwong): Support resolution changes. | 184 // TODO(ajwong): Support resolution changes. |
221 NOTIMPLEMENTED(); | 185 NOTIMPLEMENTED(); |
222 } | 186 } |
223 | 187 |
224 media::VideoFrame::Format CapturerLinuxPimpl::pixel_format() const { | 188 media::VideoFrame::Format CapturerLinux::pixel_format() const { |
225 return pixel_format_; | 189 return pixel_format_; |
226 } | 190 } |
227 | 191 |
228 void CapturerLinuxPimpl::ClearInvalidRects() { | 192 void CapturerLinux::ClearInvalidRects() { |
229 helper_.ClearInvalidRects(); | 193 helper_.ClearInvalidRects(); |
230 } | 194 } |
231 | 195 |
232 void CapturerLinuxPimpl::InvalidateRects(const InvalidRects& inval_rects) { | 196 void CapturerLinux::InvalidateRects(const InvalidRects& inval_rects) { |
233 helper_.InvalidateRects(inval_rects); | 197 helper_.InvalidateRects(inval_rects); |
234 } | 198 } |
235 | 199 |
236 void CapturerLinuxPimpl::InvalidateScreen(const gfx::Size& size) { | 200 void CapturerLinux::InvalidateScreen(const gfx::Size& size) { |
237 helper_.InvalidateScreen(size); | 201 helper_.InvalidateScreen(size); |
238 } | 202 } |
239 | 203 |
240 void CapturerLinuxPimpl::InvalidateFullScreen() { | 204 void CapturerLinux::InvalidateFullScreen() { |
241 helper_.InvalidateFullScreen(); | 205 helper_.InvalidateFullScreen(); |
242 } | 206 } |
243 | 207 |
244 void CapturerLinuxPimpl::CaptureInvalidRects( | 208 void CapturerLinux::CaptureInvalidRects( |
245 CaptureCompletedCallback* callback) { | 209 CaptureCompletedCallback* callback) { |
246 CalculateInvalidRects(); | 210 CalculateInvalidRects(); |
247 | 211 |
248 InvalidRects rects; | 212 InvalidRects rects; |
249 helper_.SwapInvalidRects(rects); | 213 helper_.SwapInvalidRects(rects); |
250 | 214 |
251 CaptureRects(rects, callback); | 215 CaptureRects(rects, callback); |
252 } | 216 } |
253 | 217 |
254 void CapturerLinuxPimpl::CalculateInvalidRects() { | 218 void CapturerLinux::CalculateInvalidRects() { |
255 if (helper_.IsCaptureFullScreen(gfx::Size(width_, height_))) | 219 if (helper_.IsCaptureFullScreen(gfx::Size(width_, height_))) |
256 capture_fullscreen_ = true; | 220 capture_fullscreen_ = true; |
257 | 221 |
258 // TODO(ajwong): The capture_fullscreen_ logic here is very ugly. Refactor. | 222 // TODO(ajwong): The capture_fullscreen_ logic here is very ugly. Refactor. |
259 | 223 |
260 // Find the number of events that are outstanding "now." We don't just loop | 224 // Find the number of events that are outstanding "now." We don't just loop |
261 // on XPending because we want to guarantee this terminates. | 225 // on XPending because we want to guarantee this terminates. |
262 int events_to_process = XPending(display_); | 226 int events_to_process = XPending(display_); |
263 XEvent e; | 227 XEvent e; |
264 InvalidRects invalid_rects; | 228 InvalidRects invalid_rects; |
(...skipping 22 matching lines...) Expand all Loading... |
287 | 251 |
288 if (capture_fullscreen_) { | 252 if (capture_fullscreen_) { |
289 // TODO(hclam): Check the new dimension again. | 253 // TODO(hclam): Check the new dimension again. |
290 helper_.InvalidateScreen(gfx::Size(width_, height_)); | 254 helper_.InvalidateScreen(gfx::Size(width_, height_)); |
291 capture_fullscreen_ = false; | 255 capture_fullscreen_ = false; |
292 } else { | 256 } else { |
293 helper_.InvalidateRects(invalid_rects); | 257 helper_.InvalidateRects(invalid_rects); |
294 } | 258 } |
295 } | 259 } |
296 | 260 |
297 void CapturerLinuxPimpl::CaptureRects( | 261 void CapturerLinux::CaptureRects( |
298 const InvalidRects& rects, | 262 const InvalidRects& rects, |
299 Capturer::CaptureCompletedCallback* callback) { | 263 Capturer::CaptureCompletedCallback* callback) { |
300 scoped_ptr<CaptureCompletedCallback> callback_deleter(callback); | 264 scoped_ptr<CaptureCompletedCallback> callback_deleter(callback); |
301 | 265 |
302 uint8* buffer = buffers_[current_buffer_]; | 266 uint8* buffer = buffers_[current_buffer_]; |
303 DataPlanes planes; | 267 DataPlanes planes; |
304 planes.data[0] = buffer; | 268 planes.data[0] = buffer; |
305 planes.strides[0] = stride_; | 269 planes.strides[0] = stride_; |
306 | 270 |
307 scoped_refptr<CaptureData> capture_data(new CaptureData( | 271 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; | 311 capture_data->mutable_dirty_rects() = rects; |
348 last_invalid_rects_ = rects; | 312 last_invalid_rects_ = rects; |
349 last_buffer_ = buffer; | 313 last_buffer_ = buffer; |
350 | 314 |
351 current_buffer_ = (current_buffer_ + 1) % kNumBuffers; | 315 current_buffer_ = (current_buffer_ + 1) % kNumBuffers; |
352 helper_.set_size_most_recent(capture_data->size()); | 316 helper_.set_size_most_recent(capture_data->size()); |
353 | 317 |
354 callback->Run(capture_data); | 318 callback->Run(capture_data); |
355 } | 319 } |
356 | 320 |
357 void CapturerLinuxPimpl::DeinitXlib() { | 321 void CapturerLinux::DeinitXlib() { |
358 if (gc_) { | 322 if (gc_) { |
359 XFreeGC(display_, gc_); | 323 XFreeGC(display_, gc_); |
360 gc_ = NULL; | 324 gc_ = NULL; |
361 } | 325 } |
362 | 326 |
363 if (display_) { | 327 if (display_) { |
364 XCloseDisplay(display_); | 328 XCloseDisplay(display_); |
365 display_ = NULL; | 329 display_ = NULL; |
366 } | 330 } |
367 } | 331 } |
368 | 332 |
369 void CapturerLinuxPimpl::FastBlit(uint8* image, const gfx::Rect& rect, | 333 void CapturerLinux::FastBlit(uint8* image, const gfx::Rect& rect, |
370 CaptureData* capture_data) { | 334 CaptureData* capture_data) { |
371 uint8* src_pos = image; | 335 uint8* src_pos = image; |
372 int src_stride = x_server_pixel_buffer_.GetStride(); | 336 int src_stride = x_server_pixel_buffer_.GetStride(); |
373 int dst_x = rect.x(), dst_y = rect.y(); | 337 int dst_x = rect.x(), dst_y = rect.y(); |
374 | 338 |
375 DataPlanes planes = capture_data->data_planes(); | 339 DataPlanes planes = capture_data->data_planes(); |
376 uint8* dst_buffer = planes.data[0]; | 340 uint8* dst_buffer = planes.data[0]; |
377 | 341 |
378 const int dst_stride = planes.strides[0]; | 342 const int dst_stride = planes.strides[0]; |
379 | 343 |
380 uint8* dst_pos = dst_buffer + dst_stride * dst_y; | 344 uint8* dst_pos = dst_buffer + dst_stride * dst_y; |
381 dst_pos += dst_x * kBytesPerPixel; | 345 dst_pos += dst_x * kBytesPerPixel; |
382 | 346 |
383 int height = rect.height(), row_bytes = rect.width() * kBytesPerPixel; | 347 int height = rect.height(), row_bytes = rect.width() * kBytesPerPixel; |
384 for (int y = 0; y < height; ++y) { | 348 for (int y = 0; y < height; ++y) { |
385 memcpy(dst_pos, src_pos, row_bytes); | 349 memcpy(dst_pos, src_pos, row_bytes); |
386 src_pos += src_stride; | 350 src_pos += src_stride; |
387 dst_pos += dst_stride; | 351 dst_pos += dst_stride; |
388 } | 352 } |
389 } | 353 } |
390 | 354 |
391 void CapturerLinuxPimpl::SlowBlit(uint8* image, const gfx::Rect& rect, | 355 void CapturerLinux::SlowBlit(uint8* image, const gfx::Rect& rect, |
392 CaptureData* capture_data) { | 356 CaptureData* capture_data) { |
393 DataPlanes planes = capture_data->data_planes(); | 357 DataPlanes planes = capture_data->data_planes(); |
394 uint8* dst_buffer = planes.data[0]; | 358 uint8* dst_buffer = planes.data[0]; |
395 const int dst_stride = planes.strides[0]; | 359 const int dst_stride = planes.strides[0]; |
396 int src_stride = x_server_pixel_buffer_.GetStride(); | 360 int src_stride = x_server_pixel_buffer_.GetStride(); |
397 int dst_x = rect.x(), dst_y = rect.y(); | 361 int dst_x = rect.x(), dst_y = rect.y(); |
398 int width = rect.width(), height = rect.height(); | 362 int width = rect.width(), height = rect.height(); |
399 | 363 |
400 unsigned int red_mask = x_server_pixel_buffer_.GetRedMask(); | 364 unsigned int red_mask = x_server_pixel_buffer_.GetRedMask(); |
401 unsigned int blue_mask = x_server_pixel_buffer_.GetBlueMask(); | 365 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; | 396 uint32_t b = (((pixel & blue_mask) >> blue_shift) * 255) / max_blue; |
433 uint32_t g = (((pixel & green_mask) >> green_shift) * 255) / max_green; | 397 uint32_t g = (((pixel & green_mask) >> green_shift) * 255) / max_green; |
434 // Write as 32-bit RGB. | 398 // Write as 32-bit RGB. |
435 dst_pos_32[x] = r << 16 | g << 8 | b; | 399 dst_pos_32[x] = r << 16 | g << 8 | b; |
436 } | 400 } |
437 dst_pos += dst_stride; | 401 dst_pos += dst_stride; |
438 src_pos += src_stride; | 402 src_pos += src_stride; |
439 } | 403 } |
440 } | 404 } |
441 | 405 |
442 const gfx::Size& CapturerLinuxPimpl::size_most_recent() const { | 406 const gfx::Size& CapturerLinux::size_most_recent() const { |
443 return helper_.size_most_recent(); | 407 return helper_.size_most_recent(); |
444 } | 408 } |
445 | 409 |
| 410 } // namespace |
| 411 |
446 // static | 412 // static |
447 Capturer* Capturer::Create() { | 413 Capturer* Capturer::Create() { |
448 return new CapturerLinux(); | 414 return new CapturerLinux(); |
449 } | 415 } |
450 | 416 |
451 } // namespace remoting | 417 } // namespace remoting |
OLD | NEW |