From 7f63060162edc2aee11f727acf5c764b24f3587a Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Wed, 3 Jul 2019 19:54:36 +0800 Subject: [PATCH 07/14] HACK: xvimagesink: Support dma buffer rendering Send dma buffer to xv port when it supports dma port attributes. Change-Id: I69d94ffb700eb95af83799cdd5cde476d2930f92 Signed-off-by: Jeffy Chen --- sys/xvimage/meson.build | 4 +- sys/xvimage/xvcontext.c | 71 +++++++++- sys/xvimage/xvcontext.h | 20 +++ sys/xvimage/xvimagesink.c | 270 ++++++++++++++++++++++++++++++++++++-- sys/xvimage/xvimagesink.h | 10 ++ 5 files changed, 361 insertions(+), 14 deletions(-) diff --git a/sys/xvimage/meson.build b/sys/xvimage/meson.build index ce84ed0..2873c9b 100644 --- a/sys/xvimage/meson.build +++ b/sys/xvimage/meson.build @@ -12,6 +12,8 @@ if cc.has_argument ('-Wno-deprecated-declarations') no_warn_args += '-Wno-deprecated-declarations' endif +libdrm_dep = dependency('libdrm') + xvideo_dep = dependency('xv', required : get_option('xvideo')) if xvideo_dep.found() @@ -19,7 +21,7 @@ if xvideo_dep.found() xvimage_sources, c_args : gst_plugins_base_args + no_warn_args, include_directories: [configinc, libsinc], - dependencies : [video_dep, gst_base_dep, gst_dep, x11_dep, xshm_dep, xvideo_dep, xi_dep, libm], + dependencies : [video_dep, gst_base_dep, gst_dep, x11_dep, xshm_dep, xvideo_dep, xi_dep, libdrm_dep, libm, allocators_dep], install : true, install_dir : plugins_install_dir, ) diff --git a/sys/xvimage/xvcontext.c b/sys/xvimage/xvcontext.c index 75bebd9..975c3a9 100644 --- a/sys/xvimage/xvcontext.c +++ b/sys/xvimage/xvcontext.c @@ -105,7 +105,7 @@ gst_lookup_xv_port_from_adaptor (GstXvContext * context, the port via XvGrabPort */ static GstCaps * gst_xvcontext_get_xv_support (GstXvContext * context, - const GstXvContextConfig * config, GError ** error) + GstXvContextConfig * config, GError ** error) { gint i; XvAdaptorInfo *adaptors; @@ -158,9 +158,11 @@ gst_xvcontext_get_xv_support (GstXvContext * context, if (!context->xv_port_id) goto no_ports; + config->dma_client_id = context->xv_port_id; + /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */ { - int count, todo = 4; + int count, todo = 7; XvAttribute *const attr = XvQueryPortAttributes (context->disp, context->xv_port_id, &count); static const char autopaint[] = "XV_AUTOPAINT_COLORKEY"; @@ -168,6 +170,9 @@ gst_xvcontext_get_xv_support (GstXvContext * context, static const char colorkey[] = "XV_COLORKEY"; static const char iturbt709[] = "XV_ITURBT_709"; static const char *xv_colorspace = "XV_COLORSPACE"; + static const char dma_client_id[] = XV_DMA_CLIENT_PROP; + static const char dma_drm_fourcc[] = XV_DMA_DRM_FOURCC_PROP; + static const char dma_drm_afbc[] = XV_DMA_DRM_AFBC_PROP; GST_DEBUG ("Checking %d Xv port attributes", count); @@ -176,6 +181,7 @@ gst_xvcontext_get_xv_support (GstXvContext * context, context->have_colorkey = FALSE; context->have_iturbt709 = FALSE; context->have_xvcolorspace = FALSE; + context->have_dma_client = FALSE; for (i = 0; ((i < count) && todo); i++) { GST_DEBUG ("Got attribute %s", attr[i].name); @@ -247,6 +253,19 @@ gst_xvcontext_get_xv_support (GstXvContext * context, } else if (!strcmp (attr[i].name, xv_colorspace)) { context->have_xvcolorspace = TRUE; todo--; + } else if (!strcmp (attr[i].name, dma_client_id)) { + const Atom atom = XInternAtom (context->disp, dma_client_id, False); + + XvSetPortAttribute (context->disp, context->xv_port_id, atom, + config->dma_client_id); + todo--; + context->have_dma_client = TRUE; + } else if (!strcmp (attr[i].name, dma_drm_fourcc)) { + todo--; + context->have_dma_drm_fourcc = TRUE; + } else if (!strcmp (attr[i].name, dma_drm_afbc)) { + todo--; + context->have_dma_drm_afbc = TRUE; } } @@ -361,6 +380,50 @@ gst_xvcontext_get_xv_support (GstXvContext * context, if (gst_caps_is_empty (caps)) goto no_caps; + if (context->have_dma_drm_afbc) { + GstCaps *format_caps; + + format_caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, "NV12", + "width", GST_TYPE_INT_RANGE, 1, max_w, + "height", GST_TYPE_INT_RANGE, 1, max_h, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); + gst_caps_set_simple (format_caps, "arm-afbc", G_TYPE_INT, 1, NULL); + gst_caps_append (caps, format_caps); + } + + if (context->have_dma_drm_fourcc) { + GstCaps *format_caps; + + format_caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, "NV16", + "width", GST_TYPE_INT_RANGE, 1, max_w, + "height", GST_TYPE_INT_RANGE, 1, max_h, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); + if (context->have_dma_drm_afbc) { + gst_caps_ref (format_caps); + gst_caps_append (caps, format_caps); + + gst_caps_set_simple (format_caps, "arm-afbc", G_TYPE_INT, 1, NULL); + } + gst_caps_append (caps, format_caps); + + format_caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, "NV12_10LE40", + "width", GST_TYPE_INT_RANGE, 1, max_w, + "height", GST_TYPE_INT_RANGE, 1, max_h, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); + if (context->have_dma_drm_afbc) { + gst_caps_ref (format_caps); + gst_caps_append (caps, format_caps); + + gst_caps_set_simple (format_caps, "arm-afbc", G_TYPE_INT, 1, NULL); + } + gst_caps_append (caps, format_caps); + } + + GST_DEBUG ("Final caps caps: %" GST_PTR_FORMAT, caps); + return caps; /* ERRORS */ @@ -920,6 +983,10 @@ gst_xvcontext_get_format_from_info (GstXvContext * context, { GList *list = NULL; + /* HACK: Use NV12 format for fake formats */ + if (context->drm_fourcc != -1) + return DRM_FORMAT_NV12; + list = context->formats_list; while (list) { diff --git a/sys/xvimage/xvcontext.h b/sys/xvimage/xvcontext.h index ea5424d..e515fcc 100644 --- a/sys/xvimage/xvcontext.h +++ b/sys/xvimage/xvcontext.h @@ -42,6 +42,19 @@ #include +#include + +#define XV_DMA_CLIENT_PROP "XV_DMA_CLIENT_ID" +#define XV_DMA_VER_STRIDE_PROP "XV_DMA_VER_STRIDE" +#define XV_DMA_HOR_STRIDE_PROP "XV_DMA_HOR_STRIDE" +#define XV_DMA_DRM_FOURCC_PROP "XV_DMA_DRM_FOURCC" +#define XV_DMA_DRM_AFBC_PROP "XV_DMA_DRM_AFBC" +#define XV_DMA_CLIENT_PATH "/tmp/.xv_dma_client" + +#ifndef DRM_FORMAT_NV12_10 +#define DRM_FORMAT_NV12_10 fourcc_code('N', 'A', '1', '2') +#endif + G_BEGIN_DECLS typedef struct _GstXvContextConfig GstXvContextConfig; @@ -70,6 +83,8 @@ struct _GstXvContextConfig gint hue; gint saturation; gboolean cb_changed; + + guint dma_client_id; }; /** @@ -167,6 +182,11 @@ struct _GstXvContext gboolean have_double_buffer; gboolean have_iturbt709; gboolean have_xvcolorspace; + gboolean have_dma_client; + gboolean have_dma_drm_fourcc; + gboolean have_dma_drm_afbc; + + guint32 drm_fourcc; GList *formats_list; diff --git a/sys/xvimage/xvimagesink.c b/sys/xvimage/xvimagesink.c index e9fcb6c..44c98a9 100644 --- a/sys/xvimage/xvimagesink.c +++ b/sys/xvimage/xvimagesink.c @@ -121,6 +121,7 @@ #include /* Helper functions */ #include +#include /* Object header */ #include "xvimagesink.h" @@ -137,6 +138,11 @@ #include #endif +#include +#include +#include +#include + GST_DEBUG_CATEGORY_EXTERN (gst_debug_xv_context); GST_DEBUG_CATEGORY_EXTERN (gst_debug_xv_image_pool); GST_DEBUG_CATEGORY (gst_debug_xv_image_sink); @@ -235,6 +241,173 @@ GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (xvimagesink, "xvimagesink", /* */ /* ============================================================= */ +static void +gst_xv_image_sink_check_dma_client (GstXvImageSink * xvimagesink) +{ + GstXvContext *context = xvimagesink->context; + Atom prop_atom; + int xv_value = 0; + + if (!context->have_dma_client) + return; + + g_mutex_lock (&context->lock); + prop_atom = XInternAtom (context->disp, XV_DMA_CLIENT_PROP, True); + if (prop_atom != None) { + XvGetPortAttribute (context->disp, context->xv_port_id, + prop_atom, &xv_value); + } + g_mutex_unlock (&context->lock); + + context->have_dma_client = xv_value > 0; +} + +static void +gst_xv_image_sink_flush_dma_client (GstXvImageSink * xvimagesink) +{ + GstXvContext *context = xvimagesink->context; + Atom prop_atom; + int xv_value; + + if (!context->have_dma_client) + return; + + g_mutex_lock (&context->lock); + prop_atom = XInternAtom (context->disp, XV_DMA_CLIENT_PROP, True); + if (prop_atom != None) { + XvSetPortAttribute (context->disp, context->xv_port_id, + prop_atom, xvimagesink->config.dma_client_id); + XvGetPortAttribute (context->disp, context->xv_port_id, + prop_atom, &xv_value); + } + g_mutex_unlock (&context->lock); +} + +static void +gst_xv_image_sink_disable_dma_client (GstXvImageSink * xvimagesink) +{ + GstXvContext *context = xvimagesink->context; + Atom prop_atom; + + if (!context->have_dma_client) + return; + + g_mutex_lock (&context->lock); + prop_atom = XInternAtom (context->disp, XV_DMA_CLIENT_PROP, True); + if (prop_atom != None) { + XvSetPortAttribute (context->disp, context->xv_port_id, prop_atom, 0); + } + g_mutex_unlock (&context->lock); + + context->have_dma_client = FALSE; +} + +static gboolean +gst_xv_image_sink_send_dma_params (GstXvImageSink * xvimagesink, + gint hor_stride, gint ver_stride, gboolean afbc) +{ + GstXvContext *context = xvimagesink->context; + Atom prop_atom; + gboolean error = FALSE; + + if (!context->have_dma_client) + return FALSE; + + g_mutex_lock (&context->lock); + prop_atom = XInternAtom (context->disp, XV_DMA_HOR_STRIDE_PROP, True); + if (prop_atom != None) { + XvSetPortAttribute (context->disp, context->xv_port_id, + prop_atom, hor_stride); + } else { + error = TRUE; + } + prop_atom = XInternAtom (context->disp, XV_DMA_VER_STRIDE_PROP, True); + if (prop_atom != None) { + XvSetPortAttribute (context->disp, context->xv_port_id, + prop_atom, ver_stride); + } else { + error = TRUE; + } + prop_atom = XInternAtom (context->disp, XV_DMA_DRM_FOURCC_PROP, True); + if (prop_atom != None) { + XvSetPortAttribute (context->disp, context->xv_port_id, + prop_atom, context->drm_fourcc); + } + prop_atom = XInternAtom (context->disp, XV_DMA_DRM_AFBC_PROP, True); + if (prop_atom != None) { + XvSetPortAttribute (context->disp, context->xv_port_id, prop_atom, afbc); + } + g_mutex_unlock (&context->lock); + + if (error == TRUE) { + gst_xv_image_sink_disable_dma_client (xvimagesink); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_xv_image_sink_send_dma_fd (GstXvImageSink * xvimagesink, gint dma_fd) +{ + GstXvContext *context = xvimagesink->context; + struct sockaddr_un addr; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *header; + gchar buf[CMSG_SPACE (sizeof (int))]; + gint socket_fd; + + if (!context->have_dma_client) + return FALSE; + + gst_xv_image_sink_flush_dma_client (xvimagesink); + + socket_fd = socket (PF_UNIX, SOCK_DGRAM, 0); + if (socket_fd < 0) + goto failed; + + addr.sun_family = AF_LOCAL; + snprintf (addr.sun_path, sizeof (addr.sun_path), + XV_DMA_CLIENT_PATH ".%d", xvimagesink->config.dma_client_id); + addr.sun_path[sizeof (addr.sun_path) - 1] = '\0'; + + if (connect (socket_fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) + goto failed; + + iov.iov_base = buf; + iov.iov_len = 1; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = buf; + msg.msg_controllen = sizeof (buf); + msg.msg_name = NULL; + msg.msg_namelen = 0; + + header = CMSG_FIRSTHDR (&msg); + header->cmsg_level = SOL_SOCKET; + header->cmsg_type = SCM_RIGHTS; + + header->cmsg_len = CMSG_LEN (sizeof (int)); + *((int *) CMSG_DATA (header)) = dma_fd; + sendmsg (socket_fd, &msg, 0); + + /* Send am empty msg at the end */ + header->cmsg_len = CMSG_LEN (0); + sendmsg (socket_fd, &msg, 0); + + close (socket_fd); + return TRUE; + +failed: + gst_xv_image_sink_disable_dma_client (xvimagesink); + + if (socket_fd >= 0) + close (socket_fd); + + return FALSE; +} /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE * if no window was available */ @@ -322,6 +495,13 @@ gst_xv_image_sink_xvimage_put (GstXvImageSink * xvimagesink, memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle)); } + if (gst_buffer_n_memory (xvimage) > 1) { + GstMemory *dma_mem = gst_buffer_peek_memory (xvimage, 1); + gint dma_fd = gst_dmabuf_memory_get_fd (dma_mem); + if (dma_fd >= 0) + gst_xv_image_sink_send_dma_fd (xvimagesink, dma_fd); + } + gst_xvimage_memory_render (mem, &src, xwindow, &result, draw_border); g_mutex_unlock (&xvimagesink->flow_lock); @@ -844,6 +1024,27 @@ config_failed: } } +static gboolean +gst_xv_video_info_from_caps (GstVideoInfo * info, const GstCaps * caps) +{ + GstStructure *s; + gint value; + + if (!gst_video_info_from_caps (info, caps)) + return FALSE; + + /* parse AFBC from caps */ + s = gst_caps_get_structure (caps, 0); + if (gst_structure_get_int (s, "arm-afbc", &value)) { + if (value) + GST_VIDEO_INFO_SET_AFBC (info); + else + GST_VIDEO_INFO_UNSET_AFBC (info); + } + + return TRUE; +} + static gboolean gst_xv_image_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) { @@ -866,7 +1067,7 @@ gst_xv_image_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) if (!gst_caps_can_intersect (context->caps, caps)) goto incompatible_caps; - if (!gst_video_info_from_caps (&info, caps)) + if (!gst_xv_video_info_from_caps (&info, caps)) goto invalid_format; xvimagesink->fps_n = info.fps_n; @@ -976,6 +1177,20 @@ gst_xv_image_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) gst_object_unref (oldpool); } + context->drm_fourcc = -1; + + if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_NV12_10LE40) { + if (!context->have_dma_drm_fourcc) + return FALSE; + + context->drm_fourcc = DRM_FORMAT_NV12_10; + } else if (GST_VIDEO_INFO_FORMAT (&info) == GST_VIDEO_FORMAT_NV16) { + if (!context->have_dma_drm_fourcc) + return FALSE; + + context->drm_fourcc = DRM_FORMAT_NV16; + } + return TRUE; /* ERRORS */ @@ -1128,6 +1343,47 @@ gst_xv_image_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf) if (res != GST_FLOW_OK) goto no_buffer; + if ((crop_meta = gst_buffer_get_video_crop_meta (buf))) { + GstVideoCropMeta *dmeta = gst_buffer_add_video_crop_meta (to_put); + + dmeta->x = crop_meta->x; + dmeta->y = crop_meta->y; + dmeta->width = crop_meta->width; + dmeta->height = crop_meta->height; + } + + mem = gst_buffer_peek_memory (buf, 0); + gst_xv_image_sink_check_dma_client (xvimagesink); + if (gst_is_dmabuf_memory (mem) && xvimagesink->context->have_dma_client) { + GstVideoMeta *vmeta = gst_buffer_get_video_meta (buf); + gint hor_stride, ver_stride; + + /* If this buffer is dmabuf and the xserver supports dma_client, we will + send the dmabuf fd directly */ + GST_LOG_OBJECT (xvimagesink, "buffer %p is dmabuf, will send dmabuf fd", + buf); + + /* Stash the dmabuf in index 1 */ + gst_buffer_insert_memory (to_put, 1, gst_buffer_get_memory (buf, 0)); + + /* Try to send dmabuf params */ + if (vmeta) { + hor_stride = vmeta->stride[0]; + ver_stride = vmeta->height; + + if (vmeta->n_planes > 1) + ver_stride = vmeta->offset[1] / hor_stride; + } else { + hor_stride = xvimagesink->info.width; + ver_stride = xvimagesink->info.height; + } + + if (gst_xv_image_sink_send_dma_params (xvimagesink, + hor_stride, ver_stride, + GST_VIDEO_INFO_IS_AFBC (&xvimagesink->info))) + goto put_image; + } + GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink, "slow copy buffer %p into bufferpool buffer %p", buf, to_put); @@ -1143,17 +1399,9 @@ gst_xv_image_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf) gst_video_frame_unmap (&dest); gst_video_frame_unmap (&src); - - if ((crop_meta = gst_buffer_get_video_crop_meta (buf))) { - GstVideoCropMeta *dmeta = gst_buffer_add_video_crop_meta (to_put); - - dmeta->x = crop_meta->x; - dmeta->y = crop_meta->y; - dmeta->width = crop_meta->width; - dmeta->height = crop_meta->height; - } } +put_image: if (!gst_xv_image_sink_xvimage_put (xvimagesink, to_put)) goto no_window; @@ -1242,7 +1490,7 @@ gst_xv_image_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) if (caps == NULL) goto no_caps; - if (!gst_video_info_from_caps (&info, caps)) + if (!gst_xv_video_info_from_caps (&info, caps)) goto invalid_caps; /* the normal size of a frame */ diff --git a/sys/xvimage/xvimagesink.h b/sys/xvimage/xvimagesink.h index 6f5ffa1..35a3081 100644 --- a/sys/xvimage/xvimagesink.h +++ b/sys/xvimage/xvimagesink.h @@ -25,6 +25,16 @@ /* Helper functions */ #include +#ifndef GST_VIDEO_FLAG_ARM_AFBC +#define GST_VIDEO_FLAG_ARM_AFBC (1UL << 31) +#define GST_VIDEO_INFO_SET_AFBC(i) \ + GST_VIDEO_INFO_FLAG_SET (i, GST_VIDEO_FLAG_ARM_AFBC) +#define GST_VIDEO_INFO_UNSET_AFBC(i) \ + GST_VIDEO_INFO_FLAG_UNSET (i, GST_VIDEO_FLAG_ARM_AFBC) +#define GST_VIDEO_INFO_IS_AFBC(i) \ + GST_VIDEO_INFO_FLAG_IS_SET (i, GST_VIDEO_FLAG_ARM_AFBC) +#endif + G_BEGIN_DECLS #define GST_TYPE_XV_IMAGE_SINK \ (gst_xv_image_sink_get_type()) -- 2.20.1