~ubuntu-branches/debian/stretch/sflphone/stretch

« back to all changes in this revision

Viewing changes to kde/src/lib/videorenderer.cpp

  • Committer: Package Import Robot
  • Author(s): Francois Marier, Francois Marier, Mark Purcell
  • Date: 2014-10-18 15:08:50 UTC
  • mfrom: (1.1.12)
  • Revision ID: package-import@ubuntu.com-20141018150850-2exfk34ckb15pcwi
Tags: 1.4.1-0.1
[ Francois Marier ]
* Non-maintainer upload
* New upstream release (closes: #759576, #741130)
  - debian/rules +PJPROJECT_VERSION := 2.2.1
  - add upstream patch to fix broken TLS support
  - add patch to fix pjproject regression

[ Mark Purcell ]
* Build-Depends:
  - sflphone-daemon + libavformat-dev, libavcodec-dev, libswscale-dev,
  libavdevice-dev, libavutil-dev
  - sflphone-gnome + libclutter-gtk-1.0-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/****************************************************************************
2
 
 *   Copyright (C) 2012-2014 by Savoir-Faire Linux                          *
3
 
 *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
4
 
 *                                                                          *
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.     *
9
 
 *                                                                          *
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.                        *
14
 
 *                                                                          *
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"
19
 
 
20
 
#include <QtCore/QDebug>
21
 
#include <QtCore/QMutex>
22
 
 
23
 
#include <sys/ipc.h>
24
 
#include <sys/sem.h>
25
 
#include <sys/shm.h>
26
 
#include <fcntl.h>
27
 
#include <unistd.h>
28
 
#include <sys/mman.h>
29
 
#include <semaphore.h>
30
 
#include <errno.h>
31
 
// #include <linux/time.h>
32
 
#include <time.h>
33
 
 
34
 
#ifndef CLOCK_REALTIME
35
 
#define CLOCK_REALTIME 0
36
 
#endif
37
 
 
38
 
#include <QtCore/QTimer>
39
 
 
40
 
///Shared memory object
41
 
struct SHMHeader{
42
 
   sem_t notification;
43
 
   sem_t mutex;
44
 
 
45
 
   unsigned m_BufferGen;
46
 
   int m_BufferSize;
47
 
   /* The header will be aligned on 16-byte boundaries */
48
 
   char padding[8];
49
 
 
50
 
#pragma GCC diagnostic push
51
 
#pragma GCC diagnostic ignored "-pedantic"
52
 
   char m_Data[];
53
 
#pragma GCC diagnostic pop
54
 
};
55
 
 
56
 
///Constructor
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()),
61
 
   m_Id(id)
62
 
{
63
 
   setObjectName("VideoRenderer:"+id);
64
 
}
65
 
 
66
 
///Destructor
67
 
VideoRenderer::~VideoRenderer()
68
 
{
69
 
   stopShm();
70
 
   //delete m_pShmArea;
71
 
}
72
 
 
73
 
///Get the data from shared memory and transform it into a QByteArray
74
 
bool VideoRenderer::renderToBitmap(QByteArray& data)
75
 
{
76
 
   if (!m_isRendering) {
77
 
      return false;
78
 
   }
79
 
 
80
 
   if (!shmLock()) {
81
 
      return false;
82
 
   }
83
 
 
84
 
   // wait for a new buffer
85
 
   while (m_BufferGen == m_pShmArea->m_BufferGen) {
86
 
      shmUnlock();
87
 
      const struct timespec timeout = createTimeout();
88
 
      int err = sem_timedwait(&m_pShmArea->notification, &timeout);
89
 
      // Useful for debugging
90
 
//       switch (errno ) {
91
 
//          case EINTR:
92
 
//             qDebug() << "Unlock failed: Interrupted function call (POSIX.1); see signal(7)";
93
 
//             ok = false;
94
 
//             return QByteArray();
95
 
//             break;
96
 
//          case EINVAL:
97
 
//             qDebug() << "Unlock failed: Invalid argument (POSIX.1)";
98
 
//             ok = false;
99
 
//             return QByteArray();
100
 
//             break;
101
 
//          case EAGAIN:
102
 
//             qDebug() << "Unlock failed: Resource temporarily unavailable (may be the same value as EWOULDBLOCK) (POSIX.1)";
103
 
//             ok = false;
104
 
//             return QByteArray();
105
 
//             break;
106
 
//          case ETIMEDOUT:
107
 
//             qDebug() << "Unlock failed: Connection timed out (POSIX.1)";
108
 
//             ok = false;
109
 
//             return QByteArray();
110
 
//             break;
111
 
//       }
112
 
      if (err < 0) {
113
 
         return false;
114
 
      }
115
 
 
116
 
      if (!shmLock()) {
117
 
         return false;
118
 
      }
119
 
   }
120
 
 
121
 
   if (!resizeShm()) {
122
 
      qDebug() << "Could not resize shared memory";
123
 
      return false;
124
 
   }
125
 
 
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;
130
 
   shmUnlock();
131
 
//    return data;
132
 
   return true;
133
 
}
134
 
 
135
 
///Connect to the shared memory
136
 
bool VideoRenderer::startShm()
137
 
{
138
 
   if (fd != -1) {
139
 
      qDebug() << "fd must be -1";
140
 
      return false;
141
 
   }
142
 
 
143
 
   fd = shm_open(m_ShmPath.toAscii(), O_RDWR, 0);
144
 
   if (fd < 0) {
145
 
      qDebug() << "could not open shm area " << m_ShmPath << ", shm_open failed:" << strerror(errno);
146
 
      return false;
147
 
   }
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";
155
 
      return false;
156
 
   }
157
 
   emit started();
158
 
   return true;
159
 
}
160
 
 
161
 
///Disconnect from the shared memory
162
 
void VideoRenderer::stopShm()
163
 
{
164
 
   if (fd >= 0)
165
 
      close(fd);
166
 
   fd = -1;
167
 
 
168
 
   if (m_pShmArea != MAP_FAILED)
169
 
      munmap(m_pShmArea, m_ShmAreaLen);
170
 
   m_ShmAreaLen = 0;
171
 
   m_pShmArea = (SHMHeader*) MAP_FAILED;
172
 
}
173
 
 
174
 
///Resize the shared memory
175
 
bool VideoRenderer::resizeShm()
176
 
{
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;
179
 
 
180
 
      shmUnlock();
181
 
      if (munmap(m_pShmArea, m_ShmAreaLen)) {
182
 
            qDebug() << "Could not unmap shared area:" << strerror(errno);
183
 
            return false;
184
 
      }
185
 
 
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;
191
 
 
192
 
      if (!m_pShmArea) {
193
 
            m_pShmArea = nullptr;
194
 
            qDebug() << "Could not remap shared area";
195
 
            return false;
196
 
      }
197
 
 
198
 
      m_ShmAreaLen = new_size;
199
 
      if (!shmLock())
200
 
            return false;
201
 
   }
202
 
   return true;
203
 
}
204
 
 
205
 
///Lock the memory while the copy is being made
206
 
bool VideoRenderer::shmLock()
207
 
{
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";
213
 
      return false;
214
 
   }
215
 
   return true;
216
 
}
217
 
 
218
 
///Remove the lock, allow a new frame to be drawn
219
 
void VideoRenderer::shmUnlock()
220
 
{
221
 
   sem_post(&m_pShmArea->mutex);
222
 
}
223
 
 
224
 
///Create a SHM timeout
225
 
timespec VideoRenderer::createTimeout()
226
 
{
227
 
   timespec timeout = {0, 0};
228
 
   if (clock_gettime(CLOCK_REALTIME, &timeout) == -1)
229
 
      qDebug() << "clock_gettime";
230
 
   timeout.tv_sec += TIMEOUT_SEC;
231
 
   return timeout;
232
 
}
233
 
 
234
 
 
235
 
/*****************************************************************************
236
 
 *                                                                           *
237
 
 *                                   Slots                                   *
238
 
 *                                                                           *
239
 
 ****************************************************************************/
240
 
 
241
 
///Update the buffer
242
 
void VideoRenderer::timedEvents()
243
 
{
244
 
   m_pMutex->lock();
245
 
 
246
 
   bool ok = renderToBitmap(m_Frame);
247
 
   m_pMutex->unlock();
248
 
   if (ok == true) {
249
 
      emit frameUpdated();
250
 
   }
251
 
   else {
252
 
      qDebug() << "Frame dropped";
253
 
      usleep(rand()%1000); //Be sure it can come back in sync
254
 
   }
255
 
}
256
 
 
257
 
///Start the rendering loop
258
 
void VideoRenderer::startRendering()
259
 
{
260
 
   QMutexLocker locker(m_pMutex);
261
 
   startShm();
262
 
   if (!m_pTimer) {
263
 
      m_pTimer = new QTimer(this);
264
 
      connect(m_pTimer,SIGNAL(timeout()),this,SLOT(timedEvents()));
265
 
      m_pTimer->setInterval(42);
266
 
   }
267
 
   m_pTimer->start();
268
 
   m_isRendering = true;
269
 
}
270
 
 
271
 
///Stop the rendering loop
272
 
void VideoRenderer::stopRendering()
273
 
{
274
 
   QMutexLocker locker(m_pMutex);
275
 
   m_isRendering = false;
276
 
   qDebug() << "Stopping rendering on" << m_Id;
277
 
   if (m_pTimer)
278
 
      m_pTimer->stop();
279
 
   emit stopped();
280
 
   stopShm();
281
 
   //qDebug() << "Video stopped for call" << id;
282
 
   //emit videoStopped();
283
 
}
284
 
 
285
 
 
286
 
/*****************************************************************************
287
 
 *                                                                           *
288
 
 *                                 Getters                                   *
289
 
 *                                                                           *
290
 
 ****************************************************************************/
291
 
 
292
 
///Get the raw bytes directly from the SHM, not recommended, but optimal
293
 
const char* VideoRenderer::rawData()
294
 
{
295
 
   return m_isRendering?m_Frame.data():nullptr;
296
 
}
297
 
 
298
 
///Is this redenrer active
299
 
bool VideoRenderer::isRendering()
300
 
{
301
 
   return m_isRendering;
302
 
}
303
 
 
304
 
///Return the current framerate
305
 
QByteArray VideoRenderer::currentFrame()
306
 
{
307
 
   return m_Frame;
308
 
}
309
 
 
310
 
///Return the current resolution
311
 
Resolution VideoRenderer::activeResolution()
312
 
{
313
 
   return m_Res;
314
 
}
315
 
 
316
 
///Get mutex, in case renderer and views are not in the same thread
317
 
QMutex* VideoRenderer::mutex()
318
 
{
319
 
   return m_pMutex;
320
 
}
321
 
 
322
 
 
323
 
/*****************************************************************************
324
 
 *                                                                           *
325
 
 *                                 Setters                                   *
326
 
 *                                                                           *
327
 
 ****************************************************************************/
328
 
 
329
 
void VideoRenderer::setResolution(QSize size)
330
 
{
331
 
   m_Res = size;
332
 
   m_Width = size.width();
333
 
   m_Height = size.height();
334
 
}
335
 
 
336
 
void VideoRenderer::setShmPath(QString path)
337
 
{
338
 
   m_ShmPath = path;
339
 
}