• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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