/* * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/modules/video_render/linux/video_x11_channel.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { #define DISP_MAX 128 static Display *dispArray[DISP_MAX]; static int dispCount = 0; VideoX11Channel::VideoX11Channel(int32_t id) : _crit(*CriticalSectionWrapper::CreateCriticalSection()), _display(NULL), _shminfo(), _image(NULL), _window(0L), _gc(NULL), _width(DEFAULT_RENDER_FRAME_WIDTH), _height(DEFAULT_RENDER_FRAME_HEIGHT), _outWidth(0), _outHeight(0), _xPos(0), _yPos(0), _prepared(false), _dispCount(0), _buffer(NULL), _top(0.0), _left(0.0), _right(0.0), _bottom(0.0), _Id(id) { } VideoX11Channel::~VideoX11Channel() { if (_prepared) { _crit.Enter(); ReleaseWindow(); _crit.Leave(); } delete &_crit; } int32_t VideoX11Channel::RenderFrame(const uint32_t streamId, I420VideoFrame& videoFrame) { CriticalSectionScoped cs(&_crit); if (_width != videoFrame.width() || _height != videoFrame.height()) { if (FrameSizeChange(videoFrame.width(), videoFrame.height(), 1) == -1) { return -1; } } return DeliverFrame(videoFrame); } int32_t VideoX11Channel::FrameSizeChange(int32_t width, int32_t height, int32_t /*numberOfStreams */) { CriticalSectionScoped cs(&_crit); if (_prepared) { RemoveRenderer(); } if (CreateLocalRenderer(width, height) == -1) { return -1; } return 0; } int32_t VideoX11Channel::DeliverFrame(const I420VideoFrame& videoFrame) { CriticalSectionScoped cs(&_crit); if (!_prepared) { return 0; } if (!dispArray[_dispCount]) { return -1; } ConvertFromI420(videoFrame, kARGB, 0, _buffer); // Put image in window. XShmPutImage(_display, _window, _gc, _image, 0, 0, _xPos, _yPos, _width, _height, True); // Very important for the image to update properly! XSync(_display, False); return 0; } int32_t VideoX11Channel::GetFrameSize(int32_t& width, int32_t& height) { width = _width; height = _height; return 0; } int32_t VideoX11Channel::Init(Window window, float left, float top, float right, float bottom) { WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", __FUNCTION__); CriticalSectionScoped cs(&_crit); _window = window; _left = left; _right = right; _top = top; _bottom = bottom; _display = XOpenDisplay(NULL); // Use default display if (!_window || !_display) { return -1; } if (dispCount < DISP_MAX) { dispArray[dispCount] = _display; _dispCount = dispCount; dispCount++; } else { return -1; } if ((1 < left || left < 0) || (1 < top || top < 0) || (1 < right || right < 0) || (1 < bottom || bottom < 0)) { return -1; } // calculate position and size of rendered video int x, y; unsigned int winWidth, winHeight, borderwidth, depth; Window rootret; if (XGetGeometry(_display, _window, &rootret, &x, &y, &winWidth, &winHeight, &borderwidth, &depth) == 0) { return -1; } _xPos = (int32_t) (winWidth * left); _yPos = (int32_t) (winHeight * top); _outWidth = (int32_t) (winWidth * (right - left)); _outHeight = (int32_t) (winHeight * (bottom - top)); if (_outWidth % 2) _outWidth++; // the renderer want's sizes that are multiples of two if (_outHeight % 2) _outHeight++; _gc = XCreateGC(_display, _window, 0, 0); if (!_gc) { // Failed to create the graphics context. assert(false); return -1; } if (CreateLocalRenderer(winWidth, winHeight) == -1) { return -1; } return 0; } int32_t VideoX11Channel::ChangeWindow(Window window) { WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", __FUNCTION__); CriticalSectionScoped cs(&_crit); // Stop the rendering, if we are rendering... RemoveRenderer(); _window = window; // calculate position and size of rendered video int x, y; unsigned int winWidth, winHeight, borderwidth, depth; Window rootret; if (XGetGeometry(_display, _window, &rootret, &x, &y, &winWidth, &winHeight, &borderwidth, &depth) == -1) { return -1; } _xPos = (int) (winWidth * _left); _yPos = (int) (winHeight * _top); _outWidth = (int) (winWidth * (_right - _left)); _outHeight = (int) (winHeight * (_bottom - _top)); if (_outWidth % 2) _outWidth++; // the renderer want's sizes that are multiples of two if (_outHeight % 2) _outHeight++; // Prepare rendering using the if (CreateLocalRenderer(_width, _height) == -1) { return -1; } return 0; } int32_t VideoX11Channel::ReleaseWindow() { WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", __FUNCTION__); CriticalSectionScoped cs(&_crit); RemoveRenderer(); if (_gc) { XFreeGC(_display, _gc); _gc = NULL; } if (_display) { XCloseDisplay(_display); _display = NULL; } return 0; } int32_t VideoX11Channel::CreateLocalRenderer(int32_t width, int32_t height) { WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", __FUNCTION__); CriticalSectionScoped cs(&_crit); if (!_window || !_display) { return -1; } if (_prepared) { WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _Id, "Renderer already prepared, exits."); return -1; } _width = width; _height = height; // create shared memory image _image = XShmCreateImage(_display, CopyFromParent, 24, ZPixmap, NULL, &_shminfo, _width, _height); // this parameter needs to be the same for some reason. _shminfo.shmid = shmget(IPC_PRIVATE, (_image->bytes_per_line * _image->height), IPC_CREAT | 0777); _shminfo.shmaddr = _image->data = (char*) shmat(_shminfo.shmid, 0, 0); if (_image->data == reinterpret_cast(-1)) { return -1; } _buffer = (unsigned char*) _image->data; _shminfo.readOnly = False; // attach image to display if (!XShmAttach(_display, &_shminfo)) { //printf("XShmAttach failed !\n"); return -1; } XSync(_display, False); _prepared = true; return 0; } int32_t VideoX11Channel::RemoveRenderer() { WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", __FUNCTION__); if (!_prepared) { return 0; } _prepared = false; // Free the memory. XShmDetach(_display, &_shminfo); XDestroyImage( _image ); _image = NULL; shmdt(_shminfo.shmaddr); _shminfo.shmaddr = NULL; _buffer = NULL; shmctl(_shminfo.shmid, IPC_RMID, 0); _shminfo.shmid = 0; return 0; } int32_t VideoX11Channel::GetStreamProperties(uint32_t& zOrder, float& left, float& top, float& right, float& bottom) const { WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", __FUNCTION__); zOrder = 0; // no z-order support yet left = _left; top = _top; right = _right; bottom = _bottom; return 0; } } // namespace webrtc