]> code.ossystems Code Review - meta-freescale.git/commitdiff
kmscube: Add patches for better GStreamer support and for YUY2 support 31/15931/1
authorCarlos Rafael Giani <dv@pseudoterminal.org>
Tue, 11 Apr 2017 07:33:04 +0000 (09:33 +0200)
committerFabio Berton <fabio.berton@ossystems.com.br>
Mon, 26 Jun 2017 20:12:55 +0000 (17:12 -0300)
Change-Id: I17c77cbae9f23ba415a5d4bb433fcee28a04deab
Signed-off-by: Carlos Rafael Giani <dv@pseudoterminal.org>
Signed-off-by: Otavio Salvador <otavio@ossystems.com.br>
Signed-off-by: Fabio Berton <fabio.berton@ossystems.com.br>
12 files changed:
recipes-graphics/kmscube/kmscube/0001-output-more-and-improved-information-about-EGL-and-O.patch [new file with mode: 0644]
recipes-graphics/kmscube/kmscube/0002-add-kmscube-GStreamer-debug-category.patch [new file with mode: 0644]
recipes-graphics/kmscube/kmscube/0003-gst-decoder.c-add-support-for-YUY2-pixel-format.patch [new file with mode: 0644]
recipes-graphics/kmscube/kmscube/0004-gst-decoder.c-look-at-the-caps-event-instead-of-the-.patch [new file with mode: 0644]
recipes-graphics/kmscube/kmscube/0005-gst-decoder.c-Use-element-factory-name-to-detect-V4L.patch [new file with mode: 0644]
recipes-graphics/kmscube/kmscube/0006-gst-decoder.c-add-bus-watch.patch [new file with mode: 0644]
recipes-graphics/kmscube/kmscube/0007-gst-decoder.c-minor-cleanup.patch [new file with mode: 0644]
recipes-graphics/kmscube/kmscube/0008-gst-decoder.c-use-a-custom-appsink-subclass-to-make-.patch [new file with mode: 0644]
recipes-graphics/kmscube/kmscube/0009-gst-decoder.c-Improve-synchronicity-of-video-output.patch [new file with mode: 0644]
recipes-graphics/kmscube/kmscube/0010-gst-decoder.c-improve-buffer_to_image-function.patch [new file with mode: 0644]
recipes-graphics/kmscube/kmscube/use-dri-card1-by-default.patch [new file with mode: 0644]
recipes-graphics/kmscube/kmscube_%.bbappend [new file with mode: 0644]

diff --git a/recipes-graphics/kmscube/kmscube/0001-output-more-and-improved-information-about-EGL-and-O.patch b/recipes-graphics/kmscube/kmscube/0001-output-more-and-improved-information-about-EGL-and-O.patch
new file mode 100644 (file)
index 0000000..677a0d3
--- /dev/null
@@ -0,0 +1,51 @@
+From fc0da8b8d4c2e15258ffc55710e495c967c69c11 Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Fri, 7 Apr 2017 20:42:32 +0200
+Subject: [PATCH kmscube 01/10] output more and improved information about EGL
+ and OpenGL ES 2.x
+
+Upstream-Status: Submitted [mesa-dev mailing list]
+
+Signed-off-by: Carlos Rafael Giani <dv@pseudoterminal.org>
+---
+ common.c | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/common.c b/common.c
+index 4bf3c5a..4d29eef 100644
+--- a/common.c
++++ b/common.c
+@@ -97,9 +97,12 @@ int init_egl(struct egl *egl, const struct gbm *gbm)
+       printf("Using display %p with EGL version %d.%d\n",
+                       egl->display, major, minor);
+-      printf("EGL Version \"%s\"\n", eglQueryString(egl->display, EGL_VERSION));
+-      printf("EGL Vendor \"%s\"\n", eglQueryString(egl->display, EGL_VENDOR));
+-      printf("EGL Extensions \"%s\"\n", eglQueryString(egl->display, EGL_EXTENSIONS));
++      printf("===================================\n");
++      printf("EGL information:\n");
++      printf("  version: \"%s\"\n", eglQueryString(egl->display, EGL_VERSION));
++      printf("  vendor: \"%s\"\n", eglQueryString(egl->display, EGL_VENDOR));
++      printf("  extensions: \"%s\"\n", eglQueryString(egl->display, EGL_EXTENSIONS));
++      printf("===================================\n");
+       if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+               printf("failed to bind api EGL_OPENGL_ES_API\n");
+@@ -128,7 +131,13 @@ int init_egl(struct egl *egl, const struct gbm *gbm)
+       /* connect the context to the surface */
+       eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context);
+-      printf("GL Extensions: \"%s\"\n", glGetString(GL_EXTENSIONS));
++      printf("OpenGL ES 2.x information:\n");
++      printf("  version: \"%s\"\n", glGetString(GL_VERSION));
++      printf("  shading language version: \"%s\"\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
++      printf("  vendor: \"%s\"\n", glGetString(GL_VENDOR));
++      printf("  renderer: \"%s\"\n", glGetString(GL_RENDERER));
++      printf("  extensions: \"%s\"\n", glGetString(GL_EXTENSIONS));
++      printf("===================================\n");
+       return 0;
+ }
+-- 
+2.7.4
+
diff --git a/recipes-graphics/kmscube/kmscube/0002-add-kmscube-GStreamer-debug-category.patch b/recipes-graphics/kmscube/kmscube/0002-add-kmscube-GStreamer-debug-category.patch
new file mode 100644 (file)
index 0000000..2ef2e65
--- /dev/null
@@ -0,0 +1,56 @@
+From 6d6ff5895e2a86f2c4a7684da97085aa7c5b2b2a Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Fri, 7 Apr 2017 23:05:22 +0200
+Subject: [PATCH kmscube 02/10] add "kmscube" GStreamer debug category
+
+Without this, the various GST_* log macros won't output anything.
+To enable, add "kmscube:<loglevel>" to the GST_DEBUG environment variable.
+Example:   GST_DEBUG=kmscube:5
+
+Upstream-Status: Submitted [mesa-dev mailing list]
+
+Signed-off-by: Carlos Rafael Giani <dv@pseudoterminal.org>
+---
+ gst-decoder.c | 3 +++
+ kmscube.c     | 4 +++-
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/gst-decoder.c b/gst-decoder.c
+index 1140213..22dc068 100644
+--- a/gst-decoder.c
++++ b/gst-decoder.c
+@@ -39,6 +39,9 @@
+ #include <gst/app/gstappsink.h>
+ #include <gst/video/gstvideometa.h>
++GST_DEBUG_CATEGORY_EXTERN(kmscube_debug);
++#define GST_CAT_DEFAULT kmscube_debug
++
+ struct decoder {
+       GMainLoop          *loop;
+       GstElement         *pipeline;
+diff --git a/kmscube.c b/kmscube.c
+index 54f4328..8e2d008 100644
+--- a/kmscube.c
++++ b/kmscube.c
+@@ -32,7 +32,8 @@
+ #include "drm-common.h"
+ #ifdef HAVE_GST
+-#  include <gst/gst.h>
++#include <gst/gst.h>
++GST_DEBUG_CATEGORY(kmscube_debug);
+ #endif
+ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+@@ -77,6 +78,7 @@ int main(int argc, char *argv[])
+ #ifdef HAVE_GST
+       gst_init(&argc, &argv);
++      GST_DEBUG_CATEGORY_INIT(kmscube_debug, "kmscube", 0, "kmscube video pipeline");
+ #endif
+       while ((opt = getopt_long_only(argc, argv, shortopts, longopts, NULL)) != -1) {
+-- 
+2.7.4
+
diff --git a/recipes-graphics/kmscube/kmscube/0003-gst-decoder.c-add-support-for-YUY2-pixel-format.patch b/recipes-graphics/kmscube/kmscube/0003-gst-decoder.c-add-support-for-YUY2-pixel-format.patch
new file mode 100644 (file)
index 0000000..bae0792
--- /dev/null
@@ -0,0 +1,32 @@
+From 2cb0be7d7e52180a2403d9fa1f2d58b964878f9f Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Fri, 7 Apr 2017 20:43:45 +0200
+Subject: [PATCH kmscube 03/10] gst-decoder.c: add support for YUY2 pixel
+ format
+
+This format is used for example by the i.MX6 CODA hardware video codec
+
+Upstream-Status: Submitted [mesa-dev mailing list]
+
+Signed-off-by: Carlos Rafael Giani <dv@pseudoterminal.org>
+---
+ gst-decoder.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/gst-decoder.c b/gst-decoder.c
+index 22dc068..fd28201 100644
+--- a/gst-decoder.c
++++ b/gst-decoder.c
+@@ -91,6 +91,9 @@ pad_probe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
+       case GST_VIDEO_FORMAT_NV12:
+               dec->format = DRM_FORMAT_NV12;
+               break;
++      case GST_VIDEO_FORMAT_YUY2:
++              dec->format = DRM_FORMAT_YUYV;
++              break;
+       default:
+               GST_ERROR("unknown format\n");
+               return GST_PAD_PROBE_OK;
+-- 
+2.7.4
+
diff --git a/recipes-graphics/kmscube/kmscube/0004-gst-decoder.c-look-at-the-caps-event-instead-of-the-.patch b/recipes-graphics/kmscube/kmscube/0004-gst-decoder.c-look-at-the-caps-event-instead-of-the-.patch
new file mode 100644 (file)
index 0000000..754ba22
--- /dev/null
@@ -0,0 +1,59 @@
+From 88a7a4427e34db3288a0b43b5e111f108caa3ce3 Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Fri, 7 Apr 2017 21:44:25 +0200
+Subject: [PATCH kmscube 04/10] gst-decoder.c: look at the caps event instead
+ of the allocation query
+
+Upstream-Status: Submitted [mesa-dev mailing list]
+
+Signed-off-by: Carlos Rafael Giani <dv@pseudoterminal.org>
+---
+ gst-decoder.c | 13 ++++++-------
+ 1 file changed, 6 insertions(+), 7 deletions(-)
+
+diff --git a/gst-decoder.c b/gst-decoder.c
+index fd28201..51304a2 100644
+--- a/gst-decoder.c
++++ b/gst-decoder.c
+@@ -63,24 +63,23 @@ static GstPadProbeReturn
+ pad_probe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
+ {
+       struct decoder *dec = user_data;
+-      GstQuery *query = GST_PAD_PROBE_INFO_QUERY(info);
+-      gboolean need_pool;
++      GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info);
+       GstCaps *caps;
+       (void)pad;
+-      if (GST_QUERY_TYPE(query) != GST_QUERY_ALLOCATION)
++      if (GST_EVENT_TYPE(event) != GST_EVENT_CAPS)
+               return GST_PAD_PROBE_OK;
+-      gst_query_parse_allocation(query, &caps, &need_pool);
++      gst_event_parse_caps(event, &caps);
+       if (!caps) {
+-              GST_ERROR("allocation query without caps");
++              GST_ERROR("caps event without caps");
+               return GST_PAD_PROBE_OK;
+       }
+       if (!gst_video_info_from_caps(&dec->info, caps)) {
+-              GST_ERROR("allocation query with invalid caps");
++              GST_ERROR("caps event with invalid video caps");
+               return GST_PAD_PROBE_OK;
+       }
+@@ -156,7 +155,7 @@ video_init(const struct egl *egl, const struct gbm *gbm, const char *filename)
+       g_object_set(G_OBJECT(dec->sink), "max-buffers", 2, NULL);
+       gst_pad_add_probe(gst_element_get_static_pad(dec->sink, "sink"),
+-                      GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM,
++                      GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+                       pad_probe, dec, NULL);
+       /* hack to make sure we get dmabuf's from v4l2video0dec.. */
+-- 
+2.7.4
+
diff --git a/recipes-graphics/kmscube/kmscube/0005-gst-decoder.c-Use-element-factory-name-to-detect-V4L.patch b/recipes-graphics/kmscube/kmscube/0005-gst-decoder.c-Use-element-factory-name-to-detect-V4L.patch
new file mode 100644 (file)
index 0000000..59766ff
--- /dev/null
@@ -0,0 +1,48 @@
+From 0e42a19481dac6a0d971eb7ae7503c3db0f349e0 Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Fri, 7 Apr 2017 21:45:27 +0200
+Subject: [PATCH kmscube 05/10] gst-decoder.c: Use element factory name to
+ detect V4L2 video decoder
+
+Upstream-Status: Submitted [mesa-dev mailing list]
+
+Signed-off-by: Carlos Rafael Giani <dv@pseudoterminal.org>
+---
+ gst-decoder.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/gst-decoder.c b/gst-decoder.c
+index 51304a2..05d73b7 100644
+--- a/gst-decoder.c
++++ b/gst-decoder.c
+@@ -115,15 +115,24 @@ gst_thread_func(void *args)
+ static void
+ element_added_cb(GstBin *bin, GstElement *element, gpointer user_data)
+ {
++      GstElementFactory *elem_factory;
++      gchar const *factory_name;
++
+       (void)user_data;
+       (void)bin;
+-      printf("added: %s\n", GST_OBJECT_NAME(element));
++      elem_factory = gst_element_get_factory(element);
++      factory_name = gst_plugin_feature_get_name(elem_factory);
++
++      GST_DEBUG("added element %s (created with factory %s)", GST_OBJECT_NAME(element), factory_name);
+-      // XXX is there a better way to do this, like match class name?
+-      if (strstr(GST_OBJECT_NAME(element), "v4l2video0dec") == GST_OBJECT_NAME(element)) {
++      /* v4l2 video decoder factories are generated by the GStreamer v4l probe.
++       * The format is v4l2videoNdec, where N is an integer. So, check if the
++       * element's factory name fits this pattern. */
++      if (g_str_has_prefix(factory_name, "v4l2video") && g_str_has_suffix(factory_name, "dec")) {
+               /* yes, "capture" rather than "output" because v4l2 is bonkers */
+               gst_util_set_object_arg(G_OBJECT(element), "capture-io-mode", "dmabuf");
++              printf("found GStreamer V4L2 video decoder element with name \"%s\"\n", GST_OBJECT_NAME(element));
+       }
+ }
+-- 
+2.7.4
+
diff --git a/recipes-graphics/kmscube/kmscube/0006-gst-decoder.c-add-bus-watch.patch b/recipes-graphics/kmscube/kmscube/0006-gst-decoder.c-add-bus-watch.patch
new file mode 100644 (file)
index 0000000..de99110
--- /dev/null
@@ -0,0 +1,150 @@
+From 42e67ce706bf02fa6643fa5822610091c466dbfb Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Fri, 7 Apr 2017 21:49:52 +0200
+Subject: [PATCH kmscube 06/10] gst-decoder.c: add bus watch
+
+The bus watch is useful for logging state changes, printing out
+info/warning/error messages and handling common GStreamer activities like
+latency redistribution and state change requests (that are sent by
+elements since they are not allowed to directly change the state).
+
+State changes and error messages can also cause a dot graph of the
+pipeline to be generated if the GST_DEBUG_DUMP_DOT_DIR environment
+variable is set. See the GST_DEBUG_BIN_TO_DOT_FILE documentation for more.
+
+Upstream-Status: Submitted [mesa-dev mailing list]
+
+Signed-off-by: Carlos Rafael Giani <dv@pseudoterminal.org>
+---
+ gst-decoder.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 103 insertions(+)
+
+diff --git a/gst-decoder.c b/gst-decoder.c
+index 05d73b7..d1cb18c 100644
+--- a/gst-decoder.c
++++ b/gst-decoder.c
+@@ -136,11 +136,108 @@ element_added_cb(GstBin *bin, GstElement *element, gpointer user_data)
+       }
+ }
++static gboolean
++bus_watch_cb(GstBus *bus, GstMessage *msg, gpointer user_data)
++{
++      struct decoder *dec = (struct decoder *)user_data;
++
++      (void)bus;
++
++      switch (GST_MESSAGE_TYPE(msg)) {
++      case GST_MESSAGE_STATE_CHANGED: {
++              gchar *dotfilename;
++              GstState old_gst_state, cur_gst_state, pending_gst_state;
++
++              /* Only consider state change messages coming from
++               * the toplevel element. */
++              if (GST_MESSAGE_SRC(msg) != GST_OBJECT(dec->pipeline))
++                      break;
++
++              gst_message_parse_state_changed(msg, &old_gst_state, &cur_gst_state, &pending_gst_state);
++
++              printf(
++                      "GStreamer state change:  old: %s  current: %s  pending: %s\n",
++                      gst_element_state_get_name(old_gst_state),
++                      gst_element_state_get_name(cur_gst_state),
++                      gst_element_state_get_name(pending_gst_state)
++              );
++
++              dotfilename = g_strdup_printf(
++                      "statechange__old-%s__cur-%s__pending-%s",
++                      gst_element_state_get_name(old_gst_state),
++                      gst_element_state_get_name(cur_gst_state),
++                      gst_element_state_get_name(pending_gst_state)
++              );
++              GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(dec->pipeline), GST_DEBUG_GRAPH_SHOW_ALL, dotfilename);
++              g_free(dotfilename);
++
++              break;
++      }
++      case GST_MESSAGE_REQUEST_STATE: {
++              GstState requested_state;
++              gst_message_parse_request_state(msg, &requested_state);
++              printf(
++                      "state change to %s was requested by %s\n",
++                      gst_element_state_get_name(requested_state),
++                      GST_MESSAGE_SRC_NAME(msg)
++              );
++              gst_element_set_state(GST_ELEMENT(dec->pipeline), requested_state);
++              break;
++      }
++      case GST_MESSAGE_LATENCY: {
++              printf("redistributing latency\n");
++              gst_bin_recalculate_latency(GST_BIN(dec->pipeline));
++              break;
++      }
++      case GST_MESSAGE_INFO:
++      case GST_MESSAGE_WARNING:
++      case GST_MESSAGE_ERROR: {
++              GError *error = NULL;
++              gchar *debug_info = NULL;
++              gchar const *prefix;
++
++              switch (GST_MESSAGE_TYPE(msg)) {
++                      case GST_MESSAGE_INFO:
++                              gst_message_parse_info(msg, &error, &debug_info);
++                              prefix = "INFO";
++                              break;
++                      case GST_MESSAGE_WARNING:
++                              gst_message_parse_warning(msg, &error, &debug_info);
++                              prefix = "WARNING";
++                              break;
++                      case GST_MESSAGE_ERROR:
++                              gst_message_parse_error(msg, &error, &debug_info);
++                              prefix = "ERROR";
++                              break;
++                      default:
++                              g_assert_not_reached();
++              }
++              printf("GStreamer %s: %s; debug info: %s", prefix, error->message, debug_info);
++
++              g_clear_error(&error);
++              g_free(debug_info);
++
++              if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) {
++                      GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(dec->pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "error");
++              }
++
++              // TODO: stop mainloop in case of an error
++
++              break;
++      }
++      default:
++              break;
++      }
++
++      return TRUE;
++}
++
+ struct decoder *
+ video_init(const struct egl *egl, const struct gbm *gbm, const char *filename)
+ {
+       struct decoder *dec;
+       GstElement *src, *decodebin;
++      GstBus *bus;
+       dec = calloc(1, sizeof(*dec));
+       dec->loop = g_main_loop_new(NULL, FALSE);
+@@ -171,6 +268,12 @@ video_init(const struct egl *egl, const struct gbm *gbm, const char *filename)
+       decodebin = gst_bin_get_by_name(GST_BIN(dec->pipeline), "decode");
+       g_signal_connect(decodebin, "element-added", G_CALLBACK(element_added_cb), dec);
++      /* add bus to be able to receive error message, handle latency
++       * requests, produce pipeline dumps, etc. */
++      bus = gst_pipeline_get_bus(GST_PIPELINE(dec->pipeline));
++      gst_bus_add_watch(bus, bus_watch_cb, dec);
++      gst_object_unref(GST_OBJECT(bus));
++
+       /* let 'er rip! */
+       gst_element_set_state(dec->pipeline, GST_STATE_PLAYING);
+-- 
+2.7.4
+
diff --git a/recipes-graphics/kmscube/kmscube/0007-gst-decoder.c-minor-cleanup.patch b/recipes-graphics/kmscube/kmscube/0007-gst-decoder.c-minor-cleanup.patch
new file mode 100644 (file)
index 0000000..959ed18
--- /dev/null
@@ -0,0 +1,75 @@
+From eebd58fde0a5142afe0a871a3aa4e255faac9c18 Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Fri, 7 Apr 2017 23:09:29 +0200
+Subject: [PATCH kmscube 07/10] gst-decoder.c: minor cleanup
+
+Upstream-Status: Submitted [mesa-dev mailing list]
+
+Signed-off-by: Carlos Rafael Giani <dv@pseudoterminal.org>
+---
+ gst-decoder.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+diff --git a/gst-decoder.c b/gst-decoder.c
+index d1cb18c..768aa1b 100644
+--- a/gst-decoder.c
++++ b/gst-decoder.c
+@@ -42,6 +42,8 @@
+ GST_DEBUG_CATEGORY_EXTERN(kmscube_debug);
+ #define GST_CAT_DEFAULT kmscube_debug
++#define MAX_NUM_PLANES 3
++
+ struct decoder {
+       GMainLoop          *loop;
+       GstElement         *pipeline;
+@@ -83,7 +85,7 @@ pad_probe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
+               return GST_PAD_PROBE_OK;
+       }
+-      switch (dec->info.finfo->format) {
++      switch (GST_VIDEO_INFO_FORMAT(&(dec->info))) {
+       case GST_VIDEO_FORMAT_I420:
+               dec->format = DRM_FORMAT_YUV420;
+               break;
+@@ -264,7 +266,7 @@ video_init(const struct egl *egl, const struct gbm *gbm, const char *filename)
+                       GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+                       pad_probe, dec, NULL);
+-      /* hack to make sure we get dmabuf's from v4l2video0dec.. */
++      /* callback needed to make sure we get dmabuf's from v4l2videoNdec.. */
+       decodebin = gst_bin_get_by_name(GST_BIN(dec->pipeline), "decode");
+       g_signal_connect(decodebin, "element-added", G_CALLBACK(element_added_cb), dec);
+@@ -322,7 +324,7 @@ buf_to_fd(const struct gbm *gbm, int size, void *ptr)
+ static EGLImage
+ buffer_to_image(struct decoder *dec, GstBuffer *buf)
+ {
+-      struct { int fd, offset, stride; } planes[3];
++      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);
+@@ -412,6 +414,7 @@ buffer_to_image(struct decoder *dec, GstBuffer *buf)
+                               EGL_LINUX_DMA_BUF_EXT, NULL, attr);
+       }
++      /* Cleanup */
+       for (unsigned i = 0; i < nmems; i++)
+               close(planes[i].fd);
+@@ -426,8 +429,10 @@ video_frame(struct decoder *dec)
+       EGLImage   frame = NULL;
+       samp = gst_app_sink_pull_sample(GST_APP_SINK(dec->sink));
+-      if (!samp)
++      if (!samp) {
++              GST_DEBUG("got no appsink sample");
+               return NULL;
++      }
+       buf = gst_sample_get_buffer(samp);
+-- 
+2.7.4
+
diff --git a/recipes-graphics/kmscube/kmscube/0008-gst-decoder.c-use-a-custom-appsink-subclass-to-make-.patch b/recipes-graphics/kmscube/kmscube/0008-gst-decoder.c-use-a-custom-appsink-subclass-to-make-.patch
new file mode 100644 (file)
index 0000000..d4769d8
--- /dev/null
@@ -0,0 +1,254 @@
+From a5b27790efb40775209d0e079d4bf021ef1c953c Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Fri, 7 Apr 2017 22:55:58 +0200
+Subject: [PATCH kmscube 08/10] gst-decoder.c: use a custom appsink subclass to
+ make sure videometa exists
+
+Upstream-Status: Submitted [mesa-dev mailing list]
+
+Signed-off-by: Carlos Rafael Giani <dv@pseudoterminal.org>
+---
+ Makefile.am         |   2 +-
+ gst-decoder.c       |  20 ++++++++-
+ gst-video-appsink.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ gst-video-appsink.h |  31 +++++++++++++
+ kmscube.c           |   2 +
+ 5 files changed, 178 insertions(+), 3 deletions(-)
+ create mode 100644 gst-video-appsink.c
+ create mode 100644 gst-video-appsink.h
+
+diff --git a/Makefile.am b/Makefile.am
+index a36087d..0407e89 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -57,5 +57,5 @@ kmscube_SOURCES = \
+ if ENABLE_GST
+ kmscube_LDADD += $(GST_LIBS)
+ kmscube_CFLAGS += $(GST_CFLAGS)
+-kmscube_SOURCES += cube-video.c gst-decoder.c
++kmscube_SOURCES += cube-video.c gst-decoder.c gst-video-appsink.c
+ endif
+diff --git a/gst-decoder.c b/gst-decoder.c
+index 768aa1b..188fe4b 100644
+--- a/gst-decoder.c
++++ b/gst-decoder.c
+@@ -246,9 +246,25 @@ video_init(const struct egl *egl, const struct gbm *gbm, const char *filename)
+       dec->gbm = gbm;
+       dec->egl = egl;
+-      /* Setup pipeline: */
++      /* Setup pipeline. Note that we use video_appsink here, not appsink.
++       * video_appsink is an appsink subclass that adds the videometa to
++       * the allocation query's list of metas supported by the sink.
++       *
++       * In some cases, sinks that do not declare videometa as supported
++       * can trigger unexpected behavior when used with video decoders.
++       * For example, with the v4l2videoNdec decoder, if the sink did not
++       * declare videometa as supported, and if the decoder detects that
++       * video frames have padding rows/columns (for example, a 1920x1088
++       * frame whose last 8 rows are padding rows), then it will perform
++       * an internal CPU-based copy that does not have these padding pixels.
++       *
++       * To avoid such CPU-based copies, make sure the sink declares
++       * videometa as supported. This has the side benefit that the code
++       * inside buffer_to_image() can also make use of the videmeta
++       * information.
++       */
+       static const char *pipeline =
+-              "filesrc name=\"src\" ! decodebin name=\"decode\" ! video/x-raw ! appsink sync=false name=\"sink\"";
++              "filesrc name=\"src\" ! decodebin name=\"decode\" ! video/x-raw ! video_appsink name=\"sink\"";
+       dec->pipeline = gst_parse_launch(pipeline, NULL);
+       dec->sink = gst_bin_get_by_name(GST_BIN(dec->pipeline), "sink");
+diff --git a/gst-video-appsink.c b/gst-video-appsink.c
+new file mode 100644
+index 0000000..2c5c15b
+--- /dev/null
++++ b/gst-video-appsink.c
+@@ -0,0 +1,126 @@
++/*
++ * Copyright (c) 2017 Carlos Rafael Giani <dv@pseudoterminal.org>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sub license,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include <gst/gst.h>
++#include <gst/app/app.h>
++#include <gst/video/video.h>
++#include "gst-video-appsink.h"
++
++
++typedef struct _GstVideoAppsink GstVideoAppsink;
++typedef struct _GstVideoAppsinkClass GstVideoAppsinkClass;
++
++
++#define GST_TYPE_VIDEO_APPSINK             (gst_video_appsink_get_type())
++#define GST_VIDEO_APPSINK(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_VIDEO_APPSINK,GstVideoAppsink))
++#define GST_VIDEO_APPSINK_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_VIDEO_APPSINK, GstVideoAppsinkClass))
++#define GST_IS_VIDEO_APPSINK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_VIDEO_APPSINK))
++#define GST_IS_VIDEO_APPSINK_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VIDEO_APPSINK))
++
++
++struct _GstVideoAppsink
++{
++      GstAppSink parent;
++};
++
++
++struct _GstVideoAppsinkClass
++{
++      GstAppSinkClass parent_class;
++};
++
++
++GType gst_video_appsink_get_type(void);
++
++
++static gboolean gst_video_appsink_sink_propose_allocation (GstBaseSink *bsink, GstQuery *query);
++
++
++G_DEFINE_TYPE(GstVideoAppsink, gst_video_appsink, GST_TYPE_APP_SINK);
++
++
++static void
++gst_video_appsink_class_init(GstVideoAppsinkClass *klass)
++{
++      GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
++      GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS(klass);
++
++      base_sink_class->propose_allocation =
++              GST_DEBUG_FUNCPTR(gst_video_appsink_sink_propose_allocation);
++
++      gst_element_class_set_static_metadata(
++              element_class,
++              "video appsink",
++              "Video/Sink",
++              "Appsink subclass for video streams (adds video meta)",
++              "Carlos Rafael Giani <dv@pseudoterminal.org>"
++      );
++}
++
++
++static void
++gst_video_appsink_init(GstVideoAppsink *video_appsink)
++{
++      /* QoS and max-lateness lines taken from gstvideosink.c */
++      gst_base_sink_set_max_lateness(GST_BASE_SINK(video_appsink), 20 * GST_MSECOND);
++      gst_base_sink_set_qos_enabled(GST_BASE_SINK(video_appsink), TRUE);
++}
++
++
++static gboolean
++gst_video_appsink_sink_propose_allocation (GstBaseSink *bsink, GstQuery *query)
++{
++      (void)bsink;
++
++      gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL);
++
++      return TRUE;
++}
++
++
++
++
++static gboolean
++init_internal_video_appsink_plugin(GstPlugin *plugin)
++{
++      gst_element_register(plugin, "video_appsink", GST_RANK_NONE, gst_video_appsink_get_type());
++      return TRUE;
++}
++
++
++void
++register_gst_video_appsink(void)
++{
++      gst_plugin_register_static(
++              GST_VERSION_MAJOR,
++              GST_VERSION_MINOR,
++              "internal video appsink plugin",
++              "internal video appsink plugin",
++              init_internal_video_appsink_plugin,
++              "1.0",
++              "BSD",
++              "kmscube",
++              "kmscube",
++              "kmscube"
++      );
++}
+diff --git a/gst-video-appsink.h b/gst-video-appsink.h
+new file mode 100644
+index 0000000..d47484a
+--- /dev/null
++++ b/gst-video-appsink.h
+@@ -0,0 +1,31 @@
++/*
++ * Copyright (c) 2017 Carlos Rafael Giani <dv@pseudoterminal.org>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sub license,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef _GST_VIDEO_APPSINK_H
++#define _GST_VIDEO_APPSINK_H
++
++
++void register_gst_video_appsink(void);
++
++
++#endif /* _GST_VIDEO_APPSINK_H */
+diff --git a/kmscube.c b/kmscube.c
+index 8e2d008..61509ef 100644
+--- a/kmscube.c
++++ b/kmscube.c
+@@ -33,6 +33,7 @@
+ #ifdef HAVE_GST
+ #include <gst/gst.h>
++#include "gst-video-appsink.h"
+ GST_DEBUG_CATEGORY(kmscube_debug);
+ #endif
+@@ -79,6 +80,7 @@ int main(int argc, char *argv[])
+ #ifdef HAVE_GST
+       gst_init(&argc, &argv);
+       GST_DEBUG_CATEGORY_INIT(kmscube_debug, "kmscube", 0, "kmscube video pipeline");
++      register_gst_video_appsink();
+ #endif
+       while ((opt = getopt_long_only(argc, argv, shortopts, longopts, NULL)) != -1) {
+-- 
+2.7.4
+
diff --git a/recipes-graphics/kmscube/kmscube/0009-gst-decoder.c-Improve-synchronicity-of-video-output.patch b/recipes-graphics/kmscube/kmscube/0009-gst-decoder.c-Improve-synchronicity-of-video-output.patch
new file mode 100644 (file)
index 0000000..cd6fa88
--- /dev/null
@@ -0,0 +1,50 @@
+From 82b273fae95ef5c501eb9d81d62ce8d1db49eede Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Fri, 7 Apr 2017 22:57:42 +0200
+Subject: [PATCH kmscube 09/10] gst-decoder.c: Improve synchronicity of video
+ output
+
+sync=false causes a sink to output data as fast as it can. This explains
+why without max-buffers=2 the pipeline used to use up 100's of MB of
+buffers.
+
+Instead, set sync to true, and enable frame dropping, to make sure the
+video playback speed remains intact.
+
+Upstream-Status: Submitted [mesa-dev mailing list]
+
+Signed-off-by: Carlos Rafael Giani <dv@pseudoterminal.org>
+---
+ gst-decoder.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/gst-decoder.c b/gst-decoder.c
+index 188fe4b..67efbfd 100644
+--- a/gst-decoder.c
++++ b/gst-decoder.c
+@@ -273,10 +273,19 @@ video_init(const struct egl *egl, const struct gbm *gbm, const char *filename)
+       g_object_set(G_OBJECT(src), "location", filename, NULL);
+       gst_object_unref(src);
+-      /* if we don't limit max-buffers then we can let the decoder outrun
+-       * vsync and quickly chew up 100's of MB of buffers:
++      /* If frames cannot be displayed on time, drop them. This makes
++       * sure playback speed remains intact even if the EGL display
++       * intervals don't match the pipeline's decoding intervals.
++       * sync=TRUE makes sure the sink blocks the pipeline until the
++       * video frame is scheduled to be output (based on its PTS).
++       * This also avoids excessive CPU usage, since the blocking
++       * wait puts the streaming thread to sleep until the video
++       * frame is due.
++       * max-buffers=1 makes sure only one frame is queued (more are
++       * not necessary).
+        */
+-      g_object_set(G_OBJECT(dec->sink), "max-buffers", 2, NULL);
++      g_object_set(G_OBJECT(dec->sink), "drop", (gboolean)TRUE, "max-buffers", 1,
++              "sync", (gboolean)TRUE, NULL);
+       gst_pad_add_probe(gst_element_get_static_pad(dec->sink, "sink"),
+                       GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+-- 
+2.7.4
+
diff --git a/recipes-graphics/kmscube/kmscube/0010-gst-decoder.c-improve-buffer_to_image-function.patch b/recipes-graphics/kmscube/kmscube/0010-gst-decoder.c-improve-buffer_to_image-function.patch
new file mode 100644 (file)
index 0000000..7d006e3
--- /dev/null
@@ -0,0 +1,259 @@
+From f8c5cf72af8add2e0db6d3f73443f1adcff28e4a Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Fri, 7 Apr 2017 22:58:11 +0200
+Subject: [PATCH kmscube 10/10] gst-decoder.c: improve buffer_to_image()
+ function
+
+* 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
+
+Upstream-Status: Submitted [mesa-dev mailing list]
+
+Signed-off-by: Carlos Rafael Giani <dv@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 <rclark@redhat.com>
++ * Copyright (c) 2017 Carlos Rafael Giani <dv@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
+
diff --git a/recipes-graphics/kmscube/kmscube/use-dri-card1-by-default.patch b/recipes-graphics/kmscube/kmscube/use-dri-card1-by-default.patch
new file mode 100644 (file)
index 0000000..4844ff7
--- /dev/null
@@ -0,0 +1,18 @@
+On i.MX devices, etnaviv is located at /dev/dri/card1 . For convenience, make
+this the default instead of card0.
+
+Upstream-Status: Inappropriate [i.MX specific]
+
+diff --git a/kmscube.c b/kmscube.c
+index 61509ef..9cc05d7 100644
+--- a/kmscube.c
++++ b/kmscube.c
+@@ -71,7 +71,7 @@ static void usage(const char *name)
+ int main(int argc, char *argv[])
+ {
+-      const char *device = "/dev/dri/card0";
++      const char *device = "/dev/dri/card1";
+       const char *video = NULL;
+       enum mode mode = SMOOTH;
+       int atomic = 0;
diff --git a/recipes-graphics/kmscube/kmscube_%.bbappend b/recipes-graphics/kmscube/kmscube_%.bbappend
new file mode 100644 (file)
index 0000000..2152690
--- /dev/null
@@ -0,0 +1,14 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+
+SRC_URI_append = " file://0001-output-more-and-improved-information-about-EGL-and-O.patch \
+                   file://0002-add-kmscube-GStreamer-debug-category.patch \
+                   file://0003-gst-decoder.c-add-support-for-YUY2-pixel-format.patch \
+                   file://0004-gst-decoder.c-look-at-the-caps-event-instead-of-the-.patch \
+                   file://0005-gst-decoder.c-Use-element-factory-name-to-detect-V4L.patch \
+                   file://0006-gst-decoder.c-add-bus-watch.patch \
+                   file://0007-gst-decoder.c-minor-cleanup.patch \
+                   file://0008-gst-decoder.c-use-a-custom-appsink-subclass-to-make-.patch \
+                   file://0009-gst-decoder.c-Improve-synchronicity-of-video-output.patch \
+                   file://0010-gst-decoder.c-improve-buffer_to_image-function.patch \
+                   file://use-dri-card1-by-default.patch \
+"