translit(dnm, m, l) AM_CONDITIONAL(USE_KMS, true)
AG_GST_CHECK_FEATURE(KMS, [drm/kms libraries], kms, [
AG_GST_PKG_CHECK_MODULES(GST_VIDEO, gstreamer-video-1.0)
+ AG_GST_PKG_CHECK_MODULES(GST_ALLOCATORS, gstreamer-allocators-1.0)
PKG_CHECK_MODULES([DRM], [libdrm libkms], HAVE_KMS=yes, HAVE_KMS=no)
])
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_BASE_CFLAGS) \
$(GST_VIDEO_CFLAGS) \
+ $(GST_ALLOCATORS_CFLAGS) \
$(GST_CFLAGS) \
$(DRM_CFLAGS) \
$(NULL)
$(GST_PLUGINS_BASE_LIBS) \
$(GST_BASE_LIBS) \
$(GST_VIDEO_LIBS) \
+ $(GST_ALLOCATORS_LIBS) \
$(GST_LIBS) \
$(DRM_LIBS) \
$(NULL)
return TRUE;
}
+static void
+gst_kms_allocator_memory_reset (GstKMSAllocator * allocator, GstKMSMemory * mem)
+{
+ if (mem->fb_id) {
+ GST_DEBUG_OBJECT (allocator, "removing fb id %d", mem->fb_id);
+ drmModeRmFB (allocator->priv->fd, mem->fb_id);
+ mem->fb_id = 0;
+ }
+
+ if (!ensure_kms_driver (allocator))
+ return;
+
+ if (mem->bo) {
+ kms_bo_destroy (&mem->bo);
+ mem->bo = NULL;
+ }
+}
+
+static gboolean
+gst_kms_allocator_memory_create (GstKMSAllocator * allocator,
+ GstKMSMemory * kmsmem, GstVideoInfo * vinfo)
+{
+ gint ret;
+ guint attrs[] = {
+ KMS_WIDTH, GST_VIDEO_INFO_WIDTH (vinfo),
+ KMS_HEIGHT, GST_VIDEO_INFO_HEIGHT (vinfo),
+ KMS_TERMINATE_PROP_LIST,
+ };
+
+ if (kmsmem->bo)
+ return TRUE;
+
+ if (!ensure_kms_driver (allocator))
+ return FALSE;
+
+ ret = kms_bo_create (allocator->priv->driver, attrs, &kmsmem->bo);
+ if (ret) {
+ GST_ERROR_OBJECT (allocator, "Failed to create buffer object: %s (%d)",
+ strerror (-ret), ret);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static void
gst_kms_allocator_free (GstAllocator * allocator, GstMemory * mem)
{
alloc = GST_KMS_ALLOCATOR (allocator);
kmsmem = (GstKMSMemory *) mem;
- if (kmsmem->fb_id)
- drmModeRmFB (alloc->priv->fd, kmsmem->fb_id);
-
- if (ensure_kms_driver (alloc) && kmsmem->bo)
- kms_bo_destroy (&kmsmem->bo);
-
+ gst_kms_allocator_memory_reset (alloc, kmsmem);
g_slice_free (GstKMSMemory, kmsmem);
}
if (kmsmem->bo) {
kms_bo_get_prop (kmsmem->bo, KMS_HANDLE, &bo_handles[0]);
+ for (i = 1; i < 4; i++)
+ bo_handles[i] = bo_handles[0];
} else {
- return FALSE;
+ for (i = 0; i < 4; i++)
+ bo_handles[i] = kmsmem->gem_handle[i];
}
- /* @FIXME: fill the other handles */
- bo_handles[1] = bo_handles[0];
- bo_handles[2] = bo_handles[0];
+ GST_DEBUG_OBJECT (alloc, "bo handles: %d, %d, %d, %d", bo_handles[0],
+ bo_handles[1], bo_handles[2], bo_handles[3]);
for (i = 0; i < 4; i++) {
offsets[i] = GST_VIDEO_INFO_PLANE_OFFSET (vinfo, i);
return TRUE;
}
-GstMemory *
-gst_kms_allocator_bo_alloc (GstAllocator * allocator, GstVideoInfo * vinfo)
+static GstMemory *
+gst_kms_allocator_alloc_empty (GstAllocator * allocator, GstVideoInfo * vinfo)
{
- GstKMSAllocator *alloc;
GstKMSMemory *kmsmem;
GstMemory *mem;
- int ret;
- guint attrs[] = {
- KMS_WIDTH, GST_VIDEO_INFO_WIDTH (vinfo),
- KMS_HEIGHT, GST_VIDEO_INFO_HEIGHT (vinfo),
- KMS_TERMINATE_PROP_LIST,
- };
kmsmem = g_slice_new0 (GstKMSMemory);
if (!kmsmem)
return NULL;
-
mem = GST_MEMORY_CAST (kmsmem);
+
gst_memory_init (mem, GST_MEMORY_FLAG_NO_SHARE, allocator, NULL,
GST_VIDEO_INFO_SIZE (vinfo), 0, 0, GST_VIDEO_INFO_SIZE (vinfo));
- alloc = GST_KMS_ALLOCATOR (allocator);
- if (!ensure_kms_driver (alloc))
- goto fail;
+ return mem;
+}
+
+GstMemory *
+gst_kms_allocator_bo_alloc (GstAllocator * allocator, GstVideoInfo * vinfo)
+{
+ GstKMSAllocator *alloc;
+ GstKMSMemory *kmsmem;
+ GstMemory *mem;
+ mem = gst_kms_allocator_alloc_empty (allocator, vinfo);
+ if (!mem)
+ return NULL;
+
+ alloc = GST_KMS_ALLOCATOR (allocator);
kmsmem = (GstKMSMemory *) mem;
- ret = kms_bo_create (alloc->priv->driver, attrs, &kmsmem->bo);
- if (ret) {
- GST_ERROR_OBJECT (alloc, "Failed to create buffer object: %s (%d)",
- strerror (-ret), ret);
+ if (!gst_kms_allocator_memory_create (alloc, kmsmem, vinfo))
goto fail;
- }
if (!gst_kms_allocator_add_fb (alloc, kmsmem, vinfo))
goto fail;
gst_memory_unref (mem);
return NULL;
}
+
+GstKMSMemory *
+gst_kms_allocator_dmabuf_import (GstAllocator * allocator, gint * prime_fds,
+ gint n_planes, GstVideoInfo * vinfo)
+{
+ GstKMSAllocator *alloc;
+ GstMemory *mem;
+ GstKMSMemory *tmp;
+ gint i, ret;
+
+ g_return_val_if_fail (n_planes <= GST_VIDEO_MAX_PLANES, FALSE);
+
+ mem = gst_kms_allocator_alloc_empty (allocator, vinfo);
+ if (!mem)
+ return FALSE;
+
+ tmp = (GstKMSMemory *) mem;
+ alloc = GST_KMS_ALLOCATOR (allocator);
+ for (i = 0; i < n_planes; i++) {
+ ret = drmPrimeFDToHandle (alloc->priv->fd, prime_fds[i],
+ &tmp->gem_handle[i]);
+ if (ret)
+ goto import_fd_failed;
+ }
+
+ if (!gst_kms_allocator_add_fb (alloc, tmp, vinfo))
+ goto failed;
+
+ return tmp;
+
+ /* ERRORS */
+import_fd_failed:
+ {
+ GST_ERROR_OBJECT (alloc, "Failed to import prime fd %d: %s (%d)",
+ prime_fds[i], strerror (-ret), ret);
+ /* fallback */
+ }
+
+failed:
+ {
+ gst_memory_unref (mem);
+ return NULL;
+ }
+}
GstMemory parent;
guint32 fb_id;
+ guint32 gem_handle[GST_VIDEO_MAX_PLANES];
struct kms_bo *bo;
};
GstMemory* gst_kms_allocator_bo_alloc (GstAllocator *allocator,
GstVideoInfo *vinfo);
+GstKMSMemory* gst_kms_allocator_dmabuf_import (GstAllocator *allocator,
+ gint *prime_fds,
+ gint n_planes,
+ GstVideoInfo *vinfo);
+
G_END_DECLS
#endif
#include <gst/video/video.h>
+#include <gst/allocators/gstdmabuf.h>
#include <drm.h>
#include <xf86drm.h>
{
gint ret;
guint64 has_dumb_buffer;
+ guint64 has_prime;
has_dumb_buffer = 0;
ret = drmGetCap (self->fd, DRM_CAP_DUMB_BUFFER, &has_dumb_buffer);
return FALSE;
}
+ has_prime = 0;
+ ret = drmGetCap (self->fd, DRM_CAP_PRIME, &has_prime);
+ if (ret)
+ GST_WARNING_OBJECT (self, "could not get prime capability");
+ else
+ self->has_prime_import = (gboolean) (has_prime & DRM_PRIME_CAP_IMPORT);
+
+ GST_INFO_OBJECT (self, "prime import (%s)",
+ self->has_prime_import ? "✓" : "✗");
+
return TRUE;
}
}
}
+static GstMemory *
+get_cached_kmsmem (GstMemory * mem)
+{
+ return gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
+ g_quark_from_static_string ("kmsmem"));
+}
+
+static void
+set_cached_kmsmem (GstMemory * mem, GstMemory * kmsmem)
+{
+ return gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
+ g_quark_from_static_string ("kmsmem"), kmsmem,
+ (GDestroyNotify) gst_memory_unref);
+}
+
+static gsize
+get_plane_data_size (GstVideoInfo * info, guint plane)
+{
+ gint padded_height;
+ gsize plane_size;
+
+ padded_height = info->height;
+ padded_height =
+ GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, plane, padded_height);
+
+ plane_size = GST_VIDEO_INFO_PLANE_STRIDE (info, plane) * padded_height;
+
+ return plane_size;
+}
+
+static gboolean
+gst_kms_sink_import_dmabuf (GstKMSSink * self, GstBuffer * inbuf,
+ GstBuffer ** outbuf)
+{
+ gint prime_fds[GST_VIDEO_MAX_PLANES] = { 0, };
+ GstVideoMeta *meta;
+ guint i, n_mem, n_planes;
+ GstKMSMemory *kmsmem;
+ guint mems_idx[GST_VIDEO_MAX_PLANES];
+ gsize mems_skip[GST_VIDEO_MAX_PLANES];
+ GstMemory *mems[GST_VIDEO_MAX_PLANES];
+
+ if (!self->has_prime_import)
+ return FALSE;
+
+ /* This will eliminate most non-dmabuf out there */
+ if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0)))
+ return FALSE;
+
+ n_planes = GST_VIDEO_INFO_N_PLANES (&self->vinfo);
+ n_mem = gst_buffer_n_memory (inbuf);
+ meta = gst_buffer_get_video_meta (inbuf);
+
+ /* We cannot have multiple dmabuf per plane */
+ if (n_mem > n_planes)
+ return FALSE;
+
+ /* Update video info based on video meta */
+ if (meta) {
+ GST_VIDEO_INFO_WIDTH (&self->vinfo) = meta->width;
+ GST_VIDEO_INFO_HEIGHT (&self->vinfo) = meta->height;
+
+ for (i = 0; i < meta->n_planes; i++) {
+ GST_VIDEO_INFO_PLANE_OFFSET (&self->vinfo, i) = meta->offset[i];
+ GST_VIDEO_INFO_PLANE_STRIDE (&self->vinfo, i) = meta->stride[i];
+ }
+ }
+
+ /* Find and validate all memories */
+ for (i = 0; i < n_planes; i++) {
+ guint plane_size;
+ guint length;
+
+ plane_size = get_plane_data_size (&self->vinfo, i);
+ if (!gst_buffer_find_memory (inbuf,
+ GST_VIDEO_INFO_PLANE_OFFSET (&self->vinfo, i), plane_size,
+ &mems_idx[i], &length, &mems_skip[i]))
+ return FALSE;
+
+ /* We can't have more then one dmabuf per plane */
+ if (length != 1)
+ return FALSE;
+
+ mems[i] = gst_buffer_peek_memory (inbuf, mems_idx[i]);
+
+ /* And all memory found must be dmabuf */
+ if (!gst_is_dmabuf_memory (mems[i]))
+ return FALSE;
+ }
+
+ kmsmem = (GstKMSMemory *) get_cached_kmsmem (mems[0]);
+ if (kmsmem) {
+ GST_LOG_OBJECT (self, "found KMS mem %p in DMABuf mem %p with fb id = %d",
+ kmsmem, mems[0], kmsmem->fb_id);
+ goto wrap_mem;
+ }
+
+ for (i = 0; i < n_planes; i++)
+ prime_fds[i] = gst_dmabuf_memory_get_fd (mems[i]);
+
+ GST_LOG_OBJECT (self, "found these prime ids: %d, %d, %d, %d", prime_fds[0],
+ prime_fds[1], prime_fds[2], prime_fds[3]);
+
+ kmsmem = gst_kms_allocator_dmabuf_import (self->allocator, prime_fds,
+ n_planes, &self->vinfo);
+ if (!kmsmem)
+ return FALSE;
+
+ GST_LOG_OBJECT (self, "setting KMS mem %p to DMABuf mem %p with fb id = %d",
+ kmsmem, mems[0], kmsmem->fb_id);
+ set_cached_kmsmem (mems[0], GST_MEMORY_CAST (kmsmem));
+
+wrap_mem:
+ *outbuf = gst_buffer_new ();
+ if (!*outbuf)
+ return FALSE;
+ gst_buffer_append_memory (*outbuf, gst_memory_ref (GST_MEMORY_CAST (kmsmem)));
+ gst_buffer_add_parent_buffer_meta (*outbuf, inbuf);
+
+ return TRUE;
+}
+
static GstBuffer *
gst_kms_sink_get_input_buffer (GstKMSSink * self, GstBuffer * inbuf)
{
if (gst_is_kms_memory (mem))
return gst_buffer_ref (inbuf);
+ buf = NULL;
+ if (gst_kms_sink_import_dmabuf (self, inbuf, &buf))
+ return buf;
+
+ GST_LOG_OBJECT (self, "frame copy");
+
if (!gst_buffer_pool_set_active (self->pool, TRUE))
goto activate_pool_failed;
- buf = NULL;
ret = gst_buffer_pool_acquire_buffer (self->pool, &buf, NULL);
if (ret != GST_FLOW_OK)
goto create_buffer_failed;
guint16 hdisplay, vdisplay;
+ /* capabilities */
+ gboolean has_prime_import;
+
GstVideoInfo vinfo;
GstCaps *allowed_caps;
GstBufferPool *pool;