blob: a1683586fdcba34474bce351e617c0f993325c5c [file] [log] [blame]
[email protected]cb3b1f9312010-06-07 19:58:231// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// 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
13#include "remoting/base/types.h"
14
[email protected]cb3b1f9312010-06-07 19:58:2315namespace remoting {
16
[email protected]66980d82010-10-21 20:05:0117static int IndexOfLowestBit(unsigned int mask) {
18 int i = 0;
19
20 // Extra-special do-while premature optimization, just to make dmaclach@
21 // happy.
22 do {
23 if (mask & 1) {
24 return i;
25 }
26 mask >>= 1;
27 ++i;
28 } while (mask);
29
30 NOTREACHED() << "mask should never be 0.";
31 return 0;
32}
33
34static bool IsRgb32(XImage* image) {
35 return (IndexOfLowestBit(image->red_mask) == 16) &&
36 (IndexOfLowestBit(image->green_mask) == 8) &&
37 (IndexOfLowestBit(image->blue_mask) == 0);
38}
39
[email protected]66980d82010-10-21 20:05:0140// Private Implementation pattern to avoid leaking the X11 types into the header
41// file.
42class CapturerLinuxPimpl {
43 public:
44 explicit CapturerLinuxPimpl(CapturerLinux* capturer);
45 ~CapturerLinuxPimpl();
46
47 bool Init(); // TODO(ajwong): Do we really want this to be synchronous?
48 void CalculateInvalidRects();
49 void CaptureRects(const InvalidRects& rects,
50 Capturer::CaptureCompletedCallback* callback);
51
52 private:
53 void DeinitXlib();
54 // We expose two forms of blitting to handle variations in the pixel format.
55 // In FastBlit, the operation is effectively a memcpy.
56 void FastBlit(XImage* image, int dest_x, int dest_y,
57 CaptureData* capture_data);
58 void SlowBlit(XImage* image, int dest_x, int dest_y,
59 CaptureData* capture_data);
60
61 static const int kBytesPerPixel = 4;
62
63 // Reference to containing class so we can access friend functions.
[email protected]cb8c9628b2010-11-11 21:11:0264 // Not owned.
[email protected]66980d82010-10-21 20:05:0165 CapturerLinux* capturer_;
66
67 // X11 graphics context.
68 Display* display_;
69 GC gc_;
70 Window root_window_;
[email protected]e1a6c462011-03-04 12:15:3171 int width_;
72 int height_;
[email protected]66980d82010-10-21 20:05:0173
74 // XDamage information.
75 Damage damage_handle_;
76 int damage_event_base_;
77 int damage_error_base_;
78
79 // Capture state.
80 uint8* buffers_[CapturerLinux::kNumBuffers];
81 int stride_;
82 bool capture_fullscreen_;
[email protected]9b4b2d12011-02-03 00:33:5883
84 // Invalid rects in the last capture. This is used to synchronize current with
85 // the previous buffer used.
86 InvalidRects last_invalid_rects_;
87
88 // Last capture buffer used.
89 uint8* last_buffer_;
[email protected]66980d82010-10-21 20:05:0190};
91
[email protected]a7b869bd2010-11-17 05:17:3592CapturerLinux::CapturerLinux(MessageLoop* message_loop)
93 : Capturer(message_loop),
94 pimpl_(new CapturerLinuxPimpl(this)) {
[email protected]66980d82010-10-21 20:05:0195 // TODO(ajwong): This should be moved into an Init() method on Capturer
96 // itself. Then we can remove the CHECK.
97 CHECK(pimpl_->Init());
[email protected]cb3b1f9312010-06-07 19:58:2398}
99
100CapturerLinux::~CapturerLinux() {
101}
102
[email protected]804c99622010-07-01 15:02:53103void CapturerLinux::ScreenConfigurationChanged() {
[email protected]66980d82010-10-21 20:05:01104 // TODO(ajwong): Support resolution changes.
105 NOTIMPLEMENTED();
[email protected]cb3b1f9312010-06-07 19:58:23106}
107
[email protected]88552a92010-08-06 22:50:00108void CapturerLinux::CalculateInvalidRects() {
[email protected]66980d82010-10-21 20:05:01109 pimpl_->CalculateInvalidRects();
[email protected]88552a92010-08-06 22:50:00110}
111
112void CapturerLinux::CaptureRects(const InvalidRects& rects,
[email protected]804c99622010-07-01 15:02:53113 CaptureCompletedCallback* callback) {
[email protected]66980d82010-10-21 20:05:01114 pimpl_->CaptureRects(rects, callback);
115}
116
117CapturerLinuxPimpl::CapturerLinuxPimpl(CapturerLinux* capturer)
118 : capturer_(capturer),
119 display_(NULL),
120 gc_(NULL),
121 root_window_(BadValue),
[email protected]e1a6c462011-03-04 12:15:31122 width_(0),
123 height_(0),
[email protected]66980d82010-10-21 20:05:01124 damage_handle_(BadValue),
125 damage_event_base_(-1),
126 damage_error_base_(-1),
127 stride_(0),
[email protected]9b4b2d12011-02-03 00:33:58128 capture_fullscreen_(true),
129 last_buffer_(NULL) {
[email protected]66980d82010-10-21 20:05:01130 for (int i = 0; i < CapturerLinux::kNumBuffers; i++) {
131 buffers_[i] = NULL;
132 }
133}
134
135CapturerLinuxPimpl::~CapturerLinuxPimpl() {
136 DeinitXlib();
137
138 for (int i = 0; i < CapturerLinux::kNumBuffers; i++) {
139 delete [] buffers_[i];
140 buffers_[i] = NULL;
141 }
142}
143
144bool CapturerLinuxPimpl::Init() {
145 // TODO(ajwong): We should specify the display string we are attaching to
146 // in the constructor.
147 display_ = XOpenDisplay(NULL);
148 if (!display_) {
149 LOG(ERROR) << "Unable to open display";
150 return false;
151 }
152
153 root_window_ = RootWindow(display_, DefaultScreen(display_));
154 if (root_window_ == BadValue) {
155 LOG(ERROR) << "Unable to get the root window";
156 DeinitXlib();
157 return false;
158 }
159
160 gc_ = XCreateGC(display_, root_window_, 0, NULL);
161 if (gc_ == NULL) {
162 LOG(ERROR) << "Unable to get graphics context";
163 DeinitXlib();
164 return false;
165 }
166
167 // Setup XDamage to report changes in the damage window. Mark the whole
168 // window as invalid.
169 if (!XDamageQueryExtension(display_, &damage_event_base_,
170 &damage_error_base_)) {
171 LOG(ERROR) << "Server does not support XDamage.";
172 DeinitXlib();
173 return false;
174 }
175 damage_handle_ = XDamageCreate(display_, root_window_,
176 XDamageReportDeltaRectangles);
177 if (damage_handle_ == BadValue) {
178 LOG(ERROR) << "Unable to create damage handle.";
179 DeinitXlib();
180 return false;
181 }
182
183 // TODO(ajwong): We should be able to replace this with a XDamageAdd().
184 capture_fullscreen_ = true;
185
186 // Set up the dimensions of the catpure framebuffer.
187 XWindowAttributes root_attr;
188 XGetWindowAttributes(display_, root_window_, &root_attr);
[email protected]e1a6c462011-03-04 12:15:31189 width_ = root_attr.width;
190 height_ = root_attr.height;
191 stride_ = width_ * kBytesPerPixel;
192 VLOG(1) << "Initialized with Geometry: " << width_ << "x" << height_;
[email protected]66980d82010-10-21 20:05:01193
194 // Allocate the screen buffers.
195 for (int i = 0; i < CapturerLinux::kNumBuffers; i++) {
[email protected]e1a6c462011-03-04 12:15:31196 buffers_[i] = new uint8[width_ * height_ * kBytesPerPixel];
[email protected]66980d82010-10-21 20:05:01197 }
198
199 return true;
200}
201
202void CapturerLinuxPimpl::CalculateInvalidRects() {
[email protected]e1a6c462011-03-04 12:15:31203 if (capturer_->IsCaptureFullScreen(width_, height_))
[email protected]0544b7fa2011-02-09 23:07:15204 capture_fullscreen_ = true;
205
[email protected]66980d82010-10-21 20:05:01206 // TODO(ajwong): The capture_fullscreen_ logic here is very ugly. Refactor.
207
208 // Find the number of events that are outstanding "now." We don't just loop
209 // on XPending because we want to guarantee this terminates.
210 int events_to_process = XPending(display_);
211 XEvent e;
212 InvalidRects invalid_rects;
213 for (int i = 0; i < events_to_process; i++) {
214 XNextEvent(display_, &e);
215 if (e.type == damage_event_base_ + XDamageNotify) {
216 // If we're doing a full screen capture, we should just drain the events.
217 if (!capture_fullscreen_) {
218 XDamageNotifyEvent *event = reinterpret_cast<XDamageNotifyEvent*>(&e);
219 gfx::Rect damage_rect(event->area.x, event->area.y, event->area.width,
220 event->area.height);
[email protected]97d82ed2011-03-07 08:01:48221
222 // TODO(hclam): Perform more checks on the rect.
223 if (damage_rect.width() <= 0 && damage_rect.height() <= 0)
224 continue;
225
[email protected]66980d82010-10-21 20:05:01226 invalid_rects.insert(damage_rect);
[email protected]97d82ed2011-03-07 08:01:48227 VLOG(3) << "Damage received for rect at ("
[email protected]66980d82010-10-21 20:05:01228 << damage_rect.x() << "," << damage_rect.y() << ") size ("
229 << damage_rect.width() << "," << damage_rect.height() << ")";
230 }
231 } else {
232 LOG(WARNING) << "Got unknown event type: " << e.type;
233 }
234 }
235
236 if (capture_fullscreen_) {
[email protected]97d82ed2011-03-07 08:01:48237 // TODO(hclam): Check the new dimension again.
[email protected]467aca52011-03-10 11:51:08238 capturer_->InvalidateScreen(gfx::Size(width_, height_));
[email protected]66980d82010-10-21 20:05:01239 capture_fullscreen_ = false;
240 } else {
241 capturer_->InvalidateRects(invalid_rects);
242 }
243}
244
245void CapturerLinuxPimpl::CaptureRects(
246 const InvalidRects& rects,
247 Capturer::CaptureCompletedCallback* callback) {
248 uint8* buffer = buffers_[capturer_->current_buffer_];
249 DataPlanes planes;
250 planes.data[0] = buffer;
251 planes.strides[0] = stride_;
252
[email protected]467aca52011-03-10 11:51:08253 scoped_refptr<CaptureData> capture_data(new CaptureData(
254 planes, gfx::Size(width_, height_), media::VideoFrame::RGB32));
[email protected]66980d82010-10-21 20:05:01255
[email protected]9b4b2d12011-02-03 00:33:58256 // Synchronize the current buffer with the last one since we do not capture
257 // the entire desktop. Note that encoder may be reading from the previous
258 // buffer at this time so thread access complaints are false positives.
259
260 // TODO(hclam): We can reduce the amount of copying here by subtracting
261 // |rects| from |last_invalid_rects_|.
262 for (InvalidRects::const_iterator it = last_invalid_rects_.begin();
263 last_buffer_ && it != last_invalid_rects_.end();
264 ++it) {
[email protected]47277232011-02-04 02:19:17265 int offset = it->y() * stride_ + it->x() * kBytesPerPixel;
[email protected]9b4b2d12011-02-03 00:33:58266 for (int i = 0; i < it->height(); ++i) {
267 memcpy(buffer + offset, last_buffer_ + offset,
268 it->width() * kBytesPerPixel);
[email protected]e1a6c462011-03-04 12:15:31269 offset += width_ * kBytesPerPixel;
[email protected]9b4b2d12011-02-03 00:33:58270 }
271 }
272
[email protected]66980d82010-10-21 20:05:01273 for (InvalidRects::const_iterator it = rects.begin();
274 it != rects.end();
275 ++it) {
276 XImage* image = XGetImage(display_, root_window_, it->x(), it->y(),
277 it->width(), it->height(), AllPlanes, ZPixmap);
278
279 // Check if we can fastpath the blit.
280 if ((image->depth == 24 || image->depth == 32) &&
281 image->bits_per_pixel == 32 &&
282 IsRgb32(image)) {
283 VLOG(3) << "Fast blitting";
284 FastBlit(image, it->x(), it->y(), capture_data);
285 } else {
286 VLOG(3) << "Slow blitting";
287 SlowBlit(image, it->x(), it->y(), capture_data);
288 }
289
290 XDestroyImage(image);
291 }
292
293 // TODO(ajwong): We should only repair the rects that were copied!
294 XDamageSubtract(display_, damage_handle_, None, None);
295
[email protected]d3a4b09d2011-02-22 18:16:53296 capture_data->mutable_dirty_rects() = rects;
297 last_invalid_rects_ = rects;
[email protected]9b4b2d12011-02-03 00:33:58298 last_buffer_ = buffer;
299
[email protected]66980d82010-10-21 20:05:01300 // TODO(ajwong): These completion signals back to the upper class are very
301 // strange. Fix it.
302 capturer_->FinishCapture(capture_data, callback);
303}
304
305void CapturerLinuxPimpl::DeinitXlib() {
306 if (gc_) {
307 XFreeGC(display_, gc_);
308 gc_ = NULL;
309 }
310
311 if (display_) {
312 XCloseDisplay(display_);
313 display_ = NULL;
314 }
315}
316
317void CapturerLinuxPimpl::FastBlit(XImage* image, int dest_x, int dest_y,
318 CaptureData* capture_data) {
319 uint8* src_pos = reinterpret_cast<uint8*>(image->data);
320
321 DataPlanes planes = capture_data->data_planes();
322 uint8* dst_buffer = planes.data[0];
323
324 const int dst_stride = planes.strides[0];
325 const int src_stride = image->bytes_per_line;
326
[email protected]d3a4b09d2011-02-22 18:16:53327 uint8* dst_pos = dst_buffer + dst_stride * dest_y;
[email protected]9b4b2d12011-02-03 00:33:58328 dst_pos += dest_x * kBytesPerPixel;
[email protected]d3a4b09d2011-02-22 18:16:53329
[email protected]9b4b2d12011-02-03 00:33:58330 for (int y = 0; y < image->height; ++y) {
331 memcpy(dst_pos, src_pos, image->width * kBytesPerPixel);
[email protected]66980d82010-10-21 20:05:01332
333 src_pos += src_stride;
[email protected]d3a4b09d2011-02-22 18:16:53334 dst_pos += dst_stride;
[email protected]66980d82010-10-21 20:05:01335 }
336}
337
338void CapturerLinuxPimpl::SlowBlit(XImage* image, int dest_x, int dest_y,
339 CaptureData* capture_data) {
340 DataPlanes planes = capture_data->data_planes();
341 uint8* dst_buffer = planes.data[0];
342 const int dst_stride = planes.strides[0];
343
344 unsigned int red_shift = IndexOfLowestBit(image->red_mask);
345 unsigned int blue_shift = IndexOfLowestBit(image->blue_mask);
346 unsigned int green_shift = IndexOfLowestBit(image->green_mask);
347
348 unsigned int max_red = image->red_mask >> red_shift;
349 unsigned int max_blue = image->blue_mask >> blue_shift;
350 unsigned int max_green = image->green_mask >> green_shift;
351
[email protected]3a90dd3a2011-02-22 15:06:59352 // Produce an upside-down image.
[email protected]e1a6c462011-03-04 12:15:31353 uint8* dst_pos = dst_buffer + dst_stride * (height_ - dest_y - 1);
[email protected]3a90dd3a2011-02-22 15:06:59354 dst_pos += dest_x * kBytesPerPixel;
355 // TODO(jamiewalch): Optimize, perhaps using MMX code or by converting to
356 // YUV directly
[email protected]66980d82010-10-21 20:05:01357 for (int y = 0; y < image->height; y++) {
[email protected]3a90dd3a2011-02-22 15:06:59358 uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos);
[email protected]66980d82010-10-21 20:05:01359 for (int x = 0; x < image->width; x++) {
360 unsigned long pixel = XGetPixel(image, x, y);
361 uint32_t r = (((pixel & image->red_mask) >> red_shift) * max_red) / 255;
362 uint32_t g =
363 (((pixel & image->green_mask) >> green_shift) * max_blue) / 255;
364 uint32_t b =
365 (((pixel & image->blue_mask) >> blue_shift) * max_green) / 255;
366
367 // Write as 32-bit RGB.
[email protected]3a90dd3a2011-02-22 15:06:59368 dst_pos_32[x] = r << 16 | g << 8 | b;
[email protected]66980d82010-10-21 20:05:01369 }
[email protected]3a90dd3a2011-02-22 15:06:59370 dst_pos -= dst_stride;
[email protected]66980d82010-10-21 20:05:01371 }
[email protected]cb3b1f9312010-06-07 19:58:23372}
373
[email protected]f901a072010-11-23 22:18:12374// static
375Capturer* Capturer::Create(MessageLoop* message_loop) {
376 return new CapturerLinux(message_loop);
377}
378
[email protected]cb3b1f9312010-06-07 19:58:23379} // namespace remoting