* 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 <d...@pseudoterminal.org> --- gst-decoder.c | 196 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 119 insertions(+), 77 deletions(-) diff --git a/gst-decoder.c b/gst-decoder.c index 67efbfd..cc5c3b2 100644 --- a/gst-decoder.c +++ b/gst-decoder.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2017 Rob Clark <rcl...@redhat.com> + * Copyright (c) 2017 Carlos Rafael Giani <d...@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; } @@ -352,89 +356,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 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev