1 /*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
12
13 #include <assert.h>
14 #include <string.h>
15 #include <sys/shm.h>
16
17 #include "webrtc/modules/desktop_capture/desktop_frame.h"
18 #include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
19 #include "webrtc/system_wrappers/include/logging.h"
20
21 namespace {
22
23 // Returns the number of bits |mask| has to be shifted left so its last
24 // (most-significant) bit set becomes the most-significant bit of the word.
25 // When |mask| is 0 the function returns 31.
MaskToShift(uint32_t mask)26 uint32_t MaskToShift(uint32_t mask) {
27 int shift = 0;
28 if ((mask & 0xffff0000u) == 0) {
29 mask <<= 16;
30 shift += 16;
31 }
32 if ((mask & 0xff000000u) == 0) {
33 mask <<= 8;
34 shift += 8;
35 }
36 if ((mask & 0xf0000000u) == 0) {
37 mask <<= 4;
38 shift += 4;
39 }
40 if ((mask & 0xc0000000u) == 0) {
41 mask <<= 2;
42 shift += 2;
43 }
44 if ((mask & 0x80000000u) == 0)
45 shift += 1;
46
47 return shift;
48 }
49
50 // Returns true if |image| is in RGB format.
IsXImageRGBFormat(XImage * image)51 bool IsXImageRGBFormat(XImage* image) {
52 return image->bits_per_pixel == 32 &&
53 image->red_mask == 0xff0000 &&
54 image->green_mask == 0xff00 &&
55 image->blue_mask == 0xff;
56 }
57
58 } // namespace
59
60 namespace webrtc {
61
XServerPixelBuffer()62 XServerPixelBuffer::XServerPixelBuffer()
63 : display_(NULL), window_(0),
64 x_image_(NULL),
65 shm_segment_info_(NULL), shm_pixmap_(0), shm_gc_(NULL) {
66 }
67
~XServerPixelBuffer()68 XServerPixelBuffer::~XServerPixelBuffer() {
69 Release();
70 }
71
Release()72 void XServerPixelBuffer::Release() {
73 if (x_image_) {
74 XDestroyImage(x_image_);
75 x_image_ = NULL;
76 }
77 if (shm_pixmap_) {
78 XFreePixmap(display_, shm_pixmap_);
79 shm_pixmap_ = 0;
80 }
81 if (shm_gc_) {
82 XFreeGC(display_, shm_gc_);
83 shm_gc_ = NULL;
84 }
85 if (shm_segment_info_) {
86 if (shm_segment_info_->shmaddr != reinterpret_cast<char*>(-1))
87 shmdt(shm_segment_info_->shmaddr);
88 if (shm_segment_info_->shmid != -1)
89 shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
90 delete shm_segment_info_;
91 shm_segment_info_ = NULL;
92 }
93 window_ = 0;
94 }
95
Init(Display * display,Window window)96 bool XServerPixelBuffer::Init(Display* display, Window window) {
97 Release();
98 display_ = display;
99
100 XWindowAttributes attributes;
101 {
102 XErrorTrap error_trap(display_);
103 if (!XGetWindowAttributes(display_, window, &attributes) ||
104 error_trap.GetLastErrorAndDisable() != 0) {
105 return false;
106 }
107 }
108
109 window_size_ = DesktopSize(attributes.width, attributes.height);
110 window_ = window;
111 InitShm(attributes);
112
113 return true;
114 }
115
InitShm(const XWindowAttributes & attributes)116 void XServerPixelBuffer::InitShm(const XWindowAttributes& attributes) {
117 Visual* default_visual = attributes.visual;
118 int default_depth = attributes.depth;
119
120 int major, minor;
121 Bool have_pixmaps;
122 if (!XShmQueryVersion(display_, &major, &minor, &have_pixmaps)) {
123 // Shared memory not supported. CaptureRect will use the XImage API instead.
124 return;
125 }
126
127 bool using_shm = false;
128 shm_segment_info_ = new XShmSegmentInfo;
129 shm_segment_info_->shmid = -1;
130 shm_segment_info_->shmaddr = reinterpret_cast<char*>(-1);
131 shm_segment_info_->readOnly = False;
132 x_image_ = XShmCreateImage(display_, default_visual, default_depth, ZPixmap,
133 0, shm_segment_info_, window_size_.width(),
134 window_size_.height());
135 if (x_image_) {
136 shm_segment_info_->shmid = shmget(
137 IPC_PRIVATE, x_image_->bytes_per_line * x_image_->height,
138 IPC_CREAT | 0600);
139 if (shm_segment_info_->shmid != -1) {
140 shm_segment_info_->shmaddr = x_image_->data =
141 reinterpret_cast<char*>(shmat(shm_segment_info_->shmid, 0, 0));
142 if (x_image_->data != reinterpret_cast<char*>(-1)) {
143 XErrorTrap error_trap(display_);
144 using_shm = XShmAttach(display_, shm_segment_info_);
145 XSync(display_, False);
146 if (error_trap.GetLastErrorAndDisable() != 0)
147 using_shm = false;
148 if (using_shm) {
149 LOG(LS_VERBOSE) << "Using X shared memory segment "
150 << shm_segment_info_->shmid;
151 }
152 }
153 } else {
154 LOG(LS_WARNING) << "Failed to get shared memory segment. "
155 "Performance may be degraded.";
156 }
157 }
158
159 if (!using_shm) {
160 LOG(LS_WARNING) << "Not using shared memory. Performance may be degraded.";
161 Release();
162 return;
163 }
164
165 if (have_pixmaps)
166 have_pixmaps = InitPixmaps(default_depth);
167
168 shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
169 shm_segment_info_->shmid = -1;
170
171 LOG(LS_VERBOSE) << "Using X shared memory extension v"
172 << major << "." << minor
173 << " with" << (have_pixmaps ? "" : "out") << " pixmaps.";
174 }
175
InitPixmaps(int depth)176 bool XServerPixelBuffer::InitPixmaps(int depth) {
177 if (XShmPixmapFormat(display_) != ZPixmap)
178 return false;
179
180 {
181 XErrorTrap error_trap(display_);
182 shm_pixmap_ = XShmCreatePixmap(display_, window_,
183 shm_segment_info_->shmaddr,
184 shm_segment_info_,
185 window_size_.width(),
186 window_size_.height(), depth);
187 XSync(display_, False);
188 if (error_trap.GetLastErrorAndDisable() != 0) {
189 // |shm_pixmap_| is not not valid because the request was not processed
190 // by the X Server, so zero it.
191 shm_pixmap_ = 0;
192 return false;
193 }
194 }
195
196 {
197 XErrorTrap error_trap(display_);
198 XGCValues shm_gc_values;
199 shm_gc_values.subwindow_mode = IncludeInferiors;
200 shm_gc_values.graphics_exposures = False;
201 shm_gc_ = XCreateGC(display_, window_,
202 GCSubwindowMode | GCGraphicsExposures,
203 &shm_gc_values);
204 XSync(display_, False);
205 if (error_trap.GetLastErrorAndDisable() != 0) {
206 XFreePixmap(display_, shm_pixmap_);
207 shm_pixmap_ = 0;
208 shm_gc_ = 0; // See shm_pixmap_ comment above.
209 return false;
210 }
211 }
212
213 return true;
214 }
215
IsWindowValid() const216 bool XServerPixelBuffer::IsWindowValid() const {
217 XWindowAttributes attributes;
218 {
219 XErrorTrap error_trap(display_);
220 if (!XGetWindowAttributes(display_, window_, &attributes) ||
221 error_trap.GetLastErrorAndDisable() != 0) {
222 return false;
223 }
224 }
225 return true;
226 }
227
Synchronize()228 void XServerPixelBuffer::Synchronize() {
229 if (shm_segment_info_ && !shm_pixmap_) {
230 // XShmGetImage can fail if the display is being reconfigured.
231 XErrorTrap error_trap(display_);
232 XShmGetImage(display_, window_, x_image_, 0, 0, AllPlanes);
233 }
234 }
235
CaptureRect(const DesktopRect & rect,DesktopFrame * frame)236 void XServerPixelBuffer::CaptureRect(const DesktopRect& rect,
237 DesktopFrame* frame) {
238 assert(rect.right() <= window_size_.width());
239 assert(rect.bottom() <= window_size_.height());
240
241 uint8_t* data;
242
243 if (shm_segment_info_) {
244 if (shm_pixmap_) {
245 XCopyArea(display_, window_, shm_pixmap_, shm_gc_,
246 rect.left(), rect.top(), rect.width(), rect.height(),
247 rect.left(), rect.top());
248 XSync(display_, False);
249 }
250 data = reinterpret_cast<uint8_t*>(x_image_->data) +
251 rect.top() * x_image_->bytes_per_line +
252 rect.left() * x_image_->bits_per_pixel / 8;
253 } else {
254 if (x_image_)
255 XDestroyImage(x_image_);
256 x_image_ = XGetImage(display_, window_, rect.left(), rect.top(),
257 rect.width(), rect.height(), AllPlanes, ZPixmap);
258 data = reinterpret_cast<uint8_t*>(x_image_->data);
259 }
260
261 if (IsXImageRGBFormat(x_image_)) {
262 FastBlit(data, rect, frame);
263 } else {
264 SlowBlit(data, rect, frame);
265 }
266 }
267
FastBlit(uint8_t * image,const DesktopRect & rect,DesktopFrame * frame)268 void XServerPixelBuffer::FastBlit(uint8_t* image,
269 const DesktopRect& rect,
270 DesktopFrame* frame) {
271 uint8_t* src_pos = image;
272 int src_stride = x_image_->bytes_per_line;
273 int dst_x = rect.left(), dst_y = rect.top();
274
275 uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
276 dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
277
278 int height = rect.height();
279 int row_bytes = rect.width() * DesktopFrame::kBytesPerPixel;
280 for (int y = 0; y < height; ++y) {
281 memcpy(dst_pos, src_pos, row_bytes);
282 src_pos += src_stride;
283 dst_pos += frame->stride();
284 }
285 }
286
SlowBlit(uint8_t * image,const DesktopRect & rect,DesktopFrame * frame)287 void XServerPixelBuffer::SlowBlit(uint8_t* image,
288 const DesktopRect& rect,
289 DesktopFrame* frame) {
290 int src_stride = x_image_->bytes_per_line;
291 int dst_x = rect.left(), dst_y = rect.top();
292 int width = rect.width(), height = rect.height();
293
294 uint32_t red_mask = x_image_->red_mask;
295 uint32_t green_mask = x_image_->red_mask;
296 uint32_t blue_mask = x_image_->blue_mask;
297
298 uint32_t red_shift = MaskToShift(red_mask);
299 uint32_t green_shift = MaskToShift(green_mask);
300 uint32_t blue_shift = MaskToShift(blue_mask);
301
302 int bits_per_pixel = x_image_->bits_per_pixel;
303
304 uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
305 uint8_t* src_pos = image;
306 dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
307 // TODO(hclam): Optimize, perhaps using MMX code or by converting to
308 // YUV directly.
309 // TODO(sergeyu): This code doesn't handle XImage byte order properly and
310 // won't work with 24bpp images. Fix it.
311 for (int y = 0; y < height; y++) {
312 uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos);
313 uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos);
314 uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos);
315 for (int x = 0; x < width; x++) {
316 // Dereference through an appropriately-aligned pointer.
317 uint32_t pixel;
318 if (bits_per_pixel == 32) {
319 pixel = src_pos_32[x];
320 } else if (bits_per_pixel == 16) {
321 pixel = src_pos_16[x];
322 } else {
323 pixel = src_pos[x];
324 }
325 uint32_t r = (pixel & red_mask) << red_shift;
326 uint32_t g = (pixel & green_mask) << green_shift;
327 uint32_t b = (pixel & blue_mask) << blue_shift;
328 // Write as 32-bit RGB.
329 dst_pos_32[x] = ((r >> 8) & 0xff0000) | ((g >> 16) & 0xff00) |
330 ((b >> 24) & 0xff);
331 }
332 dst_pos += frame->stride();
333 src_pos += src_stride;
334 }
335 }
336
337 } // namespace webrtc
338