plugins: add support for dma_buf exports (v4l2src).

Allow v4l2src element to connected to vaapipostproc or vaapisink when
"io-mode" is set to "dmabuf-import". In practice, this is a more likely
operational mode with uvcvideo. Supporting v4lsrc with "io-mode" set
to "dmabuf" could work, but with more demanding driver or kernel reqs.

Note: with GStreamer 1.4, v4l2src (gst-plugins-good) needs to be built
with --without-libv4l2.

https://bugzilla.gnome.org/show_bug.cgi?id=743635
This commit is contained in:
Gwenole Beauchesne 2015-01-26 18:30:47 +01:00
parent 667749c67e
commit 82fc406dfd
5 changed files with 405 additions and 10 deletions

View file

@ -24,6 +24,7 @@
#include "gst/vaapi/sysdeps.h"
#include <gst/vaapi/gstvaapisurface_drm.h>
#include <gst/base/gstpushsrc.h>
#include "gstvaapipluginbase.h"
#include "gstvaapipluginutil.h"
#include "gstvaapivideocontext.h"
@ -392,6 +393,42 @@ gst_vaapi_plugin_base_ensure_uploader (GstVaapiPluginBase * plugin)
return TRUE;
}
/* Checks whether the supplied pad peer element supports DMABUF sharing */
/* XXX: this is a workaround to the absence of any proposer way to
specify DMABUF memory capsfeatures or bufferpool option to downstream */
static gboolean
has_dmabuf_capable_peer (GstVaapiPluginBase * plugin, GstPad * pad)
{
GstPad *other_pad = NULL;
GstElement *element = NULL;
gchar *element_name = NULL;
gboolean is_dmabuf_capable = FALSE;
gint v;
do {
other_pad = gst_pad_get_peer (pad);
if (!other_pad)
break;
element = gst_pad_get_parent_element (other_pad);
if (!element || !GST_IS_PUSH_SRC (element))
break;
element_name = gst_element_get_name (element);
if (!element_name || sscanf (element_name, "v4l2src%d", &v) != 1)
break;
v = 0;
g_object_get (element, "io-mode", &v, NULL);
is_dmabuf_capable = v == 5; /* "dmabuf-import" enum value */
} while (0);
g_free (element_name);
g_clear_object (&element);
g_clear_object (&other_pad);
return is_dmabuf_capable;
}
/**
* ensure_sinkpad_buffer_pool:
* @plugin: a #GstVaapiPluginBase
@ -536,6 +573,16 @@ gst_vaapi_plugin_base_propose_allocation (GstVaapiPluginBase * plugin,
return FALSE;
gst_query_add_allocation_pool (query, plugin->sinkpad_buffer_pool,
plugin->sinkpad_buffer_size, 0, 0);
if (has_dmabuf_capable_peer (plugin, plugin->sinkpad)) {
GstStructure *const config =
gst_buffer_pool_get_config (plugin->sinkpad_buffer_pool);
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_DMABUF_MEMORY);
if (!gst_buffer_pool_set_config (plugin->sinkpad_buffer_pool, config))
goto error_pool_config;
}
}
gst_query_add_allocation_meta (query, GST_VAAPI_VIDEO_META_API_TYPE, NULL);
@ -548,6 +595,11 @@ error_no_caps:
GST_ERROR ("no caps specified");
return FALSE;
}
error_pool_config:
{
GST_ERROR ("failed to reset buffer pool config");
return FALSE;
}
}
#endif

View file

@ -46,10 +46,12 @@ struct _GstVaapiVideoBufferPoolPrivate
GstVideoInfo video_info[2];
guint video_info_index;
GstAllocator *allocator;
GstVideoInfo alloc_info;
GstVaapiDisplay *display;
guint has_video_meta:1;
guint has_video_alignment:1;
guint has_texture_upload_meta:1;
guint use_dmabuf_memory:1;
};
#define GST_VAAPI_VIDEO_BUFFER_POOL_GET_PRIVATE(obj) \
@ -105,8 +107,7 @@ gst_vaapi_video_buffer_pool_get_property (GObject * object, guint prop_id,
static void
fill_video_alignment (GstVaapiVideoBufferPool * pool, GstVideoAlignment * align)
{
GstVideoInfo *const vip =
&GST_VAAPI_VIDEO_ALLOCATOR_CAST (pool->priv->allocator)->image_info;
GstVideoInfo *const vip = &pool->priv->alloc_info;
guint i;
gst_video_alignment_reset (align);
@ -140,26 +141,48 @@ gst_vaapi_video_buffer_pool_set_config (GstBufferPool * pool,
GstVideoInfo *const new_vip = &priv->video_info[!priv->video_info_index];
GstVideoAlignment align;
GstAllocator *allocator;
gboolean changed_caps;
gboolean changed_caps, use_dmabuf_memory;
if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
goto error_invalid_config;
if (!caps || !gst_video_info_from_caps (new_vip, caps))
goto error_no_caps;
use_dmabuf_memory = gst_buffer_pool_config_has_option (config,
GST_BUFFER_POOL_OPTION_DMABUF_MEMORY);
if (priv->use_dmabuf_memory != use_dmabuf_memory) {
priv->use_dmabuf_memory = use_dmabuf_memory;
g_clear_object (&priv->allocator);
}
changed_caps = !priv->allocator ||
GST_VIDEO_INFO_FORMAT (cur_vip) != GST_VIDEO_INFO_FORMAT (new_vip) ||
GST_VIDEO_INFO_WIDTH (cur_vip) != GST_VIDEO_INFO_WIDTH (new_vip) ||
GST_VIDEO_INFO_HEIGHT (cur_vip) != GST_VIDEO_INFO_HEIGHT (new_vip);
if (changed_caps) {
allocator = gst_vaapi_video_allocator_new (priv->display, new_vip, 0);
const GstVideoInfo *alloc_vip;
guint flags = 0;
if (use_dmabuf_memory) {
/* XXX: also needs fixed strides/offsets */
flags |= GST_VAAPI_SURFACE_ALLOC_FLAG_LINEAR_STORAGE;
allocator =
gst_vaapi_dmabuf_allocator_new (priv->display, new_vip, flags);
} else {
allocator = gst_vaapi_video_allocator_new (priv->display, new_vip, 0);
}
if (!allocator)
goto error_create_allocator;
gst_object_replace ((GstObject **) & priv->allocator,
GST_OBJECT_CAST (allocator));
gst_object_unref (allocator);
priv->video_info_index ^= 1;
alloc_vip = gst_allocator_get_vaapi_video_info (allocator, NULL);
if (!alloc_vip)
goto error_create_allocator_info;
priv->alloc_info = *alloc_vip;
}
if (!gst_buffer_pool_config_has_option (config,
@ -199,6 +222,11 @@ error_create_allocator:
GST_ERROR ("failed to create GstVaapiVideoAllocator object");
return FALSE;
}
error_create_allocator_info:
{
GST_ERROR ("failed to create GstVaapiVideoAllocator `video-info'");
return FALSE;
}
error_no_vaapi_video_meta_option:
{
GST_ERROR ("no GstVaapiVideoMeta option");
@ -235,15 +263,17 @@ gst_vaapi_video_buffer_pool_alloc_buffer (GstBufferPool * pool,
if (!buffer)
goto error_create_buffer;
mem = gst_vaapi_video_memory_new (priv->allocator, meta);
if (priv->use_dmabuf_memory)
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);
gst_buffer_append_memory (buffer, mem);
if (priv->has_video_meta) {
GstVideoInfo *const vip =
&GST_VAAPI_VIDEO_ALLOCATOR_CAST (priv->allocator)->image_info;
GstVideoInfo *const vip = &priv->alloc_info;
GstVideoMeta *vmeta;
vmeta = gst_buffer_add_video_meta_full (buffer, 0,
@ -251,9 +281,13 @@ gst_vaapi_video_buffer_pool_alloc_buffer (GstBufferPool * pool,
GST_VIDEO_INFO_HEIGHT (vip), GST_VIDEO_INFO_N_PLANES (vip),
&GST_VIDEO_INFO_PLANE_OFFSET (vip, 0),
&GST_VIDEO_INFO_PLANE_STRIDE (vip, 0));
vmeta->map = gst_video_meta_map_vaapi_memory;
vmeta->unmap = gst_video_meta_unmap_vaapi_memory;
if (GST_VAAPI_IS_VIDEO_MEMORY (mem)) {
vmeta->map = gst_video_meta_map_vaapi_memory;
vmeta->unmap = gst_video_meta_unmap_vaapi_memory;
}
}
#if GST_CHECK_VERSION(1,1,0) && USE_GLX
if (priv->has_texture_upload_meta)
gst_buffer_add_texture_upload_meta (buffer);
@ -295,7 +329,8 @@ gst_vaapi_video_buffer_pool_reset_buffer (GstBufferPool * pool,
GstMemory *const mem = gst_buffer_peek_memory (buffer, 0);
/* Release the underlying surface proxy */
gst_vaapi_video_memory_reset_surface (GST_VAAPI_VIDEO_MEMORY_CAST (mem));
if (GST_VAAPI_IS_VIDEO_MEMORY (mem))
gst_vaapi_video_memory_reset_surface (GST_VAAPI_VIDEO_MEMORY_CAST (mem));
GST_BUFFER_POOL_CLASS (gst_vaapi_video_buffer_pool_parent_class)->reset_buffer
(pool, buffer);
@ -344,6 +379,7 @@ gst_vaapi_video_buffer_pool_init (GstVaapiVideoBufferPool * pool)
gst_video_info_init (&priv->video_info[0]);
gst_video_info_init (&priv->video_info[1]);
gst_video_info_init (&priv->alloc_info);
}
GstBufferPool *

View file

@ -69,6 +69,17 @@ typedef struct _GstVaapiVideoBufferPoolPrivate GstVaapiVideoBufferPoolPrivate;
"GstBufferPoolOptionVideoGLTextureUploadMeta"
#endif
/**
* GST_BUFFER_POOL_OPTION_DMABUF_MEMORY:
*
* An option that can be activated on bufferpool to request dmabuf
* handles on buffers from the pool.
*/
#ifndef GST_BUFFER_POOL_OPTION_DMABUF_MEMORY
#define GST_BUFFER_POOL_OPTION_DMABUF_MEMORY \
"GstBufferPoolOptionDMABUFMemory"
#endif
/**
* GstVaapiVideoBufferPoolAcquireFlags:
* @GST_VAAPI_VIDEO_BUFFER_POOL_ACQUIRE_FLAG_NO_ALLOC: option to

View file

@ -21,6 +21,7 @@
*/
#include "gst/vaapi/sysdeps.h"
#include <gst/vaapi/gstvaapisurface_drm.h>
#include <gst/vaapi/gstvaapisurfacepool.h>
#include <gst/vaapi/gstvaapiimagepool.h>
#include "gstvaapivideomemory.h"
@ -298,6 +299,8 @@ gst_vaapi_video_memory_new (GstAllocator * base_allocator,
const GstVideoInfo *vip;
GstVaapiVideoMemory *mem;
g_return_val_if_fail (GST_VAAPI_IS_VIDEO_ALLOCATOR (allocator), NULL);
mem = g_slice_new (GstVaapiVideoMemory);
if (!mem)
return NULL;
@ -725,6 +728,9 @@ gst_vaapi_video_allocator_new (GstVaapiDisplay * display,
&allocator->image_info);
if (!allocator->image_pool)
goto error_create_image_pool;
gst_allocator_set_vaapi_video_info (GST_ALLOCATOR_CAST (allocator),
&allocator->image_info, 0);
return GST_ALLOCATOR_CAST (allocator);
/* ERRORS */
@ -741,3 +747,260 @@ error_create_image_pool:
return NULL;
}
}
/* ------------------------------------------------------------------------ */
/* --- GstVaapiDmaBufMemory --- */
/* ------------------------------------------------------------------------ */
#define GST_VAAPI_BUFFER_PROXY_QUARK gst_vaapi_buffer_proxy_quark_get ()
static GQuark
gst_vaapi_buffer_proxy_quark_get (void)
{
static gsize g_quark;
if (g_once_init_enter (&g_quark)) {
gsize quark = (gsize) g_quark_from_static_string ("GstVaapiBufferProxy");
g_once_init_leave (&g_quark, quark);
}
return g_quark;
}
GstMemory *
gst_vaapi_dmabuf_memory_new (GstAllocator * allocator, GstVaapiVideoMeta * meta)
{
GstMemory *mem;
GstVaapiDisplay *display;
GstVaapiSurface *surface;
GstVaapiSurfaceProxy *proxy;
GstVaapiBufferProxy *dmabuf_proxy;
const GstVideoInfo *vip;
guint flags;
g_return_val_if_fail (allocator != NULL, NULL);
g_return_val_if_fail (meta != NULL, NULL);
vip = gst_allocator_get_vaapi_video_info (allocator, &flags);
if (!vip)
return NULL;
display = gst_vaapi_video_meta_get_display (meta);
if (!meta)
return NULL;
surface = gst_vaapi_surface_new_full (display, vip, flags);
if (!surface)
goto error_create_surface;
proxy = gst_vaapi_surface_proxy_new (surface);
if (!proxy)
goto error_create_surface_proxy;
dmabuf_proxy = gst_vaapi_surface_get_dma_buf_handle (surface);
gst_vaapi_object_unref (surface);
if (!dmabuf_proxy)
goto error_create_dmabuf_proxy;
gst_vaapi_video_meta_set_surface_proxy (meta, proxy);
gst_vaapi_surface_proxy_unref (proxy);
mem = gst_dmabuf_allocator_alloc (allocator,
gst_vaapi_buffer_proxy_get_handle (dmabuf_proxy),
gst_vaapi_buffer_proxy_get_size (dmabuf_proxy));
if (!mem)
goto error_create_dmabuf_memory;
gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (mem),
GST_VAAPI_BUFFER_PROXY_QUARK, dmabuf_proxy,
(GDestroyNotify) gst_vaapi_buffer_proxy_unref);
return mem;
/* ERRORS */
error_create_surface:
{
GST_ERROR ("failed to create VA surface (format:%s size:%ux%u)",
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (vip)),
GST_VIDEO_INFO_WIDTH (vip), GST_VIDEO_INFO_HEIGHT (vip));
return NULL;
}
error_create_surface_proxy:
{
GST_ERROR ("failed to create VA surface proxy");
gst_vaapi_object_unref (surface);
return NULL;
}
error_create_dmabuf_proxy:
{
GST_ERROR ("failed to export VA surface to DMABUF");
gst_vaapi_surface_proxy_unref (proxy);
return NULL;
}
error_create_dmabuf_memory:
{
GST_ERROR ("failed to create DMABUF memory");
gst_vaapi_buffer_proxy_unref (dmabuf_proxy);
return NULL;
}
}
/* ------------------------------------------------------------------------ */
/* --- GstVaapiDmaBufAllocator --- */
/* ------------------------------------------------------------------------ */
GstAllocator *
gst_vaapi_dmabuf_allocator_new (GstVaapiDisplay * display,
const GstVideoInfo * vip, guint flags)
{
GstAllocator *allocator = NULL;
GstVaapiSurface *surface = NULL;
GstVaapiImage *image = NULL;
GstVideoInfo alloc_info;
g_return_val_if_fail (display != NULL, NULL);
g_return_val_if_fail (vip != NULL, NULL);
do {
surface = gst_vaapi_surface_new_full (display, vip, flags);
if (!surface)
break;
image = gst_vaapi_surface_derive_image (surface);
if (!image || !gst_vaapi_image_map (image))
break;
gst_video_info_set_format (&alloc_info, GST_VIDEO_INFO_FORMAT (vip),
GST_VIDEO_INFO_WIDTH (vip), GST_VIDEO_INFO_HEIGHT (vip));
gst_video_info_update_from_image (&alloc_info, image);
gst_vaapi_image_unmap (image);
allocator = gst_dmabuf_allocator_new ();
if (!allocator)
break;
gst_allocator_set_vaapi_video_info (allocator, &alloc_info, flags);
} while (0);
gst_vaapi_object_replace (&image, NULL);
gst_vaapi_object_replace (&surface, NULL);
return allocator;
}
/* ------------------------------------------------------------------------ */
/* --- GstVaapiVideoInfo = { GstVideoInfo, flags } --- */
/* ------------------------------------------------------------------------ */
static GstVideoInfo *
gst_vaapi_video_info_copy (const GstVideoInfo * vip)
{
GstVideoInfo *out_vip;
out_vip = g_slice_new (GstVideoInfo);
if (!out_vip)
return NULL;
gst_video_info_init (out_vip);
*out_vip = *vip;
return out_vip;
}
static void
gst_vaapi_video_info_free (GstVideoInfo * vip)
{
g_slice_free (GstVideoInfo, vip);
}
#define GST_VAAPI_TYPE_VIDEO_INFO gst_vaapi_video_info_get_type ()
static GType
gst_vaapi_video_info_get_type (void)
{
static gsize g_type;
if (g_once_init_enter (&g_type)) {
GType type;
type = g_boxed_type_register_static ("GstVaapiVideoInfo",
(GBoxedCopyFunc) gst_vaapi_video_info_copy,
(GBoxedFreeFunc) gst_vaapi_video_info_free);
g_once_init_leave (&g_type, type);
}
return (GType) g_type;
}
#define GST_VAAPI_VIDEO_INFO_QUARK gst_vaapi_video_info_quark_get ()
static GQuark
gst_vaapi_video_info_quark_get (void)
{
static gsize g_quark;
if (g_once_init_enter (&g_quark)) {
gsize quark = (gsize) g_quark_from_static_string ("GstVaapiVideoInfo");
g_once_init_leave (&g_quark, quark);
}
return g_quark;
}
#define INFO_QUARK info_quark_get ()
static GQuark
info_quark_get (void)
{
static gsize g_quark;
if (g_once_init_enter (&g_quark)) {
gsize quark = (gsize) g_quark_from_static_string ("info");
g_once_init_leave (&g_quark, quark);
}
return g_quark;
}
#define FLAGS_QUARK flags_quark_get ()
static GQuark
flags_quark_get (void)
{
static gsize g_quark;
if (g_once_init_enter (&g_quark)) {
gsize quark = (gsize) g_quark_from_static_string ("flags");
g_once_init_leave (&g_quark, quark);
}
return g_quark;
}
const GstVideoInfo *
gst_allocator_get_vaapi_video_info (GstAllocator * allocator,
guint * out_flags_ptr)
{
const GstStructure *structure;
const GValue *value;
g_return_val_if_fail (GST_IS_ALLOCATOR (allocator), NULL);
structure =
g_object_get_qdata (G_OBJECT (allocator), GST_VAAPI_VIDEO_INFO_QUARK);
if (!structure)
return NULL;
if (out_flags_ptr) {
value = gst_structure_id_get_value (structure, FLAGS_QUARK);
if (!value)
return NULL;
*out_flags_ptr = g_value_get_uint (value);
}
value = gst_structure_id_get_value (structure, INFO_QUARK);
if (!value)
return NULL;
return g_value_get_boxed (value);
}
gboolean
gst_allocator_set_vaapi_video_info (GstAllocator * allocator,
const GstVideoInfo * vip, guint flags)
{
g_return_val_if_fail (GST_IS_ALLOCATOR (allocator), FALSE);
g_return_val_if_fail (vip != NULL, FALSE);
g_object_set_qdata_full (G_OBJECT (allocator), GST_VAAPI_VIDEO_INFO_QUARK,
gst_structure_new_id (GST_VAAPI_VIDEO_INFO_QUARK,
INFO_QUARK, GST_VAAPI_TYPE_VIDEO_INFO, vip,
FLAGS_QUARK, G_TYPE_UINT, flags, NULL),
(GDestroyNotify) gst_structure_free);
return TRUE;
}

View file

@ -28,6 +28,9 @@
#include <gst/vaapi/gstvaapidisplay.h>
#include <gst/vaapi/gstvaapivideopool.h>
#include "gstvaapivideometa.h"
#if GST_CHECK_VERSION(1,1,0)
#include <gst/allocators/allocators.h>
#endif
G_BEGIN_DECLS
@ -113,6 +116,10 @@ struct _GstVaapiVideoMemory
gboolean use_direct_rendering;
};
G_GNUC_INTERNAL
GQuark
gst_vaapi_video_memory_quark_get (void);
G_GNUC_INTERNAL
GstMemory *
gst_vaapi_video_memory_new (GstAllocator * allocator, GstVaapiVideoMeta * meta);
@ -189,6 +196,32 @@ GstAllocator *
gst_vaapi_video_allocator_new (GstVaapiDisplay * display,
const GstVideoInfo * vip, guint flags);
/* ------------------------------------------------------------------------ */
/* --- GstVaapiDmaBufMemory --- */
/* ------------------------------------------------------------------------ */
G_GNUC_INTERNAL
GstMemory *
gst_vaapi_dmabuf_memory_new (GstAllocator * allocator,
GstVaapiVideoMeta * meta);
/* ------------------------------------------------------------------------ */
/* --- GstVaapiDmaBufAllocator --- */
/* ------------------------------------------------------------------------ */
G_GNUC_INTERNAL
GstAllocator *
gst_vaapi_dmabuf_allocator_new (GstVaapiDisplay * display,
const GstVideoInfo * vip, guint flags);
const GstVideoInfo *
gst_allocator_get_vaapi_video_info (GstAllocator * allocator,
guint * out_flags_ptr);
gboolean
gst_allocator_set_vaapi_video_info (GstAllocator * allocator,
const GstVideoInfo * vip, guint flags);
G_END_DECLS
#endif /* GST_VAAPI_VIDEO_MEMORY_H */