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
16namespace webrtc {
17
18#define DISP_MAX 128
19
20static Display *dispArray[DISP_MAX];
21static int dispCount = 0;
22
23
24VideoX11Channel::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
35VideoX11Channel::~VideoX11Channel()
36{
37    if (_prepared)
38    {
39        _crit.Enter();
40        ReleaseWindow();
41        _crit.Leave();
42    }
43    delete &_crit;
44}
45
46int32_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
58int32_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
75int32_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
96int32_t VideoX11Channel::GetFrameSize(int32_t& width, int32_t& height)
97{
98    width = _width;
99    height = _height;
100
101    return 0;
102}
103
104int32_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
174int32_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
210int32_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
229int32_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
275int32_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
298int32_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