[Mesa-dev] [PATCH v3 08/12] gst-decoder.c: improve buffer_to_image() function

Carlos Rafael Giani dv at pseudoterminal.org
Wed Apr 26 18:56:20 UTC 2017


* Make EGL image attribute specification code more generic, and not
  specific to certain pixel formats, implicitely gaining support for YUY2
* Better handling of gstbuffers with multiple memory blocks
* Print out more information about the stream
* Use the GST_VIDEO_INFO_* macros instead of directly accessing the
  GstVideoInfo fields; this is what the GStreamer documentation recommends

Signed-off-by: Carlos Rafael Giani <dv at pseudoterminal.org>
---
 gst-decoder.c | 196 +++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 119 insertions(+), 77 deletions(-)

diff --git a/gst-decoder.c b/gst-decoder.c
index 768aa1b..fc4207c 100644
--- a/gst-decoder.c
+++ b/gst-decoder.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2017 Rob Clark <rclark at redhat.com>
+ * Copyright (c) 2017 Carlos Rafael Giani <dv at pseudoterminal.org>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -44,6 +45,12 @@ GST_DEBUG_CATEGORY_EXTERN(kmscube_debug);
 
 #define MAX_NUM_PLANES 3
 
+inline static const char *
+yesno(int yes)
+{
+	return yes ? "yes" : "no";
+}
+
 struct decoder {
 	GMainLoop          *loop;
 	GstElement         *pipeline;
@@ -100,9 +107,6 @@ pad_probe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
 		return GST_PAD_PROBE_OK;
 	}
 
-	GST_DEBUG("got: %ux%u@%4.4s\n", dec->info.width, dec->info.height,
-			(char *)&dec->format);
-
 	return GST_PAD_PROBE_OK;
 }
 
@@ -327,89 +331,127 @@ buffer_to_image(struct decoder *dec, GstBuffer *buf)
 	struct { int fd, offset, stride; } planes[MAX_NUM_PLANES];
 	GstVideoMeta *meta = gst_buffer_get_video_meta(buf);
 	EGLImage image;
-	unsigned nmems = gst_buffer_n_memory(buf);
-	unsigned nplanes = (dec->format == DRM_FORMAT_YUV420) ? 3 : 2;
-	unsigned i;
-
-	if (nmems == nplanes) {
-		// XXX TODO..
-	} else if (nmems == 1) {
-		GstMemory *mem = gst_buffer_peek_memory(buf, 0);
-		int fd;
-
-		if (dec->frame == 0) {
-			printf("%s zero-copy\n", gst_is_dmabuf_memory(mem) ? "using" : "not");
+	guint nmems = gst_buffer_n_memory(buf);
+	guint nplanes = GST_VIDEO_INFO_N_PLANES(&(dec->info));
+	guint i;
+	guint width, height;
+	gboolean is_dmabuf_mem;
+	GstMemory *mem;
+	int dmabuf_fd = -1;
+
+	static const EGLint egl_dmabuf_plane_fd_attr[MAX_NUM_PLANES] = {
+		EGL_DMA_BUF_PLANE0_FD_EXT,
+		EGL_DMA_BUF_PLANE1_FD_EXT,
+		EGL_DMA_BUF_PLANE2_FD_EXT,
+	};
+	static const EGLint egl_dmabuf_plane_offset_attr[MAX_NUM_PLANES] = {
+		EGL_DMA_BUF_PLANE0_OFFSET_EXT,
+		EGL_DMA_BUF_PLANE1_OFFSET_EXT,
+		EGL_DMA_BUF_PLANE2_OFFSET_EXT,
+	};
+	static const EGLint egl_dmabuf_plane_pitch_attr[MAX_NUM_PLANES] = {
+		EGL_DMA_BUF_PLANE0_PITCH_EXT,
+		EGL_DMA_BUF_PLANE1_PITCH_EXT,
+		EGL_DMA_BUF_PLANE2_PITCH_EXT,
+	};
+
+	/* Query gst_is_dmabuf_memory() here, since the gstmemory
+	 * block might get merged below by gst_buffer_map(), meaning
+	 * that the mem pointer would become invalid */
+	mem = gst_buffer_peek_memory(buf, 0);
+	is_dmabuf_mem = gst_is_dmabuf_memory(mem);
+
+	if (nmems > 1) {
+		if (is_dmabuf_mem) {
+			/* this case currently is not defined */
+
+			GST_FIXME("gstbuffers with multiple memory blocks and DMABUF "
+			          "memory currently are not supported");
+			return EGL_NO_IMAGE_KHR;
 		}
 
-		if (gst_is_dmabuf_memory(mem)) {
-			fd = dup(gst_dmabuf_memory_get_fd(mem));
-		} else {
-			GstMapInfo info;
-			gst_memory_map(mem, &info, GST_MAP_READ);
-			fd = buf_to_fd(dec->gbm, info.size, info.data);
-			gst_memory_unmap(mem, &info);
-		}
+		/* if this is not DMABUF memory, then the gst_buffer_map()
+		 * call below will automatically merge the memory blocks
+		 */
+	}
 
-		// XXX why don't we get meta??
-		if (meta) {
-			for (i = 0; i < nplanes; i++) {
-				planes[i].fd = fd;
-				planes[i].offset = meta->offset[i];
-				planes[i].stride = meta->stride[i];
-			}
-		} else {
-			int offset = 0, stride = dec->info.width, height = dec->info.height;
-
-			for (i = 0; i < nplanes; i++) {
-
-				if (i == 1) {
-					height /= 2;
-					if (nplanes == 3)
-						stride /= 2;
-				}
-
-				planes[i].fd = fd;
-				planes[i].offset = offset;
-				planes[i].stride = stride;
-
-				offset += stride * height;
-			}
-		}
+	if (is_dmabuf_mem) {
+		dmabuf_fd = dup(gst_dmabuf_memory_get_fd(mem));
+	} else {
+		GstMapInfo map_info;
+		gst_buffer_map(buf, &map_info, GST_MAP_READ);
+		dmabuf_fd = buf_to_fd(dec->gbm, map_info.size, map_info.data);
+		gst_buffer_unmap(buf, &map_info);
 	}
 
-	if (dec->format == DRM_FORMAT_NV12) {
-		const EGLint attr[] = {
-			EGL_WIDTH, dec->info.width,
-			EGL_HEIGHT, dec->info.height,
-			EGL_LINUX_DRM_FOURCC_EXT, dec->format,
-			EGL_DMA_BUF_PLANE0_FD_EXT, planes[0].fd,
-			EGL_DMA_BUF_PLANE0_OFFSET_EXT, planes[0].offset,
-			EGL_DMA_BUF_PLANE0_PITCH_EXT, planes[0].stride,
-			EGL_DMA_BUF_PLANE1_FD_EXT, planes[1].fd,
-			EGL_DMA_BUF_PLANE1_OFFSET_EXT, planes[1].offset,
-			EGL_DMA_BUF_PLANE1_PITCH_EXT, planes[1].stride,
-			EGL_NONE
-		};
+	if (dmabuf_fd < 0) {
+		GST_ERROR("could not obtain DMABUF FD");
+		return EGL_NO_IMAGE_KHR;
+	}
 
-		image = dec->egl->eglCreateImageKHR(dec->egl->display, EGL_NO_CONTEXT,
-				EGL_LINUX_DMA_BUF_EXT, NULL, attr);
+	/* Usually, a videometa should be present, since by using the internal kmscube
+	 * video_appsink element instead of the regular appsink, it is guaranteed that
+	 * video meta support is declared in the video_appsink's allocation query.
+	 * However, this assumes that upstream elements actually look at the allocation
+	 * query's contents properly, or that they even send a query at all. If this
+	 * is not the case, then upstream might decide to push frames without adding
+	 * a meta. It can happen, and in this case, look at the video info data as
+	 * a fallback (it is computed out of the input caps).
+	 */
+	if (meta) {
+		for (i = 0; i < nplanes; i++) {
+			planes[i].fd = dmabuf_fd;
+			planes[i].offset = meta->offset[i];
+			planes[i].stride = meta->stride[i];
+		}
 	} else {
-		const EGLint attr[] = {
-			EGL_WIDTH, dec->info.width,
-			EGL_HEIGHT, dec->info.height,
-			EGL_LINUX_DRM_FOURCC_EXT, dec->format,
-			EGL_DMA_BUF_PLANE0_FD_EXT, planes[0].fd,
-			EGL_DMA_BUF_PLANE0_OFFSET_EXT, planes[0].offset,
-			EGL_DMA_BUF_PLANE0_PITCH_EXT, planes[0].stride,
-			EGL_DMA_BUF_PLANE1_FD_EXT, planes[1].fd,
-			EGL_DMA_BUF_PLANE1_OFFSET_EXT, planes[1].offset,
-			EGL_DMA_BUF_PLANE1_PITCH_EXT, planes[1].stride,
-			EGL_DMA_BUF_PLANE2_FD_EXT, planes[2].fd,
-			EGL_DMA_BUF_PLANE2_OFFSET_EXT, planes[2].offset,
-			EGL_DMA_BUF_PLANE2_PITCH_EXT, planes[2].stride,
-			EGL_NONE
+		for (i = 0; i < nplanes; i++) {
+			planes[i].fd = dmabuf_fd;
+			planes[i].offset = GST_VIDEO_INFO_PLANE_OFFSET(&(dec->info), i);
+			planes[i].stride = GST_VIDEO_INFO_PLANE_STRIDE(&(dec->info), i);
+		}
+	}
+
+	width = GST_VIDEO_INFO_WIDTH(&(dec->info));
+	height = GST_VIDEO_INFO_HEIGHT(&(dec->info));
+
+	/* output some information at the beginning (= when the first frame is handled) */
+	if (dec->frame == 0) {
+		GstVideoFormat pixfmt;
+		const char *pixfmt_str;
+
+		pixfmt = GST_VIDEO_INFO_FORMAT(&(dec->info));
+		pixfmt_str = gst_video_format_to_string(pixfmt);
+
+		printf("===================================\n");
+		printf("GStreamer video stream information:\n");
+		printf("  size: %u x %u pixel\n", width, height);
+		printf("  pixel format: %s  number of planes: %u\n", pixfmt_str, nplanes);
+		printf("  can use zero-copy: %s\n", yesno(is_dmabuf_mem));
+		printf("  video meta found: %s\n", yesno(meta != NULL));
+		printf("===================================\n");
+	}
+
+	{
+		/* Initialize the first 6 attributes with values that are
+		 * plane invariant (width, height, format) */
+		EGLint attr[6 + 6*(MAX_NUM_PLANES) + 1] = {
+			EGL_WIDTH, width,
+			EGL_HEIGHT, height,
+			EGL_LINUX_DRM_FOURCC_EXT, dec->format
 		};
 
+		for (i = 0; i < nplanes; i++) {
+			attr[6 + 6*i + 0] = egl_dmabuf_plane_fd_attr[i];
+			attr[6 + 6*i + 1] = planes[i].fd;
+			attr[6 + 6*i + 2] = egl_dmabuf_plane_offset_attr[i];
+			attr[6 + 6*i + 3] = planes[i].offset;
+			attr[6 + 6*i + 4] = egl_dmabuf_plane_pitch_attr[i];
+			attr[6 + 6*i + 5] = planes[i].stride;
+		}
+
+		attr[6 + 6*nplanes] = EGL_NONE;
+
 		image = dec->egl->eglCreateImageKHR(dec->egl->display, EGL_NO_CONTEXT,
 				EGL_LINUX_DMA_BUF_EXT, NULL, attr);
 	}
-- 
2.7.4



More information about the mesa-dev mailing list