[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, ¶ms);
++
++ /* 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