blob: 6aaff4e3d0d5f50b2970678ee7188b6746d112a0 [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
[email protected]0b7e4282011-04-04 22:44:115#include "remoting/host/capturer.h"
[email protected]cb3b1f9312010-06-07 19:58:236
[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]0b7e4282011-04-04 22:44:1113#include "base/basictypes.h"
[email protected]bab76b9b2011-03-29 14:40:2014#include "base/logging.h"
[email protected]0b7e4282011-04-04 22:44:1115#include "base/memory/scoped_ptr.h"
[email protected]66980d82010-10-21 20:05:0116#include "remoting/base/types.h"
[email protected]bab76b9b2011-03-29 14:40:2017#include "remoting/host/capturer_helper.h"
[email protected]089d1862011-03-21 13:12:3418#include "remoting/host/x_server_pixel_buffer.h"
[email protected]66980d82010-10-21 20:05:0119
[email protected]cb3b1f9312010-06-07 19:58:2320namespace remoting {
21
[email protected]0b7e4282011-04-04 22:44:1122namespace {
[email protected]66980d82010-10-21 20:05:0123
[email protected]0b7e4282011-04-04 22:44:1124// A class to perform capturing for Linux.
25class CapturerLinux : public Capturer {
26 public:
27 CapturerLinux();
28 virtual ~CapturerLinux();
29
[email protected]bab76b9b2011-03-29 14:40:2030
31 // Capturer interface.
[email protected]0b7e4282011-04-04 22:44:1132 virtual void ScreenConfigurationChanged() OVERRIDE;
33 virtual media::VideoFrame::Format pixel_format() const OVERRIDE;
34 virtual void ClearInvalidRects() OVERRIDE;
35 virtual void InvalidateRects(const InvalidRects& inval_rects) OVERRIDE;
36 virtual void InvalidateScreen(const gfx::Size& size) OVERRIDE;
37 virtual void InvalidateFullScreen() OVERRIDE;
38 virtual void CaptureInvalidRects(CaptureCompletedCallback* callback) OVERRIDE;
39 virtual const gfx::Size& size_most_recent() const OVERRIDE;
[email protected]bab76b9b2011-03-29 14:40:2040
41 private:
[email protected]0b7e4282011-04-04 22:44:1142 bool Init(); // TODO(ajwong): Do we really want this to be synchronous?
[email protected]66980d82010-10-21 20:05:0143 void CalculateInvalidRects();
44 void CaptureRects(const InvalidRects& rects,
45 Capturer::CaptureCompletedCallback* callback);
46
[email protected]66980d82010-10-21 20:05:0147 void DeinitXlib();
48 // We expose two forms of blitting to handle variations in the pixel format.
49 // In FastBlit, the operation is effectively a memcpy.
[email protected]089d1862011-03-21 13:12:3450 void FastBlit(uint8* image, const gfx::Rect& rect, CaptureData* capture_data);
51 void SlowBlit(uint8* image, const gfx::Rect& rect, CaptureData* capture_data);
[email protected]66980d82010-10-21 20:05:0152
53 static const int kBytesPerPixel = 4;
54
[email protected]66980d82010-10-21 20:05:0155 // X11 graphics context.
56 Display* display_;
57 GC gc_;
58 Window root_window_;
[email protected]e1a6c462011-03-04 12:15:3159 int width_;
60 int height_;
[email protected]66980d82010-10-21 20:05:0161
62 // XDamage information.
63 Damage damage_handle_;
64 int damage_event_base_;
65 int damage_error_base_;
66
[email protected]089d1862011-03-21 13:12:3467 // Access to the X Server's pixel buffer.
68 XServerPixelBuffer x_server_pixel_buffer_;
69
[email protected]bab76b9b2011-03-29 14:40:2070 // A thread-safe list of invalid rectangles, and the size of the most
71 // recently captured screen.
72 CapturerHelper helper_;
73
[email protected]66980d82010-10-21 20:05:0174 // Capture state.
[email protected]bab76b9b2011-03-29 14:40:2075 static const int kNumBuffers = 2;
76 uint8* buffers_[kNumBuffers];
77 int current_buffer_;
[email protected]66980d82010-10-21 20:05:0178 int stride_;
79 bool capture_fullscreen_;
[email protected]9b4b2d12011-02-03 00:33:5880
[email protected]bab76b9b2011-03-29 14:40:2081 // Format of pixels returned in buffer.
82 media::VideoFrame::Format pixel_format_;
83
[email protected]9b4b2d12011-02-03 00:33:5884 // 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]0b7e4282011-04-04 22:44:1190
91 DISALLOW_COPY_AND_ASSIGN(CapturerLinux);
[email protected]66980d82010-10-21 20:05:0192};
93
[email protected]bab76b9b2011-03-29 14:40:2094CapturerLinux::CapturerLinux()
[email protected]bab76b9b2011-03-29 14:40:2095 : display_(NULL),
[email protected]66980d82010-10-21 20:05:0196 gc_(NULL),
97 root_window_(BadValue),
[email protected]e1a6c462011-03-04 12:15:3198 width_(0),
99 height_(0),
[email protected]66980d82010-10-21 20:05:01100 damage_handle_(BadValue),
101 damage_event_base_(-1),
102 damage_error_base_(-1),
[email protected]bab76b9b2011-03-29 14:40:20103 current_buffer_(0),
[email protected]66980d82010-10-21 20:05:01104 stride_(0),
[email protected]9b4b2d12011-02-03 00:33:58105 capture_fullscreen_(true),
[email protected]bab76b9b2011-03-29 14:40:20106 pixel_format_(media::VideoFrame::RGB32),
[email protected]9b4b2d12011-02-03 00:33:58107 last_buffer_(NULL) {
[email protected]bab76b9b2011-03-29 14:40:20108 for (int i = 0; i < kNumBuffers; i++) {
[email protected]66980d82010-10-21 20:05:01109 buffers_[i] = NULL;
110 }
[email protected]0b7e4282011-04-04 22:44:11111 CHECK(Init());
[email protected]66980d82010-10-21 20:05:01112}
113
[email protected]0b7e4282011-04-04 22:44:11114CapturerLinux::~CapturerLinux() {
[email protected]66980d82010-10-21 20:05:01115 DeinitXlib();
116
[email protected]bab76b9b2011-03-29 14:40:20117 for (int i = 0; i < kNumBuffers; i++) {
[email protected]66980d82010-10-21 20:05:01118 delete [] buffers_[i];
119 buffers_[i] = NULL;
120 }
121}
122
[email protected]0b7e4282011-04-04 22:44:11123bool CapturerLinux::Init() {
[email protected]66980d82010-10-21 20:05:01124 // 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.
[email protected]bab76b9b2011-03-29 14:40:20176 for (int i = 0; i < 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
[email protected]0b7e4282011-04-04 22:44:11183void CapturerLinux::ScreenConfigurationChanged() {
[email protected]bab76b9b2011-03-29 14:40:20184 // TODO(ajwong): Support resolution changes.
185 NOTIMPLEMENTED();
186}
187
[email protected]0b7e4282011-04-04 22:44:11188media::VideoFrame::Format CapturerLinux::pixel_format() const {
[email protected]bab76b9b2011-03-29 14:40:20189 return pixel_format_;
190}
191
[email protected]0b7e4282011-04-04 22:44:11192void CapturerLinux::ClearInvalidRects() {
[email protected]bab76b9b2011-03-29 14:40:20193 helper_.ClearInvalidRects();
194}
195
[email protected]0b7e4282011-04-04 22:44:11196void CapturerLinux::InvalidateRects(const InvalidRects& inval_rects) {
[email protected]bab76b9b2011-03-29 14:40:20197 helper_.InvalidateRects(inval_rects);
198}
199
[email protected]0b7e4282011-04-04 22:44:11200void CapturerLinux::InvalidateScreen(const gfx::Size& size) {
[email protected]bab76b9b2011-03-29 14:40:20201 helper_.InvalidateScreen(size);
202}
203
[email protected]0b7e4282011-04-04 22:44:11204void CapturerLinux::InvalidateFullScreen() {
[email protected]bab76b9b2011-03-29 14:40:20205 helper_.InvalidateFullScreen();
206}
207
[email protected]0b7e4282011-04-04 22:44:11208void CapturerLinux::CaptureInvalidRects(
[email protected]bab76b9b2011-03-29 14:40:20209 CaptureCompletedCallback* callback) {
210 CalculateInvalidRects();
211
212 InvalidRects rects;
213 helper_.SwapInvalidRects(rects);
214
215 CaptureRects(rects, callback);
216}
217
[email protected]0b7e4282011-04-04 22:44:11218void CapturerLinux::CalculateInvalidRects() {
[email protected]bab76b9b2011-03-29 14:40:20219 if (helper_.IsCaptureFullScreen(gfx::Size(width_, height_)))
[email protected]0544b7fa2011-02-09 23:07:15220 capture_fullscreen_ = true;
221
[email protected]66980d82010-10-21 20:05:01222 // TODO(ajwong): The capture_fullscreen_ logic here is very ugly. Refactor.
223
224 // Find the number of events that are outstanding "now." We don't just loop
225 // on XPending because we want to guarantee this terminates.
226 int events_to_process = XPending(display_);
227 XEvent e;
228 InvalidRects invalid_rects;
229 for (int i = 0; i < events_to_process; i++) {
230 XNextEvent(display_, &e);
231 if (e.type == damage_event_base_ + XDamageNotify) {
232 // If we're doing a full screen capture, we should just drain the events.
233 if (!capture_fullscreen_) {
234 XDamageNotifyEvent *event = reinterpret_cast<XDamageNotifyEvent*>(&e);
235 gfx::Rect damage_rect(event->area.x, event->area.y, event->area.width,
236 event->area.height);
[email protected]97d82ed2011-03-07 08:01:48237
238 // TODO(hclam): Perform more checks on the rect.
239 if (damage_rect.width() <= 0 && damage_rect.height() <= 0)
240 continue;
241
[email protected]66980d82010-10-21 20:05:01242 invalid_rects.insert(damage_rect);
[email protected]97d82ed2011-03-07 08:01:48243 VLOG(3) << "Damage received for rect at ("
[email protected]66980d82010-10-21 20:05:01244 << damage_rect.x() << "," << damage_rect.y() << ") size ("
245 << damage_rect.width() << "," << damage_rect.height() << ")";
246 }
247 } else {
248 LOG(WARNING) << "Got unknown event type: " << e.type;
249 }
250 }
251
252 if (capture_fullscreen_) {
[email protected]97d82ed2011-03-07 08:01:48253 // TODO(hclam): Check the new dimension again.
[email protected]bab76b9b2011-03-29 14:40:20254 helper_.InvalidateScreen(gfx::Size(width_, height_));
[email protected]66980d82010-10-21 20:05:01255 capture_fullscreen_ = false;
256 } else {
[email protected]bab76b9b2011-03-29 14:40:20257 helper_.InvalidateRects(invalid_rects);
[email protected]66980d82010-10-21 20:05:01258 }
259}
260
[email protected]0b7e4282011-04-04 22:44:11261void CapturerLinux::CaptureRects(
[email protected]66980d82010-10-21 20:05:01262 const InvalidRects& rects,
263 Capturer::CaptureCompletedCallback* callback) {
[email protected]bab76b9b2011-03-29 14:40:20264 scoped_ptr<CaptureCompletedCallback> callback_deleter(callback);
265
266 uint8* buffer = buffers_[current_buffer_];
[email protected]66980d82010-10-21 20:05:01267 DataPlanes planes;
268 planes.data[0] = buffer;
269 planes.strides[0] = stride_;
270
[email protected]467aca52011-03-10 11:51:08271 scoped_refptr<CaptureData> capture_data(new CaptureData(
272 planes, gfx::Size(width_, height_), media::VideoFrame::RGB32));
[email protected]66980d82010-10-21 20:05:01273
[email protected]9b4b2d12011-02-03 00:33:58274 // Synchronize the current buffer with the last one since we do not capture
275 // the entire desktop. Note that encoder may be reading from the previous
276 // buffer at this time so thread access complaints are false positives.
277
278 // TODO(hclam): We can reduce the amount of copying here by subtracting
279 // |rects| from |last_invalid_rects_|.
280 for (InvalidRects::const_iterator it = last_invalid_rects_.begin();
281 last_buffer_ && it != last_invalid_rects_.end();
282 ++it) {
[email protected]47277232011-02-04 02:19:17283 int offset = it->y() * stride_ + it->x() * kBytesPerPixel;
[email protected]9b4b2d12011-02-03 00:33:58284 for (int i = 0; i < it->height(); ++i) {
285 memcpy(buffer + offset, last_buffer_ + offset,
286 it->width() * kBytesPerPixel);
[email protected]e1a6c462011-03-04 12:15:31287 offset += width_ * kBytesPerPixel;
[email protected]9b4b2d12011-02-03 00:33:58288 }
289 }
290
[email protected]66980d82010-10-21 20:05:01291 for (InvalidRects::const_iterator it = rects.begin();
292 it != rects.end();
293 ++it) {
[email protected]089d1862011-03-21 13:12:34294 uint8* image = x_server_pixel_buffer_.CaptureRect(*it);
[email protected]66980d82010-10-21 20:05:01295 // Check if we can fastpath the blit.
[email protected]089d1862011-03-21 13:12:34296 int depth = x_server_pixel_buffer_.GetDepth();
297 int bpp = x_server_pixel_buffer_.GetBitsPerPixel();
298 bool is_rgb = x_server_pixel_buffer_.IsRgb();
299 if ((depth == 24 || depth == 32) && bpp == 32 && is_rgb) {
[email protected]66980d82010-10-21 20:05:01300 VLOG(3) << "Fast blitting";
[email protected]089d1862011-03-21 13:12:34301 FastBlit(image, *it, capture_data);
[email protected]66980d82010-10-21 20:05:01302 } else {
303 VLOG(3) << "Slow blitting";
[email protected]089d1862011-03-21 13:12:34304 SlowBlit(image, *it, capture_data);
[email protected]66980d82010-10-21 20:05:01305 }
[email protected]66980d82010-10-21 20:05:01306 }
307
308 // TODO(ajwong): We should only repair the rects that were copied!
309 XDamageSubtract(display_, damage_handle_, None, None);
310
[email protected]d3a4b09d2011-02-22 18:16:53311 capture_data->mutable_dirty_rects() = rects;
312 last_invalid_rects_ = rects;
[email protected]9b4b2d12011-02-03 00:33:58313 last_buffer_ = buffer;
314
[email protected]bab76b9b2011-03-29 14:40:20315 current_buffer_ = (current_buffer_ + 1) % kNumBuffers;
316 helper_.set_size_most_recent(capture_data->size());
317
318 callback->Run(capture_data);
[email protected]66980d82010-10-21 20:05:01319}
320
[email protected]0b7e4282011-04-04 22:44:11321void CapturerLinux::DeinitXlib() {
[email protected]66980d82010-10-21 20:05:01322 if (gc_) {
323 XFreeGC(display_, gc_);
324 gc_ = NULL;
325 }
326
327 if (display_) {
328 XCloseDisplay(display_);
329 display_ = NULL;
330 }
331}
332
[email protected]0b7e4282011-04-04 22:44:11333void CapturerLinux::FastBlit(uint8* image, const gfx::Rect& rect,
[email protected]66980d82010-10-21 20:05:01334 CaptureData* capture_data) {
[email protected]089d1862011-03-21 13:12:34335 uint8* src_pos = image;
336 int src_stride = x_server_pixel_buffer_.GetStride();
337 int dst_x = rect.x(), dst_y = rect.y();
[email protected]66980d82010-10-21 20:05:01338
339 DataPlanes planes = capture_data->data_planes();
340 uint8* dst_buffer = planes.data[0];
341
342 const int dst_stride = planes.strides[0];
[email protected]66980d82010-10-21 20:05:01343
[email protected]089d1862011-03-21 13:12:34344 uint8* dst_pos = dst_buffer + dst_stride * dst_y;
345 dst_pos += dst_x * kBytesPerPixel;
[email protected]d3a4b09d2011-02-22 18:16:53346
[email protected]089d1862011-03-21 13:12:34347 int height = rect.height(), row_bytes = rect.width() * kBytesPerPixel;
348 for (int y = 0; y < height; ++y) {
349 memcpy(dst_pos, src_pos, row_bytes);
[email protected]66980d82010-10-21 20:05:01350 src_pos += src_stride;
[email protected]d3a4b09d2011-02-22 18:16:53351 dst_pos += dst_stride;
[email protected]66980d82010-10-21 20:05:01352 }
353}
354
[email protected]0b7e4282011-04-04 22:44:11355void CapturerLinux::SlowBlit(uint8* image, const gfx::Rect& rect,
[email protected]66980d82010-10-21 20:05:01356 CaptureData* capture_data) {
357 DataPlanes planes = capture_data->data_planes();
358 uint8* dst_buffer = planes.data[0];
359 const int dst_stride = planes.strides[0];
[email protected]089d1862011-03-21 13:12:34360 int src_stride = x_server_pixel_buffer_.GetStride();
361 int dst_x = rect.x(), dst_y = rect.y();
362 int width = rect.width(), height = rect.height();
[email protected]66980d82010-10-21 20:05:01363
[email protected]089d1862011-03-21 13:12:34364 unsigned int red_mask = x_server_pixel_buffer_.GetRedMask();
365 unsigned int blue_mask = x_server_pixel_buffer_.GetBlueMask();
366 unsigned int green_mask = x_server_pixel_buffer_.GetGreenMask();
367 unsigned int red_shift = x_server_pixel_buffer_.GetRedShift();
368 unsigned int blue_shift = x_server_pixel_buffer_.GetBlueShift();
369 unsigned int green_shift = x_server_pixel_buffer_.GetGreenShift();
[email protected]66980d82010-10-21 20:05:01370
[email protected]089d1862011-03-21 13:12:34371 unsigned int max_red = red_mask >> red_shift;
372 unsigned int max_blue = blue_mask >> blue_shift;
373 unsigned int max_green = green_mask >> green_shift;
[email protected]66980d82010-10-21 20:05:01374
[email protected]089d1862011-03-21 13:12:34375 unsigned int bits_per_pixel = x_server_pixel_buffer_.GetBitsPerPixel();
376
377 uint8* dst_pos = dst_buffer + dst_stride * dst_y;
378 uint8* src_pos = image;
379 dst_pos += dst_x * kBytesPerPixel;
380 // TODO(hclam): Optimize, perhaps using MMX code or by converting to
[email protected]3a90dd3a2011-02-22 15:06:59381 // YUV directly
[email protected]089d1862011-03-21 13:12:34382 for (int y = 0; y < height; y++) {
[email protected]3a90dd3a2011-02-22 15:06:59383 uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos);
[email protected]089d1862011-03-21 13:12:34384 uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos);
385 uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos);
386 for (int x = 0; x < width; x++) {
387 // Dereference through an appropriately-aligned pointer.
388 uint32_t pixel;
389 if (bits_per_pixel == 32)
390 pixel = src_pos_32[x];
391 else if (bits_per_pixel == 16)
392 pixel = src_pos_16[x];
393 else
394 pixel = src_pos[x];
395 uint32_t r = (((pixel & red_mask) >> red_shift) * 255) / max_red;
396 uint32_t b = (((pixel & blue_mask) >> blue_shift) * 255) / max_blue;
397 uint32_t g = (((pixel & green_mask) >> green_shift) * 255) / max_green;
[email protected]66980d82010-10-21 20:05:01398 // Write as 32-bit RGB.
[email protected]3a90dd3a2011-02-22 15:06:59399 dst_pos_32[x] = r << 16 | g << 8 | b;
[email protected]66980d82010-10-21 20:05:01400 }
[email protected]089d1862011-03-21 13:12:34401 dst_pos += dst_stride;
402 src_pos += src_stride;
[email protected]66980d82010-10-21 20:05:01403 }
[email protected]cb3b1f9312010-06-07 19:58:23404}
405
[email protected]0b7e4282011-04-04 22:44:11406const gfx::Size& CapturerLinux::size_most_recent() const {
[email protected]bab76b9b2011-03-29 14:40:20407 return helper_.size_most_recent();
408}
409
[email protected]0b7e4282011-04-04 22:44:11410} // namespace
411
[email protected]f901a072010-11-23 22:18:12412// static
[email protected]bab76b9b2011-03-29 14:40:20413Capturer* Capturer::Create() {
414 return new CapturerLinux();
[email protected]f901a072010-11-23 22:18:12415}
416
[email protected]cb3b1f9312010-06-07 19:58:23417} // namespace remoting