Home | History | Annotate | Download | only in x11
      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/interface/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