1
/****************************************************************************
2
* Copyright (C) 2012-2014 by Savoir-Faire Linux *
3
* Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
5
* This library is free software; you can redistribute it and/or *
6
* modify it under the terms of the GNU Lesser General Public *
7
* License as published by the Free Software Foundation; either *
8
* version 2.1 of the License, or (at your option) any later version. *
10
* This library is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
13
* Lesser General Public License for more details. *
15
* You should have received a copy of the GNU General Public License *
16
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17
***************************************************************************/
18
#include "videorenderer.h"
20
#include <QtCore/QDebug>
21
#include <QtCore/QMutex>
29
#include <semaphore.h>
31
// #include <linux/time.h>
34
#ifndef CLOCK_REALTIME
35
#define CLOCK_REALTIME 0
38
#include <QtCore/QTimer>
40
///Shared memory object
47
/* The header will be aligned on 16-byte boundaries */
50
#pragma GCC diagnostic push
51
#pragma GCC diagnostic ignored "-pedantic"
53
#pragma GCC diagnostic pop
57
VideoRenderer::VideoRenderer(const QString& id, const QString& shmPath, Resolution res): QObject(nullptr),
58
m_Width(res.width()), m_Height(res.height()), m_ShmPath(shmPath), fd(-1),
59
m_pShmArea((SHMHeader*)MAP_FAILED), m_ShmAreaLen(0), m_BufferGen(0),
60
m_isRendering(false),m_pTimer(nullptr),m_Res(res),m_pMutex(new QMutex()),
63
setObjectName("VideoRenderer:"+id);
67
VideoRenderer::~VideoRenderer()
73
///Get the data from shared memory and transform it into a QByteArray
74
bool VideoRenderer::renderToBitmap(QByteArray& data)
84
// wait for a new buffer
85
while (m_BufferGen == m_pShmArea->m_BufferGen) {
87
const struct timespec timeout = createTimeout();
88
int err = sem_timedwait(&m_pShmArea->notification, &timeout);
89
// Useful for debugging
92
// qDebug() << "Unlock failed: Interrupted function call (POSIX.1); see signal(7)";
94
// return QByteArray();
97
// qDebug() << "Unlock failed: Invalid argument (POSIX.1)";
99
// return QByteArray();
102
// qDebug() << "Unlock failed: Resource temporarily unavailable (may be the same value as EWOULDBLOCK) (POSIX.1)";
104
// return QByteArray();
107
// qDebug() << "Unlock failed: Connection timed out (POSIX.1)";
109
// return QByteArray();
122
qDebug() << "Could not resize shared memory";
126
if (data.size() != m_pShmArea->m_BufferSize)
127
data.resize(m_pShmArea->m_BufferSize);
128
memcpy(data.data(),m_pShmArea->m_Data,m_pShmArea->m_BufferSize);
129
m_BufferGen = m_pShmArea->m_BufferGen;
135
///Connect to the shared memory
136
bool VideoRenderer::startShm()
139
qDebug() << "fd must be -1";
143
fd = shm_open(m_ShmPath.toAscii(), O_RDWR, 0);
145
qDebug() << "could not open shm area " << m_ShmPath << ", shm_open failed:" << strerror(errno);
148
m_ShmAreaLen = sizeof(SHMHeader);
149
#pragma GCC diagnostic push
150
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
151
m_pShmArea = (SHMHeader*) mmap(NULL, m_ShmAreaLen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
152
#pragma GCC diagnostic pop
153
if (m_pShmArea == MAP_FAILED) {
154
qDebug() << "Could not map shm area, mmap failed";
161
///Disconnect from the shared memory
162
void VideoRenderer::stopShm()
168
if (m_pShmArea != MAP_FAILED)
169
munmap(m_pShmArea, m_ShmAreaLen);
171
m_pShmArea = (SHMHeader*) MAP_FAILED;
174
///Resize the shared memory
175
bool VideoRenderer::resizeShm()
177
while (( (unsigned int) sizeof(SHMHeader) + (unsigned int) m_pShmArea->m_BufferSize) > (unsigned int) m_ShmAreaLen) {
178
const size_t new_size = sizeof(SHMHeader) + m_pShmArea->m_BufferSize;
181
if (munmap(m_pShmArea, m_ShmAreaLen)) {
182
qDebug() << "Could not unmap shared area:" << strerror(errno);
186
#pragma GCC diagnostic push
187
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
188
m_pShmArea = (SHMHeader*) mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
189
#pragma GCC diagnostic pop
190
m_ShmAreaLen = new_size;
193
m_pShmArea = nullptr;
194
qDebug() << "Could not remap shared area";
198
m_ShmAreaLen = new_size;
205
///Lock the memory while the copy is being made
206
bool VideoRenderer::shmLock()
208
const timespec timeout = createTimeout();
209
/* We need an upper limit on how long we'll wait to avoid locking the whole GUI */
210
if (sem_timedwait(&m_pShmArea->mutex, &timeout) < 0) {
211
if (errno == ETIMEDOUT)
212
qDebug() << "Timed out before shm lock was acquired";
218
///Remove the lock, allow a new frame to be drawn
219
void VideoRenderer::shmUnlock()
221
sem_post(&m_pShmArea->mutex);
224
///Create a SHM timeout
225
timespec VideoRenderer::createTimeout()
227
timespec timeout = {0, 0};
228
if (clock_gettime(CLOCK_REALTIME, &timeout) == -1)
229
qDebug() << "clock_gettime";
230
timeout.tv_sec += TIMEOUT_SEC;
235
/*****************************************************************************
239
****************************************************************************/
242
void VideoRenderer::timedEvents()
246
bool ok = renderToBitmap(m_Frame);
252
qDebug() << "Frame dropped";
253
usleep(rand()%1000); //Be sure it can come back in sync
257
///Start the rendering loop
258
void VideoRenderer::startRendering()
260
QMutexLocker locker(m_pMutex);
263
m_pTimer = new QTimer(this);
264
connect(m_pTimer,SIGNAL(timeout()),this,SLOT(timedEvents()));
265
m_pTimer->setInterval(42);
268
m_isRendering = true;
271
///Stop the rendering loop
272
void VideoRenderer::stopRendering()
274
QMutexLocker locker(m_pMutex);
275
m_isRendering = false;
276
qDebug() << "Stopping rendering on" << m_Id;
281
//qDebug() << "Video stopped for call" << id;
282
//emit videoStopped();
286
/*****************************************************************************
290
****************************************************************************/
292
///Get the raw bytes directly from the SHM, not recommended, but optimal
293
const char* VideoRenderer::rawData()
295
return m_isRendering?m_Frame.data():nullptr;
298
///Is this redenrer active
299
bool VideoRenderer::isRendering()
301
return m_isRendering;
304
///Return the current framerate
305
QByteArray VideoRenderer::currentFrame()
310
///Return the current resolution
311
Resolution VideoRenderer::activeResolution()
316
///Get mutex, in case renderer and views are not in the same thread
317
QMutex* VideoRenderer::mutex()
323
/*****************************************************************************
327
****************************************************************************/
329
void VideoRenderer::setResolution(QSize size)
332
m_Width = size.width();
333
m_Height = size.height();
336
void VideoRenderer::setShmPath(QString path)