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. 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. 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 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 68 XServerPixelBuffer::~XServerPixelBuffer() { 69 Release(); 70 } 71 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 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 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 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 216 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 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 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 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 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