diff --git a/sys/decklink/gstdecklink.cpp b/sys/decklink/gstdecklink.cpp index 08b49541af..49f3143df8 100644 --- a/sys/decklink/gstdecklink.cpp +++ b/sys/decklink/gstdecklink.cpp @@ -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);