[meta-xilinx] [PATCH 2/2] gstreamer1.0-plugins-bad related bbapend file and patches.

Dhaval Shah dhaval.shah at xilinx.com
Thu Feb 2 19:46:27 PST 2017


All the patches from the 2016.4 maintained earlier as locally.
Now, ported to the 2017.1 and related bbapend file is also added.

Signed-off-by: Dhaval Shah <dshah at xilinx.com>
---
 ...1-gst-plugins-bad-Copy-kmssink-from-1.9.2.patch | 2549 ++++++++++++++++++++
 .../0002-Compile-kms.patch                         |   79 +
 ...03-gst-kmssink-Add-support-for-xilinx-drm.patch |   30 +
 ...sink-override-stride-if-defined-in-driver.patch |   53 +
 ...05-kmssink-Fix-selection-of-source-region.patch |   87 +
 ...-kmssink-Scale-up-to-the-screen-dimension.patch |   30 +
 .../0007-kmssink-experimentation.patch             |   88 +
 .../gstreamer/gstreamer1.0-plugins-bad_%.bbappend  |   18 +
 8 files changed, 2934 insertions(+)
 create mode 100644 recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-gst-plugins-bad-Copy-kmssink-from-1.9.2.patch
 create mode 100644 recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0002-Compile-kms.patch
 create mode 100644 recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0003-gst-kmssink-Add-support-for-xilinx-drm.patch
 create mode 100644 recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-kmssink-override-stride-if-defined-in-driver.patch
 create mode 100644 recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0005-kmssink-Fix-selection-of-source-region.patch
 create mode 100644 recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0006-kmssink-Scale-up-to-the-screen-dimension.patch
 create mode 100644 recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0007-kmssink-experimentation.patch
 create mode 100644 recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_%.bbappend

diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-gst-plugins-bad-Copy-kmssink-from-1.9.2.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-gst-plugins-bad-Copy-kmssink-from-1.9.2.patch
new file mode 100644
index 0000000..92b0d85
--- /dev/null
+++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0001-gst-plugins-bad-Copy-kmssink-from-1.9.2.patch
@@ -0,0 +1,2549 @@
+From 9950865f1ecd7a038279aed0507a5f0a56d70f21 Mon Sep 17 00:00:00 2001
+From: Devarsh Thakkar <devarsht at xilinx.com>
+Date: Thu, 8 Sep 2016 15:36:18 +0530
+Subject: [PATCH 1/4] gst-plugins-bad: Copy kmssink from 1.9.2
+
+This copies kmssink support from gst-bad-plugin
+1.9.2.
+
+Signed-off-by: Devarsh Thakkar <devarsht at xilinx.com>
+---
+ sys/kms/Makefile.am        |   41 ++
+ sys/kms/gstkmsallocator.c  |  431 +++++++++++++++
+ sys/kms/gstkmsallocator.h  |   91 ++++
+ sys/kms/gstkmsbufferpool.c |  213 ++++++++
+ sys/kms/gstkmsbufferpool.h |   75 +++
+ sys/kms/gstkmssink.c       | 1300 ++++++++++++++++++++++++++++++++++++++++++++
+ sys/kms/gstkmssink.h       |   87 +++
+ sys/kms/gstkmsutils.c      |  179 ++++++
+ sys/kms/gstkmsutils.h      |   45 ++
+ 9 files changed, 2462 insertions(+)
+ create mode 100644 sys/kms/Makefile.am
+ create mode 100644 sys/kms/gstkmsallocator.c
+ create mode 100644 sys/kms/gstkmsallocator.h
+ create mode 100644 sys/kms/gstkmsbufferpool.c
+ create mode 100644 sys/kms/gstkmsbufferpool.h
+ create mode 100644 sys/kms/gstkmssink.c
+ create mode 100644 sys/kms/gstkmssink.h
+ create mode 100644 sys/kms/gstkmsutils.c
+ create mode 100644 sys/kms/gstkmsutils.h
+
+diff --git a/sys/kms/Makefile.am b/sys/kms/Makefile.am
+new file mode 100644
+index 0000000..9b12c72
+--- /dev/null
++++ b/sys/kms/Makefile.am
+@@ -0,0 +1,41 @@
++plugin_LTLIBRARIES = libgstkmssink.la
++
++libgstkmssink_la_SOURCES = 			\
++	gstkmssink.c				\
++	gstkmsutils.c				\
++	gstkmsallocator.c			\
++	gstkmsbufferpool.c			\
++	$(NUL)
++
++libgstkmssink_la_CFLAGS = 			\
++	$(GST_PLUGINS_BASE_CFLAGS) 		\
++	$(GST_BASE_CFLAGS) 			\
++	$(GST_VIDEO_CFLAGS)			\
++	$(GST_ALLOCATORS_CFLAGS)		\
++	$(GST_CFLAGS) 				\
++	$(DRM_CFLAGS) 				\
++	$(NULL)
++
++libgstkmssink_la_LIBADD = 			\
++	$(GST_PLUGINS_BASE_LIBS) 		\
++	$(GST_BASE_LIBS) 			\
++	$(GST_VIDEO_LIBS)			\
++	$(GST_ALLOCATORS_LIBS)			\
++	$(GST_LIBS) 				\
++	$(DRM_LIBS) 				\
++	$(NULL)
++
++libgstkmssink_la_LDFLAGS = 			\
++	$(GST_PLUGIN_LDFLAGS)			\
++	$(NULL)
++
++libgstkmssink_la_LIBTOOLFLAGS = 		\
++	$(GST_PLUGIN_LIBTOOLFLAGS) 		\
++	$(NULL)
++
++noinst_HEADERS = 				\
++	gstkmssink.h 				\
++	gstkmsutils.h				\
++	gstkmsallocator.h			\
++	gstkmsbufferpool.h			\
++	$(NULL)
+diff --git a/sys/kms/gstkmsallocator.c b/sys/kms/gstkmsallocator.c
+new file mode 100644
+index 0000000..7df1aa3
+--- /dev/null
++++ b/sys/kms/gstkmsallocator.c
+@@ -0,0 +1,431 @@
++/* GStreamer
++ *
++ * Copyright (C) 2016 Igalia
++ *
++ * Authors:
++ *  Víctor Manuel Jáquez Leal <vjaquez at igalia.com>
++ *  Javier Martin <javiermartin at by.com.es>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ *
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <xf86drm.h>
++#include <xf86drmMode.h>
++#include <string.h>
++#include <unistd.h>
++
++#include "gstkmsallocator.h"
++#include "gstkmsutils.h"
++
++#define GST_CAT_DEFAULT kmsallocator_debug
++GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
++
++#define GST_KMS_MEMORY_TYPE "KMSMemory"
++
++struct _GstKMSAllocatorPrivate
++{
++  int fd;
++  struct kms_driver *driver;
++};
++
++#define parent_class gst_kms_allocator_parent_class
++G_DEFINE_TYPE_WITH_CODE (GstKMSAllocator, gst_kms_allocator, GST_TYPE_ALLOCATOR,
++    G_ADD_PRIVATE (GstKMSAllocator);
++    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "kmsallocator", 0,
++        "KMS allocator"));
++
++enum
++{
++  PROP_DRM_FD = 1,
++  PROP_N,
++};
++
++static GParamSpec *g_props[PROP_N] = { NULL, };
++
++gboolean
++gst_is_kms_memory (GstMemory * mem)
++{
++  return gst_memory_is_type (mem, GST_KMS_MEMORY_TYPE);
++}
++
++guint32
++gst_kms_memory_get_fb_id (GstMemory * mem)
++{
++  if (!gst_is_kms_memory (mem))
++    return 0;
++  return ((GstKMSMemory *) mem)->fb_id;
++}
++
++static gboolean
++ensure_kms_driver (GstKMSAllocator * alloc)
++{
++  GstKMSAllocatorPrivate *priv;
++  int err;
++
++  priv = alloc->priv;
++
++  if (priv->driver)
++    return TRUE;
++
++  if (priv->fd < 0)
++    return FALSE;
++
++  err = kms_create (priv->fd, &priv->driver);
++  if (err) {
++    GST_ERROR_OBJECT (alloc, "Could not create KMS driver: %s",
++        strerror (-err));
++    return FALSE;
++  }
++
++  return TRUE;
++}
++
++static void
++gst_kms_allocator_memory_reset (GstKMSAllocator * allocator, GstKMSMemory * mem)
++{
++  if (mem->fb_id) {
++    GST_DEBUG_OBJECT (allocator, "removing fb id %d", mem->fb_id);
++    drmModeRmFB (allocator->priv->fd, mem->fb_id);
++    mem->fb_id = 0;
++  }
++
++  if (!ensure_kms_driver (allocator))
++    return;
++
++  if (mem->bo) {
++    kms_bo_destroy (&mem->bo);
++    mem->bo = NULL;
++  }
++}
++
++static gboolean
++gst_kms_allocator_memory_create (GstKMSAllocator * allocator,
++    GstKMSMemory * kmsmem, GstVideoInfo * vinfo)
++{
++  gint ret;
++  guint attrs[] = {
++    KMS_WIDTH, GST_VIDEO_INFO_WIDTH (vinfo),
++    KMS_HEIGHT, GST_VIDEO_INFO_HEIGHT (vinfo),
++    KMS_TERMINATE_PROP_LIST,
++  };
++
++  if (kmsmem->bo)
++    return TRUE;
++
++  if (!ensure_kms_driver (allocator))
++    return FALSE;
++
++  ret = kms_bo_create (allocator->priv->driver, attrs, &kmsmem->bo);
++  if (ret) {
++    GST_ERROR_OBJECT (allocator, "Failed to create buffer object: %s (%d)",
++        strerror (-ret), ret);
++    return FALSE;
++  }
++
++  return TRUE;
++}
++
++static void
++gst_kms_allocator_free (GstAllocator * allocator, GstMemory * mem)
++{
++  GstKMSAllocator *alloc;
++  GstKMSMemory *kmsmem;
++
++  alloc = GST_KMS_ALLOCATOR (allocator);
++  kmsmem = (GstKMSMemory *) mem;
++
++  gst_kms_allocator_memory_reset (alloc, kmsmem);
++  g_slice_free (GstKMSMemory, kmsmem);
++}
++
++static void
++gst_kms_allocator_set_property (GObject * object, guint prop_id,
++    const GValue * value, GParamSpec * pspec)
++{
++  GstKMSAllocator *alloc;
++
++  alloc = GST_KMS_ALLOCATOR (object);
++
++  switch (prop_id) {
++    case PROP_DRM_FD:{
++      int fd = g_value_get_int (value);
++      if (fd > -1)
++        alloc->priv->fd = dup (fd);
++      break;
++    }
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++  }
++}
++
++static void
++gst_kms_allocator_get_property (GObject * object, guint prop_id,
++    GValue * value, GParamSpec * pspec)
++{
++  GstKMSAllocator *alloc;
++
++  alloc = GST_KMS_ALLOCATOR (object);
++
++  switch (prop_id) {
++    case PROP_DRM_FD:
++      g_value_set_int (value, alloc->priv->fd);
++      break;
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++  }
++}
++
++static void
++gst_kms_allocator_finalize (GObject * obj)
++{
++  GstKMSAllocator *alloc;
++
++  alloc = GST_KMS_ALLOCATOR (obj);
++
++  if (alloc->priv->driver)
++    kms_destroy (&alloc->priv->driver);
++
++  if (alloc->priv->fd > -1)
++    close (alloc->priv->fd);
++
++  G_OBJECT_CLASS (parent_class)->finalize (obj);
++}
++
++static void
++gst_kms_allocator_class_init (GstKMSAllocatorClass * klass)
++{
++  GObjectClass *gobject_class;
++  GstAllocatorClass *allocator_class;
++
++  allocator_class = GST_ALLOCATOR_CLASS (klass);
++  gobject_class = G_OBJECT_CLASS (klass);
++
++  allocator_class->free = gst_kms_allocator_free;
++
++  gobject_class->set_property = gst_kms_allocator_set_property;
++  gobject_class->get_property = gst_kms_allocator_get_property;
++  gobject_class->finalize = gst_kms_allocator_finalize;
++
++  g_props[PROP_DRM_FD] = g_param_spec_int ("drm-fd", "DRM fd",
++      "DRM file descriptor", -1, G_MAXINT, -1,
++      G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
++
++  g_object_class_install_properties (gobject_class, PROP_N, g_props);
++}
++
++static gpointer
++gst_kms_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
++{
++  GstKMSMemory *kmsmem;
++  int err;
++  gpointer out;
++
++  if (!ensure_kms_driver ((GstKMSAllocator *) mem->allocator))
++    return NULL;
++
++  kmsmem = (GstKMSMemory *) mem;
++  if (!kmsmem->bo)
++    return NULL;
++
++  out = NULL;
++  err = kms_bo_map (kmsmem->bo, &out);
++  if (err) {
++    GST_ERROR ("could not map memory: %s %d", strerror (-err), err);
++    return NULL;
++  }
++
++  return out;
++}
++
++static void
++gst_kms_memory_unmap (GstMemory * mem)
++{
++  GstKMSMemory *kmsmem;
++
++  if (!ensure_kms_driver ((GstKMSAllocator *) mem->allocator))
++    return;
++
++  kmsmem = (GstKMSMemory *) mem;
++  if (kmsmem->bo)
++    kms_bo_unmap (kmsmem->bo);
++}
++
++static void
++gst_kms_allocator_init (GstKMSAllocator * allocator)
++{
++  GstAllocator *alloc;
++
++  alloc = GST_ALLOCATOR_CAST (allocator);
++
++  allocator->priv = gst_kms_allocator_get_instance_private (allocator);
++  allocator->priv->fd = -1;
++
++  alloc->mem_type = GST_KMS_MEMORY_TYPE;
++  alloc->mem_map = gst_kms_memory_map;
++  alloc->mem_unmap = gst_kms_memory_unmap;
++  /* Use the default, fallback copy function */
++
++  GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
++}
++
++GstAllocator *
++gst_kms_allocator_new (int fd)
++{
++  return g_object_new (GST_TYPE_KMS_ALLOCATOR, "name",
++      "KMSMemory::allocator", "drm-fd", fd, NULL);
++}
++
++/* The mem_offsets are relative to the GstMemory start, unlike the vinfo->offset
++ * which are relative to the GstBuffer start. */
++static gboolean
++gst_kms_allocator_add_fb (GstKMSAllocator * alloc, GstKMSMemory * kmsmem,
++    gsize mem_offsets[GST_VIDEO_MAX_PLANES], GstVideoInfo * vinfo)
++{
++  int i, ret;
++  gint num_planes = GST_VIDEO_INFO_N_PLANES (vinfo);
++  guint32 w, h, fmt, bo_handles[4] = { 0, };
++  guint32 offsets[4] = { 0, };
++  guint32 pitches[4] = { 0, };
++
++  if (kmsmem->fb_id)
++    return TRUE;
++
++  w = GST_VIDEO_INFO_WIDTH (vinfo);
++  h = GST_VIDEO_INFO_HEIGHT (vinfo);
++  fmt = gst_drm_format_from_video (GST_VIDEO_INFO_FORMAT (vinfo));
++
++  if (kmsmem->bo) {
++    kms_bo_get_prop (kmsmem->bo, KMS_HANDLE, &bo_handles[0]);
++    for (i = 1; i < num_planes; i++)
++      bo_handles[i] = bo_handles[0];
++  } else {
++    for (i = 0; i < num_planes; i++)
++      bo_handles[i] = kmsmem->gem_handle[i];
++  }
++
++  GST_DEBUG_OBJECT (alloc, "bo handles: %d, %d, %d, %d", bo_handles[0],
++      bo_handles[1], bo_handles[2], bo_handles[3]);
++
++  for (i = 0; i < num_planes; i++) {
++    offsets[i] = mem_offsets[i];
++    pitches[i] = GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i);
++    GST_DEBUG_OBJECT (alloc, "Create FB plane %i with stride %u and offset %u",
++        i, pitches[i], offsets[i]);
++  }
++
++  ret = drmModeAddFB2 (alloc->priv->fd, w, h, fmt, bo_handles, pitches,
++      offsets, &kmsmem->fb_id, 0);
++  if (ret) {
++    GST_ERROR_OBJECT (alloc, "Failed to bind to framebuffer: %s (%d)",
++        strerror (-ret), ret);
++    return FALSE;
++  }
++  return TRUE;
++}
++
++static GstMemory *
++gst_kms_allocator_alloc_empty (GstAllocator * allocator, GstVideoInfo * vinfo)
++{
++  GstKMSMemory *kmsmem;
++  GstMemory *mem;
++
++  kmsmem = g_slice_new0 (GstKMSMemory);
++  if (!kmsmem)
++    return NULL;
++  mem = GST_MEMORY_CAST (kmsmem);
++
++  gst_memory_init (mem, GST_MEMORY_FLAG_NO_SHARE, allocator, NULL,
++      GST_VIDEO_INFO_SIZE (vinfo), 0, 0, GST_VIDEO_INFO_SIZE (vinfo));
++
++  return mem;
++}
++
++GstMemory *
++gst_kms_allocator_bo_alloc (GstAllocator * allocator, GstVideoInfo * vinfo)
++{
++  GstKMSAllocator *alloc;
++  GstKMSMemory *kmsmem;
++  GstMemory *mem;
++
++  mem = gst_kms_allocator_alloc_empty (allocator, vinfo);
++  if (!mem)
++    return NULL;
++
++  alloc = GST_KMS_ALLOCATOR (allocator);
++  kmsmem = (GstKMSMemory *) mem;
++  if (!gst_kms_allocator_memory_create (alloc, kmsmem, vinfo))
++    goto fail;
++  if (!gst_kms_allocator_add_fb (alloc, kmsmem, vinfo->offset, vinfo))
++    goto fail;
++
++  return mem;
++
++  /* ERRORS */
++fail:
++  gst_memory_unref (mem);
++  return NULL;
++}
++
++GstKMSMemory *
++gst_kms_allocator_dmabuf_import (GstAllocator * allocator, gint * prime_fds,
++    gint n_planes, gsize offsets[GST_VIDEO_MAX_PLANES], GstVideoInfo * vinfo)
++{
++  GstKMSAllocator *alloc;
++  GstMemory *mem;
++  GstKMSMemory *tmp;
++  gint i, ret;
++
++  g_return_val_if_fail (n_planes <= GST_VIDEO_MAX_PLANES, FALSE);
++
++  mem = gst_kms_allocator_alloc_empty (allocator, vinfo);
++  if (!mem)
++    return FALSE;
++
++  tmp = (GstKMSMemory *) mem;
++  alloc = GST_KMS_ALLOCATOR (allocator);
++  for (i = 0; i < n_planes; i++) {
++    ret = drmPrimeFDToHandle (alloc->priv->fd, prime_fds[i],
++        &tmp->gem_handle[i]);
++    if (ret)
++      goto import_fd_failed;
++  }
++
++  if (!gst_kms_allocator_add_fb (alloc, tmp, offsets, vinfo))
++    goto failed;
++
++  return tmp;
++
++  /* ERRORS */
++import_fd_failed:
++  {
++    GST_ERROR_OBJECT (alloc, "Failed to import prime fd %d: %s (%d)",
++        prime_fds[i], strerror (-ret), ret);
++    /* fallback */
++  }
++
++failed:
++  {
++    gst_memory_unref (mem);
++    return NULL;
++  }
++}
+diff --git a/sys/kms/gstkmsallocator.h b/sys/kms/gstkmsallocator.h
+new file mode 100644
+index 0000000..fefd4c2
+--- /dev/null
++++ b/sys/kms/gstkmsallocator.h
+@@ -0,0 +1,91 @@
++/* GStreamer
++ *
++ * Copyright (C) 2016 Igalia
++ *
++ * Authors:
++ *  Víctor Manuel Jáquez Leal <vjaquez at igalia.com>
++ *  Javier Martin <javiermartin at by.com.es>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ *
++ */
++
++#ifndef __GST_KMS_ALLOCATOR_H__
++#define __GST_KMS_ALLOCATOR_H__
++
++#include <gst/gst.h>
++#include <gst/video/video.h>
++#include <libkms.h>
++
++G_BEGIN_DECLS
++
++#define GST_TYPE_KMS_ALLOCATOR	\
++   (gst_kms_allocator_get_type())
++#define GST_IS_KMS_ALLOCATOR(obj)				\
++   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_KMS_ALLOCATOR))
++#define GST_IS_KMS_ALLOCATOR_CLASS(klass)			\
++   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_KMS_ALLOCATOR))
++#define GST_KMS_ALLOCATOR_GET_CLASS(obj)			\
++   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_KMS_ALLOCATOR, GstKMSAllocatorClass))
++#define GST_KMS_ALLOCATOR(obj)				\
++   (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_KMS_ALLOCATOR, GstKMSAllocator))
++#define GST_KMS_ALLOCATOR_CLASS(klass)			\
++   (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_KMS_ALLOCATOR, GstKMSAllocatorClass))
++
++typedef struct _GstKMSAllocator GstKMSAllocator;
++typedef struct _GstKMSAllocatorClass GstKMSAllocatorClass;
++typedef struct _GstKMSAllocatorPrivate GstKMSAllocatorPrivate;
++typedef struct _GstKMSMemory GstKMSMemory;
++
++struct _GstKMSMemory
++{
++  GstMemory parent;
++
++  guint32 fb_id;
++  guint32 gem_handle[GST_VIDEO_MAX_PLANES];
++  struct kms_bo *bo;
++};
++
++struct _GstKMSAllocator
++{
++  GstAllocator parent;
++  GstKMSAllocatorPrivate *priv;
++};
++
++struct _GstKMSAllocatorClass {
++  GstAllocatorClass parent_class;
++};
++
++GType gst_kms_allocator_get_type (void) G_GNUC_CONST;
++
++gboolean gst_is_kms_memory (GstMemory *mem);
++guint32 gst_kms_memory_get_fb_id (GstMemory *mem);
++
++GstAllocator* gst_kms_allocator_new (gint fd);
++
++GstMemory*    gst_kms_allocator_bo_alloc (GstAllocator *allocator,
++					  GstVideoInfo *vinfo);
++
++GstKMSMemory* gst_kms_allocator_dmabuf_import (GstAllocator *allocator,
++					       gint *prime_fds,
++					       gint n_planes,
++					       gsize offsets[GST_VIDEO_MAX_PLANES],
++					       GstVideoInfo *vinfo);
++
++G_END_DECLS
++
++
++#endif /* __GST_KMS_ALLOCATOR_H__ */
+diff --git a/sys/kms/gstkmsbufferpool.c b/sys/kms/gstkmsbufferpool.c
+new file mode 100644
+index 0000000..329135c
+--- /dev/null
++++ b/sys/kms/gstkmsbufferpool.c
+@@ -0,0 +1,213 @@
++/*
++ * GStreamer
++ * Copyright (C) 2016 Igalia
++ *
++ * Authors:
++ *  Víctor Manuel Jáquez Leal <vjaquez at igalia.com>
++ *  Javier Martin <javiermartin at by.com.es>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ *
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <gst/video/gstvideometa.h>
++
++#include "gstkmsbufferpool.h"
++#include "gstkmsallocator.h"
++
++GST_DEBUG_CATEGORY_STATIC (gst_kms_buffer_pool_debug);
++#define GST_CAT_DEFAULT gst_kms_buffer_pool_debug
++
++struct _GstKMSBufferPoolPrivate
++{
++  gint fd;
++  GstVideoInfo vinfo;
++  GstAllocator *allocator;
++  gboolean add_videometa;
++};
++
++#define parent_class gst_kms_buffer_pool_parent_class
++G_DEFINE_TYPE_WITH_CODE (GstKMSBufferPool, gst_kms_buffer_pool,
++    GST_TYPE_VIDEO_BUFFER_POOL, G_ADD_PRIVATE (GstKMSBufferPool);
++    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "kmsbufferpool", 0,
++        "KMS buffer pool"));
++
++static const gchar **
++gst_kms_buffer_pool_get_options (GstBufferPool * pool)
++{
++  static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
++    GST_BUFFER_POOL_OPTION_KMS_BUFFER, NULL
++  };
++  return options;
++}
++
++static gboolean
++gst_kms_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
++{
++  GstKMSBufferPool *vpool;
++  GstKMSBufferPoolPrivate *priv;
++  GstCaps *caps;
++  GstVideoInfo vinfo;
++  GstAllocator *allocator;
++  GstAllocationParams params;
++
++  vpool = GST_KMS_BUFFER_POOL_CAST (pool);
++  priv = vpool->priv;
++
++  if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
++    goto wrong_config;
++
++  if (!caps)
++    goto no_caps;
++
++  /* now parse the caps from the config */
++  if (!gst_video_info_from_caps (&vinfo, caps))
++    goto wrong_caps;
++
++  allocator = NULL;
++  gst_buffer_pool_config_get_allocator (config, &allocator, &params);
++
++  /* not our allocator, not our buffers */
++  if (allocator && GST_IS_KMS_ALLOCATOR (allocator)) {
++    if (priv->allocator)
++      gst_object_unref (priv->allocator);
++    if ((priv->allocator = allocator))
++      gst_object_ref (allocator);
++  }
++  if (!priv->allocator)
++    goto no_allocator;
++
++  priv->vinfo = vinfo;
++
++  /* enable metadata based on config of the pool */
++  priv->add_videometa = gst_buffer_pool_config_has_option (config,
++      GST_BUFFER_POOL_OPTION_VIDEO_META);
++
++  return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
++
++  /* ERRORS */
++wrong_config:
++  {
++    GST_WARNING_OBJECT (pool, "invalid config");
++    return FALSE;
++  }
++no_caps:
++  {
++    GST_WARNING_OBJECT (pool, "no caps in config");
++    return FALSE;
++  }
++wrong_caps:
++  {
++    GST_WARNING_OBJECT (pool,
++        "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
++    return FALSE;
++  }
++no_allocator:
++  {
++    GST_WARNING_OBJECT (pool, "no valid allocator in pool");
++    return FALSE;
++  }
++}
++
++static GstFlowReturn
++gst_kms_buffer_pool_alloc_buffer (GstBufferPool * pool, GstBuffer ** buffer,
++    GstBufferPoolAcquireParams * params)
++{
++  GstKMSBufferPool *vpool;
++  GstKMSBufferPoolPrivate *priv;
++  GstVideoInfo *info;
++  GstMemory *mem;
++
++  vpool = GST_KMS_BUFFER_POOL_CAST (pool);
++  priv = vpool->priv;
++  info = &priv->vinfo;
++
++  *buffer = gst_buffer_new ();
++  if (*buffer == NULL)
++    goto no_memory;
++  mem = gst_kms_allocator_bo_alloc (priv->allocator, info);
++  if (!mem) {
++    gst_buffer_unref (*buffer);
++    goto no_memory;
++  }
++  gst_buffer_append_memory (*buffer, mem);
++
++  if (priv->add_videometa) {
++    GST_DEBUG_OBJECT (pool, "adding GstVideoMeta");
++
++    gst_buffer_add_video_meta_full (*buffer, GST_VIDEO_FRAME_FLAG_NONE,
++        GST_VIDEO_INFO_FORMAT (info),
++        GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info),
++        GST_VIDEO_INFO_N_PLANES (info), info->offset, info->stride);
++  }
++
++  return GST_FLOW_OK;
++
++  /* ERROR */
++no_memory:
++  {
++    GST_WARNING_OBJECT (pool, "can't create memory");
++    return GST_FLOW_ERROR;
++  }
++}
++
++static void
++gst_kms_buffer_pool_finalize (GObject * object)
++{
++  GstKMSBufferPool *pool;
++  GstKMSBufferPoolPrivate *priv;
++
++  pool = GST_KMS_BUFFER_POOL (object);
++  priv = pool->priv;
++
++  if (priv->allocator)
++    gst_object_unref (priv->allocator);
++
++  G_OBJECT_CLASS (parent_class)->finalize (object);
++}
++
++static void
++gst_kms_buffer_pool_init (GstKMSBufferPool * pool)
++{
++  pool->priv = gst_kms_buffer_pool_get_instance_private (pool);
++  pool->priv->fd = -1;
++}
++
++static void
++gst_kms_buffer_pool_class_init (GstKMSBufferPoolClass * klass)
++{
++  GObjectClass *gobject_class;
++  GstBufferPoolClass *gstbufferpool_class;
++
++  gobject_class = (GObjectClass *) klass;
++  gstbufferpool_class = (GstBufferPoolClass *) klass;
++
++  gobject_class->finalize = gst_kms_buffer_pool_finalize;
++
++  gstbufferpool_class->get_options = gst_kms_buffer_pool_get_options;
++  gstbufferpool_class->set_config = gst_kms_buffer_pool_set_config;
++  gstbufferpool_class->alloc_buffer = gst_kms_buffer_pool_alloc_buffer;
++}
++
++GstBufferPool *
++gst_kms_buffer_pool_new (void)
++{
++  return g_object_new (GST_TYPE_KMS_BUFFER_POOL, NULL);
++}
+diff --git a/sys/kms/gstkmsbufferpool.h b/sys/kms/gstkmsbufferpool.h
+new file mode 100644
+index 0000000..1ed9884
+--- /dev/null
++++ b/sys/kms/gstkmsbufferpool.h
+@@ -0,0 +1,75 @@
++/*
++ * GStreamer
++ * Copyright (C) 2016 Igalia
++ *
++ * Authors:
++ *  Víctor Manuel Jáquez Leal <vjaquez at igalia.com>
++ *  Javier Martin <javiermartin at by.com.es>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ *
++ */
++
++#ifndef __GST_KMS_BUFFER_POOL_H__
++#define __GST_KMS_BUFFER_POOL_H__
++
++#include <gst/gst.h>
++#include <gst/video/video.h>
++
++#include "gstkmssink.h"
++
++G_BEGIN_DECLS
++
++/**
++ * GST_BUFFER_POOL_OPTION_KMS_BUFFER:
++ *
++ * An option that can be activated on buffer pool to request KMS
++ * buffers.
++ */
++#define GST_BUFFER_POOL_OPTION_KMS_BUFFER "GstBufferPoolOptionKMSBuffer"
++
++/* video bufferpool */
++typedef struct _GstKMSBufferPool GstKMSBufferPool;
++typedef struct _GstKMSBufferPoolClass GstKMSBufferPoolClass;
++typedef struct _GstKMSBufferPoolPrivate GstKMSBufferPoolPrivate;
++
++#define GST_TYPE_KMS_BUFFER_POOL \
++  (gst_kms_buffer_pool_get_type())
++#define GST_IS_KMS_BUFFER_POOL(obj) \
++  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_KMS_BUFFER_POOL))
++#define GST_KMS_BUFFER_POOL(obj) \
++  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_KMS_BUFFER_POOL, GstKMSBufferPool))
++#define GST_KMS_BUFFER_POOL_CAST(obj) \
++  ((GstKMSBufferPool*)(obj))
++
++struct _GstKMSBufferPool
++{
++  GstVideoBufferPool parent;
++  GstKMSBufferPoolPrivate *priv;
++};
++
++struct _GstKMSBufferPoolClass
++{
++  GstVideoBufferPoolClass parent_class;
++};
++
++GType gst_kms_buffer_pool_get_type (void) G_GNUC_CONST;
++
++GstBufferPool *gst_kms_buffer_pool_new (void);
++
++G_END_DECLS
++
++#endif /* __GST_KMS_BUFFER_POOL_H__ */
+diff --git a/sys/kms/gstkmssink.c b/sys/kms/gstkmssink.c
+new file mode 100644
+index 0000000..72cb1f7
+--- /dev/null
++++ b/sys/kms/gstkmssink.c
+@@ -0,0 +1,1300 @@
++/* GStreamer
++ *
++ * Copyright (C) 2016 Igalia
++ *
++ * Authors:
++ *  Víctor Manuel Jáquez Leal <vjaquez at igalia.com>
++ *  Javier Martin <javiermartin at by.com.es>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ *
++ */
++
++/**
++ * SECTION:element-kmssink
++ * @short_description: A KMS/DRM based video sink
++ *
++ * kmssink is a simple video sink that renders video frames directly
++ * in a plane of a DRM device.
++ *
++ * <refsect2>
++ * <title>Example launch line</title>
++ * |[
++ * gst-launch-1.0 videotestsrc ! kmssink
++ * ]|
++ * </refsect2>
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <gst/video/video.h>
++#include <gst/allocators/gstdmabuf.h>
++
++#include <drm.h>
++#include <xf86drm.h>
++#include <xf86drmMode.h>
++#include <drm_fourcc.h>
++
++#include <string.h>
++
++#include "gstkmssink.h"
++#include "gstkmsutils.h"
++#include "gstkmsbufferpool.h"
++#include "gstkmsallocator.h"
++
++#define GST_PLUGIN_NAME "kmssink"
++#define GST_PLUGIN_DESC "Video sink using the Linux kernel mode setting API"
++
++GST_DEBUG_CATEGORY_STATIC (gst_kms_sink_debug);
++GST_DEBUG_CATEGORY_STATIC (CAT_PERFORMANCE);
++#define GST_CAT_DEFAULT gst_kms_sink_debug
++
++#define parent_class gst_kms_sink_parent_class
++G_DEFINE_TYPE_WITH_CODE (GstKMSSink, gst_kms_sink, GST_TYPE_VIDEO_SINK,
++    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_PLUGIN_NAME, 0,
++        GST_PLUGIN_DESC);
++    GST_DEBUG_CATEGORY_GET (CAT_PERFORMANCE, "GST_PERFORMANCE"));
++
++enum
++{
++  PROP_DRIVER_NAME = 1,
++  PROP_CONNECTOR_ID,
++  PROP_PLANE_ID,
++  PROP_N
++};
++
++static GParamSpec *g_properties[PROP_N] = { NULL, };
++
++static int
++kms_open (gchar ** driver)
++{
++  static const char *drivers[] = { "i915", "radeon", "nouveau", "vmwgfx",
++    "exynos", "amdgpu", "imx-drm", "rockchip", "atmel-hlcdc"
++  };
++  int i, fd = -1;
++
++  for (i = 0; i < G_N_ELEMENTS (drivers); i++) {
++    fd = drmOpen (drivers[i], NULL);
++    if (fd >= 0) {
++      if (driver)
++        *driver = g_strdup (drivers[i]);
++      break;
++    }
++  }
++
++  return fd;
++}
++
++static drmModePlane *
++find_plane_for_crtc (int fd, drmModeRes * res, drmModePlaneRes * pres,
++    int crtc_id)
++{
++  drmModePlane *plane;
++  int i, pipe;
++
++  plane = NULL;
++  pipe = -1;
++  for (i = 0; i < res->count_crtcs; i++) {
++    if (crtc_id == res->crtcs[i]) {
++      pipe = i;
++      break;
++    }
++  }
++
++  if (pipe == -1)
++    return NULL;
++
++  for (i = 0; i < pres->count_planes; i++) {
++    plane = drmModeGetPlane (fd, pres->planes[i]);
++    if (plane->possible_crtcs & (1 << pipe))
++      return plane;
++    drmModeFreePlane (plane);
++  }
++
++  return NULL;
++}
++
++static drmModeCrtc *
++find_crtc_for_connector (int fd, drmModeRes * res, drmModeConnector * conn,
++    guint * pipe)
++{
++  int i;
++  int crtc_id;
++  drmModeEncoder *enc;
++  drmModeCrtc *crtc;
++
++  crtc_id = -1;
++  for (i = 0; i < res->count_encoders; i++) {
++    enc = drmModeGetEncoder (fd, res->encoders[i]);
++    if (enc) {
++      if (enc->encoder_id == conn->encoder_id) {
++        crtc_id = enc->crtc_id;
++        drmModeFreeEncoder (enc);
++        break;
++      }
++      drmModeFreeEncoder (enc);
++    }
++  }
++
++  if (crtc_id == -1)
++    return NULL;
++
++  for (i = 0; i < res->count_crtcs; i++) {
++    crtc = drmModeGetCrtc (fd, res->crtcs[i]);
++    if (crtc) {
++      if (crtc_id == crtc->crtc_id) {
++        if (pipe)
++          *pipe = i;
++        return crtc;
++      }
++      drmModeFreeCrtc (crtc);
++    }
++  }
++
++  return NULL;
++}
++
++static gboolean
++connector_is_used (int fd, drmModeRes * res, drmModeConnector * conn)
++{
++  gboolean result;
++  drmModeCrtc *crtc;
++
++  result = FALSE;
++  crtc = find_crtc_for_connector (fd, res, conn, NULL);
++  if (crtc) {
++    result = crtc->buffer_id != 0;
++    drmModeFreeCrtc (crtc);
++  }
++
++  return result;
++}
++
++static drmModeConnector *
++find_used_connector_by_type (int fd, drmModeRes * res, int type)
++{
++  int i;
++  drmModeConnector *conn;
++
++  conn = NULL;
++  for (i = 0; i < res->count_connectors; i++) {
++    conn = drmModeGetConnector (fd, res->connectors[i]);
++    if (conn) {
++      if ((conn->connector_type == type) && connector_is_used (fd, res, conn))
++        return conn;
++      drmModeFreeConnector (conn);
++    }
++  }
++
++  return NULL;
++}
++
++static drmModeConnector *
++find_first_used_connector (int fd, drmModeRes * res)
++{
++  int i;
++  drmModeConnector *conn;
++
++  conn = NULL;
++  for (i = 0; i < res->count_connectors; i++) {
++    conn = drmModeGetConnector (fd, res->connectors[i]);
++    if (conn) {
++      if (connector_is_used (fd, res, conn))
++        return conn;
++      drmModeFreeConnector (conn);
++    }
++  }
++
++  return NULL;
++}
++
++static drmModeConnector *
++find_main_monitor (int fd, drmModeRes * res)
++{
++  /* Find the LVDS and eDP connectors: those are the main screens. */
++  static const int priority[] = { DRM_MODE_CONNECTOR_LVDS,
++    DRM_MODE_CONNECTOR_eDP
++  };
++  int i;
++  drmModeConnector *conn;
++
++  conn = NULL;
++  for (i = 0; !conn && i < G_N_ELEMENTS (priority); i++)
++    conn = find_used_connector_by_type (fd, res, priority[i]);
++
++  /* if we didn't find a connector, grab the first one in use */
++  if (!conn)
++    conn = find_first_used_connector (fd, res);
++
++  return conn;
++}
++
++static void
++log_drm_version (GstKMSSink * self)
++{
++#ifndef GST_DISABLE_GST_DEBUG
++  drmVersion *v;
++
++  v = drmGetVersion (self->fd);
++  if (v) {
++    GST_INFO_OBJECT (self, "DRM v%d.%d.%d [%s — %s — %s]", v->version_major,
++        v->version_minor, v->version_patchlevel, GST_STR_NULL (v->name),
++        GST_STR_NULL (v->desc), GST_STR_NULL (v->date));
++    drmFreeVersion (v);
++  } else {
++    GST_WARNING_OBJECT (self, "could not get driver information: %s",
++        GST_STR_NULL (self->devname));
++  }
++#endif
++  return;
++}
++
++static gboolean
++get_drm_caps (GstKMSSink * self)
++{
++  gint ret;
++  guint64 has_dumb_buffer;
++  guint64 has_prime;
++  guint64 has_async_page_flip;
++
++  has_dumb_buffer = 0;
++  ret = drmGetCap (self->fd, DRM_CAP_DUMB_BUFFER, &has_dumb_buffer);
++  if (ret)
++    GST_WARNING_OBJECT (self, "could not get dumb buffer capability");
++  if (has_dumb_buffer == 0) {
++    GST_ERROR_OBJECT (self, "driver cannot handle dumb buffers");
++    return FALSE;
++  }
++
++  has_prime = 0;
++  ret = drmGetCap (self->fd, DRM_CAP_PRIME, &has_prime);
++  if (ret)
++    GST_WARNING_OBJECT (self, "could not get prime capability");
++  else
++    self->has_prime_import = (gboolean) (has_prime & DRM_PRIME_CAP_IMPORT);
++
++  has_async_page_flip = 0;
++  ret = drmGetCap (self->fd, DRM_CAP_ASYNC_PAGE_FLIP, &has_async_page_flip);
++  if (ret)
++    GST_WARNING_OBJECT (self, "could not get async page flip capability");
++  else
++    self->has_async_page_flip = (gboolean) has_async_page_flip;
++
++  GST_INFO_OBJECT (self, "prime import (%s) / async page flip (%s)",
++      self->has_prime_import ? "✓" : "✗",
++      self->has_async_page_flip ? "✓" : "✗");
++
++  return TRUE;
++}
++
++static gboolean
++ensure_allowed_caps (GstKMSSink * self, drmModePlane * plane, drmModeRes * res)
++{
++  GstCaps *out_caps, *caps;
++  int i;
++  GstVideoFormat fmt;
++  const gchar *format;
++
++  if (self->allowed_caps)
++    return TRUE;
++
++  out_caps = gst_caps_new_empty ();
++  if (!out_caps)
++    return FALSE;
++
++  for (i = 0; i < plane->count_formats; i++) {
++    fmt = gst_video_format_from_drm (plane->formats[i]);
++    if (fmt == GST_VIDEO_FORMAT_UNKNOWN) {
++      GST_INFO_OBJECT (self, "ignoring format %" GST_FOURCC_FORMAT,
++          GST_FOURCC_ARGS (plane->formats[i]));
++      continue;
++    }
++
++    format = gst_video_format_to_string (fmt);
++    caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, format,
++        "width", GST_TYPE_INT_RANGE, res->min_width, res->max_width,
++        "height", GST_TYPE_INT_RANGE, res->min_height, res->max_height,
++        "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
++    if (!caps)
++      continue;
++
++    out_caps = gst_caps_merge (out_caps, caps);
++  }
++
++  self->allowed_caps = gst_caps_simplify (out_caps);
++
++  GST_DEBUG_OBJECT (self, "allowed caps = %" GST_PTR_FORMAT,
++      self->allowed_caps);
++
++  return TRUE;
++}
++
++static gboolean
++gst_kms_sink_start (GstBaseSink * bsink)
++{
++  GstKMSSink *self;
++  drmModeRes *res;
++  drmModeConnector *conn;
++  drmModeCrtc *crtc;
++  drmModePlaneRes *pres;
++  drmModePlane *plane;
++  gboolean universal_planes;
++  gboolean ret;
++
++  self = GST_KMS_SINK (bsink);
++  universal_planes = FALSE;
++  ret = FALSE;
++  res = NULL;
++  conn = NULL;
++  crtc = NULL;
++  pres = NULL;
++  plane = NULL;
++
++  if (self->devname)
++    self->fd = drmOpen (self->devname, NULL);
++  else
++    self->fd = kms_open (&self->devname);
++  if (self->fd < 0)
++    goto open_failed;
++
++  log_drm_version (self);
++  if (!get_drm_caps (self))
++    goto bail;
++
++  res = drmModeGetResources (self->fd);
++  if (!res)
++    goto resources_failed;
++
++  if (self->conn_id == -1)
++    conn = find_main_monitor (self->fd, res);
++  else
++    conn = drmModeGetConnector (self->fd, self->conn_id);
++  if (!conn)
++    goto connector_failed;
++
++  crtc = find_crtc_for_connector (self->fd, res, conn, &self->pipe);
++  if (!crtc)
++    goto crtc_failed;
++
++retry_find_plane:
++  if (universal_planes &&
++      drmSetClientCap (self->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1))
++    goto set_cap_failed;
++
++  pres = drmModeGetPlaneResources (self->fd);
++  if (!pres)
++    goto plane_resources_failed;
++
++  if (self->plane_id == -1)
++    plane = find_plane_for_crtc (self->fd, res, pres, crtc->crtc_id);
++  else
++    plane = drmModeGetPlane (self->fd, self->plane_id);
++  if (!plane)
++    goto plane_failed;
++
++  /* let's get the available color formats in plane */
++  if (!ensure_allowed_caps (self, plane, res))
++    goto bail;
++
++  self->conn_id = conn->connector_id;
++  self->crtc_id = crtc->crtc_id;
++  self->plane_id = plane->plane_id;
++
++  GST_INFO_OBJECT (self, "connector id = %d / crtc id = %d / plane id = %d",
++      self->conn_id, self->crtc_id, self->plane_id);
++
++  self->hdisplay = crtc->mode.hdisplay;
++  self->vdisplay = crtc->mode.vdisplay;
++  self->buffer_id = crtc->buffer_id;
++
++  self->mm_width = conn->mmWidth;
++  self->mm_height = conn->mmHeight;
++
++  GST_INFO_OBJECT (self, "display size: pixels = %dx%d / millimeters = %dx%d",
++      self->hdisplay, self->vdisplay, self->mm_width, self->mm_height);
++
++  self->pollfd.fd = self->fd;
++  gst_poll_add_fd (self->poll, &self->pollfd);
++  gst_poll_fd_ctl_read (self->poll, &self->pollfd, TRUE);
++
++  ret = TRUE;
++
++bail:
++  if (plane)
++    drmModeFreePlane (plane);
++  if (pres)
++    drmModeFreePlaneResources (pres);
++  if (crtc)
++    drmModeFreeCrtc (crtc);
++  if (conn)
++    drmModeFreeConnector (conn);
++  if (res)
++    drmModeFreeResources (res);
++
++  if (!ret && self->fd >= 0) {
++    drmClose (self->fd);
++    self->fd = -1;
++  }
++
++  return ret;
++
++  /* ERRORS */
++open_failed:
++  {
++    GST_ERROR_OBJECT (self, "Could not open DRM module %s: %s",
++        GST_STR_NULL (self->devname), strerror (errno));
++    return FALSE;
++  }
++
++resources_failed:
++  {
++    GST_ERROR_OBJECT (self, "drmModeGetResources failed: %s (%d)",
++        strerror (errno), errno);
++    goto bail;
++  }
++
++connector_failed:
++  {
++    GST_ERROR_OBJECT (self, "Could not find a valid monitor connector");
++    goto bail;
++  }
++
++crtc_failed:
++  {
++    GST_ERROR_OBJECT (self, "Could not find a crtc for connector");
++    goto bail;
++  }
++
++set_cap_failed:
++  {
++    GST_ERROR_OBJECT (self, "Could not set universal planes capability bit");
++    goto bail;
++  }
++
++plane_resources_failed:
++  {
++    GST_ERROR_OBJECT (self, "drmModeGetPlaneResources failed: %s (%d)",
++        strerror (errno), errno);
++    goto bail;
++  }
++
++plane_failed:
++  {
++    if (universal_planes) {
++      GST_ERROR_OBJECT (self, "Could not find a plane for crtc");
++      goto bail;
++    } else {
++      universal_planes = TRUE;
++      goto retry_find_plane;
++    }
++  }
++}
++
++static gboolean
++gst_kms_sink_stop (GstBaseSink * bsink)
++{
++  GstKMSSink *self;
++
++  self = GST_KMS_SINK (bsink);
++
++  gst_buffer_replace (&self->last_buffer, NULL);
++  gst_caps_replace (&self->allowed_caps, NULL);
++  gst_object_replace ((GstObject **) & self->pool, NULL);
++  gst_object_replace ((GstObject **) & self->allocator, NULL);
++
++  gst_poll_remove_fd (self->poll, &self->pollfd);
++  gst_poll_restart (self->poll);
++  gst_poll_fd_init (&self->pollfd);
++
++  if (self->fd >= 0) {
++    drmClose (self->fd);
++    self->fd = -1;
++  }
++
++  return TRUE;
++}
++
++static GstCaps *
++gst_kms_sink_get_allowed_caps (GstKMSSink * self)
++{
++  if (!self->allowed_caps)
++    return NULL;                /* base class will return the template caps */
++  return gst_caps_ref (self->allowed_caps);
++}
++
++static GstCaps *
++gst_kms_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
++{
++  GstKMSSink *self;
++  GstCaps *caps, *out_caps;
++
++  self = GST_KMS_SINK (bsink);
++
++  caps = gst_kms_sink_get_allowed_caps (self);
++  if (caps && filter) {
++    out_caps = gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
++    gst_caps_unref (caps);
++  } else {
++    out_caps = caps;
++  }
++
++  return out_caps;
++}
++
++static void
++ensure_kms_allocator (GstKMSSink * self)
++{
++  if (self->allocator)
++    return;
++  self->allocator = gst_kms_allocator_new (self->fd);
++}
++
++static GstBufferPool *
++gst_kms_sink_create_pool (GstKMSSink * self, GstCaps * caps, gsize size,
++    gint min)
++{
++  GstBufferPool *pool;
++  GstStructure *config;
++
++  pool = gst_kms_buffer_pool_new ();
++  if (!pool)
++    goto pool_failed;
++
++  config = gst_buffer_pool_get_config (pool);
++  gst_buffer_pool_config_set_params (config, caps, size, min, 0);
++  gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
++
++  ensure_kms_allocator (self);
++  gst_buffer_pool_config_set_allocator (config, self->allocator, NULL);
++
++  if (!gst_buffer_pool_set_config (pool, config))
++    goto config_failed;
++
++  return pool;
++
++  /* ERRORS */
++pool_failed:
++  {
++    GST_ERROR_OBJECT (self, "failed to create buffer pool");
++    return NULL;
++  }
++config_failed:
++  {
++    GST_ERROR_OBJECT (self, "failed to set config");
++    gst_object_unref (pool);
++    return NULL;
++  }
++}
++
++static gboolean
++gst_kms_sink_calculate_display_ratio (GstKMSSink * self, GstVideoInfo * vinfo)
++{
++  guint dar_n, dar_d;
++  guint video_width, video_height;
++  guint video_par_n, video_par_d;
++  guint dpy_par_n, dpy_par_d;
++
++  video_width = GST_VIDEO_INFO_WIDTH (vinfo);
++  video_height = GST_VIDEO_INFO_HEIGHT (vinfo);
++  video_par_n = GST_VIDEO_INFO_PAR_N (vinfo);
++  video_par_d = GST_VIDEO_INFO_PAR_D (vinfo);
++
++  gst_video_calculate_device_ratio (self->hdisplay, self->vdisplay,
++      self->mm_width, self->mm_height, &dpy_par_n, &dpy_par_d);
++
++  if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, video_width,
++          video_height, video_par_n, video_par_d, dpy_par_n, dpy_par_d))
++    return FALSE;
++
++  GST_DEBUG_OBJECT (self, "video calculated display ratio: %d/%d", dar_n,
++      dar_d);
++
++  /* now find a width x height that respects this display ratio.
++   * prefer those that have one of w/h the same as the incoming video
++   * using wd / hd = dar_n / dar_d */
++
++  /* start with same height, because of interlaced video */
++  /* check hd / dar_d is an integer scale factor, and scale wd with the PAR */
++  if (video_height % dar_d == 0) {
++    GST_DEBUG_OBJECT (self, "keeping video height");
++    GST_VIDEO_SINK_WIDTH (self) = (guint)
++        gst_util_uint64_scale_int (video_height, dar_n, dar_d);
++    GST_VIDEO_SINK_HEIGHT (self) = video_height;
++  } else if (video_width % dar_n == 0) {
++    GST_DEBUG_OBJECT (self, "keeping video width");
++    GST_VIDEO_SINK_WIDTH (self) = video_width;
++    GST_VIDEO_SINK_HEIGHT (self) = (guint)
++        gst_util_uint64_scale_int (video_width, dar_d, dar_n);
++  } else {
++    GST_DEBUG_OBJECT (self, "approximating while keeping video height");
++    GST_VIDEO_SINK_WIDTH (self) = (guint)
++        gst_util_uint64_scale_int (video_height, dar_n, dar_d);
++    GST_VIDEO_SINK_HEIGHT (self) = video_height;
++  }
++  GST_DEBUG_OBJECT (self, "scaling to %dx%d", GST_VIDEO_SINK_WIDTH (self),
++      GST_VIDEO_SINK_HEIGHT (self));
++
++  return TRUE;
++}
++
++static gboolean
++gst_kms_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
++{
++  GstKMSSink *self;
++  GstVideoInfo vinfo;
++  GstBufferPool *newpool, *oldpool;
++
++  self = GST_KMS_SINK (bsink);
++
++  if (!gst_video_info_from_caps (&vinfo, caps))
++    goto invalid_format;
++
++  if (!gst_kms_sink_calculate_display_ratio (self, &vinfo))
++    goto no_disp_ratio;
++
++  if (GST_VIDEO_SINK_WIDTH (self) <= 0 || GST_VIDEO_SINK_HEIGHT (self) <= 0)
++    goto invalid_size;
++
++  /* create a new pool for the new configuration */
++  newpool = gst_kms_sink_create_pool (self, caps, GST_VIDEO_INFO_SIZE (&vinfo),
++      2);
++  if (!newpool)
++    goto no_pool;
++
++  /* we don't activate the internal pool yet as it may not be needed */
++  oldpool = self->pool;
++  self->pool = newpool;
++
++  if (oldpool) {
++    gst_buffer_pool_set_active (oldpool, FALSE);
++    gst_object_unref (oldpool);
++  }
++
++  self->vinfo = vinfo;
++
++  GST_DEBUG_OBJECT (self, "negotiated caps = %" GST_PTR_FORMAT, caps);
++
++  return TRUE;
++
++  /* ERRORS */
++invalid_format:
++  {
++    GST_ERROR_OBJECT (self, "caps invalid");
++    return FALSE;
++  }
++
++invalid_size:
++  {
++    GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
++        ("Invalid image size."));
++    return FALSE;
++  }
++
++no_disp_ratio:
++  {
++    GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
++        ("Error calculating the output display ratio of the video."));
++    return FALSE;
++  }
++no_pool:
++  {
++    /* Already warned in create_pool */
++    return FALSE;
++  }
++}
++
++static gboolean
++gst_kms_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
++{
++  GstKMSSink *self;
++  GstCaps *caps;
++  gboolean need_pool;
++  GstVideoInfo vinfo;
++  GstBufferPool *pool;
++  gsize size;
++
++  self = GST_KMS_SINK (bsink);
++
++  gst_query_parse_allocation (query, &caps, &need_pool);
++  if (!caps)
++    goto no_caps;
++  if (!gst_video_info_from_caps (&vinfo, caps))
++    goto invalid_caps;
++
++  size = GST_VIDEO_INFO_SIZE (&vinfo);
++
++  pool = NULL;
++  if (need_pool) {
++    pool = gst_kms_sink_create_pool (self, caps, size, 0);
++    if (!pool)
++      goto no_pool;
++  }
++
++  if (pool) {
++    /* we need at least 2 buffer because we hold on to the last one */
++    gst_query_add_allocation_pool (query, pool, size, 2, 0);
++    gst_object_unref (pool);
++  }
++
++  gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
++  gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
++
++  return TRUE;
++
++  /* ERRORS */
++no_caps:
++  {
++    GST_DEBUG_OBJECT (bsink, "no caps specified");
++    return FALSE;
++  }
++invalid_caps:
++  {
++    GST_DEBUG_OBJECT (bsink, "invalid caps specified");
++    return FALSE;
++  }
++no_pool:
++  {
++    /* Already warned in create_pool */
++    return FALSE;
++  }
++}
++
++static void
++gst_kms_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
++    GstClockTime * start, GstClockTime * end)
++{
++  GstKMSSink *self;
++
++  self = GST_KMS_SINK (bsink);
++
++  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
++    *start = GST_BUFFER_TIMESTAMP (buf);
++    if (GST_BUFFER_DURATION_IS_VALID (buf))
++      *end = *start + GST_BUFFER_DURATION (buf);
++    else {
++      if (GST_VIDEO_INFO_FPS_N (&self->vinfo) > 0) {
++        *end = *start +
++            gst_util_uint64_scale_int (GST_SECOND,
++            GST_VIDEO_INFO_FPS_D (&self->vinfo),
++            GST_VIDEO_INFO_FPS_N (&self->vinfo));
++      }
++    }
++  }
++}
++
++static void
++sync_handler (gint fd, guint frame, guint sec, guint usec, gpointer data)
++{
++  gboolean *waiting;
++
++  waiting = data;
++  *waiting = FALSE;
++}
++
++static gboolean
++gst_kms_sink_sync (GstKMSSink * self)
++{
++  gint ret;
++  gboolean waiting;
++  drmEventContext evctxt = {
++    .version = DRM_EVENT_CONTEXT_VERSION,
++    .page_flip_handler = sync_handler,
++    .vblank_handler = sync_handler,
++  };
++  drmVBlank vbl = {
++    .request = {
++          .type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
++          .sequence = 1,
++          .signal = (gulong) & waiting,
++        },
++  };
++
++  if (self->pipe == 1)
++    vbl.request.type |= DRM_VBLANK_SECONDARY;
++  else if (self->pipe > 1)
++    vbl.request.type |= self->pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
++
++  waiting = TRUE;
++  if (!self->has_async_page_flip) {
++    ret = drmWaitVBlank (self->fd, &vbl);
++    if (ret)
++      goto vblank_failed;
++  } else {
++    ret = drmModePageFlip (self->fd, self->crtc_id, self->buffer_id,
++        DRM_MODE_PAGE_FLIP_EVENT, &waiting);
++    if (ret)
++      goto pageflip_failed;
++  }
++
++  while (waiting) {
++    do {
++      ret = gst_poll_wait (self->poll, 3 * GST_SECOND);
++    } while (ret == -1 && (errno == EAGAIN || errno == EINTR));
++
++    ret = drmHandleEvent (self->fd, &evctxt);
++    if (ret)
++      goto event_failed;
++  }
++
++  return TRUE;
++
++  /* ERRORS */
++vblank_failed:
++  {
++    GST_WARNING_OBJECT (self, "drmWaitVBlank failed: %s (%d)", strerror (-ret),
++        ret);
++    return FALSE;
++  }
++pageflip_failed:
++  {
++    GST_WARNING_OBJECT (self, "drmModePageFlip failed: %s (%d)",
++        strerror (-ret), ret);
++    return FALSE;
++  }
++event_failed:
++  {
++    GST_ERROR_OBJECT (self, "drmHandleEvent failed: %s (%d)", strerror (-ret),
++        ret);
++    return FALSE;
++  }
++}
++
++static GstMemory *
++get_cached_kmsmem (GstMemory * mem)
++{
++  return gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
++      g_quark_from_static_string ("kmsmem"));
++}
++
++static void
++set_cached_kmsmem (GstMemory * mem, GstMemory * kmsmem)
++{
++  return gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
++      g_quark_from_static_string ("kmsmem"), kmsmem,
++      (GDestroyNotify) gst_memory_unref);
++}
++
++static gboolean
++gst_kms_sink_import_dmabuf (GstKMSSink * self, GstBuffer * inbuf,
++    GstBuffer ** outbuf)
++{
++  gint prime_fds[GST_VIDEO_MAX_PLANES] = { 0, };
++  GstVideoMeta *meta;
++  guint i, n_mem, n_planes;
++  GstKMSMemory *kmsmem;
++  guint mems_idx[GST_VIDEO_MAX_PLANES];
++  gsize mems_skip[GST_VIDEO_MAX_PLANES];
++  GstMemory *mems[GST_VIDEO_MAX_PLANES];
++
++  if (!self->has_prime_import)
++    return FALSE;
++
++  /* This will eliminate most non-dmabuf out there */
++  if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0)))
++    return FALSE;
++
++  n_planes = GST_VIDEO_INFO_N_PLANES (&self->vinfo);
++  n_mem = gst_buffer_n_memory (inbuf);
++  meta = gst_buffer_get_video_meta (inbuf);
++
++  GST_TRACE_OBJECT (self, "Found a dmabuf with %u planes and %u memories",
++      n_planes, n_mem);
++
++  /* We cannot have multiple dmabuf per plane */
++  if (n_mem > n_planes)
++    return FALSE;
++
++  /* Update video info based on video meta */
++  if (meta) {
++    GST_VIDEO_INFO_WIDTH (&self->vinfo) = meta->width;
++    GST_VIDEO_INFO_HEIGHT (&self->vinfo) = meta->height;
++
++    for (i = 0; i < meta->n_planes; i++) {
++      GST_VIDEO_INFO_PLANE_OFFSET (&self->vinfo, i) = meta->offset[i];
++      GST_VIDEO_INFO_PLANE_STRIDE (&self->vinfo, i) = meta->stride[i];
++    }
++  }
++
++  /* Find and validate all memories */
++  for (i = 0; i < n_planes; i++) {
++    guint length;
++
++    if (!gst_buffer_find_memory (inbuf,
++            GST_VIDEO_INFO_PLANE_OFFSET (&self->vinfo, i), 1,
++            &mems_idx[i], &length, &mems_skip[i]))
++      return FALSE;
++
++    mems[i] = gst_buffer_peek_memory (inbuf, mems_idx[i]);
++
++    /* And all memory found must be dmabuf */
++    if (!gst_is_dmabuf_memory (mems[i]))
++      return FALSE;
++  }
++
++  kmsmem = (GstKMSMemory *) get_cached_kmsmem (mems[0]);
++  if (kmsmem) {
++    GST_LOG_OBJECT (self, "found KMS mem %p in DMABuf mem %p with fb id = %d",
++        kmsmem, mems[0], kmsmem->fb_id);
++    goto wrap_mem;
++  }
++
++  for (i = 0; i < n_planes; i++)
++    prime_fds[i] = gst_dmabuf_memory_get_fd (mems[i]);
++
++  GST_LOG_OBJECT (self, "found these prime ids: %d, %d, %d, %d", prime_fds[0],
++      prime_fds[1], prime_fds[2], prime_fds[3]);
++
++  kmsmem = gst_kms_allocator_dmabuf_import (self->allocator, prime_fds,
++      n_planes, mems_skip, &self->vinfo);
++  if (!kmsmem)
++    return FALSE;
++
++  GST_LOG_OBJECT (self, "setting KMS mem %p to DMABuf mem %p with fb id = %d",
++      kmsmem, mems[0], kmsmem->fb_id);
++  set_cached_kmsmem (mems[0], GST_MEMORY_CAST (kmsmem));
++
++wrap_mem:
++  *outbuf = gst_buffer_new ();
++  if (!*outbuf)
++    return FALSE;
++  gst_buffer_append_memory (*outbuf, gst_memory_ref (GST_MEMORY_CAST (kmsmem)));
++  gst_buffer_add_parent_buffer_meta (*outbuf, inbuf);
++
++  return TRUE;
++}
++
++static GstBuffer *
++gst_kms_sink_get_input_buffer (GstKMSSink * self, GstBuffer * inbuf)
++{
++  GstMemory *mem;
++  GstBuffer *buf;
++  GstFlowReturn ret;
++  GstVideoFrame inframe, outframe;
++  gboolean success;
++
++  mem = gst_buffer_peek_memory (inbuf, 0);
++  if (!mem)
++    return NULL;
++
++  if (gst_is_kms_memory (mem))
++    return gst_buffer_ref (inbuf);
++
++  buf = NULL;
++  if (gst_kms_sink_import_dmabuf (self, inbuf, &buf))
++    return buf;
++
++  GST_CAT_INFO_OBJECT (CAT_PERFORMANCE, self, "frame copy");
++
++  if (!gst_buffer_pool_set_active (self->pool, TRUE))
++    goto activate_pool_failed;
++
++  ret = gst_buffer_pool_acquire_buffer (self->pool, &buf, NULL);
++  if (ret != GST_FLOW_OK)
++    goto create_buffer_failed;
++
++  if (!gst_video_frame_map (&inframe, &self->vinfo, inbuf, GST_MAP_READ))
++    goto error_map_src_buffer;
++
++  if (!gst_video_frame_map (&outframe, &self->vinfo, buf, GST_MAP_WRITE))
++    goto error_map_dst_buffer;
++
++  success = gst_video_frame_copy (&outframe, &inframe);
++  gst_video_frame_unmap (&outframe);
++  gst_video_frame_unmap (&inframe);
++  if (!success)
++    goto error_copy_buffer;
++
++  return buf;
++
++bail:
++  {
++    if (buf)
++      gst_buffer_unref (buf);
++    return NULL;
++  }
++
++  /* ERRORS */
++activate_pool_failed:
++  {
++    GST_ELEMENT_ERROR (self, STREAM, FAILED, ("failed to activate buffer pool"),
++        ("failed to activate buffer pool"));
++    goto bail;
++  }
++create_buffer_failed:
++  {
++    GST_ELEMENT_ERROR (self, STREAM, FAILED, ("allocation failed"),
++        ("failed to create buffer"));
++    goto bail;
++  }
++error_copy_buffer:
++  {
++    GST_WARNING_OBJECT (self, "failed to upload buffer");
++    goto bail;
++  }
++error_map_dst_buffer:
++  {
++    gst_video_frame_unmap (&inframe);
++    /* fall-through */
++  }
++error_map_src_buffer:
++  {
++    GST_WARNING_OBJECT (self, "failed to map buffer");
++    goto bail;
++  }
++}
++
++static GstFlowReturn
++gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
++{
++  gint ret;
++  GstBuffer *buffer;
++  guint32 fb_id;
++  GstKMSSink *self;
++  GstVideoCropMeta *crop;
++  GstVideoRectangle src = { 0, };
++  GstVideoRectangle dst = { 0, };
++  GstVideoRectangle result;
++  GstFlowReturn res;
++
++  self = GST_KMS_SINK (vsink);
++
++  res = GST_FLOW_ERROR;
++
++  buffer = gst_kms_sink_get_input_buffer (self, buf);
++  if (!buffer)
++    return GST_FLOW_ERROR;
++  fb_id = gst_kms_memory_get_fb_id (gst_buffer_peek_memory (buffer, 0));
++  if (fb_id == 0)
++    goto buffer_invalid;
++
++  GST_TRACE_OBJECT (self, "displaying fb %d", fb_id);
++
++  {
++    if ((crop = gst_buffer_get_video_crop_meta (buffer))) {
++      src.x = crop->x;
++      src.y = crop->y;
++      src.w = crop->width;
++      src.h = crop->height;
++    } else {
++      src.w = GST_VIDEO_SINK_WIDTH (self);
++      src.h = GST_VIDEO_SINK_HEIGHT (self);
++    }
++  }
++
++  dst.w = self->hdisplay;
++  dst.h = self->vdisplay;
++
++  gst_video_sink_center_rect (src, dst, &result, FALSE);
++
++  /* if the frame size is bigger than the display size, the source
++   * must be the display size */
++  src.w = MIN (src.w, self->hdisplay);
++  src.h = MIN (src.h, self->vdisplay);
++
++  ret = drmModeSetPlane (self->fd, self->plane_id, self->crtc_id, fb_id, 0,
++      result.x, result.y, result.w, result.h,
++      /* source/cropping coordinates are given in Q16 */
++      src.x << 16, src.y << 16, src.w << 16, src.h << 16);
++  if (ret)
++    goto set_plane_failed;
++
++  /* Wait for the previous frame to complete redraw */
++  if (!gst_kms_sink_sync (self))
++    goto bail;
++
++  gst_buffer_replace (&self->last_buffer, buffer);
++
++  res = GST_FLOW_OK;
++
++bail:
++  gst_buffer_unref (buffer);
++  return res;
++
++  /* ERRORS */
++buffer_invalid:
++  {
++    GST_ERROR_OBJECT (self, "invalid buffer: it doesn't have a fb id");
++    goto bail;
++  }
++set_plane_failed:
++  {
++    GST_DEBUG_OBJECT (self, "result = { %d, %d, %d, %d} / "
++        "src = { %d, %d, %d %d } / dst = { %d, %d, %d %d }", result.x, result.y,
++        result.w, result.h, src.x, src.y, src.w, src.h, dst.x, dst.y, dst.w,
++        dst.h);
++    GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
++        (NULL), ("drmModeSetPlane failed: %s (%d)", strerror (-ret), ret));
++    goto bail;
++  }
++}
++
++static void
++gst_kms_sink_set_property (GObject * object, guint prop_id,
++    const GValue * value, GParamSpec * pspec)
++{
++  GstKMSSink *sink;
++
++  sink = GST_KMS_SINK (object);
++
++  switch (prop_id) {
++    case PROP_DRIVER_NAME:
++      sink->devname = g_value_dup_string (value);
++      break;
++    case PROP_CONNECTOR_ID:
++      sink->conn_id = g_value_get_int (value);
++      break;
++    case PROP_PLANE_ID:
++      sink->plane_id = g_value_get_int (value);
++      break;
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++  }
++}
++
++static void
++gst_kms_sink_get_property (GObject * object, guint prop_id,
++    GValue * value, GParamSpec * pspec)
++{
++  GstKMSSink *sink;
++
++  sink = GST_KMS_SINK (object);
++
++  switch (prop_id) {
++    case PROP_DRIVER_NAME:
++      g_value_take_string (value, sink->devname);
++      break;
++    case PROP_CONNECTOR_ID:
++      g_value_set_int (value, sink->conn_id);
++      break;
++    case PROP_PLANE_ID:
++      g_value_set_int (value, sink->plane_id);
++      break;
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++      break;
++  }
++}
++
++static void
++gst_kms_sink_finalize (GObject * object)
++{
++  GstKMSSink *sink;
++
++  sink = GST_KMS_SINK (object);
++  g_clear_pointer (&sink->devname, g_free);
++  gst_poll_free (sink->poll);
++
++  G_OBJECT_CLASS (parent_class)->finalize (object);
++}
++
++static void
++gst_kms_sink_init (GstKMSSink * sink)
++{
++  sink->fd = -1;
++  sink->conn_id = -1;
++  sink->plane_id = -1;
++  gst_poll_fd_init (&sink->pollfd);
++  sink->poll = gst_poll_new (TRUE);
++  gst_video_info_init (&sink->vinfo);
++}
++
++static void
++gst_kms_sink_class_init (GstKMSSinkClass * klass)
++{
++  GObjectClass *gobject_class;
++  GstElementClass *element_class;
++  GstBaseSinkClass *basesink_class;
++  GstVideoSinkClass *videosink_class;
++  GstCaps *caps;
++
++  gobject_class = G_OBJECT_CLASS (klass);
++  element_class = GST_ELEMENT_CLASS (klass);
++  basesink_class = GST_BASE_SINK_CLASS (klass);
++  videosink_class = GST_VIDEO_SINK_CLASS (klass);
++
++  gst_element_class_set_static_metadata (element_class, "KMS video sink",
++      "Sink/Video", GST_PLUGIN_DESC, "Víctor Jáquez <vjaquez at igalia.com>");
++
++  caps = gst_kms_sink_caps_template_fill ();
++  gst_element_class_add_pad_template (element_class,
++      gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps));
++  gst_caps_unref (caps);
++
++  basesink_class->start = GST_DEBUG_FUNCPTR (gst_kms_sink_start);
++  basesink_class->stop = GST_DEBUG_FUNCPTR (gst_kms_sink_stop);
++  basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_kms_sink_set_caps);
++  basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_kms_sink_get_caps);
++  basesink_class->propose_allocation = gst_kms_sink_propose_allocation;
++  basesink_class->get_times = gst_kms_sink_get_times;
++
++  videosink_class->show_frame = gst_kms_sink_show_frame;
++
++  gobject_class->finalize = gst_kms_sink_finalize;
++  gobject_class->set_property = gst_kms_sink_set_property;
++  gobject_class->get_property = gst_kms_sink_get_property;
++
++  /**
++   * kmssink:driver-name:
++   *
++   * If you have a system with multiple GPUs, you can choose which GPU
++   * to use setting the DRM device driver name. Otherwise, the first
++   * one from an internal list is used.
++   */
++  g_properties[PROP_DRIVER_NAME] = g_param_spec_string ("driver-name",
++      "device name", "DRM device driver name", NULL,
++      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
++
++  /**
++   * kmssink:connector-id:
++   *
++   * A GPU has several output connectors, for example: LVDS, VGA,
++   * HDMI, etc. By default the first LVDS is tried, then the first
++   * eDP, and at the end, the first connected one.
++   */
++  g_properties[PROP_CONNECTOR_ID] = g_param_spec_int ("connector-id",
++      "Connector ID", "DRM connector id", -1, G_MAXINT32, -1,
++      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
++
++   /**
++   * kmssink:plane-id:
++   *
++   * There could be several planes associated with a CRTC.
++   * By default the first plane that's possible to use with a given
++   * CRTC is tried.
++   */
++  g_properties[PROP_PLANE_ID] = g_param_spec_int ("plane-id",
++      "Plane ID", "DRM plane id", -1, G_MAXINT32, -1,
++      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
++
++  g_object_class_install_properties (gobject_class, PROP_N, g_properties);
++}
++
++static gboolean
++plugin_init (GstPlugin * plugin)
++{
++  if (!gst_element_register (plugin, GST_PLUGIN_NAME, GST_RANK_SECONDARY,
++          GST_TYPE_KMS_SINK))
++    return FALSE;
++
++  return TRUE;
++}
++
++GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, kms,
++    GST_PLUGIN_DESC, plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
++    GST_PACKAGE_ORIGIN)
+diff --git a/sys/kms/gstkmssink.h b/sys/kms/gstkmssink.h
+new file mode 100644
+index 0000000..494f440
+--- /dev/null
++++ b/sys/kms/gstkmssink.h
+@@ -0,0 +1,87 @@
++/* GStreamer
++ *
++ * Copyright (C) 2016 Igalia
++ *
++ * Authors:
++ *  Víctor Manuel Jáquez Leal <vjaquez at igalia.com>
++ *  Javier Martin <javiermartin at by.com.es>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ *
++ */
++
++#ifndef __GST_KMS_SINK_H__
++#define __GST_KMS_SINK_H__
++
++#include <gst/video/gstvideosink.h>
++
++G_BEGIN_DECLS
++
++#define GST_TYPE_KMS_SINK \
++  (gst_kms_sink_get_type())
++#define GST_KMS_SINK(obj) \
++  (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_KMS_SINK, GstKMSSink))
++#define GST_KMS_SINK_CLASS(klass) \
++  (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_KMS_SINK, GstKMSSinkClass))
++#define GST_IS_KMS_SINK(obj) \
++  (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_KMS_SINK))
++#define GST_IS_KMS_SINK_CLASS(klass) \
++  (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_KMS_SINK))
++
++typedef struct _GstKMSSink GstKMSSink;
++typedef struct _GstKMSSinkClass GstKMSSinkClass;
++
++struct _GstKMSSink {
++  GstVideoSink videosink;
++
++  /*< private >*/
++  gint fd;
++  gint conn_id;
++  gint crtc_id;
++  gint plane_id;
++  guint pipe;
++
++  /* crtc data */
++  guint16 hdisplay, vdisplay;
++  guint32 buffer_id;
++
++  /* capabilities */
++  gboolean has_prime_import;
++  gboolean has_async_page_flip;
++
++  GstVideoInfo vinfo;
++  GstCaps *allowed_caps;
++  GstBufferPool *pool;
++  GstAllocator *allocator;
++  GstBuffer *last_buffer;
++
++  gchar *devname;
++
++  guint32 mm_width, mm_height;
++
++  GstPoll *poll;
++  GstPollFD pollfd;
++};
++
++struct _GstKMSSinkClass {
++  GstVideoSinkClass parent_class;
++};
++
++GType gst_kms_sink_get_type (void) G_GNUC_CONST;
++
++G_END_DECLS
++
++#endif /* __GST_KMS_SINK_H__ */
+diff --git a/sys/kms/gstkmsutils.c b/sys/kms/gstkmsutils.c
+new file mode 100644
+index 0000000..ddf8d2d
+--- /dev/null
++++ b/sys/kms/gstkmsutils.c
+@@ -0,0 +1,179 @@
++/* GStreamer
++ *
++ * Copyright (C) 2016 Igalia
++ *
++ * Authors:
++ *  Víctor Manuel Jáquez Leal <vjaquez at igalia.com>
++ *  Javier Martin <javiermartin at by.com.es>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ *
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <drm_fourcc.h>
++
++#include "gstkmsutils.h"
++
++/* *INDENT-OFF* */
++static const struct
++{
++  guint32 fourcc;
++  GstVideoFormat format;
++} format_map[] = {
++#define DEF_FMT(fourcc, fmt) \
++  { DRM_FORMAT_##fourcc,GST_VIDEO_FORMAT_##fmt }
++
++  /* DEF_FMT (XRGB1555, ???), */
++  /* DEF_FMT (XBGR1555, ???), */
++#if G_BYTE_ORDER == G_LITTLE_ENDIAN
++  DEF_FMT (ARGB8888, BGRA),
++  DEF_FMT (XRGB8888, BGRx),
++  DEF_FMT (ABGR8888, RGBA),
++  DEF_FMT (XBGR8888, RGBx),
++#else
++  DEF_FMT (ARGB8888, ARGB),
++  DEF_FMT (XRGB8888, xRGB),
++  DEF_FMT (ABGR8888, ABGR),
++  DEF_FMT (XBGR8888, xBGR),
++#endif
++  DEF_FMT (UYVY, UYVY),
++  DEF_FMT (YUYV, YUY2),
++  DEF_FMT (YVYU, YVYU),
++  DEF_FMT (YUV420, I420),
++  DEF_FMT (YVU420, YV12),
++  DEF_FMT (YUV422, Y42B),
++  DEF_FMT (NV12, NV12),
++  DEF_FMT (NV21, NV21),
++  DEF_FMT (NV16, NV16),
++
++#undef DEF_FMT
++};
++/* *INDENT-ON* */
++
++GstVideoFormat
++gst_video_format_from_drm (guint32 drmfmt)
++{
++  gint i;
++
++  for (i = 0; i < G_N_ELEMENTS (format_map); i++) {
++    if (format_map[i].fourcc == drmfmt)
++      return format_map[i].format;
++  }
++
++  return GST_VIDEO_FORMAT_UNKNOWN;
++}
++
++guint32
++gst_drm_format_from_video (GstVideoFormat fmt)
++{
++  gint i;
++
++  for (i = 0; i < G_N_ELEMENTS (format_map); i++) {
++    if (format_map[i].format == fmt)
++      return format_map[i].fourcc;
++  }
++
++  return 0;
++}
++
++static GstStructure *
++gst_video_format_to_structure (GstVideoFormat format)
++{
++  GstStructure *structure;
++
++  structure = NULL;
++  if (format != GST_VIDEO_FORMAT_UNKNOWN)
++    structure = gst_structure_new ("video/x-raw", "format", G_TYPE_STRING,
++        gst_video_format_to_string (format), NULL);
++
++  return structure;
++}
++
++GstCaps *
++gst_kms_sink_caps_template_fill (void)
++{
++  gint i;
++  GstCaps *caps;
++  GstStructure *template;
++
++  caps = gst_caps_new_empty ();
++  for (i = 0; i < G_N_ELEMENTS (format_map); i++) {
++    template = gst_video_format_to_structure (format_map[i].format);
++    gst_structure_set (template,
++        "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
++        "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
++        "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
++    gst_caps_append_structure (caps, template);
++  }
++  return gst_caps_simplify (caps);
++}
++
++static const gint device_par_map[][2] = {
++  {1, 1},                       /* regular screen */
++  {16, 15},                     /* PAL TV */
++  {11, 10},                     /* 525 line Rec.601 video */
++  {54, 59},                     /* 625 line Rec.601 video */
++  {64, 45},                     /* 1280x1024 on 16:9 display */
++  {5, 3},                       /* 1280x1024 on 4:3 display */
++  {4, 3}                        /* 800x600 on 16:9 display */
++};
++
++#define DELTA(ratio, idx, w) \
++  (ABS(ratio - ((gdouble)device_par_map[idx][w] / device_par_map[idx][!(w)])))
++
++void
++gst_video_calculate_device_ratio (guint dev_width, guint dev_height,
++    guint dev_width_mm, guint dev_height_mm,
++    guint * dpy_par_n, guint * dpy_par_d)
++{
++  gdouble ratio, delta, cur_delta;
++  gint i, j, index, windex;
++
++  /* First, calculate the "real" ratio based on the X values; which is
++   * the "physical" w/h divided by the w/h in pixels of the display */
++  if (dev_width == 0 || dev_height == 0
++      || dev_width_mm == 0 || dev_height_mm == 0)
++    ratio = 1.0;
++  else
++    ratio = (gdouble) (dev_width_mm * dev_height) / (dev_height_mm * dev_width);
++
++  /* Now, find the one from device_par_map[][2] with the lowest delta
++   * to the real one */
++  delta = DELTA (ratio, 0, 0);
++  index = 0;
++  windex = 0;
++
++  for (i = 1; i < G_N_ELEMENTS (device_par_map); i++) {
++    for (j = 0; j < 2; j++) {
++      cur_delta = DELTA (ratio, i, j);
++      if (cur_delta < delta) {
++        index = i;
++        windex = j;
++        delta = cur_delta;
++      }
++    }
++  }
++
++  if (dpy_par_n)
++    *dpy_par_n = device_par_map[index][windex];
++
++  if (dpy_par_d)
++    *dpy_par_d = device_par_map[index][windex ^ 1];
++}
+diff --git a/sys/kms/gstkmsutils.h b/sys/kms/gstkmsutils.h
+new file mode 100644
+index 0000000..75e9ba3
+--- /dev/null
++++ b/sys/kms/gstkmsutils.h
+@@ -0,0 +1,45 @@
++/* GStreamer
++ *
++ * Copyright (C) 2016 Igalia
++ *
++ * Authors:
++ *  Víctor Manuel Jáquez Leal <vjaquez at igalia.com>
++ *  Javier Martin <javiermartin at by.com.es>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ *
++ */
++
++#ifndef __GST_KMS_UTILS_H__
++#define __GST_KMS_UTILS_H__
++
++#include <gst/video/video.h>
++
++G_BEGIN_DECLS
++
++GstVideoFormat gst_video_format_from_drm (guint32 drmfmt);
++guint32        gst_drm_format_from_video (GstVideoFormat fmt);
++GstCaps *      gst_kms_sink_caps_template_fill (void);
++void           gst_video_calculate_device_ratio (guint dev_width,
++						 guint dev_height,
++						 guint dev_width_mm,
++						 guint dev_height_mm,
++						 guint * dpy_par_n,
++						 guint * dpy_par_d);
++
++G_END_DECLS
++
++#endif
+-- 
+2.1.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0002-Compile-kms.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0002-Compile-kms.patch
new file mode 100644
index 0000000..e6d3c3b
--- /dev/null
+++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0002-Compile-kms.patch
@@ -0,0 +1,79 @@
+From c2bc8302959e293c1da7c998c86e6c7090e48c3c Mon Sep 17 00:00:00 2001
+From: Devarsh Thakkar <devarsht at xilinx.com>
+Date: Thu, 8 Sep 2016 15:38:23 +0530
+Subject: [PATCH 2/3] gst-kmssink : Compile kms
+
+This is to compile necessary files needed for
+kmssink element.
+
+Signed-off-by: Devarsh Thakkar <devarsht at xilinx.com>
+---
+ configure.ac    |  9 +++++++++
+ sys/Makefile.am | 10 ++++++++--
+ 2 files changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index 1dc9b7f..1d5c0f9 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -2303,6 +2303,13 @@ AG_GST_CHECK_FEATURE(KATE, [Kate], kate, [
+   AC_SUBST(TIGER_LIBS)
+ ],,,[AM_CONDITIONAL(USE_TIGER, false)])
+ 
++dnl *** kms ***
++translit(dnm, m, l) AM_CONDITIONAL(USE_KMS, true)
++AG_GST_CHECK_FEATURE(KMS, [drm/kms libraries], kms, [
++  AG_GST_PKG_CHECK_MODULES(GST_ALLOCATORS, gstreamer-allocators-1.0)
++  PKG_CHECK_MODULES([DRM], [libdrm libkms], HAVE_KMS=yes, HAVE_KMS=no)
++])
++
+ dnl *** ladspa ***
+ translit(dnm, m, l) AM_CONDITIONAL(USE_LADSPA, true)
+ AG_GST_CHECK_FEATURE(LADSPA, [ladspa], ladspa, [
+@@ -3355,6 +3362,7 @@ AM_CONDITIONAL(USE_GTK3, false)
+ AM_CONDITIONAL(USE_GTK3_GL, false)
+ AM_CONDITIONAL(USE_HLS, false)
+ AM_CONDITIONAL(USE_KATE, false)
++AM_CONDITIONAL(USE_KMS, false)
+ AM_CONDITIONAL(USE_TIGER, false)
+ AM_CONDITIONAL(USE_LADSPA, false)
+ AM_CONDITIONAL(USE_LV2, false)
+@@ -3601,6 +3609,7 @@ sys/dshowsrcwrapper/Makefile
+ sys/dshowvideosink/Makefile
+ sys/dvb/Makefile
+ sys/fbdev/Makefile
++sys/kms/Makefile
+ sys/linsys/Makefile
+ sys/nvenc/Makefile
+ sys/opensles/Makefile
+diff --git a/sys/Makefile.am b/sys/Makefile.am
+index 32f79fb..9a34006 100644
+--- a/sys/Makefile.am
++++ b/sys/Makefile.am
+@@ -64,6 +64,12 @@ else
+ FBDEV_DIR=
+ endif
+ 
++if USE_KMS
++KMS_DIR=kms
++else
++KMS_DIR=
++endif
++
+ if USE_DVB
+ DVB_DIR=dvb
+ else
+@@ -148,9 +154,9 @@ else
+ TINYALSA_DIR=
+ endif
+ 
+-SUBDIRS = $(ACM_DIR) $(ANDROID_MEDIA_DIR) $(APPLE_MEDIA_DIR) $(AVC_DIR) $(BLUEZ_DIR) $(D3DVIDEOSINK_DIR) $(DECKLINK_DIR) $(DIRECTSOUND_DIR) $(WINKS_DIR) $(DVB_DIR) $(FBDEV_DIR) $(LINSYS_DIR) $(OPENSLES_DIR) $(PVR_DIR) $(SHM_DIR) $(UVCH264_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR) $(WINSCREENCAP_DIR) $(WASAPI_DIR) $(NVENC_DIR) $(TINYALSA_DIR)
++SUBDIRS = $(ACM_DIR) $(ANDROID_MEDIA_DIR) $(APPLE_MEDIA_DIR) $(AVC_DIR) $(BLUEZ_DIR) $(D3DVIDEOSINK_DIR) $(DECKLINK_DIR) $(DIRECTSOUND_DIR) $(WINKS_DIR) $(DVB_DIR) $(FBDEV_DIR) $(KMS_DIR) $(LINSYS_DIR) $(OPENSLES_DIR) $(PVR_DIR) $(SHM_DIR) $(UVCH264_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR) $(WINSCREENCAP_DIR) $(WASAPI_DIR) $(NVENC_DIR) $(TINYALSA_DIR)
+ 
+-DIST_SUBDIRS = acmenc acmmp3dec androidmedia applemedia applemedia-nonpublic avc bluez d3dvideosink decklink directsound dvb linsys fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \
++DIST_SUBDIRS = acmenc acmmp3dec androidmedia applemedia applemedia-nonpublic avc bluez d3dvideosink decklink directsound dvb linsys fbdev kms dshowdecwrapper dshowsrcwrapper dshowvideosink \
+ 		opensles pvr2d shm uvch264 vcd vdpau wasapi wininet winks winscreencap \
+ 		nvenc tinyalsa
+ 
+--
+2.5.0 
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0003-gst-kmssink-Add-support-for-xilinx-drm.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0003-gst-kmssink-Add-support-for-xilinx-drm.patch
new file mode 100644
index 0000000..852951d
--- /dev/null
+++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0003-gst-kmssink-Add-support-for-xilinx-drm.patch
@@ -0,0 +1,30 @@
+From 91034d1a80ea76a5bf89fde80420c4884d346cbf Mon Sep 17 00:00:00 2001
+From: Devarsh Thakkar <devarsht at xilinx.com>
+Date: Thu, 8 Sep 2016 15:43:17 +0530
+Subject: [PATCH 3/4] gst-kmssink : Add support for xilinx-drm
+
+Now xilinx drm driver will also be searched
+while searching for drm drivers needed for using
+kmssink.
+
+Signed-off-by: Devarsh Thakkar <devarsht at xilinx.com>
+---
+ sys/kms/gstkmssink.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sys/kms/gstkmssink.c b/sys/kms/gstkmssink.c
+index 72cb1f7..861aad7 100644
+--- a/sys/kms/gstkmssink.c
++++ b/sys/kms/gstkmssink.c
+@@ -84,7 +84,7 @@ static int
+ kms_open (gchar ** driver)
+ {
+   static const char *drivers[] = { "i915", "radeon", "nouveau", "vmwgfx",
+-    "exynos", "amdgpu", "imx-drm", "rockchip", "atmel-hlcdc"
++    "exynos", "amdgpu", "imx-drm", "rockchip", "atmel-hlcdc", "xilinx_drm"
+   };
+   int i, fd = -1;
+ 
+-- 
+2.1.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-kmssink-override-stride-if-defined-in-driver.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-kmssink-override-stride-if-defined-in-driver.patch
new file mode 100644
index 0000000..8de2073
--- /dev/null
+++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-kmssink-override-stride-if-defined-in-driver.patch
@@ -0,0 +1,53 @@
+From 504f1a7e1fe5942c87bda3dee4eb5b8876bb6529 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?V=C3=ADctor=20Manuel=20J=C3=A1quez=20Leal?=
+ <vjaquez at igalia.com>
+Date: Fri, 5 Aug 2016 18:17:32 +0200
+Subject: [PATCH] kmssink: override stride if defined in driver
+
+Some kms drivers demands specific pitches over the ones calculated by
+GstVideoInfo. For example, intel driver demands strides round up 64.
+
+This patch queries the driver for the prefered pitch and overwrites it
+in the pool's GstVideoInfo structure.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=768446
+---
+ sys/kms/gstkmsallocator.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/sys/kms/gstkmsallocator.c b/sys/kms/gstkmsallocator.c
+index 7df1aa3..e031848 100644
+--- a/sys/kms/gstkmsallocator.c
++++ b/sys/kms/gstkmsallocator.c
+@@ -303,7 +303,7 @@ gst_kms_allocator_add_fb (GstKMSAllocator * alloc, GstKMSMemory * kmsmem,
+ {
+   int i, ret;
+   gint num_planes = GST_VIDEO_INFO_N_PLANES (vinfo);
+-  guint32 w, h, fmt, bo_handles[4] = { 0, };
++  guint32 w, h, fmt, pitch = 0, bo_handles[4] = { 0, };
+   guint32 offsets[4] = { 0, };
+   guint32 pitches[4] = { 0, };
+ 
+@@ -318,6 +318,10 @@ gst_kms_allocator_add_fb (GstKMSAllocator * alloc, GstKMSMemory * kmsmem,
+     kms_bo_get_prop (kmsmem->bo, KMS_HANDLE, &bo_handles[0]);
+     for (i = 1; i < num_planes; i++)
+       bo_handles[i] = bo_handles[0];
++
++    /* Get the bo pitch calculated by the kms driver.
++     * If it's defined, it will overwrite the video info's stride */
++    kms_bo_get_prop (kmsmem->bo, KMS_PITCH, &pitch);
+   } else {
+     for (i = 0; i < num_planes; i++)
+       bo_handles[i] = kmsmem->gem_handle[i];
+@@ -328,6 +332,8 @@ gst_kms_allocator_add_fb (GstKMSAllocator * alloc, GstKMSMemory * kmsmem,
+ 
+   for (i = 0; i < num_planes; i++) {
+     offsets[i] = mem_offsets[i];
++    if (pitch)
++      GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i) = pitch;
+     pitches[i] = GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i);
+     GST_DEBUG_OBJECT (alloc, "Create FB plane %i with stride %u and offset %u",
+         i, pitches[i], offsets[i]);
+-- 
+2.1.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0005-kmssink-Fix-selection-of-source-region.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0005-kmssink-Fix-selection-of-source-region.patch
new file mode 100644
index 0000000..4f818e9
--- /dev/null
+++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0005-kmssink-Fix-selection-of-source-region.patch
@@ -0,0 +1,87 @@
+From 1a62d5735f5a326a2524e52054ce98ead24f76a2 Mon Sep 17 00:00:00 2001
+From: Nicolas Dufresne <nicolas.dufresne at collabora.com>
+Date: Thu, 8 Sep 2016 11:21:09 -0400
+Subject: [PATCH] kmssink: Fix selection of source region
+
+The source region was scaled for display before being passed
+to drmModeSetPlane, which resulted in a portion of the video
+being cropped. While when crop meta was present, the rectangle
+was not centered since we where using unscaled width/height.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=767422
+---
+ sys/kms/gstkmssink.c | 44 ++++++++++++++++++++++++++++++--------------
+ 1 file changed, 30 insertions(+), 14 deletions(-)
+
+diff --git a/sys/kms/gstkmssink.c b/sys/kms/gstkmssink.c
+index 861aad7..286e288 100644
+--- a/sys/kms/gstkmssink.c
++++ b/sys/kms/gstkmssink.c
+@@ -1084,27 +1084,37 @@ gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
+ 
+   GST_TRACE_OBJECT (self, "displaying fb %d", fb_id);
+ 
+-  {
+-    if ((crop = gst_buffer_get_video_crop_meta (buffer))) {
+-      src.x = crop->x;
+-      src.y = crop->y;
+-      src.w = crop->width;
+-      src.h = crop->height;
+-    } else {
+-      src.w = GST_VIDEO_SINK_WIDTH (self);
+-      src.h = GST_VIDEO_SINK_HEIGHT (self);
+-    }
++  if ((crop = gst_buffer_get_video_crop_meta (buffer))) {
++    GstVideoInfo vinfo = self->vinfo;
++    vinfo.width = crop->width;
++    vinfo.height = crop->height;
++
++    if (!gst_kms_sink_calculate_display_ratio (self, &vinfo))
++      goto no_disp_ratio;
++
++    src.x = crop->x;
++    src.y = crop->y;
+   }
+ 
++  src.w = GST_VIDEO_SINK_WIDTH (self);
++  src.h = GST_VIDEO_SINK_HEIGHT (self);
++
+   dst.w = self->hdisplay;
+   dst.h = self->vdisplay;
+ 
+   gst_video_sink_center_rect (src, dst, &result, FALSE);
+ 
+-  /* if the frame size is bigger than the display size, the source
+-   * must be the display size */
+-  src.w = MIN (src.w, self->hdisplay);
+-  src.h = MIN (src.h, self->vdisplay);
++  if (crop) {
++    src.w = crop->width;
++    src.h = crop->height;
++  } else {
++    src.w = GST_VIDEO_INFO_WIDTH (&self->vinfo);
++    src.h = GST_VIDEO_INFO_HEIGHT (&self->vinfo);
++  }
++
++  GST_TRACE_OBJECT (self,
++      "drmModeSetPlane at (%i,%i) %ix%i sourcing at (%i,%i) %ix%i",
++      result.x, result.y, result.w, result.h, src.x, src.y, src.w, src.h);
+ 
+   ret = drmModeSetPlane (self->fd, self->plane_id, self->crtc_id, fb_id, 0,
+       result.x, result.y, result.w, result.h,
+@@ -1141,6 +1151,12 @@ set_plane_failed:
+         (NULL), ("drmModeSetPlane failed: %s (%d)", strerror (-ret), ret));
+     goto bail;
+   }
++no_disp_ratio:
++  {
++    GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
++        ("Error calculating the output display ratio of the video."));
++    goto bail;
++  }
+ }
+ 
+ static void
+-- 
+2.1.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0006-kmssink-Scale-up-to-the-screen-dimension.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0006-kmssink-Scale-up-to-the-screen-dimension.patch
new file mode 100644
index 0000000..0df8d7c
--- /dev/null
+++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0006-kmssink-Scale-up-to-the-screen-dimension.patch
@@ -0,0 +1,30 @@
+From 40cef677401fcab1642b209c634b1c74abb45cfa Mon Sep 17 00:00:00 2001
+From: Nicolas Dufresne <nicolas.dufresne at collabora.com>
+Date: Thu, 8 Sep 2016 11:23:57 -0400
+Subject: [PATCH] kmssink: Scale up to the screen dimension
+
+In most display sink, the logic is to use as much as possible
+of the given window. In this case, the window is the screen,
+hence it's logical to scale up.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=767422
+---
+ sys/kms/gstkmssink.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sys/kms/gstkmssink.c b/sys/kms/gstkmssink.c
+index 286e288..be990f3 100644
+--- a/sys/kms/gstkmssink.c
++++ b/sys/kms/gstkmssink.c
+@@ -1102,7 +1102,7 @@ gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
+   dst.w = self->hdisplay;
+   dst.h = self->vdisplay;
+ 
+-  gst_video_sink_center_rect (src, dst, &result, FALSE);
++  gst_video_sink_center_rect (src, dst, &result, TRUE);
+ 
+   if (crop) {
+     src.w = crop->width;
+-- 
+2.1.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0007-kmssink-experimentation.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0007-kmssink-experimentation.patch
new file mode 100644
index 0000000..82231eb
--- /dev/null
+++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0007-kmssink-experimentation.patch
@@ -0,0 +1,88 @@
+From 3a3528ea280e6f9231efba2417ae97dc06516788 Mon Sep 17 00:00:00 2001
+From: Hyun Kwon <hyun.kwon at xilinx.com>
+Date: Thu, 10 Nov 2016 15:09:47 -0800
+Subject: [PATCH 1/1] kmssink: experimentation
+
+Signed-off-by: Hyun Kwon <hyun.kwon at xilinx.com>
+---
+ sys/kms/gstkmsallocator.c | 28 +++++++++++++++++++++++-----
+ 1 file changed, 23 insertions(+), 5 deletions(-)
+
+diff --git a/sys/kms/gstkmsallocator.c b/sys/kms/gstkmsallocator.c
+index e031848..d368397 100644
+--- a/sys/kms/gstkmsallocator.c
++++ b/sys/kms/gstkmsallocator.c
+@@ -126,6 +126,11 @@ gst_kms_allocator_memory_create (GstKMSAllocator * allocator,
+     KMS_HEIGHT, GST_VIDEO_INFO_HEIGHT (vinfo),
+     KMS_TERMINATE_PROP_LIST,
+   };
++  guint i, virt_height = 0;
++
++  for (i = 0; i < vinfo->finfo->n_planes; i++)
++    virt_height += GST_VIDEO_INFO_HEIGHT (vinfo) >> vinfo->finfo->h_sub[i];
++  attrs[3] = virt_height;
+ 
+   if (kmsmem->bo)
+     return TRUE;
+@@ -304,6 +309,7 @@ gst_kms_allocator_add_fb (GstKMSAllocator * alloc, GstKMSMemory * kmsmem,
+   int i, ret;
+   gint num_planes = GST_VIDEO_INFO_N_PLANES (vinfo);
+   guint32 w, h, fmt, pitch = 0, bo_handles[4] = { 0, };
++  guint32 prev_offset = 0, prev_size = 0;
+   guint32 offsets[4] = { 0, };
+   guint32 pitches[4] = { 0, };
+ 
+@@ -331,9 +337,16 @@ gst_kms_allocator_add_fb (GstKMSAllocator * alloc, GstKMSMemory * kmsmem,
+       bo_handles[1], bo_handles[2], bo_handles[3]);
+ 
+   for (i = 0; i < num_planes; i++) {
+-    offsets[i] = mem_offsets[i];
+-    if (pitch)
++    if (pitch) {
+       GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i) = pitch;
++      offsets[i] = prev_offset + prev_size;
++      GST_VIDEO_INFO_PLANE_OFFSET(vinfo, i) = offsets[i];
++      prev_offset = offsets[i];
++      prev_size = pitch * h >> vinfo->finfo->h_sub[i];
++      GST_VIDEO_INFO_SIZE(vinfo) = prev_offset + prev_size;
++    } else {
++      offsets[i] = mem_offsets[i];
++    }
+     pitches[i] = GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i);
+     GST_DEBUG_OBJECT (alloc, "Create FB plane %i with stride %u and offset %u",
+         i, pitches[i], offsets[i]);
+@@ -360,9 +373,6 @@ gst_kms_allocator_alloc_empty (GstAllocator * allocator, GstVideoInfo * vinfo)
+     return NULL;
+   mem = GST_MEMORY_CAST (kmsmem);
+ 
+-  gst_memory_init (mem, GST_MEMORY_FLAG_NO_SHARE, allocator, NULL,
+-      GST_VIDEO_INFO_SIZE (vinfo), 0, 0, GST_VIDEO_INFO_SIZE (vinfo));
+-
+   return mem;
+ }
+ 
+@@ -384,6 +394,10 @@ gst_kms_allocator_bo_alloc (GstAllocator * allocator, GstVideoInfo * vinfo)
+   if (!gst_kms_allocator_add_fb (alloc, kmsmem, vinfo->offset, vinfo))
+     goto fail;
+ 
++  /* Initialize gst memory with vinfo updated with kms device */
++  gst_memory_init (mem, GST_MEMORY_FLAG_NO_SHARE, allocator, NULL,
++      GST_VIDEO_INFO_SIZE (vinfo), 0, 0, GST_VIDEO_INFO_SIZE (vinfo));
++
+   return mem;
+ 
+   /* ERRORS */
+@@ -419,6 +433,10 @@ gst_kms_allocator_dmabuf_import (GstAllocator * allocator, gint * prime_fds,
+   if (!gst_kms_allocator_add_fb (alloc, tmp, offsets, vinfo))
+     goto failed;
+ 
++  /* Initialize gst memory with vinfo updated with kms device */
++  gst_memory_init (mem, GST_MEMORY_FLAG_NO_SHARE, allocator, NULL,
++      GST_VIDEO_INFO_SIZE (vinfo), 0, 0, GST_VIDEO_INFO_SIZE (vinfo));
++
+   return tmp;
+ 
+   /* ERRORS */
+-- 
+2.7.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_%.bbappend b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_%.bbappend
new file mode 100644
index 0000000..463cebd
--- /dev/null
+++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_%.bbappend
@@ -0,0 +1,18 @@
+PACKAGECONFIG_GL = "${@bb.utils.contains('DISTRO_FEATURES', 'opengl', ' opengl gles2', '', d)}"
+PACKAGECONFIG_append = "faad"
+
+FILESEXTRAPATHS_prepend := "${THISDIR}/gstreamer1.0-plugins-bad:"
+
+#
+# Need to make this conditional to gstreamer1
+#
+SRC_URI_append_zynqmp = " \
+    file://0001-gst-plugins-bad-Copy-kmssink-from-1.9.2.patch \
+    file://0002-Compile-kms.patch \
+    file://0003-gst-kmssink-Add-support-for-xilinx-drm.patch \
+    file://0004-kmssink-override-stride-if-defined-in-driver.patch \
+    file://0005-kmssink-Fix-selection-of-source-region.patch \
+    file://0006-kmssink-Scale-up-to-the-screen-dimension.patch \
+    file://0007-kmssink-experimentation.patch \
+"
+
-- 
1.9.1




More information about the meta-xilinx mailing list