| 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 |