plugin: bufferpool: use hashmap to cache dmabuf mem-surface

The old way of refer memory by bufferproxy is not a good one, since it
make the logic error prone.

Now it is established a map between surface-bufferproxy and its GstMemory,
caching the memory bound by a surface looked for the specified surface.
This commit is contained in:
He Junyan 2020-03-15 22:07:31 +08:00 committed by Víctor Manuel Jáquez Leal
parent 7701844813
commit ce3bf2c2ae

View file

@ -47,6 +47,8 @@ struct _GstVaapiVideoBufferPoolPrivate
guint options;
guint use_dmabuf_memory:1;
guint forced_video_meta:1;
/* Map between surface and GstMemory, only DMA */
GHashTable *dma_mem_map;
};
G_DEFINE_TYPE_WITH_PRIVATE (GstVaapiVideoBufferPool,
@ -60,6 +62,8 @@ gst_vaapi_video_buffer_pool_finalize (GObject * object)
gst_vaapi_display_replace (&priv->display, NULL);
g_clear_object (&priv->allocator);
if (priv->dma_mem_map)
g_hash_table_destroy (priv->dma_mem_map);
G_OBJECT_CLASS (gst_vaapi_video_buffer_pool_parent_class)->finalize (object);
}
@ -309,6 +313,58 @@ error_no_allocator:
}
}
static void
vaapi_buffer_pool_cache_dma_mem (GstVaapiVideoBufferPool * pool,
GstVaapiSurfaceProxy * proxy, GstMemory * mem)
{
GstVaapiVideoBufferPoolPrivate *const priv = pool->priv;
GstVaapiSurface *surface;
surface = GST_VAAPI_SURFACE_PROXY_SURFACE (proxy);
g_assert (surface);
g_assert (gst_vaapi_surface_peek_buffer_proxy (surface));
if (!priv->dma_mem_map)
priv->dma_mem_map = g_hash_table_new_full (g_direct_hash,
g_direct_equal, NULL, (GDestroyNotify) gst_memory_unref);
if (!g_hash_table_contains (priv->dma_mem_map, surface)) {
g_hash_table_insert (priv->dma_mem_map, surface, gst_memory_ref (mem));
} else {
g_assert (g_hash_table_lookup (priv->dma_mem_map, surface) == mem);
}
}
static GstMemory *
vaapi_buffer_pool_lookup_dma_mem (GstVaapiVideoBufferPool * pool,
GstVaapiSurfaceProxy * proxy)
{
GstVaapiSurface *surface;
GstVaapiVideoBufferPoolPrivate *const priv = pool->priv;
GstVaapiBufferProxy *buf_proxy;
GstMemory *mem;
g_assert (priv->use_dmabuf_memory);
if (!priv->dma_mem_map)
return NULL;
surface = GST_VAAPI_SURFACE_PROXY_SURFACE (proxy);
g_assert (surface);
buf_proxy = gst_vaapi_surface_peek_buffer_proxy (surface);
/* Have not exported yet */
if (!buf_proxy) {
g_assert (!g_hash_table_contains (priv->dma_mem_map, surface));
return NULL;
}
mem = g_hash_table_lookup (priv->dma_mem_map, surface);
g_assert (mem);
return gst_memory_ref (mem);
}
static GstFlowReturn
gst_vaapi_video_buffer_pool_alloc_buffer (GstBufferPool * pool,
GstBuffer ** out_buffer_ptr, GstBufferPoolAcquireParams * params)
@ -336,10 +392,23 @@ gst_vaapi_video_buffer_pool_alloc_buffer (GstBufferPool * pool,
if (priv_params && priv_params->proxy)
gst_vaapi_video_meta_set_surface_proxy (meta, priv_params->proxy);
if (priv->use_dmabuf_memory)
mem = gst_vaapi_dmabuf_memory_new (priv->allocator, meta);
else
if (priv->use_dmabuf_memory) {
mem = NULL;
if (priv_params && priv_params->proxy) {
mem = vaapi_buffer_pool_lookup_dma_mem (base_pool, priv_params->proxy);
if (!mem) {
mem = gst_vaapi_dmabuf_memory_new (priv->allocator, meta);
if (!mem)
goto error_create_memory;
vaapi_buffer_pool_cache_dma_mem (base_pool, priv_params->proxy, mem);
}
} else {
mem = gst_vaapi_dmabuf_memory_new (priv->allocator, meta);
}
} else {
mem = gst_vaapi_video_memory_new (priv->allocator, meta);
}
if (!mem)
goto error_create_memory;
gst_vaapi_video_meta_replace (&meta, NULL);
@ -407,7 +476,6 @@ gst_vaapi_video_buffer_pool_acquire_buffer (GstBufferPool * pool,
GstMemory *mem;
GstVaapiVideoMeta *meta;
GstVaapiSurface *surface;
GstVaapiBufferProxy *dmabuf_proxy;
ret =
GST_BUFFER_POOL_CLASS
@ -420,15 +488,20 @@ gst_vaapi_video_buffer_pool_acquire_buffer (GstBufferPool * pool,
return ret;
}
/* The point of the following dance is to attach the right GstMemory to the
* current acquired buffer. Indeed this buffer can contain any of the
* GstFdmemory since this buffer have been popped out from the buffer pool's
* FIFO. So there is no garantee that this matches the current surface. The
* va decoder driver might not even use a FIFO. So there is no way to guess
* on the ordering. In short acquire_current_buffer on the va driver and on
* the buffer pool return none matching data. So we have to manually attach
* the right GstFdMemory to the acquired GstBuffer. The right GstMemory is
* the one associated with the current surface. */
/* Some pool users, such as decode, needs to acquire a buffer for a
* specified surface (via surface proxy). If not it is a DMABuf, we
* just replace the underlying surface proxy of buffer's
* GstVaapiVideoMeta. But in DMABuf case, the thing is a little bit
* more complicated:
*
* For DMABuf, GstMemory is-a GstFdMemory, which doesn't provide a
* way to change its FD, thus once created it's bound to a
* surface. On the other side, for performace reason, when the
* buffer is released, the buffer and its memory are cached in the
* buffer pool, and at next acquire_buffer() may still reuse a
* buffer and its memory. But the pushed surface by the decoder may
* be different from the one popped by the pool, so we need to
* replace the buffer's memory with the correct one. */
g_assert (gst_buffer_n_memory (buffer) == 1);
/* Update the underlying surface proxy */
@ -439,30 +512,34 @@ gst_vaapi_video_buffer_pool_acquire_buffer (GstBufferPool * pool,
}
gst_vaapi_video_meta_set_surface_proxy (meta, priv_params->proxy);
/* Find the cached memory associated with the given surface. */
surface = GST_VAAPI_SURFACE_PROXY_SURFACE (priv_params->proxy);
dmabuf_proxy = gst_vaapi_surface_peek_buffer_proxy (surface);
if (dmabuf_proxy) {
mem = gst_vaapi_buffer_proxy_peek_mem (dmabuf_proxy);
if (mem == gst_buffer_peek_memory (buffer, 0))
mem = NULL;
else
mem = gst_memory_ref (mem);
mem = vaapi_buffer_pool_lookup_dma_mem (base_pool, priv_params->proxy);
if (mem) {
if (mem == gst_buffer_peek_memory (buffer, 0)) {
gst_memory_unref (mem);
*out_buffer_ptr = buffer;
return GST_FLOW_OK;
}
} else {
/* The given surface has not been exported yet. */
/* Should be an unexported surface */
surface = GST_VAAPI_SURFACE_PROXY_SURFACE (priv_params->proxy);
g_assert (surface);
g_assert (gst_vaapi_surface_peek_buffer_proxy (surface) == NULL);
gst_vaapi_video_meta_set_surface_proxy (meta, priv_params->proxy);
mem = gst_vaapi_dmabuf_memory_new (priv->allocator, meta);
if (mem)
vaapi_buffer_pool_cache_dma_mem (base_pool, priv_params->proxy, mem);
}
/* Attach the GstFdMemory to the output buffer. */
if (mem) {
GST_DEBUG_OBJECT (base_pool, "assigning memory %p to acquired buffer %p",
mem, buffer);
gst_buffer_replace_memory (buffer, 0, mem);
gst_buffer_unset_flags (buffer, GST_BUFFER_FLAG_TAG_MEMORY);
*out_buffer_ptr = buffer;
return GST_FLOW_OK;
} else {
gst_buffer_unref (buffer);
*out_buffer_ptr = NULL;
return GST_FLOW_ERROR;
}
*out_buffer_ptr = buffer;
return GST_FLOW_OK;
}
static void
@ -523,6 +600,7 @@ static void
gst_vaapi_video_buffer_pool_init (GstVaapiVideoBufferPool * pool)
{
pool->priv = gst_vaapi_video_buffer_pool_get_instance_private (pool);
pool->priv->dma_mem_map = NULL;
}
GstBufferPool *