From e7793429726730944a0639d5e8daaa5713b02220 Mon Sep 17 00:00:00 2001 From: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> Date: Wed, 8 Apr 2015 11:09:13 -0400 Subject: [PATCH] video: update shm to daemon implementation This patch introduces double buffered SHM, added in daemon. Refs #70056 --- src/private/shmrenderer.cpp | 144 ++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/src/private/shmrenderer.cpp b/src/private/shmrenderer.cpp index 13e7d63a..1a20a5aa 100644 --- a/src/private/shmrenderer.cpp +++ b/src/private/shmrenderer.cpp @@ -1,6 +1,7 @@ /**************************************************************************** * Copyright (C) 2012-2015 by Savoir-Faire Linux * * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * Author : Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * @@ -46,18 +47,23 @@ #endif ///Shared memory object -struct SHMHeader { - sem_t notification; - sem_t mutex; +// Implementation note: double-buffering +// Shared memory is divided in two regions, each representing one frame. +// First byte of each frame is warranted to by aligned on 16 bytes. +// One region is marked readable: this region can be safely read. +// The other region is writeable: only the producer can use it. - unsigned m_BufferGen; - int m_BufferSize; - /* The header will be aligned on 16-byte boundaries */ - char padding[8]; +struct SHMHeader { + sem_t mutex; // Lock it before any operations on following fields. + sem_t frameGenMutex; // unlocked by producer when frameGen modified + unsigned frameGen; // monotonically incremented when a producer changes readOffset + unsigned frameSize; // size in bytes of 1 frame + unsigned readOffset; // offset of readable frame in data + unsigned writeOffset; // offset of writable frame in data #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-pedantic" - char m_Data[]; + char data[]; // the whole shared memory #pragma GCC diagnostic pop }; @@ -73,7 +79,7 @@ public: QString m_ShmPath ; int fd ; SHMHeader* m_pShmArea ; - signed int m_ShmAreaLen ; + unsigned m_ShmAreaLen ; uint m_BufferGen ; QTimer* m_pTimer ; int m_fpsC ; @@ -125,7 +131,6 @@ Video::ShmRenderer::ShmRenderer(const QByteArray& id, const QString& shmPath, co Video::ShmRenderer::~ShmRenderer() { stopShm(); - //delete m_pShmArea; } ///Get the data from shared memory and transform it into a QByteArray @@ -140,41 +145,34 @@ bool Video::ShmRendererPrivate::renderToBitmap() if (!shmLock()) return false; - if (m_BufferGen == m_pShmArea->m_BufferGen) { - // wait for a new buffer - do { - shmUnlock(); - auto err = sem_trywait(&m_pShmArea->notification); - if (!err) - break; - - // Fatal error? - if (errno != EAGAIN) - return false; + if (m_BufferGen == m_pShmArea->frameGen) { + shmUnlock(); - // wait a little bit that deamon does its work - usleep(10); // must be less than the maximal framerate possible - - // stopRendering called? - if (!q_ptr->isRendering()) - return false; - } while (m_BufferGen == m_pShmArea->m_BufferGen); + // wait for a new frame, max 33ms + static const struct timespec timeout = {0, 33000000}; + if (::sem_timedwait(&m_pShmArea->frameGenMutex, &timeout) < 0) + return false; if (!shmLock()) return false; } + // valid frame to render? + if (not m_pShmArea->frameSize) + return false; + if (!q_ptr->resizeShm()) { qDebug() << "Could not resize shared memory"; - shmUnlock(); return false; } - if (static_cast<Video::Renderer*>(q_ptr)->d_ptr->otherFrame().size() != m_pShmArea->m_BufferSize) - static_cast<Video::Renderer*>(q_ptr)->d_ptr->otherFrame().resize(m_pShmArea->m_BufferSize); - memcpy(static_cast<Video::Renderer*>(q_ptr)->d_ptr->otherFrame().data(),m_pShmArea->m_Data,m_pShmArea->m_BufferSize); - m_BufferGen = m_pShmArea->m_BufferGen; - static_cast<Video::Renderer*>(q_ptr)->d_ptr->updateFrameIndex(); + auto& renderer = static_cast<Video::Renderer*>(q_ptr)->d_ptr; + auto& frame = renderer->otherFrame(); + if ((unsigned)frame.size() != m_pShmArea->frameSize) + frame.resize(m_pShmArea->frameSize); + std::copy_n(m_pShmArea->data + m_pShmArea->readOffset, m_pShmArea->frameSize, frame.data()); + m_BufferGen = m_pShmArea->frameGen; + renderer->updateFrameIndex(); shmUnlock(); #ifdef DEBUG_FPS @@ -202,20 +200,24 @@ bool Video::ShmRenderer::startShm() return false; } - d_ptr->fd = shm_open(d_ptr->m_ShmPath.toLatin1(), O_RDWR, 0); + d_ptr->fd = ::shm_open(d_ptr->m_ShmPath.toLatin1(), O_RDWR, 0); if (d_ptr->fd < 0) { - qDebug() << "could not open shm area " << d_ptr->m_ShmPath << ", shm_open failed:" << strerror(errno); + qDebug() << "could not open shm area " << d_ptr->m_ShmPath + << ", shm_open failed:" << strerror(errno); return false; } - d_ptr->m_ShmAreaLen = sizeof(SHMHeader); - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" - d_ptr->m_pShmArea = (SHMHeader*) mmap(NULL, d_ptr->m_ShmAreaLen, PROT_READ | PROT_WRITE, MAP_SHARED, d_ptr->fd, 0); - #pragma GCC diagnostic pop + + const auto areaSize = sizeof(SHMHeader); + d_ptr->m_pShmArea = (SHMHeader*) ::mmap(nullptr, areaSize, + PROT_READ | PROT_WRITE, + MAP_SHARED, d_ptr->fd, 0); if (d_ptr->m_pShmArea == MAP_FAILED) { - qDebug() << "Could not map shm area, mmap failed"; - return false; + qDebug() << "Could not remap shared area"; + return false; } + + d_ptr->m_ShmAreaLen = areaSize; + emit started(); return true; } @@ -223,12 +225,16 @@ bool Video::ShmRenderer::startShm() ///Disconnect from the shared memory void Video::ShmRenderer::stopShm() { - if (d_ptr->fd >= 0) - close(d_ptr->fd); + if (d_ptr->fd < 0) + return; + + ::close(d_ptr->fd); d_ptr->fd = -1; - if (d_ptr->m_pShmArea != MAP_FAILED) - munmap(d_ptr->m_pShmArea, d_ptr->m_ShmAreaLen); + if (d_ptr->m_pShmArea == MAP_FAILED) + return; + + ::munmap(d_ptr->m_pShmArea, d_ptr->m_ShmAreaLen); d_ptr->m_ShmAreaLen = 0; d_ptr->m_pShmArea = (SHMHeader*) MAP_FAILED; } @@ -236,39 +242,33 @@ void Video::ShmRenderer::stopShm() ///Resize the shared memory bool Video::ShmRenderer::resizeShm() { - while (( (unsigned int) sizeof(SHMHeader) + (unsigned int) d_ptr->m_pShmArea->m_BufferSize) > (unsigned int) d_ptr->m_ShmAreaLen) { - const size_t new_size = sizeof(SHMHeader) + d_ptr->m_pShmArea->m_BufferSize; - - d_ptr->shmUnlock(); - if (munmap(d_ptr->m_pShmArea, d_ptr->m_ShmAreaLen)) { - qDebug() << "Could not unmap shared area:" << strerror(errno); - return false; - } - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" - d_ptr->m_pShmArea = (SHMHeader*) mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, d_ptr->fd, 0); - #pragma GCC diagnostic pop - d_ptr->m_ShmAreaLen = new_size; + const auto areaSize = sizeof(SHMHeader) + 2 * d_ptr->m_pShmArea->frameSize + 15; + if (d_ptr->m_ShmAreaLen == areaSize) + return true; + + d_ptr->shmUnlock(); + if (::munmap(d_ptr->m_pShmArea, d_ptr->m_ShmAreaLen)) { + qDebug() << "Could not unmap shared area:" << strerror(errno); + return false; + } - if (!d_ptr->m_pShmArea) { - d_ptr->m_pShmArea = nullptr; - qDebug() << "Could not remap shared area"; - return false; - } + d_ptr->m_pShmArea = (SHMHeader*) ::mmap(nullptr, areaSize, + PROT_READ | PROT_WRITE, + MAP_SHARED, d_ptr->fd, 0); + if (d_ptr->m_pShmArea == MAP_FAILED) { + qDebug() << "Could not remap shared area"; + return false; + } - d_ptr->m_ShmAreaLen = new_size; - if (!d_ptr->shmLock()) - return true; - } - return true; + d_ptr->m_ShmAreaLen = areaSize; + return d_ptr->shmLock(); } ///Lock the memory while the copy is being made bool Video::ShmRendererPrivate::shmLock() { #ifdef Q_OS_LINUX - return sem_trywait(&m_pShmArea->mutex) >= 0; + return sem_wait(&m_pShmArea->mutex) >= 0; #else return false; #endif -- GitLab