Chromium Code Reviews| 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 |