diff --git a/configure.ac b/configure.ac index 8410645402..a63e983e46 100644 --- a/configure.ac +++ b/configure.ac @@ -2326,6 +2326,7 @@ dnl *** kms *** 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) ]) diff --git a/sys/kms/Makefile.am b/sys/kms/Makefile.am index 316b492fcc..9b12c7242c 100644 --- a/sys/kms/Makefile.am +++ b/sys/kms/Makefile.am @@ -11,6 +11,7 @@ libgstkmssink_la_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) \ $(GST_VIDEO_CFLAGS) \ + $(GST_ALLOCATORS_CFLAGS) \ $(GST_CFLAGS) \ $(DRM_CFLAGS) \ $(NULL) @@ -19,6 +20,7 @@ libgstkmssink_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) \ $(GST_BASE_LIBS) \ $(GST_VIDEO_LIBS) \ + $(GST_ALLOCATORS_LIBS) \ $(GST_LIBS) \ $(DRM_LIBS) \ $(NULL) diff --git a/sys/kms/gstkmsallocator.c b/sys/kms/gstkmsallocator.c index 5798b65cdd..5c23c926aa 100644 --- a/sys/kms/gstkmsallocator.c +++ b/sys/kms/gstkmsallocator.c @@ -98,6 +98,51 @@ ensure_kms_driver (GstKMSAllocator * alloc) 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) { @@ -107,12 +152,7 @@ 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); } @@ -272,13 +312,15 @@ gst_kms_allocator_add_fb (GstKMSAllocator * alloc, 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); @@ -295,38 +337,38 @@ gst_kms_allocator_add_fb (GstKMSAllocator * alloc, GstKMSMemory * kmsmem, return TRUE; } +static GstMemory * +gst_kms_allocator_alloc_empty (GstAllocator * allocator, GstVideoInfo * vinfo) +{ + GstKMSMemory *kmsmem; + GstMemory *mem; + + 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)); + + return mem; +} + GstMemory * gst_kms_allocator_bo_alloc (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) + mem = gst_kms_allocator_alloc_empty (allocator, vinfo); + if (!mem) 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; - 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; @@ -337,3 +379,47 @@ 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; + } +} diff --git a/sys/kms/gstkmsallocator.h b/sys/kms/gstkmsallocator.h index d77166f1a2..fa5c349450 100644 --- a/sys/kms/gstkmsallocator.h +++ b/sys/kms/gstkmsallocator.h @@ -55,6 +55,7 @@ struct _GstKMSMemory GstMemory parent; guint32 fb_id; + guint32 gem_handle[GST_VIDEO_MAX_PLANES]; struct kms_bo *bo; }; @@ -78,6 +79,11 @@ GstAllocator* gst_kms_allocator_new (gint fd); 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 diff --git a/sys/kms/gstkmssink.c b/sys/kms/gstkmssink.c index cd1401f6fe..66777e31ca 100644 --- a/sys/kms/gstkmssink.c +++ b/sys/kms/gstkmssink.c @@ -43,6 +43,7 @@ #endif #include +#include #include #include @@ -259,6 +260,7 @@ get_drm_caps (GstKMSSink * self) { gint ret; guint64 has_dumb_buffer; + guint64 has_prime; has_dumb_buffer = 0; ret = drmGetCap (self->fd, DRM_CAP_DUMB_BUFFER, &has_dumb_buffer); @@ -269,6 +271,16 @@ get_drm_caps (GstKMSSink * self) 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; } @@ -736,6 +748,128 @@ gst_kms_sink_get_times (GstBaseSink * bsink, GstBuffer * buf, } } +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) { @@ -752,10 +886,15 @@ 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; diff --git a/sys/kms/gstkmssink.h b/sys/kms/gstkmssink.h index 9cc2f2af13..90700b4f0b 100644 --- a/sys/kms/gstkmssink.h +++ b/sys/kms/gstkmssink.h @@ -55,6 +55,9 @@ struct _GstKMSSink { guint16 hdisplay, vdisplay; + /* capabilities */ + gboolean has_prime_import; + GstVideoInfo vinfo; GstCaps *allowed_caps; GstBufferPool *pool;