Skip to content

Commit

Permalink
MMALFFMPEG: Destroy the pool lazily
Browse files Browse the repository at this point in the history
  • Loading branch information
popcornmix committed Feb 12, 2016
1 parent a93db5e commit a7381e5
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 54 deletions.
146 changes: 99 additions & 47 deletions xbmc/cores/VideoPlayer/DVDCodecs/Video/MMALFFmpeg.cpp
Expand Up @@ -43,8 +43,8 @@ extern "C" {

#define CLASSNAME "CMMALYUVBuffer"

CMMALYUVBuffer::CMMALYUVBuffer(CDecoder *omv, unsigned int w, unsigned int h)
: width(w), height(h), m_omv(omv), m_refs(0), closed(false)
CMMALYUVBuffer::CMMALYUVBuffer(const CBufferPoolPtr &pool, unsigned int w, unsigned int h)
: width(w), height(h), m_refs(0), m_pool(pool)
{
// ffmpeg requirements
stride_y = width + 32;
Expand Down Expand Up @@ -92,35 +92,109 @@ long CMMALYUVBuffer::Release()
CLog::Log(LOGDEBUG, "%s::%s %p (%p) ref:%ld", CLASSNAME, __func__, this, mmal_buffer, count);
if (count == 0)
{
if (closed)
{
if (g_advancedSettings.CanLogComponent(LOGVIDEO))
CLog::Log(LOGDEBUG,"%s delete %p", __FUNCTION__, this);
delete this;
}
else
{
CSingleLock lock(m_omv->m_section);
unsigned int size = m_omv->m_usedBuffers.size();
m_omv->m_usedBuffers.erase(std::remove(m_omv->m_usedBuffers.begin(), m_omv->m_usedBuffers.end(), this), m_omv->m_usedBuffers.end());
assert(size == m_omv->m_usedBuffers.size() + 1);
mmal_buffer = NULL;
m_omv->m_freeBuffers.push_back(this);
if (g_advancedSettings.CanLogComponent(LOGVIDEO))
CLog::Log(LOGDEBUG,"%s used:%d free:%d", __FUNCTION__, m_omv->m_usedBuffers.size(), m_omv->m_freeBuffers.size());
}
if (g_advancedSettings.CanLogComponent(LOGVIDEO))
CLog::Log(LOGDEBUG,"%s used:%d free:%d", __FUNCTION__, m_pool->sizeUsed(), m_pool->sizeFree());
m_pool->ReleaseBuffer(this);
}
else
assert(count > 0);
return count;
}

//-----------------------------------------------------------------------------
// Buffer Pool
//-----------------------------------------------------------------------------

CBufferPool::CBufferPool()
{
m_closing = false;
if (g_advancedSettings.CanLogComponent(LOGVIDEO))
CLog::Log(LOGDEBUG, "%s - create %p", __FUNCTION__, this);
}

CBufferPool::~CBufferPool()
{
if (g_advancedSettings.CanLogComponent(LOGVIDEO))
CLog::Log(LOGDEBUG, "%s - destroy %p", __FUNCTION__, this);
}

void CBufferPool::Close()
{
CSingleLock lock(m_section);
m_closing = true;
if (g_advancedSettings.CanLogComponent(LOGVIDEO))
CLog::Log(LOGDEBUG, "%s - close %p", __FUNCTION__, this);

while (!m_freeBuffers.empty())
{
delete m_freeBuffers.front();
m_freeBuffers.pop_front();
}
assert(m_freeBuffers.empty());
}

CMMALYUVBuffer *CBufferPool::GetFreeBuffer()
{
CSingleLock lock(m_section);
CMMALYUVBuffer *buffer = nullptr;
if (!m_freeBuffers.empty())
{
buffer = m_freeBuffers.front();
m_freeBuffers.pop_front();
}
return buffer;
}

CMMALYUVBuffer *CBufferPool::GetBuffer(const CBufferPoolPtr &pool, unsigned int w, unsigned int h)
{
CSingleLock lock(m_section);
CMMALYUVBuffer *buffer = nullptr;
while (1)
{
CMMALYUVBuffer *buf = GetFreeBuffer();
if (!buf)
break;
if (buf->width == w && buf->height == h)
{
buffer = buf;
break;
}
assert(0); // testing
delete buf;
}
if (!buffer)
buffer = new CMMALYUVBuffer(pool, w, h);

m_usedBuffers.push_back(buffer);
return buffer;
}

void CBufferPool::ReleaseBuffer(CMMALYUVBuffer *buffer)
{
CSingleLock lock(m_section);
if (g_advancedSettings.CanLogComponent(LOGVIDEO))
CLog::Log(LOGDEBUG,"%s used:%d free:%d", __FUNCTION__, sizeUsed(), sizeFree());
if (m_closing)
delete buffer;
else
{
unsigned int size = sizeUsed();
m_usedBuffers.erase(std::remove(m_usedBuffers.begin(), m_usedBuffers.end(), buffer), m_usedBuffers.end());
assert(size == sizeUsed() + 1);
buffer->mmal_buffer = NULL;
m_freeBuffers.push_back(buffer);
}
}


//-----------------------------------------------------------------------------
// MMAL Decoder
//-----------------------------------------------------------------------------

CDecoder::CDecoder()
{
m_pool = std::make_shared<CBufferPool>();

if (g_advancedSettings.CanLogComponent(LOGVIDEO))
CLog::Log(LOGDEBUG, "%s - create %p", __FUNCTION__, this);
m_shared = 0;
Expand All @@ -130,7 +204,7 @@ CDecoder::CDecoder()
CDecoder::~CDecoder()
{
if (g_advancedSettings.CanLogComponent(LOGVIDEO))
CLog::Log(LOGDEBUG, "%s - destroy %p", __FUNCTION__, this);
CLog::Log(LOGDEBUG, "%s - destroy %p used:%d free:%d", __FUNCTION__, this, m_pool->sizeUsed(), m_pool->sizeFree());
Close();
}

Expand All @@ -142,18 +216,9 @@ long CDecoder::Release()
void CDecoder::Close()
{
CSingleLock lock(m_section);
for (auto it = m_usedBuffers.begin(); it != m_usedBuffers.end(); it++)
(*it)->closed = true;

while (!m_freeBuffers.empty())
{
delete m_freeBuffers.front();
m_freeBuffers.pop_front();
}
assert(m_freeBuffers.empty());

m_pool->Close();
if (g_advancedSettings.CanLogComponent(LOGVIDEO))
CLog::Log(LOGDEBUG,"%s used:%d free:%d", __FUNCTION__, m_usedBuffers.size(), m_freeBuffers.size());
CLog::Log(LOGDEBUG,"%s used:%d free:%d", __FUNCTION__, m_pool->sizeUsed(), m_pool->sizeFree());
}

void CDecoder::FFReleaseBuffer(void *opaque, uint8_t *data)
Expand All @@ -176,23 +241,10 @@ int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *frame, int flags)

CSingleLock lock(dec->m_section);

CMMALYUVBuffer *YUVBuffer = nullptr;
while (!dec->m_freeBuffers.empty())
{
CMMALYUVBuffer *buf = dec->m_freeBuffers.front();
dec->m_freeBuffers.pop_front();
if ((int)buf->width == frame->width && (int)buf->height == frame->height)
{
YUVBuffer = buf;
break;
}
}
if (!YUVBuffer)
YUVBuffer = new CMMALYUVBuffer(dec, frame->width, frame->height);
CMMALYUVBuffer *YUVBuffer = dec->m_pool->GetBuffer(dec->m_pool, frame->width, frame->height);

dec->m_usedBuffers.push_back(YUVBuffer);
if (g_advancedSettings.CanLogComponent(LOGVIDEO))
CLog::Log(LOGDEBUG,"%s used:%d free:%d", __FUNCTION__, dec->m_usedBuffers.size(), dec->m_freeBuffers.size());
CLog::Log(LOGDEBUG,"%s used:%d free:%d", __FUNCTION__, dec->m_pool->sizeUsed(), dec->m_pool->sizeFree());

GPU_MEM_PTR_T *gmem = YUVBuffer->gmem;
YUVBuffer->Acquire();
Expand All @@ -201,7 +253,7 @@ int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *frame, int flags)
if (!buf)
{
CLog::Log(LOGERROR, "%s av_buffer_create() failed", __FUNCTION__);
delete YUVBuffer;
dec->m_pool->ReleaseBuffer(YUVBuffer);
return -1;
}

Expand Down
33 changes: 26 additions & 7 deletions xbmc/cores/VideoPlayer/DVDCodecs/Video/MMALFFmpeg.h
Expand Up @@ -29,12 +29,14 @@
class CDecoder;
struct MMAL_BUFFER_HEADER_T;
struct GPU_MEM_PTR_T;
class CBufferPool;
typedef std::shared_ptr<CBufferPool> CBufferPoolPtr;

// a mmal video frame
class CMMALYUVBuffer
{
public:
CMMALYUVBuffer(CDecoder *omv, unsigned int w, unsigned int h);
CMMALYUVBuffer(const CBufferPoolPtr &pool, unsigned int w, unsigned int h);
virtual ~CMMALYUVBuffer();

MMAL_BUFFER_HEADER_T *mmal_buffer;
Expand All @@ -46,11 +48,29 @@ class CMMALYUVBuffer
// reference counting
CMMALYUVBuffer* Acquire();
long Release();
CDecoder *m_omv;
private:
long m_refs;
CCriticalSection m_section;
bool closed;
CBufferPoolPtr m_pool;
};

// interface between CDecoder and CMMALYUVBuffer that can outlive CDecoder
class CBufferPool
{
public:
CBufferPool();
virtual ~CBufferPool();
CMMALYUVBuffer *GetFreeBuffer();
CMMALYUVBuffer *GetBuffer(const CBufferPoolPtr &pool, unsigned int w, unsigned int h);
void ReleaseBuffer(CMMALYUVBuffer *buffer);
unsigned sizeUsed() { return m_usedBuffers.size(); }
unsigned sizeFree() { return m_freeBuffers.size(); }
void Close();

private:
CCriticalSection m_section;
std::deque<CMMALYUVBuffer *> m_usedBuffers;
std::deque<CMMALYUVBuffer *> m_freeBuffers;
bool m_closing;
};

class CDecoder
Expand All @@ -70,12 +90,11 @@ class CDecoder

static void FFReleaseBuffer(void *opaque, uint8_t *data);
static int FFGetBuffer(AVCodecContext *avctx, AVFrame *pic, int flags);
std::deque<CMMALYUVBuffer*> m_usedBuffers;
std::deque<CMMALYUVBuffer*> m_freeBuffers;
CCriticalSection m_section;

protected:
AVCodecContext *m_avctx;
CCriticalSection m_section;
unsigned int m_shared;
CBufferPoolPtr m_pool;
};

0 comments on commit a7381e5

Please sign in to comment.