wayladnsink: Add DRM Dumb allocator support

If the input is not a DMABuf, attempt to copy into a DRM Dumb
buffer and import it has a DMABuf. This will offload the
compositor from actually doing this copy (needed to handle SHM)
and may allow the software decoded stream to be rendered to
an HW layer, or even reach through some better accelerated
GL import path.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
This commit is contained in:
Colin Kinloch 2023-02-16 16:21:33 -05:00 committed by GStreamer Marge Bot
parent 755bd9143e
commit ed60843c1e
3 changed files with 233 additions and 1 deletions

View file

@ -87,7 +87,8 @@ enum
PROP_0, PROP_0,
PROP_WIDGET, PROP_WIDGET,
PROP_DISPLAY, PROP_DISPLAY,
PROP_ROTATE_METHOD PROP_ROTATE_METHOD,
PROP_DRM_DEVICE,
}; };
typedef struct _GstGtkWaylandSinkPrivate typedef struct _GstGtkWaylandSinkPrivate
@ -119,6 +120,9 @@ typedef struct _GstGtkWaylandSinkPrivate
GstVideoOrientationMethod current_rotate_method; GstVideoOrientationMethod current_rotate_method;
struct wl_callback *callback; struct wl_callback *callback;
gchar *drm_device;
gboolean skip_dumb_buffer_copy;
} GstGtkWaylandSinkPrivate; } GstGtkWaylandSinkPrivate;
#define gst_gtk_wayland_sink_parent_class parent_class #define gst_gtk_wayland_sink_parent_class parent_class
@ -161,6 +165,17 @@ gst_gtk_wayland_sink_class_init (GstGtkWaylandSinkClass * klass)
GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY, GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstGtkWaylandSink:drm-device:
*
* Since: 1.24
*/
g_object_class_install_property (gobject_class, PROP_DRM_DEVICE,
g_param_spec_string ("drm-device", "DRM Device", "Path of the "
"DRM device to use for dumb buffer allocation",
NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
gstelement_class->change_state = gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_gtk_wayland_sink_change_state); GST_DEBUG_FUNCPTR (gst_gtk_wayland_sink_change_state);
@ -207,6 +222,8 @@ gst_gtk_wayland_sink_finalize (GObject * object)
g_clear_object (&priv->gtk_widget); g_clear_object (&priv->gtk_widget);
gst_clear_caps (&priv->caps); gst_clear_caps (&priv->caps);
g_free (priv->drm_device);
G_OBJECT_CLASS (parent_class)->finalize (object); G_OBJECT_CLASS (parent_class)->finalize (object);
} }
@ -379,6 +396,11 @@ gst_gtk_wayland_sink_get_property (GObject * object, guint prop_id,
case PROP_ROTATE_METHOD: case PROP_ROTATE_METHOD:
g_value_set_enum (value, priv->current_rotate_method); g_value_set_enum (value, priv->current_rotate_method);
break; break;
case PROP_DRM_DEVICE:
GST_OBJECT_LOCK (self);
g_value_set_string (value, priv->drm_device);
GST_OBJECT_UNLOCK (self);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -390,12 +412,19 @@ gst_gtk_wayland_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec) const GValue * value, GParamSpec * pspec)
{ {
GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (object); GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (object);
GstGtkWaylandSinkPrivate *priv =
gst_gtk_wayland_sink_get_instance_private (self);
switch (prop_id) { switch (prop_id) {
case PROP_ROTATE_METHOD: case PROP_ROTATE_METHOD:
gst_gtk_wayland_sink_set_rotate_method (self, g_value_get_enum (value), gst_gtk_wayland_sink_set_rotate_method (self, g_value_get_enum (value),
FALSE); FALSE);
break; break;
case PROP_DRM_DEVICE:
GST_OBJECT_LOCK (self);
priv->drm_device = g_value_dup_string (value);
GST_OBJECT_UNLOCK (self);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -710,6 +739,8 @@ gst_gtk_wayland_sink_change_state (GstElement * element,
switch (transition) { switch (transition) {
case GST_STATE_CHANGE_READY_TO_NULL: case GST_STATE_CHANGE_READY_TO_NULL:
g_clear_object (&priv->pool);
/* fallthrough */
case GST_STATE_CHANGE_NULL_TO_NULL: case GST_STATE_CHANGE_NULL_TO_NULL:
gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_invoke_on_main ((GThreadFunc)
gst_gtk_wayland_sink_stop_on_main, element); gst_gtk_wayland_sink_stop_on_main, element);
@ -860,6 +891,41 @@ gst_gtk_wayland_activate_shm_pool (GstGtkWaylandSink * self)
return TRUE; return TRUE;
} }
static gboolean
gst_gtk_wayland_activate_drm_dumb_pool (GstGtkWaylandSink * self)
{
GstGtkWaylandSinkPrivate *priv =
gst_gtk_wayland_sink_get_instance_private (self);
GstAllocator *alloc;
if (!priv->drm_device)
return FALSE;
if (priv->pool && gst_buffer_pool_is_active (priv->pool)) {
GstStructure *config = gst_buffer_pool_get_config (priv->pool);
gboolean ret = FALSE;
gboolean is_drm_dumb = FALSE;
ret = gst_buffer_pool_config_get_allocator (config, &alloc, NULL);
gst_structure_free (config);
if (ret && alloc)
is_drm_dumb = GST_IS_DRM_DUMB_ALLOCATOR (alloc);
if (is_drm_dumb)
return TRUE;
}
alloc = gst_drm_dumb_allocator_new_with_device_path (priv->drm_device);
if (!alloc)
return FALSE;
gst_gtk_wayland_update_pool (self, alloc);
gst_object_unref (alloc);
return TRUE;
}
static gboolean static gboolean
gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
{ {
@ -877,6 +943,7 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
format = GST_VIDEO_INFO_FORMAT (&priv->video_info); format = GST_VIDEO_INFO_FORMAT (&priv->video_info);
priv->video_info_changed = TRUE; priv->video_info_changed = TRUE;
priv->skip_dumb_buffer_copy = FALSE;
/* free pooled buffer used with previous caps */ /* free pooled buffer used with previous caps */
if (priv->pool) { if (priv->pool) {
@ -1086,8 +1153,60 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer))) if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer)))
wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, priv->display, wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, priv->display,
&priv->video_info); &priv->video_info);
/* DMABuf did not work, let try and make this a dmabuf, it does not matter
* if it was a SHM since the compositor needs to copy that anyway, and
* offloading the compositor from a copy helps maintaining a smoother
* desktop.
*/
if (!priv->skip_dumb_buffer_copy) {
GstVideoFrame src, dst;
if (!gst_gtk_wayland_activate_drm_dumb_pool (self)) {
priv->skip_dumb_buffer_copy = TRUE;
goto handle_shm;
} }
ret = gst_buffer_pool_acquire_buffer (priv->pool, &to_render, NULL);
if (ret != GST_FLOW_OK)
goto no_buffer;
wlbuffer = gst_buffer_get_wl_buffer (priv->display, to_render);
/* attach a wl_buffer if there isn't one yet */
if (G_UNLIKELY (!wlbuffer)) {
wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (to_render,
priv->display, &priv->video_info);
if (G_UNLIKELY (!wbuf)) {
GST_WARNING_OBJECT (self, "failed to import DRM Dumb dmabuf");
gst_clear_buffer (&to_render);
priv->skip_dumb_buffer_copy = TRUE;
goto handle_shm;
}
wlbuffer = gst_buffer_add_wl_buffer (to_render, wbuf, priv->display);
}
if (!gst_video_frame_map (&dst, &priv->video_info, to_render,
GST_MAP_WRITE))
goto dst_map_failed;
if (!gst_video_frame_map (&src, &priv->video_info, buffer, GST_MAP_READ)) {
gst_video_frame_unmap (&dst);
goto src_map_failed;
}
gst_video_frame_copy (&dst, &src);
gst_video_frame_unmap (&src);
gst_video_frame_unmap (&dst);
goto render;
}
}
handle_shm:
if (!wbuf && gst_wl_display_check_format_for_shm (priv->display, format)) { if (!wbuf && gst_wl_display_check_format_for_shm (priv->display, format)) {
if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem)) if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem))
wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, priv->display, wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, priv->display,

View file

@ -61,6 +61,7 @@ enum
PROP_DISPLAY, PROP_DISPLAY,
PROP_FULLSCREEN, PROP_FULLSCREEN,
PROP_ROTATE_METHOD, PROP_ROTATE_METHOD,
PROP_DRM_DEVICE,
PROP_LAST PROP_LAST
}; };
@ -177,6 +178,18 @@ gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY, GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* waylandsink:drm-device:
*
* Since: 1.24
*/
g_object_class_install_property (gobject_class, PROP_DRM_DEVICE,
g_param_spec_string ("drm-device", "DRM Device", "Path of the "
"DRM device to use for dumb buffer allocation",
NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
/** /**
* waylandsink:render-rectangle: * waylandsink:render-rectangle:
* *
@ -266,6 +279,11 @@ gst_wayland_sink_get_property (GObject * object,
g_value_set_enum (value, self->current_rotate_method); g_value_set_enum (value, self->current_rotate_method);
GST_OBJECT_UNLOCK (self); GST_OBJECT_UNLOCK (self);
break; break;
case PROP_DRM_DEVICE:
GST_OBJECT_LOCK (self);
g_value_set_string (value, self->drm_device);
GST_OBJECT_UNLOCK (self);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -293,6 +311,11 @@ gst_wayland_sink_set_property (GObject * object,
gst_wayland_sink_set_rotate_method (self, g_value_get_enum (value), gst_wayland_sink_set_rotate_method (self, g_value_get_enum (value),
FALSE); FALSE);
break; break;
case PROP_DRM_DEVICE:
GST_OBJECT_LOCK (self);
self->drm_device = g_value_dup_string (value);
GST_OBJECT_UNLOCK (self);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -318,6 +341,7 @@ gst_wayland_sink_finalize (GObject * object)
gst_clear_caps (&self->caps); gst_clear_caps (&self->caps);
g_free (self->display_name); g_free (self->display_name);
g_free (self->drm_device);
g_mutex_clear (&self->display_lock); g_mutex_clear (&self->display_lock);
g_mutex_clear (&self->render_lock); g_mutex_clear (&self->render_lock);
@ -629,6 +653,39 @@ gst_wayland_activate_shm_pool (GstWaylandSink * self)
return TRUE; return TRUE;
} }
static gboolean
gst_wayland_activate_drm_dumb_pool (GstWaylandSink * self)
{
GstAllocator *alloc;
if (!self->drm_device)
return FALSE;
if (self->pool && gst_buffer_pool_is_active (self->pool)) {
GstStructure *config = gst_buffer_pool_get_config (self->pool);
gboolean ret = FALSE;
gboolean is_drm_dumb = FALSE;
ret = gst_buffer_pool_config_get_allocator (config, &alloc, NULL);
gst_structure_free (config);
if (ret && alloc)
is_drm_dumb = GST_IS_DRM_DUMB_ALLOCATOR (alloc);
if (is_drm_dumb)
return TRUE;
}
alloc = gst_drm_dumb_allocator_new_with_device_path (self->drm_device);
if (!alloc)
return FALSE;
gst_wayland_update_pool (self, alloc);
gst_object_unref (alloc);
return TRUE;
}
static gboolean static gboolean
gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
{ {
@ -644,6 +701,7 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
format = GST_VIDEO_INFO_FORMAT (&self->video_info); format = GST_VIDEO_INFO_FORMAT (&self->video_info);
self->video_info_changed = TRUE; self->video_info_changed = TRUE;
self->skip_dumb_buffer_copy = FALSE;
/* free pooled buffer used with previous caps */ /* free pooled buffer used with previous caps */
if (self->pool) { if (self->pool) {
@ -845,8 +903,60 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer))) if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer)))
wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, self->display, wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, self->display,
&self->video_info); &self->video_info);
/* DMABuf did not work, let try and make this a dmabuf, it does not matter
* if it was a SHM since the compositor needs to copy that anyway, and
* offloading the compositor from a copy helps maintaining a smoother
* desktop.
*/
if (!self->skip_dumb_buffer_copy) {
GstVideoFrame src, dst;
if (!gst_wayland_activate_drm_dumb_pool (self)) {
self->skip_dumb_buffer_copy = TRUE;
goto handle_shm;
} }
ret = gst_buffer_pool_acquire_buffer (self->pool, &to_render, NULL);
if (ret != GST_FLOW_OK)
goto no_buffer;
wlbuffer = gst_buffer_get_wl_buffer (self->display, to_render);
/* attach a wl_buffer if there isn't one yet */
if (G_UNLIKELY (!wlbuffer)) {
wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (to_render,
self->display, &self->video_info);
if (G_UNLIKELY (!wbuf)) {
GST_WARNING_OBJECT (self, "failed to import DRM Dumb dmabuf");
gst_clear_buffer (&to_render);
self->skip_dumb_buffer_copy = TRUE;
goto handle_shm;
}
wlbuffer = gst_buffer_add_wl_buffer (to_render, wbuf, self->display);
}
if (!gst_video_frame_map (&dst, &self->video_info, to_render,
GST_MAP_WRITE))
goto dst_map_failed;
if (!gst_video_frame_map (&src, &self->video_info, buffer, GST_MAP_READ)) {
gst_video_frame_unmap (&dst);
goto src_map_failed;
}
gst_video_frame_copy (&dst, &src);
gst_video_frame_unmap (&src);
gst_video_frame_unmap (&dst);
goto render;
}
}
handle_shm:
if (!wbuf && gst_wl_display_check_format_for_shm (self->display, format)) { if (!wbuf && gst_wl_display_check_format_for_shm (self->display, format)) {
if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem)) if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem))
wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, self->display, wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, self->display,

View file

@ -69,6 +69,9 @@ struct _GstWaylandSink
GstVideoOrientationMethod current_rotate_method; GstVideoOrientationMethod current_rotate_method;
struct wl_callback *callback; struct wl_callback *callback;
gchar *drm_device;
gboolean skip_dumb_buffer_copy;
}; };
struct _GstWaylandSinkClass struct _GstWaylandSinkClass