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/include/critical_section_wrapper.h"
14 #include "webrtc/system_wrappers/include/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 
VideoX11Channel(int32_t id)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 
~VideoX11Channel()35 VideoX11Channel::~VideoX11Channel()
36 {
37     if (_prepared)
38     {
39         _crit.Enter();
40         ReleaseWindow();
41         _crit.Leave();
42     }
43     delete &_crit;
44 }
45 
RenderFrame(const uint32_t streamId,const VideoFrame & videoFrame)46 int32_t VideoX11Channel::RenderFrame(const uint32_t streamId,
47                                      const VideoFrame& 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 
FrameSizeChange(int32_t width,int32_t height,int32_t)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 
DeliverFrame(const VideoFrame & videoFrame)75 int32_t VideoX11Channel::DeliverFrame(const VideoFrame& 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 
GetFrameSize(int32_t & width,int32_t & height)96 int32_t VideoX11Channel::GetFrameSize(int32_t& width, int32_t& height)
97 {
98     width = _width;
99     height = _height;
100 
101     return 0;
102 }
103 
Init(Window window,float left,float top,float right,float bottom)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 
ChangeWindow(Window window)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 
ReleaseWindow()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 
CreateLocalRenderer(int32_t width,int32_t height)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 
RemoveRenderer()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 
GetStreamProperties(uint32_t & zOrder,float & left,float & top,float & right,float & bottom) const298 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