decklinkvideosrc: Add custom memory allocator

The default memory allocator of the decklink library allocates
a fixed pool of buffers, and the number of buffers is unknown.
This makes it impossible do useful queuing downstream. The new
memory allocator can create an unlimited number of buffers,
giving all queuing features one would expect from a live source.

https://bugzilla.gnome.org/show_bug.cgi?id=782556
This commit is contained in:
Georg Lippitsch 2017-05-12 14:39:54 +02:00 committed by Sebastian Dröge
parent db36718ee4
commit f0c7fbb371

View file

@ -856,6 +856,155 @@ public:
}
};
class GStreamerDecklinkMemoryAllocator:public IDeckLinkMemoryAllocator
{
private:
GMutex m_mutex;
uint32_t m_lastBufferSize;
uint32_t m_nonEmptyCalls;
GstQueueArray * m_buffers;
gint m_refcount;
void _clearBufferPool()
{
uint8_t * buf;
if (!m_buffers)
return;
while ((buf = (uint8_t*)gst_queue_array_pop_head (m_buffers)))
g_free(buf - 128);
}
public:
GStreamerDecklinkMemoryAllocator ()
: IDeckLinkMemoryAllocator (),
m_lastBufferSize (0),
m_nonEmptyCalls (0),
m_buffers (NULL),
m_refcount (1)
{
g_mutex_init (&m_mutex);
m_buffers = gst_queue_array_new (60);
}
virtual ~ GStreamerDecklinkMemoryAllocator ()
{
Decommit();
gst_queue_array_free (m_buffers);
g_mutex_clear (&m_mutex);
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID, LPVOID *)
{
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef (void)
{
ULONG ret;
g_mutex_lock (&m_mutex);
m_refcount++;
ret = m_refcount;
g_mutex_unlock (&m_mutex);
return ret;
}
virtual ULONG STDMETHODCALLTYPE Release (void)
{
ULONG ret;
g_mutex_lock (&m_mutex);
m_refcount--;
ret = m_refcount;
g_mutex_unlock (&m_mutex);
if (ret == 0) {
delete this;
}
return ret;
}
virtual HRESULT STDMETHODCALLTYPE
AllocateBuffer (uint32_t bufferSize, void **allocatedBuffer)
{
uint8_t * buf;
g_mutex_lock (&m_mutex);
/* If buffer size changed since last call, empty buffer pool */
if (bufferSize != m_lastBufferSize) {
_clearBufferPool();
m_lastBufferSize = bufferSize;
}
/* Look if there is a free buffer in the pool */
if (!(buf = (uint8_t*) gst_queue_array_pop_head (m_buffers))) {
/* If not, alloc a new one */
buf = (uint8_t*) g_malloc (bufferSize + 128);
*((uint32_t *) buf) = bufferSize;
buf += 128;
}
*allocatedBuffer = (void *) buf;
/* If there are still unused buffers in the pool
* remove one of them every fifth call */
if (gst_queue_array_get_length (m_buffers) > 0) {
if (++m_nonEmptyCalls >= 5) {
buf = (uint8_t*) gst_queue_array_pop_head (m_buffers) - 128;
g_free (buf);
m_nonEmptyCalls = 0;
}
} else {
m_nonEmptyCalls = 0;
}
g_mutex_unlock (&m_mutex);
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE
ReleaseBuffer (void * buffer)
{
g_mutex_lock (&m_mutex);
/* Put the buffer back to the pool if size matches with current pool */
uint32_t size = *(uint32_t *) ((uint8_t*)buffer - 128);
if (size == m_lastBufferSize) {
gst_queue_array_push_tail (m_buffers, buffer);
} else {
g_free (buffer);
}
g_mutex_unlock (&m_mutex);
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE
Commit ()
{
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE
Decommit ()
{
/* Clear all remaining pools */
_clearBufferPool();
return S_OK;
}
};
#ifdef _MSC_VER
/* FIXME: We currently never deinit this */
@ -1147,6 +1296,8 @@ gst_decklink_acquire_nth_input (gint n, GstElement * src, gboolean is_audio)
}
g_mutex_lock (&input->lock);
input->input->SetVideoInputFrameMemoryAllocator(
new GStreamerDecklinkMemoryAllocator);
if (is_audio && !input->audiosrc) {
input->audiosrc = GST_ELEMENT_CAST (gst_object_ref (src));
g_mutex_unlock (&input->lock);