blob: 71de8fda912d29b709d08b03430b35641bb761aa [file] [log] [blame]
[email protected]089d1862011-03-21 13:12:341// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]cb3b1f9312010-06-07 19:58:232// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "remoting/host/capturer_linux.h"
6
[email protected]66980d82010-10-21 20:05:017#include <X11/Xlib.h>
8#include <X11/Xutil.h>
9#include <X11/extensions/Xdamage.h>
10
11#include <set>
12
[email protected]bab76b9b2011-03-29 14:40:2013#include "base/logging.h"
[email protected]66980d82010-10-21 20:05:0114#include "remoting/base/types.h"
[email protected]bab76b9b2011-03-29 14:40:2015#include "remoting/host/capturer_helper.h"
[email protected]089d1862011-03-21 13:12:3416#include "remoting/host/x_server_pixel_buffer.h"
[email protected]66980d82010-10-21 20:05:0117
[email protected]cb3b1f9312010-06-07 19:58:2318namespace remoting {
19
[email protected]66980d82010-10-21 20:05:0120// Private Implementation pattern to avoid leaking the X11 types into the header
21// file.
[email protected]bab76b9b2011-03-29 14:40:2022class CapturerLinuxPimpl : public Capturer {
[email protected]66980d82010-10-21 20:05:0123 public:
[email protected]bab76b9b2011-03-29 14:40:2024 CapturerLinuxPimpl();
25 virtual ~CapturerLinuxPimpl();
[email protected]66980d82010-10-21 20:05:0126
27 bool Init(); // TODO(ajwong): Do we really want this to be synchronous?
[email protected]bab76b9b2011-03-29 14:40:2028
29 // Capturer interface.
30 virtual void ScreenConfigurationChanged();
31 virtual media::VideoFrame::Format pixel_format() const;
32 virtual void ClearInvalidRects();
33 virtual void InvalidateRects(const InvalidRects& inval_rects);
34 virtual void InvalidateScreen(const gfx::Size& size);
35 virtual void InvalidateFullScreen();
36 virtual void CaptureInvalidRects(CaptureCompletedCallback* callback);
37 virtual const gfx::Size& size_most_recent() const;
38
39 private:
[email protected]66980d82010-10-21 20:05:0140 void CalculateInvalidRects();
41 void CaptureRects(const InvalidRects& rects,
42 Capturer::CaptureCompletedCallback* callback);
43
[email protected]66980d82010-10-21 20:05:0144 void DeinitXlib();
45 // We expose two forms of blitting to handle variations in the pixel format.
46 // In FastBlit, the operation is effectively a memcpy.
[email protected]089d1862011-03-21 13:12:3447 void FastBlit(uint8* image, const gfx::Rect& rect, CaptureData* capture_data);
48 void SlowBlit(uint8* image, const gfx::Rect& rect, CaptureData* capture_data);
[email protected]66980d82010-10-21 20:05:0149
50 static const int kBytesPerPixel = 4;
51
[email protected]66980d82010-10-21 20:05:0152 // X11 graphics context.
53 Display* display_;
54 GC gc_;
55 Window root_window_;
[email protected]e1a6c462011-03-04 12:15:3156 int width_;
57 int height_;
[email protected]66980d82010-10-21 20:05:0158
59 // XDamage information.
60 Damage damage_handle_;
61 int damage_event_base_;
62 int damage_error_base_;
63
[email protected]089d1862011-03-21 13:12:3464 // Access to the X Server's pixel buffer.
65 XServerPixelBuffer x_server_pixel_buffer_;
66
[email protected]bab76b9b2011-03-29 14:40:2067 // A thread-safe list of invalid rectangles, and the size of the most
68 // recently captured screen.
69 CapturerHelper helper_;
70
[email protected]66980d82010-10-21 20:05:0171 // Capture state.
[email protected]bab76b9b2011-03-29 14:40:2072 static const int kNumBuffers = 2;
73 uint8* buffers_[kNumBuffers];
74 int current_buffer_;
[email protected]66980d82010-10-21 20:05:0175 int stride_;
76 bool capture_fullscreen_;
[email protected]9b4b2d12011-02-03 00:33:5877
[email protected]bab76b9b2011-03-29 14:40:2078 // Format of pixels returned in buffer.
79 media::VideoFrame::Format pixel_format_;
80
[email protected]9b4b2d12011-02-03 00:33:5881 // Invalid rects in the last capture. This is used to synchronize current with
82 // the previous buffer used.
83 InvalidRects last_invalid_rects_;
84
85 // Last capture buffer used.
86 uint8* last_buffer_;
[email protected]66980d82010-10-21 20:05:0187};
88
[email protected]bab76b9b2011-03-29 14:40:2089CapturerLinux::CapturerLinux()
90 : pimpl_(new CapturerLinuxPimpl()) {
[email protected]66980d82010-10-21 20:05:0191 // TODO(ajwong): This should be moved into an Init() method on Capturer
92 // itself. Then we can remove the CHECK.
93 CHECK(pimpl_->Init());
[email protected]cb3b1f9312010-06-07 19:58:2394}
95
96CapturerLinux::~CapturerLinux() {
97}
98
[email protected]804c99622010-07-01 15:02:5399void CapturerLinux::ScreenConfigurationChanged() {
[email protected]bab76b9b2011-03-29 14:40:20100 pimpl_->ScreenConfigurationChanged();
[email protected]cb3b1f9312010-06-07 19:58:23101}
102
[email protected]bab76b9b2011-03-29 14:40:20103media::VideoFrame::Format CapturerLinux::pixel_format() const {
104 return pimpl_->pixel_format();
[email protected]88552a92010-08-06 22:50:00105}
106
[email protected]bab76b9b2011-03-29 14:40:20107void CapturerLinux::ClearInvalidRects() {
108 pimpl_->ClearInvalidRects();
[email protected]66980d82010-10-21 20:05:01109}
110
[email protected]bab76b9b2011-03-29 14:40:20111void CapturerLinux::InvalidateRects(const InvalidRects& inval_rects) {
112 pimpl_->InvalidateRects(inval_rects);
113}
114
115void CapturerLinux::InvalidateScreen(const gfx::Size& size) {
116 pimpl_->InvalidateScreen(size);
117}
118
119void CapturerLinux::InvalidateFullScreen() {
120 pimpl_->InvalidateFullScreen();
121}
122
123void CapturerLinux::CaptureInvalidRects(CaptureCompletedCallback* callback) {
124 pimpl_->CaptureInvalidRects(callback);
125}
126
127const gfx::Size& CapturerLinux::size_most_recent() const {
128 return pimpl_->size_most_recent();
129}
130
131CapturerLinuxPimpl::CapturerLinuxPimpl()
132 : display_(NULL),
[email protected]66980d82010-10-21 20:05:01133 gc_(NULL),
134 root_window_(BadValue),
[email protected]e1a6c462011-03-04 12:15:31135 width_(0),
136 height_(0),
[email protected]66980d82010-10-21 20:05:01137 damage_handle_(BadValue),
138 damage_event_base_(-1),
139 damage_error_base_(-1),
[email protected]bab76b9b2011-03-29 14:40:20140 current_buffer_(0),
[email protected]66980d82010-10-21 20:05:01141 stride_(0),
[email protected]9b4b2d12011-02-03 00:33:58142 capture_fullscreen_(true),
[email protected]bab76b9b2011-03-29 14:40:20143 pixel_format_(media::VideoFrame::RGB32),
[email protected]9b4b2d12011-02-03 00:33:58144 last_buffer_(NULL) {
[email protected]bab76b9b2011-03-29 14:40:20145 for (int i = 0; i < kNumBuffers; i++) {
[email protected]66980d82010-10-21 20:05:01146 buffers_[i] = NULL;
147 }
148}
149
150CapturerLinuxPimpl::~CapturerLinuxPimpl() {
151 DeinitXlib();
152
[email protected]bab76b9b2011-03-29 14:40:20153 for (int i = 0; i < kNumBuffers; i++) {
[email protected]66980d82010-10-21 20:05:01154 delete [] buffers_[i];
155 buffers_[i] = NULL;
156 }
157}
158
159bool CapturerLinuxPimpl::Init() {
160 // TODO(ajwong): We should specify the display string we are attaching to
161 // in the constructor.
162 display_ = XOpenDisplay(NULL);
163 if (!display_) {
164 LOG(ERROR) << "Unable to open display";
165 return false;
166 }
167
[email protected]089d1862011-03-21 13:12:34168 x_server_pixel_buffer_.Init(display_);
169
[email protected]66980d82010-10-21 20:05:01170 root_window_ = RootWindow(display_, DefaultScreen(display_));
171 if (root_window_ == BadValue) {
172 LOG(ERROR) << "Unable to get the root window";
173 DeinitXlib();
174 return false;
175 }
176
177 gc_ = XCreateGC(display_, root_window_, 0, NULL);
178 if (gc_ == NULL) {
179 LOG(ERROR) << "Unable to get graphics context";
180 DeinitXlib();
181 return false;
182 }
183
184 // Setup XDamage to report changes in the damage window. Mark the whole
185 // window as invalid.
186 if (!XDamageQueryExtension(display_, &damage_event_base_,
187 &damage_error_base_)) {
188 LOG(ERROR) << "Server does not support XDamage.";
189 DeinitXlib();
190 return false;
191 }
192 damage_handle_ = XDamageCreate(display_, root_window_,
193 XDamageReportDeltaRectangles);
194 if (damage_handle_ == BadValue) {
195 LOG(ERROR) << "Unable to create damage handle.";
196 DeinitXlib();
197 return false;
198 }
199
200 // TODO(ajwong): We should be able to replace this with a XDamageAdd().
201 capture_fullscreen_ = true;
202
203 // Set up the dimensions of the catpure framebuffer.
204 XWindowAttributes root_attr;
205 XGetWindowAttributes(display_, root_window_, &root_attr);
[email protected]e1a6c462011-03-04 12:15:31206 width_ = root_attr.width;
207 height_ = root_attr.height;
208 stride_ = width_ * kBytesPerPixel;
209 VLOG(1) << "Initialized with Geometry: " << width_ << "x" << height_;
[email protected]66980d82010-10-21 20:05:01210
211 // Allocate the screen buffers.
[email protected]bab76b9b2011-03-29 14:40:20212 for (int i = 0; i < kNumBuffers; i++) {
[email protected]e1a6c462011-03-04 12:15:31213 buffers_[i] = new uint8[width_ * height_ * kBytesPerPixel];
[email protected]66980d82010-10-21 20:05:01214 }
215
216 return true;
217}
218
[email protected]bab76b9b2011-03-29 14:40:20219void CapturerLinuxPimpl::ScreenConfigurationChanged() {
220 // TODO(ajwong): Support resolution changes.
221 NOTIMPLEMENTED();
222}
223
224media::VideoFrame::Format CapturerLinuxPimpl::pixel_format() const {
225 return pixel_format_;
226}
227
228void CapturerLinuxPimpl::ClearInvalidRects() {
229 helper_.ClearInvalidRects();
230}
231
232void CapturerLinuxPimpl::InvalidateRects(const InvalidRects& inval_rects) {
233 helper_.InvalidateRects(inval_rects);
234}
235
236void CapturerLinuxPimpl::InvalidateScreen(const gfx::Size& size) {
237 helper_.InvalidateScreen(size);
238}
239
240void CapturerLinuxPimpl::InvalidateFullScreen() {
241 helper_.InvalidateFullScreen();
242}
243
244void CapturerLinuxPimpl::CaptureInvalidRects(
245 CaptureCompletedCallback* callback) {
246 CalculateInvalidRects();
247
248 InvalidRects rects;
249 helper_.SwapInvalidRects(rects);
250
251 CaptureRects(rects, callback);
252}
253
[email protected]66980d82010-10-21 20:05:01254void CapturerLinuxPimpl::CalculateInvalidRects() {
[email protected]bab76b9b2011-03-29 14:40:20255 if (helper_.IsCaptureFullScreen(gfx::Size(width_, height_)))
[email protected]0544b7fa2011-02-09 23:07:15256 capture_fullscreen_ = true;
257
[email protected]66980d82010-10-21 20:05:01258 // TODO(ajwong): The capture_fullscreen_ logic here is very ugly. Refactor.
259
260 // Find the number of events that are outstanding "now." We don't just loop
261 // on XPending because we want to guarantee this terminates.
262 int events_to_process = XPending(display_);
263 XEvent e;
264 InvalidRects invalid_rects;
265 for (int i = 0; i < events_to_process; i++) {
266 XNextEvent(display_, &e);
267 if (e.type == damage_event_base_ + XDamageNotify) {
268 // If we're doing a full screen capture, we should just drain the events.
269 if (!capture_fullscreen_) {
270 XDamageNotifyEvent *event = reinterpret_cast<XDamageNotifyEvent*>(&e);
271 gfx::Rect damage_rect(event->area.x, event->area.y, event->area.width,
272 event->area.height);
[email protected]97d82ed2011-03-07 08:01:48273
274 // TODO(hclam): Perform more checks on the rect.
275 if (damage_rect.width() <= 0 && damage_rect.height() <= 0)
276 continue;
277
[email protected]66980d82010-10-21 20:05:01278 invalid_rects.insert(damage_rect);
[email protected]97d82ed2011-03-07 08:01:48279 VLOG(3) << "Damage received for rect at ("
[email protected]66980d82010-10-21 20:05:01280 << damage_rect.x() << "," << damage_rect.y() << ") size ("
281 << damage_rect.width() << "," << damage_rect.height() << ")";
282 }
283 } else {
284 LOG(WARNING) << "Got unknown event type: " << e.type;
285 }
286 }
287
288 if (capture_fullscreen_) {
[email protected]97d82ed2011-03-07 08:01:48289 // TODO(hclam): Check the new dimension again.
[email protected]bab76b9b2011-03-29 14:40:20290 helper_.InvalidateScreen(gfx::Size(width_, height_));
[email protected]66980d82010-10-21 20:05:01291 capture_fullscreen_ = false;
292 } else {
[email protected]bab76b9b2011-03-29 14:40:20293 helper_.InvalidateRects(invalid_rects);
[email protected]66980d82010-10-21 20:05:01294 }
295}
296
297void CapturerLinuxPimpl::CaptureRects(
298 const InvalidRects& rects,
299 Capturer::CaptureCompletedCallback* callback) {
[email protected]bab76b9b2011-03-29 14:40:20300 scoped_ptr<CaptureCompletedCallback> callback_deleter(callback);
301
302 uint8* buffer = buffers_[current_buffer_];
[email protected]66980d82010-10-21 20:05:01303 DataPlanes planes;
304 planes.data[0] = buffer;
305 planes.strides[0] = stride_;
306
[email protected]467aca52011-03-10 11:51:08307 scoped_refptr<CaptureData> capture_data(new CaptureData(
308 planes, gfx::Size(width_, height_), media::VideoFrame::RGB32));
[email protected]66980d82010-10-21 20:05:01309
[email protected]9b4b2d12011-02-03 00:33:58310 // Synchronize the current buffer with the last one since we do not capture
311 // the entire desktop. Note that encoder may be reading from the previous
312 // buffer at this time so thread access complaints are false positives.
313
314 // TODO(hclam): We can reduce the amount of copying here by subtracting
315 // |rects| from |last_invalid_rects_|.
316 for (InvalidRects::const_iterator it = last_invalid_rects_.begin();
317 last_buffer_ && it != last_invalid_rects_.end();
318 ++it) {
[email protected]47277232011-02-04 02:19:17319 int offset = it->y() * stride_ + it->x() * kBytesPerPixel;
[email protected]9b4b2d12011-02-03 00:33:58320 for (int i = 0; i < it->height(); ++i) {
321 memcpy(buffer + offset, last_buffer_ + offset,
322 it->width() * kBytesPerPixel);
[email protected]e1a6c462011-03-04 12:15:31323 offset += width_ * kBytesPerPixel;
[email protected]9b4b2d12011-02-03 00:33:58324 }
325 }
326
[email protected]66980d82010-10-21 20:05:01327 for (InvalidRects::const_iterator it = rects.begin();
328 it != rects.end();
329 ++it) {
[email protected]089d1862011-03-21 13:12:34330 uint8* image = x_server_pixel_buffer_.CaptureRect(*it);
[email protected]66980d82010-10-21 20:05:01331 // Check if we can fastpath the blit.
[email protected]089d1862011-03-21 13:12:34332 int depth = x_server_pixel_buffer_.GetDepth();
333 int bpp = x_server_pixel_buffer_.GetBitsPerPixel();
334 bool is_rgb = x_server_pixel_buffer_.IsRgb();
335 if ((depth == 24 || depth == 32) && bpp == 32 && is_rgb) {
[email protected]66980d82010-10-21 20:05:01336 VLOG(3) << "Fast blitting";
[email protected]089d1862011-03-21 13:12:34337 FastBlit(image, *it, capture_data);
[email protected]66980d82010-10-21 20:05:01338 } else {
339 VLOG(3) << "Slow blitting";
[email protected]089d1862011-03-21 13:12:34340 SlowBlit(image, *it, capture_data);
[email protected]66980d82010-10-21 20:05:01341 }
[email protected]66980d82010-10-21 20:05:01342 }
343
344 // TODO(ajwong): We should only repair the rects that were copied!
345 XDamageSubtract(display_, damage_handle_, None, None);
346
[email protected]d3a4b09d2011-02-22 18:16:53347 capture_data->mutable_dirty_rects() = rects;
348 last_invalid_rects_ = rects;
[email protected]9b4b2d12011-02-03 00:33:58349 last_buffer_ = buffer;
350
[email protected]bab76b9b2011-03-29 14:40:20351 current_buffer_ = (current_buffer_ + 1) % kNumBuffers;
352 helper_.set_size_most_recent(capture_data->size());
353
354 callback->Run(capture_data);
[email protected]66980d82010-10-21 20:05:01355}
356
357void CapturerLinuxPimpl::DeinitXlib() {
358 if (gc_) {
359 XFreeGC(display_, gc_);
360 gc_ = NULL;
361 }
362
363 if (display_) {
364 XCloseDisplay(display_);
365 display_ = NULL;
366 }
367}
368
[email protected]089d1862011-03-21 13:12:34369void CapturerLinuxPimpl::FastBlit(uint8* image, const gfx::Rect& rect,
[email protected]66980d82010-10-21 20:05:01370 CaptureData* capture_data) {
[email protected]089d1862011-03-21 13:12:34371 uint8* src_pos = image;
372 int src_stride = x_server_pixel_buffer_.GetStride();
373 int dst_x = rect.x(), dst_y = rect.y();
[email protected]66980d82010-10-21 20:05:01374
375 DataPlanes planes = capture_data->data_planes();
376 uint8* dst_buffer = planes.data[0];
377
378 const int dst_stride = planes.strides[0];
[email protected]66980d82010-10-21 20:05:01379
[email protected]089d1862011-03-21 13:12:34380 uint8* dst_pos = dst_buffer + dst_stride * dst_y;
381 dst_pos += dst_x * kBytesPerPixel;
[email protected]d3a4b09d2011-02-22 18:16:53382
[email protected]089d1862011-03-21 13:12:34383 int height = rect.height(), row_bytes = rect.width() * kBytesPerPixel;
384 for (int y = 0; y < height; ++y) {
385 memcpy(dst_pos, src_pos, row_bytes);
[email protected]66980d82010-10-21 20:05:01386 src_pos += src_stride;
[email protected]d3a4b09d2011-02-22 18:16:53387 dst_pos += dst_stride;
[email protected]66980d82010-10-21 20:05:01388 }
389}
390
[email protected]089d1862011-03-21 13:12:34391void CapturerLinuxPimpl::SlowBlit(uint8* image, const gfx::Rect& rect,
[email protected]66980d82010-10-21 20:05:01392 CaptureData* capture_data) {
393 DataPlanes planes = capture_data->data_planes();
394 uint8* dst_buffer = planes.data[0];
395 const int dst_stride = planes.strides[0];
[email protected]089d1862011-03-21 13:12:34396 int src_stride = x_server_pixel_buffer_.GetStride();
397 int dst_x = rect.x(), dst_y = rect.y();
398 int width = rect.width(), height = rect.height();
[email protected]66980d82010-10-21 20:05:01399
[email protected]089d1862011-03-21 13:12:34400 unsigned int red_mask = x_server_pixel_buffer_.GetRedMask();
401 unsigned int blue_mask = x_server_pixel_buffer_.GetBlueMask();
402 unsigned int green_mask = x_server_pixel_buffer_.GetGreenMask();
403 unsigned int red_shift = x_server_pixel_buffer_.GetRedShift();
404 unsigned int blue_shift = x_server_pixel_buffer_.GetBlueShift();
405 unsigned int green_shift = x_server_pixel_buffer_.GetGreenShift();
[email protected]66980d82010-10-21 20:05:01406
[email protected]089d1862011-03-21 13:12:34407 unsigned int max_red = red_mask >> red_shift;
408 unsigned int max_blue = blue_mask >> blue_shift;
409 unsigned int max_green = green_mask >> green_shift;
[email protected]66980d82010-10-21 20:05:01410
[email protected]089d1862011-03-21 13:12:34411 unsigned int bits_per_pixel = x_server_pixel_buffer_.GetBitsPerPixel();
412
413 uint8* dst_pos = dst_buffer + dst_stride * dst_y;
414 uint8* src_pos = image;
415 dst_pos += dst_x * kBytesPerPixel;
416 // TODO(hclam): Optimize, perhaps using MMX code or by converting to
[email protected]3a90dd3a2011-02-22 15:06:59417 // YUV directly
[email protected]089d1862011-03-21 13:12:34418 for (int y = 0; y < height; y++) {
[email protected]3a90dd3a2011-02-22 15:06:59419 uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos);
[email protected]089d1862011-03-21 13:12:34420 uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos);
421 uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos);
422 for (int x = 0; x < width; x++) {
423 // Dereference through an appropriately-aligned pointer.
424 uint32_t pixel;
425 if (bits_per_pixel == 32)
426 pixel = src_pos_32[x];
427 else if (bits_per_pixel == 16)
428 pixel = src_pos_16[x];
429 else
430 pixel = src_pos[x];
431 uint32_t r = (((pixel & red_mask) >> red_shift) * 255) / max_red;
432 uint32_t b = (((pixel & blue_mask) >> blue_shift) * 255) / max_blue;
433 uint32_t g = (((pixel & green_mask) >> green_shift) * 255) / max_green;
[email protected]66980d82010-10-21 20:05:01434 // Write as 32-bit RGB.
[email protected]3a90dd3a2011-02-22 15:06:59435 dst_pos_32[x] = r << 16 | g << 8 | b;
[email protected]66980d82010-10-21 20:05:01436 }
[email protected]089d1862011-03-21 13:12:34437 dst_pos += dst_stride;
438 src_pos += src_stride;
[email protected]66980d82010-10-21 20:05:01439 }
[email protected]cb3b1f9312010-06-07 19:58:23440}
441
[email protected]bab76b9b2011-03-29 14:40:20442const gfx::Size& CapturerLinuxPimpl::size_most_recent() const {
443 return helper_.size_most_recent();
444}
445
[email protected]f901a072010-11-23 22:18:12446// static
[email protected]bab76b9b2011-03-29 14:40:20447Capturer* Capturer::Create() {
448 return new CapturerLinux();
[email protected]f901a072010-11-23 22:18:12449}
450
[email protected]cb3b1f9312010-06-07 19:58:23451} // namespace remoting