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