blob: db0ffc4ce1651ef9b81e61863ca543860a017d11 [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
13#include "remoting/base/types.h"
[email protected]089d1862011-03-21 13:12:3414#include "remoting/host/x_server_pixel_buffer.h"
[email protected]66980d82010-10-21 20:05:0115
[email protected]cb3b1f9312010-06-07 19:58:2316namespace remoting {
17
[email protected]66980d82010-10-21 20:05:0118// Private Implementation pattern to avoid leaking the X11 types into the header
19// file.
20class CapturerLinuxPimpl {
21 public:
22 explicit CapturerLinuxPimpl(CapturerLinux* capturer);
23 ~CapturerLinuxPimpl();
24
25 bool Init(); // TODO(ajwong): Do we really want this to be synchronous?
26 void CalculateInvalidRects();
27 void CaptureRects(const InvalidRects& rects,
28 Capturer::CaptureCompletedCallback* callback);
29
30 private:
31 void DeinitXlib();
32 // We expose two forms of blitting to handle variations in the pixel format.
33 // In FastBlit, the operation is effectively a memcpy.
[email protected]089d1862011-03-21 13:12:3434 void FastBlit(uint8* image, const gfx::Rect& rect, CaptureData* capture_data);
35 void SlowBlit(uint8* image, const gfx::Rect& rect, CaptureData* capture_data);
[email protected]66980d82010-10-21 20:05:0136
37 static const int kBytesPerPixel = 4;
38
39 // Reference to containing class so we can access friend functions.
[email protected]cb8c9628b2010-11-11 21:11:0240 // Not owned.
[email protected]66980d82010-10-21 20:05:0141 CapturerLinux* capturer_;
42
43 // X11 graphics context.
44 Display* display_;
45 GC gc_;
46 Window root_window_;
[email protected]e1a6c462011-03-04 12:15:3147 int width_;
48 int height_;
[email protected]66980d82010-10-21 20:05:0149
50 // XDamage information.
51 Damage damage_handle_;
52 int damage_event_base_;
53 int damage_error_base_;
54
[email protected]089d1862011-03-21 13:12:3455 // Access to the X Server's pixel buffer.
56 XServerPixelBuffer x_server_pixel_buffer_;
57
[email protected]66980d82010-10-21 20:05:0158 // Capture state.
59 uint8* buffers_[CapturerLinux::kNumBuffers];
60 int stride_;
61 bool capture_fullscreen_;
[email protected]9b4b2d12011-02-03 00:33:5862
63 // Invalid rects in the last capture. This is used to synchronize current with
64 // the previous buffer used.
65 InvalidRects last_invalid_rects_;
66
67 // Last capture buffer used.
68 uint8* last_buffer_;
[email protected]66980d82010-10-21 20:05:0169};
70
[email protected]a7b869bd2010-11-17 05:17:3571CapturerLinux::CapturerLinux(MessageLoop* message_loop)
72 : Capturer(message_loop),
73 pimpl_(new CapturerLinuxPimpl(this)) {
[email protected]66980d82010-10-21 20:05:0174 // TODO(ajwong): This should be moved into an Init() method on Capturer
75 // itself. Then we can remove the CHECK.
76 CHECK(pimpl_->Init());
[email protected]cb3b1f9312010-06-07 19:58:2377}
78
79CapturerLinux::~CapturerLinux() {
80}
81
[email protected]804c99622010-07-01 15:02:5382void CapturerLinux::ScreenConfigurationChanged() {
[email protected]66980d82010-10-21 20:05:0183 // TODO(ajwong): Support resolution changes.
84 NOTIMPLEMENTED();
[email protected]cb3b1f9312010-06-07 19:58:2385}
86
[email protected]88552a92010-08-06 22:50:0087void CapturerLinux::CalculateInvalidRects() {
[email protected]66980d82010-10-21 20:05:0188 pimpl_->CalculateInvalidRects();
[email protected]88552a92010-08-06 22:50:0089}
90
91void CapturerLinux::CaptureRects(const InvalidRects& rects,
[email protected]804c99622010-07-01 15:02:5392 CaptureCompletedCallback* callback) {
[email protected]66980d82010-10-21 20:05:0193 pimpl_->CaptureRects(rects, callback);
94}
95
96CapturerLinuxPimpl::CapturerLinuxPimpl(CapturerLinux* capturer)
97 : capturer_(capturer),
98 display_(NULL),
99 gc_(NULL),
100 root_window_(BadValue),
[email protected]e1a6c462011-03-04 12:15:31101 width_(0),
102 height_(0),
[email protected]66980d82010-10-21 20:05:01103 damage_handle_(BadValue),
104 damage_event_base_(-1),
105 damage_error_base_(-1),
106 stride_(0),
[email protected]9b4b2d12011-02-03 00:33:58107 capture_fullscreen_(true),
108 last_buffer_(NULL) {
[email protected]66980d82010-10-21 20:05:01109 for (int i = 0; i < CapturerLinux::kNumBuffers; i++) {
110 buffers_[i] = NULL;
111 }
112}
113
114CapturerLinuxPimpl::~CapturerLinuxPimpl() {
115 DeinitXlib();
116
117 for (int i = 0; i < CapturerLinux::kNumBuffers; i++) {
118 delete [] buffers_[i];
119 buffers_[i] = NULL;
120 }
121}
122
123bool CapturerLinuxPimpl::Init() {
124 // TODO(ajwong): We should specify the display string we are attaching to
125 // in the constructor.
126 display_ = XOpenDisplay(NULL);
127 if (!display_) {
128 LOG(ERROR) << "Unable to open display";
129 return false;
130 }
131
[email protected]089d1862011-03-21 13:12:34132 x_server_pixel_buffer_.Init(display_);
133
[email protected]66980d82010-10-21 20:05:01134 root_window_ = RootWindow(display_, DefaultScreen(display_));
135 if (root_window_ == BadValue) {
136 LOG(ERROR) << "Unable to get the root window";
137 DeinitXlib();
138 return false;
139 }
140
141 gc_ = XCreateGC(display_, root_window_, 0, NULL);
142 if (gc_ == NULL) {
143 LOG(ERROR) << "Unable to get graphics context";
144 DeinitXlib();
145 return false;
146 }
147
148 // Setup XDamage to report changes in the damage window. Mark the whole
149 // window as invalid.
150 if (!XDamageQueryExtension(display_, &damage_event_base_,
151 &damage_error_base_)) {
152 LOG(ERROR) << "Server does not support XDamage.";
153 DeinitXlib();
154 return false;
155 }
156 damage_handle_ = XDamageCreate(display_, root_window_,
157 XDamageReportDeltaRectangles);
158 if (damage_handle_ == BadValue) {
159 LOG(ERROR) << "Unable to create damage handle.";
160 DeinitXlib();
161 return false;
162 }
163
164 // TODO(ajwong): We should be able to replace this with a XDamageAdd().
165 capture_fullscreen_ = true;
166
167 // Set up the dimensions of the catpure framebuffer.
168 XWindowAttributes root_attr;
169 XGetWindowAttributes(display_, root_window_, &root_attr);
[email protected]e1a6c462011-03-04 12:15:31170 width_ = root_attr.width;
171 height_ = root_attr.height;
172 stride_ = width_ * kBytesPerPixel;
173 VLOG(1) << "Initialized with Geometry: " << width_ << "x" << height_;
[email protected]66980d82010-10-21 20:05:01174
175 // Allocate the screen buffers.
176 for (int i = 0; i < CapturerLinux::kNumBuffers; i++) {
[email protected]e1a6c462011-03-04 12:15:31177 buffers_[i] = new uint8[width_ * height_ * kBytesPerPixel];
[email protected]66980d82010-10-21 20:05:01178 }
179
180 return true;
181}
182
183void CapturerLinuxPimpl::CalculateInvalidRects() {
[email protected]e1a6c462011-03-04 12:15:31184 if (capturer_->IsCaptureFullScreen(width_, height_))
[email protected]0544b7fa2011-02-09 23:07:15185 capture_fullscreen_ = true;
186
[email protected]66980d82010-10-21 20:05:01187 // TODO(ajwong): The capture_fullscreen_ logic here is very ugly. Refactor.
188
189 // Find the number of events that are outstanding "now." We don't just loop
190 // on XPending because we want to guarantee this terminates.
191 int events_to_process = XPending(display_);
192 XEvent e;
193 InvalidRects invalid_rects;
194 for (int i = 0; i < events_to_process; i++) {
195 XNextEvent(display_, &e);
196 if (e.type == damage_event_base_ + XDamageNotify) {
197 // If we're doing a full screen capture, we should just drain the events.
198 if (!capture_fullscreen_) {
199 XDamageNotifyEvent *event = reinterpret_cast<XDamageNotifyEvent*>(&e);
200 gfx::Rect damage_rect(event->area.x, event->area.y, event->area.width,
201 event->area.height);
[email protected]97d82ed2011-03-07 08:01:48202
203 // TODO(hclam): Perform more checks on the rect.
204 if (damage_rect.width() <= 0 && damage_rect.height() <= 0)
205 continue;
206
[email protected]66980d82010-10-21 20:05:01207 invalid_rects.insert(damage_rect);
[email protected]97d82ed2011-03-07 08:01:48208 VLOG(3) << "Damage received for rect at ("
[email protected]66980d82010-10-21 20:05:01209 << damage_rect.x() << "," << damage_rect.y() << ") size ("
210 << damage_rect.width() << "," << damage_rect.height() << ")";
211 }
212 } else {
213 LOG(WARNING) << "Got unknown event type: " << e.type;
214 }
215 }
216
217 if (capture_fullscreen_) {
[email protected]97d82ed2011-03-07 08:01:48218 // TODO(hclam): Check the new dimension again.
[email protected]467aca52011-03-10 11:51:08219 capturer_->InvalidateScreen(gfx::Size(width_, height_));
[email protected]66980d82010-10-21 20:05:01220 capture_fullscreen_ = false;
221 } else {
222 capturer_->InvalidateRects(invalid_rects);
223 }
224}
225
226void CapturerLinuxPimpl::CaptureRects(
227 const InvalidRects& rects,
228 Capturer::CaptureCompletedCallback* callback) {
229 uint8* buffer = buffers_[capturer_->current_buffer_];
230 DataPlanes planes;
231 planes.data[0] = buffer;
232 planes.strides[0] = stride_;
233
[email protected]467aca52011-03-10 11:51:08234 scoped_refptr<CaptureData> capture_data(new CaptureData(
235 planes, gfx::Size(width_, height_), media::VideoFrame::RGB32));
[email protected]66980d82010-10-21 20:05:01236
[email protected]9b4b2d12011-02-03 00:33:58237 // Synchronize the current buffer with the last one since we do not capture
238 // the entire desktop. Note that encoder may be reading from the previous
239 // buffer at this time so thread access complaints are false positives.
240
241 // TODO(hclam): We can reduce the amount of copying here by subtracting
242 // |rects| from |last_invalid_rects_|.
243 for (InvalidRects::const_iterator it = last_invalid_rects_.begin();
244 last_buffer_ && it != last_invalid_rects_.end();
245 ++it) {
[email protected]47277232011-02-04 02:19:17246 int offset = it->y() * stride_ + it->x() * kBytesPerPixel;
[email protected]9b4b2d12011-02-03 00:33:58247 for (int i = 0; i < it->height(); ++i) {
248 memcpy(buffer + offset, last_buffer_ + offset,
249 it->width() * kBytesPerPixel);
[email protected]e1a6c462011-03-04 12:15:31250 offset += width_ * kBytesPerPixel;
[email protected]9b4b2d12011-02-03 00:33:58251 }
252 }
253
[email protected]66980d82010-10-21 20:05:01254 for (InvalidRects::const_iterator it = rects.begin();
255 it != rects.end();
256 ++it) {
[email protected]089d1862011-03-21 13:12:34257 uint8* image = x_server_pixel_buffer_.CaptureRect(*it);
[email protected]66980d82010-10-21 20:05:01258 // Check if we can fastpath the blit.
[email protected]089d1862011-03-21 13:12:34259 int depth = x_server_pixel_buffer_.GetDepth();
260 int bpp = x_server_pixel_buffer_.GetBitsPerPixel();
261 bool is_rgb = x_server_pixel_buffer_.IsRgb();
262 if ((depth == 24 || depth == 32) && bpp == 32 && is_rgb) {
[email protected]66980d82010-10-21 20:05:01263 VLOG(3) << "Fast blitting";
[email protected]089d1862011-03-21 13:12:34264 FastBlit(image, *it, capture_data);
[email protected]66980d82010-10-21 20:05:01265 } else {
266 VLOG(3) << "Slow blitting";
[email protected]089d1862011-03-21 13:12:34267 SlowBlit(image, *it, capture_data);
[email protected]66980d82010-10-21 20:05:01268 }
[email protected]66980d82010-10-21 20:05:01269 }
270
271 // TODO(ajwong): We should only repair the rects that were copied!
272 XDamageSubtract(display_, damage_handle_, None, None);
273
[email protected]d3a4b09d2011-02-22 18:16:53274 capture_data->mutable_dirty_rects() = rects;
275 last_invalid_rects_ = rects;
[email protected]9b4b2d12011-02-03 00:33:58276 last_buffer_ = buffer;
277
[email protected]66980d82010-10-21 20:05:01278 // TODO(ajwong): These completion signals back to the upper class are very
279 // strange. Fix it.
280 capturer_->FinishCapture(capture_data, callback);
281}
282
283void CapturerLinuxPimpl::DeinitXlib() {
284 if (gc_) {
285 XFreeGC(display_, gc_);
286 gc_ = NULL;
287 }
288
289 if (display_) {
290 XCloseDisplay(display_);
291 display_ = NULL;
292 }
293}
294
[email protected]089d1862011-03-21 13:12:34295void CapturerLinuxPimpl::FastBlit(uint8* image, const gfx::Rect& rect,
[email protected]66980d82010-10-21 20:05:01296 CaptureData* capture_data) {
[email protected]089d1862011-03-21 13:12:34297 uint8* src_pos = image;
298 int src_stride = x_server_pixel_buffer_.GetStride();
299 int dst_x = rect.x(), dst_y = rect.y();
[email protected]66980d82010-10-21 20:05:01300
301 DataPlanes planes = capture_data->data_planes();
302 uint8* dst_buffer = planes.data[0];
303
304 const int dst_stride = planes.strides[0];
[email protected]66980d82010-10-21 20:05:01305
[email protected]089d1862011-03-21 13:12:34306 uint8* dst_pos = dst_buffer + dst_stride * dst_y;
307 dst_pos += dst_x * kBytesPerPixel;
[email protected]d3a4b09d2011-02-22 18:16:53308
[email protected]089d1862011-03-21 13:12:34309 int height = rect.height(), row_bytes = rect.width() * kBytesPerPixel;
310 for (int y = 0; y < height; ++y) {
311 memcpy(dst_pos, src_pos, row_bytes);
[email protected]66980d82010-10-21 20:05:01312 src_pos += src_stride;
[email protected]d3a4b09d2011-02-22 18:16:53313 dst_pos += dst_stride;
[email protected]66980d82010-10-21 20:05:01314 }
315}
316
[email protected]089d1862011-03-21 13:12:34317void CapturerLinuxPimpl::SlowBlit(uint8* image, const gfx::Rect& rect,
[email protected]66980d82010-10-21 20:05:01318 CaptureData* capture_data) {
319 DataPlanes planes = capture_data->data_planes();
320 uint8* dst_buffer = planes.data[0];
321 const int dst_stride = planes.strides[0];
[email protected]089d1862011-03-21 13:12:34322 int src_stride = x_server_pixel_buffer_.GetStride();
323 int dst_x = rect.x(), dst_y = rect.y();
324 int width = rect.width(), height = rect.height();
[email protected]66980d82010-10-21 20:05:01325
[email protected]089d1862011-03-21 13:12:34326 unsigned int red_mask = x_server_pixel_buffer_.GetRedMask();
327 unsigned int blue_mask = x_server_pixel_buffer_.GetBlueMask();
328 unsigned int green_mask = x_server_pixel_buffer_.GetGreenMask();
329 unsigned int red_shift = x_server_pixel_buffer_.GetRedShift();
330 unsigned int blue_shift = x_server_pixel_buffer_.GetBlueShift();
331 unsigned int green_shift = x_server_pixel_buffer_.GetGreenShift();
[email protected]66980d82010-10-21 20:05:01332
[email protected]089d1862011-03-21 13:12:34333 unsigned int max_red = red_mask >> red_shift;
334 unsigned int max_blue = blue_mask >> blue_shift;
335 unsigned int max_green = green_mask >> green_shift;
[email protected]66980d82010-10-21 20:05:01336
[email protected]089d1862011-03-21 13:12:34337 unsigned int bits_per_pixel = x_server_pixel_buffer_.GetBitsPerPixel();
338
339 uint8* dst_pos = dst_buffer + dst_stride * dst_y;
340 uint8* src_pos = image;
341 dst_pos += dst_x * kBytesPerPixel;
342 // TODO(hclam): Optimize, perhaps using MMX code or by converting to
[email protected]3a90dd3a2011-02-22 15:06:59343 // YUV directly
[email protected]089d1862011-03-21 13:12:34344 for (int y = 0; y < height; y++) {
[email protected]3a90dd3a2011-02-22 15:06:59345 uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos);
[email protected]089d1862011-03-21 13:12:34346 uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos);
347 uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos);
348 for (int x = 0; x < width; x++) {
349 // Dereference through an appropriately-aligned pointer.
350 uint32_t pixel;
351 if (bits_per_pixel == 32)
352 pixel = src_pos_32[x];
353 else if (bits_per_pixel == 16)
354 pixel = src_pos_16[x];
355 else
356 pixel = src_pos[x];
357 uint32_t r = (((pixel & red_mask) >> red_shift) * 255) / max_red;
358 uint32_t b = (((pixel & blue_mask) >> blue_shift) * 255) / max_blue;
359 uint32_t g = (((pixel & green_mask) >> green_shift) * 255) / max_green;
[email protected]66980d82010-10-21 20:05:01360 // Write as 32-bit RGB.
[email protected]3a90dd3a2011-02-22 15:06:59361 dst_pos_32[x] = r << 16 | g << 8 | b;
[email protected]66980d82010-10-21 20:05:01362 }
[email protected]089d1862011-03-21 13:12:34363 dst_pos += dst_stride;
364 src_pos += src_stride;
[email protected]66980d82010-10-21 20:05:01365 }
[email protected]cb3b1f9312010-06-07 19:58:23366}
367
[email protected]f901a072010-11-23 22:18:12368// static
369Capturer* Capturer::Create(MessageLoop* message_loop) {
370 return new CapturerLinux(message_loop);
371}
372
[email protected]cb3b1f9312010-06-07 19:58:23373} // namespace remoting