Home | History | Annotate | Download | only in linux
      1 /*
      2  *  Copyright (c) 2012 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/video_render/linux/video_x11_channel.h"
     12 
     13 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
     14 #include "webrtc/system_wrappers/interface/trace.h"
     15 
     16 namespace webrtc {
     17 
     18 #define DISP_MAX 128
     19 
     20 static Display *dispArray[DISP_MAX];
     21 static int dispCount = 0;
     22 
     23 
     24 VideoX11Channel::VideoX11Channel(int32_t id) :
     25     _crit(*CriticalSectionWrapper::CreateCriticalSection()), _display(NULL),
     26           _shminfo(), _image(NULL), _window(0L), _gc(NULL),
     27           _width(DEFAULT_RENDER_FRAME_WIDTH),
     28           _height(DEFAULT_RENDER_FRAME_HEIGHT), _outWidth(0), _outHeight(0),
     29           _xPos(0), _yPos(0), _prepared(false), _dispCount(0), _buffer(NULL),
     30           _top(0.0), _left(0.0), _right(0.0), _bottom(0.0),
     31           _Id(id)
     32 {
     33 }
     34 
     35 VideoX11Channel::~VideoX11Channel()
     36 {
     37     if (_prepared)
     38     {
     39         _crit.Enter();
     40         ReleaseWindow();
     41         _crit.Leave();
     42     }
     43     delete &_crit;
     44 }
     45 
     46 int32_t VideoX11Channel::RenderFrame(const uint32_t streamId,
     47                                      I420VideoFrame& videoFrame) {
     48   CriticalSectionScoped cs(&_crit);
     49   if (_width != videoFrame.width() || _height
     50       != videoFrame.height()) {
     51       if (FrameSizeChange(videoFrame.width(), videoFrame.height(), 1) == -1) {
     52         return -1;
     53     }
     54   }
     55   return DeliverFrame(videoFrame);
     56 }
     57 
     58 int32_t VideoX11Channel::FrameSizeChange(int32_t width,
     59                                          int32_t height,
     60                                          int32_t /*numberOfStreams */)
     61 {
     62     CriticalSectionScoped cs(&_crit);
     63     if (_prepared)
     64     {
     65         RemoveRenderer();
     66     }
     67     if (CreateLocalRenderer(width, height) == -1)
     68     {
     69         return -1;
     70     }
     71 
     72     return 0;
     73 }
     74 
     75 int32_t VideoX11Channel::DeliverFrame(const I420VideoFrame& videoFrame) {
     76   CriticalSectionScoped cs(&_crit);
     77   if (!_prepared) {
     78     return 0;
     79   }
     80 
     81   if (!dispArray[_dispCount]) {
     82     return -1;
     83   }
     84 
     85   ConvertFromI420(videoFrame, kARGB, 0, _buffer);
     86 
     87   // Put image in window.
     88   XShmPutImage(_display, _window, _gc, _image, 0, 0, _xPos, _yPos, _width,
     89                _height, True);
     90 
     91   // Very important for the image to update properly!
     92   XSync(_display, False);
     93   return 0;
     94 }
     95 
     96 int32_t VideoX11Channel::GetFrameSize(int32_t& width, int32_t& height)
     97 {
     98     width = _width;
     99     height = _height;
    100 
    101     return 0;
    102 }
    103 
    104 int32_t VideoX11Channel::Init(Window window, float left, float top,
    105                               float right, float bottom)
    106 {
    107     WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s",
    108                  __FUNCTION__);
    109     CriticalSectionScoped cs(&_crit);
    110 
    111     _window = window;
    112     _left = left;
    113     _right = right;
    114     _top = top;
    115     _bottom = bottom;
    116 
    117     _display = XOpenDisplay(NULL); // Use default display
    118     if (!_window || !_display)
    119     {
    120         return -1;
    121     }
    122 
    123     if (dispCount < DISP_MAX)
    124     {
    125         dispArray[dispCount] = _display;
    126         _dispCount = dispCount;
    127         dispCount++;
    128     }
    129     else
    130     {
    131         return -1;
    132     }
    133 
    134     if ((1 < left || left < 0) || (1 < top || top < 0) || (1 < right || right
    135             < 0) || (1 < bottom || bottom < 0))
    136     {
    137         return -1;
    138     }
    139 
    140     // calculate position and size of rendered video
    141     int x, y;
    142     unsigned int winWidth, winHeight, borderwidth, depth;
    143     Window rootret;
    144     if (XGetGeometry(_display, _window, &rootret, &x, &y, &winWidth,
    145                      &winHeight, &borderwidth, &depth) == 0)
    146     {
    147         return -1;
    148     }
    149 
    150     _xPos = (int32_t) (winWidth * left);
    151     _yPos = (int32_t) (winHeight * top);
    152     _outWidth = (int32_t) (winWidth * (right - left));
    153     _outHeight = (int32_t) (winHeight * (bottom - top));
    154     if (_outWidth % 2)
    155         _outWidth++; // the renderer want's sizes that are multiples of two
    156     if (_outHeight % 2)
    157         _outHeight++;
    158 
    159     _gc = XCreateGC(_display, _window, 0, 0);
    160     if (!_gc) {
    161       // Failed to create the graphics context.
    162       assert(false);
    163       return -1;
    164     }
    165 
    166     if (CreateLocalRenderer(winWidth, winHeight) == -1)
    167     {
    168         return -1;
    169     }
    170     return 0;
    171 
    172 }
    173 
    174 int32_t VideoX11Channel::ChangeWindow(Window window)
    175 {
    176     WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s",
    177                  __FUNCTION__);
    178     CriticalSectionScoped cs(&_crit);
    179 
    180     // Stop the rendering, if we are rendering...
    181     RemoveRenderer();
    182     _window = window;
    183 
    184     // calculate position and size of rendered video
    185     int x, y;
    186     unsigned int winWidth, winHeight, borderwidth, depth;
    187     Window rootret;
    188     if (XGetGeometry(_display, _window, &rootret, &x, &y, &winWidth,
    189                      &winHeight, &borderwidth, &depth) == -1)
    190     {
    191         return -1;
    192     }
    193     _xPos = (int) (winWidth * _left);
    194     _yPos = (int) (winHeight * _top);
    195     _outWidth = (int) (winWidth * (_right - _left));
    196     _outHeight = (int) (winHeight * (_bottom - _top));
    197     if (_outWidth % 2)
    198         _outWidth++; // the renderer want's sizes that are multiples of two
    199     if (_outHeight % 2)
    200         _outHeight++;
    201 
    202     // Prepare rendering using the
    203     if (CreateLocalRenderer(_width, _height) == -1)
    204     {
    205         return -1;
    206     }
    207     return 0;
    208 }
    209 
    210 int32_t VideoX11Channel::ReleaseWindow()
    211 {
    212     WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s",
    213                  __FUNCTION__);
    214     CriticalSectionScoped cs(&_crit);
    215 
    216     RemoveRenderer();
    217     if (_gc) {
    218       XFreeGC(_display, _gc);
    219       _gc = NULL;
    220     }
    221     if (_display)
    222     {
    223         XCloseDisplay(_display);
    224         _display = NULL;
    225     }
    226     return 0;
    227 }
    228 
    229 int32_t VideoX11Channel::CreateLocalRenderer(int32_t width, int32_t height)
    230 {
    231     WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s",
    232                  __FUNCTION__);
    233     CriticalSectionScoped cs(&_crit);
    234 
    235     if (!_window || !_display)
    236     {
    237         return -1;
    238     }
    239 
    240     if (_prepared)
    241     {
    242         WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _Id,
    243                      "Renderer already prepared, exits.");
    244         return -1;
    245     }
    246 
    247     _width = width;
    248     _height = height;
    249 
    250     // create shared memory image
    251     _image = XShmCreateImage(_display, CopyFromParent, 24, ZPixmap, NULL,
    252                              &_shminfo, _width, _height); // this parameter needs to be the same for some reason.
    253     _shminfo.shmid = shmget(IPC_PRIVATE, (_image->bytes_per_line
    254             * _image->height), IPC_CREAT | 0777);
    255     _shminfo.shmaddr = _image->data = (char*) shmat(_shminfo.shmid, 0, 0);
    256     if (_image->data == reinterpret_cast<char*>(-1))
    257     {
    258         return -1;
    259     }
    260     _buffer = (unsigned char*) _image->data;
    261     _shminfo.readOnly = False;
    262 
    263     // attach image to display
    264     if (!XShmAttach(_display, &_shminfo))
    265     {
    266         //printf("XShmAttach failed !\n");
    267         return -1;
    268     }
    269     XSync(_display, False);
    270 
    271     _prepared = true;
    272     return 0;
    273 }
    274 
    275 int32_t VideoX11Channel::RemoveRenderer()
    276 {
    277     WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s",
    278                  __FUNCTION__);
    279 
    280     if (!_prepared)
    281     {
    282         return 0;
    283     }
    284     _prepared = false;
    285 
    286     // Free the memory.
    287     XShmDetach(_display, &_shminfo);
    288     XDestroyImage( _image );
    289     _image = NULL;
    290     shmdt(_shminfo.shmaddr);
    291     _shminfo.shmaddr = NULL;
    292     _buffer = NULL;
    293     shmctl(_shminfo.shmid, IPC_RMID, 0);
    294     _shminfo.shmid = 0;
    295     return 0;
    296 }
    297 
    298 int32_t VideoX11Channel::GetStreamProperties(uint32_t& zOrder,
    299                                              float& left, float& top,
    300                                              float& right, float& bottom) const
    301 {
    302     WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s",
    303                  __FUNCTION__);
    304 
    305     zOrder = 0; // no z-order support yet
    306     left = _left;
    307     top = _top;
    308     right = _right;
    309     bottom = _bottom;
    310 
    311     return 0;
    312 }
    313 
    314 
    315 }  // namespace webrtc
    316