[vlc-devel] [PATCH] linux: kernel mode setting (KMS) vout plugin

Juha-Pekka Heikkila juhapekka.heikkila at gmail.com
Tue May 15 14:10:00 CEST 2018


Add new kernel mode setting video output plugin.
By default it uses 32bpp XRGB mode at output video mode but it can be 
adjusted using commandline parameters.

kms-vlc-chroma parameter to force VLC to use FourCC as output.
kms-drm-chroma parameter to give DRM FourCC request for framebuffer type.

Signed-off-by: Juha-Pekka Heikkila <juhapekka.heikkila at gmail.com>
---
 configure.ac                     |   5 +
 modules/video_output/Makefile.am |  10 +
 modules/video_output/kms.c       | 794 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 809 insertions(+)
 create mode 100644 modules/video_output/kms.c

diff --git a/configure.ac b/configure.ac
index 960e245..622516a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3338,6 +3338,11 @@ AC_CHECK_HEADER([linux/fb.h], [
 ])
 
 dnl
+dnl  Linux kernel mode setting module
+dnl
+PKG_ENABLE_MODULES_VLC([KMS], [], [libdrm >= 2.4.83], [Linux kernel mode setting output], [auto])
+
+dnl
 dnl  libcaca plugin
 dnl
 PKG_ENABLE_MODULES_VLC([CACA], [], [caca >= 0.99.beta14], [libcaca output],[auto])
diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index 17967b5..a864c1a 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -403,6 +403,16 @@ EXTRA_LTLIBRARIES += libfb_plugin.la
 vout_LTLIBRARIES += $(LTLIBfb)
 
 
+### Kernel Mode Setting ###
+
+libkms_plugin_la_SOURCES = video_output/kms.c
+libkms_plugin_la_CFLAGS = $(AM_CFLAGS) $(KMS_CFLAGS)
+libkms_plugin_la_LIBADD = $(KMS_LIBS)
+libkms_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(voutdir)'
+EXTRA_LTLIBRARIES += libkms_plugin.la
+vout_LTLIBRARIES += $(LTLIBkms)
+
+
 ### Coloured ASCII art ###
 libcaca_plugin_la_SOURCES = video_output/caca.c
 libcaca_plugin_la_CFLAGS = $(AM_CFLAGS) $(CACA_CFLAGS)
diff --git a/modules/video_output/kms.c b/modules/video_output/kms.c
new file mode 100644
index 0000000..1c02211
--- /dev/null
+++ b/modules/video_output/kms.c
@@ -0,0 +1,794 @@
+/*****************************************************************************
+ * kms.c : kernel mode setting plugin for vlc
+ *****************************************************************************
+ * Copyright © 2018 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+#include <sys/mman.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_mode.h>
+#include <drm_fourcc.h>
+#include <drm.h>
+
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_vout_display.h>
+#include <vlc_picture_pool.h>
+#include <vlc_fs.h>
+#include <poll.h>
+#include <time.h>
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+#define KMS_VAR "kms"
+
+#define DEVICE_TEXT N_("Framebuffer device")
+#define DEVICE_LONGTEXT N_(\
+    "Framebuffer device to use for rendering (usually /dev/dri/card0).")
+
+#define VLC_CHROMA_TEXT N_("Image format used by VLC (default RV32 which mean 32bpp RGB)")
+#define VLC_CHROMA_LONGTEXT N_("Chroma fourcc told to VLC about framebuffer. Default is RV32 which stands for 32bpp RGB.")
+
+#define DRM_CHROMA_TEXT N_("Image format used by DRM (default XR24 which mean 32bpp RGB)")
+#define DRM_CHROMA_LONGTEXT N_("Chroma fourcc for requested DRM framebuffer. Default is XR24 which stands for 32bpp RGB.")
+
+static int  Open (vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin ()
+    set_shortname("kms")
+    set_category(CAT_VIDEO)
+    set_subcategory(SUBCAT_VIDEO_VOUT)
+    add_loadfile(KMS_VAR, "/dev/dri/card0", DEVICE_TEXT, DEVICE_LONGTEXT,
+                 false)
+
+    add_string( "kms-vlc-chroma", NULL, VLC_CHROMA_TEXT, VLC_CHROMA_LONGTEXT,
+                true)
+    add_string( "kms-drm-chroma", NULL, DRM_CHROMA_TEXT, DRM_CHROMA_LONGTEXT,
+                true)
+    set_description(N_("Linux kernel mode setting video output"))
+    set_capability("vout display", 30)
+    set_callbacks(Open, Close)
+vlc_module_end ()
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+#define HWBUF1 0
+#define HWBUF2 1
+
+struct vout_display_sys_t {
+/*
+ * buffer information
+ */
+    uint32_t        width;
+    uint32_t        height;
+    uint32_t        stride;
+    uint32_t        size;
+    uint32_t        offsets[5];
+
+    uint32_t        handle[5];
+    uint8_t         *map[5];
+
+    uint32_t        fb[5];
+    picture_t       *picture[5];
+    picture_pool_t  *pool[5];
+
+    unsigned int    front_buf;
+
+    uint32_t        drm_fourcc;
+    vlc_fourcc_t    vlc_fourcc;
+
+/*
+ * modeset information
+ */
+    drmModeModeInfo mode;
+    uint32_t        connector_id;
+    uint32_t        crtc;
+    uint32_t        plane_id;
+    drmModeCrtc     *stored_crtc;
+
+/*
+ * other generic stuff
+ */
+    bool            remote;
+    int             drm_fd;
+    struct vt_mode  vt_mode;
+    struct termios  old_termios;
+};
+
+
+static int TtyInit(vout_display_t *vd)
+{
+    vout_display_sys_t *sys = vd->sys;
+
+    struct termios new_termios;
+    char* connectioncheckname;
+
+    connectioncheckname = ttyname(STDERR_FILENO);
+
+    if( strstr(connectioncheckname, "pts") != NULL) {
+        /*
+         * we're on remote connection
+         */
+        sys->remote = true;
+        return VLC_SUCCESS;
+    }
+
+    if (tcgetattr(0, &sys->old_termios) == -1) {
+        msg_Err(vd, "tcgetattr failed");
+    }
+
+    if (tcgetattr(0, &new_termios) == -1) {
+        msg_Err(vd, "tcgetattr failed");
+    }
+
+    new_termios.c_lflag &= ~ (ICANON);
+    new_termios.c_lflag &= ~(ECHO | ECHOCTL);
+    new_termios.c_iflag = 0;
+    new_termios.c_cc[VMIN] = 1;
+    new_termios.c_cc[VTIME] = 0;
+
+    if (tcsetattr(0, TCSAFLUSH, &new_termios) == -1) {
+        msg_Err(vd, "tcsetattr failed");
+    }
+
+    /*
+     *  Set-up tty according to new signal handler
+     */
+    if (-1 == ioctl(sys->drm_fd, VT_GETMODE, &sys->vt_mode)) {
+        msg_Err(vd, "cannot get terminal mode (%s)", vlc_strerror_c(errno));
+        goto error;
+    }
+    struct vt_mode vt_mode = sys->vt_mode;
+    vt_mode.mode   = VT_PROCESS;
+    vt_mode.waitv  = 0;
+    vt_mode.relsig = SIGUSR1;
+    vt_mode.acqsig = SIGUSR2;
+
+    if (-1 == ioctl(sys->drm_fd, VT_SETMODE, &vt_mode)) {
+        msg_Err(vd, "cannot set terminal mode (%s)", vlc_strerror_c(errno));
+        goto error;
+    }
+    return VLC_SUCCESS;
+error:
+    tcsetattr(0, 0, &sys->old_termios);
+    return VLC_EGENERIC;
+}
+
+
+static void DestroyFB(vout_display_t *vd, int buf)
+{
+    vout_display_sys_t *sys = vd->sys;
+    struct drm_mode_destroy_dumb destroy_req = { .handle = sys->handle[buf] };
+
+    munmap(sys->map[buf], sys->size);
+    drmModeRmFB(sys->drm_fd, sys->fb[buf]);
+    drmIoctl(sys->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_req);
+}
+
+
+#define ALIGN(v, a) (((v) + (a)-1) & ~((a)-1))
+
+static int CreateFB(vout_display_t *vd, int buf)
+{
+    vout_display_sys_t *sys = vd->sys;
+    struct drm_mode_create_dumb create_req = { .width = sys->width,
+                                               .height = sys->height,
+                                               .bpp = 16 };
+    struct drm_mode_destroy_dumb destroy_req;
+    struct drm_mode_map_dumb modify_req = {};
+    struct drm_mode_fb_cmd2 fb_cmd = {};
+    unsigned int tile_width = 512, tile_height = 16;
+    int ret, i;
+
+    switch(sys->drm_fourcc) {
+#ifdef DRM_FORMAT_P010
+    case DRM_FORMAT_P010:
+#endif
+#ifdef DRM_FORMAT_P012
+    case DRM_FORMAT_P012:
+#endif
+#ifdef DRM_FORMAT_P016
+    case DRM_FORMAT_P016:
+#endif
+#if defined(DRM_FORMAT_P010) || defined(DRM_FORMAT_P012) || defined(DRM_FORMAT_P016)
+        sys->stride = ALIGN(sys->width*2, tile_width);
+        sys->offsets[1] = sys->stride*ALIGN(sys->height, tile_height);
+        offsetpointer = (uint32_t*)&sys->offsets;
+        create_req.height = ALIGN(sys->height*2, tile_height);
+		break;
+#endif
+    case DRM_FORMAT_NV12:
+        sys->stride = ALIGN(sys->width, tile_width);
+        sys->offsets[1] = sys->stride*ALIGN(sys->height, tile_height);
+        create_req.height = ALIGN(sys->height*2, tile_height);
+        break;
+    default:
+        create_req.height = ALIGN(sys->height*2, tile_height);
+
+        /*
+         * width *4 so there's enough space for anything.
+         */
+        sys->stride = ALIGN(sys->width*4, tile_width);
+        break;
+    }
+
+    ret = drmIoctl(sys->drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_req);
+    if (ret < 0) {
+        msg_Err(vd, "cannot create buffer (%s)", vlc_strerror_c(errno));
+        return -errno;
+    }
+
+    sys->size = create_req.size;
+    sys->handle[buf] = create_req.handle;
+
+    /*
+     * create framebuffer object for the dumb-buffer
+     */
+    fb_cmd.width  = sys->width;
+    fb_cmd.height = sys->height;
+    fb_cmd.pixel_format = sys->drm_fourcc;
+    fb_cmd.flags = (1<<1);
+    fb_cmd.handles[0] = create_req.handle;
+    fb_cmd.pitches[0] = sys->stride;
+    fb_cmd.modifier[0] = 0;
+
+    for (i = 1; i < 4 && sys->offsets[i]; i++) {
+        fb_cmd.handles[i] = create_req.handle;
+        fb_cmd.pitches[i] = sys->stride;
+        fb_cmd.modifier[i] = 0;
+        fb_cmd.offsets[i] = sys->offsets[i];
+    }
+
+    ret = drmModeAddFB2(sys->drm_fd, sys->width, sys->height, sys->drm_fourcc,
+                        (uint32_t*)&fb_cmd.handles,
+                        (uint32_t*)&fb_cmd.pitches,
+                        (uint32_t*)&fb_cmd.offsets,
+                        (uint32_t*)&sys->fb[buf], 1<<1);
+
+    if (ret) {
+        msg_Err(vd, "cannot create framebuffer (%s)\n",
+                vlc_strerror_c(errno));
+        ret = -errno;
+        goto err_destroy;
+    }
+
+    modify_req.handle = sys->handle[buf];
+    ret = drmIoctl(sys->drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &modify_req);
+    if (ret) {
+        msg_Err(vd, "cannot map dumb buffer (%s)\n", vlc_strerror_c(errno));
+        ret = -errno;
+        goto err_fb;
+    }
+
+    sys->map[buf] = mmap(0, sys->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+                         sys->drm_fd, modify_req.offset);
+
+    if (sys->map[buf] == MAP_FAILED) {
+        msg_Err(vd, "cannot mmap dumb buffer (%s)\n", vlc_strerror_c(errno));
+        ret = -errno;
+        goto err_fb;
+    }
+    return 0;
+err_fb:
+    drmModeRmFB(sys->drm_fd, sys->fb[buf]);
+err_destroy:
+    memset(&destroy_req, 0, sizeof(destroy_req));
+    destroy_req.handle = sys->handle[buf];
+    drmIoctl(sys->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_req);
+    return ret;
+}
+
+
+static int FindCRTC(vout_display_t *vd, drmModeRes *res,
+                             drmModeConnector *conn)
+{
+    vout_display_sys_t *sys = vd->sys;
+    drmModeEncoder *enc;
+    int i, j;
+    int32_t crtc;
+
+    /*
+     * Try current encoder and crtc combo
+     */
+    if (conn->encoder_id)
+        enc = drmModeGetEncoder(sys->drm_fd, conn->encoder_id);
+    else
+        enc = NULL;
+
+    if (enc) {
+        if (enc->crtc_id) {
+            crtc = enc->crtc_id;
+
+            if (crtc >= 0) {
+                msg_Dbg(vd, "found crtc %d from current encoder!", crtc);
+                drmModeFreeEncoder(enc);
+                sys->crtc = crtc;
+                return 0;
+            }
+        }
+        drmModeFreeEncoder(enc);
+    }
+
+    /*
+     * Iterate all other available encoders to find crtc
+     */
+    for(i = 0; i < conn->count_encoders; i++) {
+        enc = drmModeGetEncoder(sys->drm_fd, conn->encoders[i]);
+        if (!enc) {
+            msg_Info(vd, "cannot retrieve encoder %u:%u (%s)", i,
+                     conn->encoders[i], vlc_strerror_c(errno));
+            continue;
+        }
+
+        for (j = 0; j < res->count_crtcs; ++j) {
+            if (!(enc->possible_crtcs & (1 << j)))
+                continue;
+
+            crtc = res->crtcs[j];
+
+            if (crtc >= 0) {
+                msg_Dbg(vd, "found crtc %d", crtc);
+                drmModeFreeEncoder(enc);
+                sys->crtc = crtc;
+                return 0;
+            }
+        }
+        drmModeFreeEncoder(enc);
+    }
+
+    msg_Err(vd , "cannot find crtc for connector %d", conn->connector_id);
+    return -ENOENT;
+}
+
+
+static int SetupDevice(vout_display_t *vd, drmModeRes *res,
+                             drmModeConnector *conn)
+{
+    vout_display_sys_t *sys = vd->sys;
+    int ret;
+
+    if (conn->connection != DRM_MODE_CONNECTED)
+        return -ENOENT;
+
+    /*
+     * check if there is at least one valid mode
+     */
+    if (conn->count_modes == 0) {
+        msg_Err(vd, "no valid mode for connector %d", conn->connector_id);
+        return -EFAULT;
+    }
+
+    memcpy(&sys->mode, &conn->modes[0], sizeof(sys->mode));
+    sys->width = conn->modes[0].hdisplay;
+    sys->height = conn->modes[0].vdisplay;
+    msg_Dbg(vd, "mode for connector %u is %ux%u", conn->connector_id,
+            sys->width, sys->height);
+
+    ret = FindCRTC(vd, res, conn);
+    if (ret) {
+        msg_Dbg(vd , "no valid crtc for connector %d", conn->connector_id);
+        return ret;
+    }
+
+    /*
+     * create framebuffer #1 for this CRTC
+     */
+    ret = CreateFB(vd, HWBUF1);
+    if (ret) {
+        msg_Err(vd, "cannot create framebuffer 0 for connector %d",
+                conn->connector_id);
+
+        return ret;
+    }
+
+    /*
+     * create framebuffer #2 for this CRTC
+     */
+    ret = CreateFB(vd, HWBUF2);
+    if (ret) {
+        msg_Err(vd, "cannot create framebuffer 1 for connector %u",
+                conn->connector_id);
+
+        DestroyFB(vd, HWBUF1);
+        return ret;
+    }
+    return 0;
+}
+
+
+static int OpenDisplay(vout_display_t *vd)
+{
+    vout_display_sys_t *sys = vd->sys;
+    char *psz_device;
+    int ret;
+    uint64_t dumbRet;
+    drmModeConnector *conn;
+    drmModeRes *modeRes;
+    drmModePlaneRes *plane_res = NULL;
+    drmModePlane *plane;
+    drmModeObjectProperties *props;
+    drmModePropertyPtr pp;
+    unsigned int c, i, propi;
+    uint32_t planetype;
+    const char types[][16] = { "OVERLAY", "PRIMARY", "CURSOR", "UNKNOWN" };
+    bool found_plane = false;
+    bool selected;
+
+    /*
+     * Open framebuffer device
+     */
+    if (!(psz_device = var_InheritString(vd, KMS_VAR))) {
+        msg_Err(vd, "don't know which drm device to open");
+        return VLC_EGENERIC;
+    }
+
+    sys->drm_fd = vlc_open(psz_device, O_RDWR);
+    if (sys->drm_fd == -1) {
+        msg_Err(vd, "cannot open %s (%s)", psz_device, vlc_strerror_c(errno));
+        free(psz_device);
+        return VLC_EGENERIC;
+    }
+
+    if(!sys->remote)
+        ioctl(sys->drm_fd, KDSETMODE, KD_GRAPHICS);
+
+    ret = drmSetMaster(sys->drm_fd);
+    if (ret < 0) {
+        msg_Err(vd, "Cannot become drm master on %s (%s)", psz_device,
+                vlc_strerror_c(-ret));
+
+        free(psz_device);
+        vlc_close(sys->drm_fd);
+        sys->drm_fd = 0;
+        return VLC_EGENERIC;
+    }
+    free(psz_device);
+
+    drmSetClientCap(sys->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+
+    /*
+     * For convenience print out in debug prints all supported
+     * drm modes so they can be seen if use verbose mode.
+     */
+    plane_res = drmModeGetPlaneResources(sys->drm_fd);
+    sys->plane_id = 0;
+
+    if( plane_res != NULL && plane_res->count_planes > 0 ) {
+        msg_Dbg(vd, "List of DRM supported modes on this machine:");
+        for(c = 0; c < plane_res->count_planes; c++) {
+
+            plane = drmModeGetPlane(sys->drm_fd, plane_res->planes[c]);
+            if(plane != NULL && plane->count_formats > 0) {
+                props = drmModeObjectGetProperties(sys->drm_fd,
+                                                   plane->plane_id,
+                                                   DRM_MODE_OBJECT_PLANE);
+
+                planetype = 3;
+                pp = NULL;
+                for(propi = 0; propi < props->count_props; propi++) {
+                    pp = drmModeGetProperty(sys->drm_fd, props->props[propi]);
+                    if(strcmp(pp->name, "type") == 0) {
+                        break;
+                    }
+                    drmModeFreeProperty(pp);
+                }
+
+                if(pp != NULL) {
+                    drmModeFreeProperty(pp);
+                    planetype = props->prop_values[propi];
+                }
+
+                for( i = 0; i < plane->count_formats; i++) {
+                    /*
+                     * we choose first plane we find which support requested
+                     * format. Only primary or overlay, cursor has its
+                     * limitations we don't want to tinker with.
+                     */
+                    if(!found_plane &&
+                            sys->drm_fourcc == plane->formats[i] &&
+                            (planetype == DRM_PLANE_TYPE_PRIMARY ||
+                            planetype == DRM_PLANE_TYPE_OVERLAY)) {
+                        sys->plane_id = plane->plane_id;
+                        found_plane = true;
+                        selected = true;
+                    }
+                    else
+                        selected = false;
+
+                    msg_Dbg(vd, "plane id %d type %s format %2d: %.4s  %s",
+                            plane->plane_id, types[planetype], i,
+                            (char*)&plane->formats[i], selected?"**":"");
+                }
+                drmModeFreePlane(plane);
+            } else {
+                msg_Dbg(vd, "Couldn't get list of DRM formats");
+            }
+        }
+        drmModeFreePlaneResources(plane_res);
+    }
+
+    ret = drmGetCap(sys->drm_fd, DRM_CAP_DUMB_BUFFER, &dumbRet);
+    if (ret < 0 || !dumbRet) {
+        msg_Err(vd, "Device '%s' does not support dumb buffers", psz_device);
+        return VLC_EGENERIC;
+    }
+
+    modeRes = drmModeGetResources(sys->drm_fd);
+    if (modeRes == NULL) {
+        msg_Err(vd, "Didn't get DRM resources. (%s)", vlc_strerror_c(errno));
+        return VLC_EGENERIC;
+    }
+
+    for (c = 0; c < (unsigned)modeRes->count_connectors && sys->crtc == 0;
+         c++) {
+
+        conn = drmModeGetConnector(sys->drm_fd, modeRes->connectors[c]);
+        if (conn == NULL) {
+            msg_Dbg(vd, "Couldn't retrieve DRM connector %d:%d (%s)", c,
+                    modeRes->connectors[c], vlc_strerror_c(errno));
+            continue;
+        }
+
+        sys->connector_id = conn->connector_id;
+
+        ret = SetupDevice(vd, modeRes, conn);
+        if (ret) {
+            if (ret != -ENOENT) {
+                errno = -ret;
+                msg_Err(vd, "cannot setup device for connector %u:%u (%s)",
+                    c, modeRes->connectors[c], vlc_strerror_c(errno));
+            }
+            drmModeFreeConnector(conn);
+            sys->connector_id = 0;
+            continue;
+        }
+        drmModeFreeConnector(conn);
+    }
+    drmModeFreeResources(modeRes);
+    if(sys->connector_id == 0) {
+        return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static int Control(vout_display_t *vd, int query, va_list args)
+{
+    (void) vd; (void) query; (void) args;
+    return VLC_EGENERIC;
+}
+
+
+static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
+{
+    vout_display_sys_t *sys = vd->sys;
+    picture_resource_t rsc;
+    int c, i;
+
+    if (!sys->pool[0] && !sys->picture[0]) {
+        sys->stored_crtc = drmModeGetCrtc(sys->drm_fd, sys->crtc);
+        memset(&rsc, 0, sizeof(rsc));
+
+        for(c = 0; c < 2; c++) {
+            for(i = 0; i < 5; i++ ) {
+                rsc.p[i].p_pixels = sys->map[c]+sys->offsets[i];
+                rsc.p[i].i_lines  = sys->height;
+                rsc.p[i].i_pitch  = sys->stride;
+            }
+            sys->picture[c] = picture_NewFromResource(&vd->fmt, &rsc);
+
+            if (!sys->picture[c]) {
+                picture_Release(sys->picture[0]);
+                return NULL;
+            }
+
+            sys->pool[c] = picture_pool_New(1, &sys->picture[c]);
+            if(!sys->pool[c])
+                picture_Release(sys->picture[c]);
+        }
+    }
+
+    return sys->pool[sys->front_buf];
+    VLC_UNUSED(count);
+}
+
+
+static void Display(vout_display_t *vd, picture_t *picture,
+                    subpicture_t *subpicture)
+{
+    vout_display_sys_t *sys = vd->sys;
+
+    if (drmModeSetPlane(sys->drm_fd, sys->plane_id, sys->crtc,
+                         sys->fb[sys->front_buf], 1<<1,
+                         0, 0, sys->width, sys->height,
+                         0, 0, sys->width << 16, sys->height << 16))
+    {
+        msg_Err(vd, "Cannot do set plane for plane id %u, fb %x (%s)\n",
+                sys->plane_id,
+                sys->fb[sys->front_buf],
+                vlc_strerror_c(errno));
+    } else {
+        sys->front_buf ^= 1;
+    }
+
+    picture_Release(picture);
+    VLC_UNUSED(subpicture);
+}
+
+
+
+/**
+ * This function allocates and initializes a KMS vout method.
+ */
+static int Open(vlc_object_t *object)
+{
+    vout_display_t *vd = (vout_display_t *)object;
+    vout_display_sys_t *sys;
+    vlc_fourcc_t local_vlc_chroma;
+    uint32_t local_drm_chroma;
+    video_format_t fmt = {};
+    char *chroma;
+
+    if (vout_display_IsWindowed(vd))
+        return VLC_EGENERIC;
+
+    /*
+     * Allocate instance and initialize some members
+     */
+    vd->sys = sys = calloc(1, sizeof(*sys));
+    if (!sys)
+        return VLC_ENOMEM;
+
+    sys->pool[0] = NULL;
+    sys->map[HWBUF1] = MAP_FAILED;
+    sys->map[HWBUF2] = MAP_FAILED;
+    sys->drm_fourcc = DRM_FORMAT_XRGB8888;
+    sys->vlc_fourcc = VLC_CODEC_RGB32;
+
+    chroma = var_InheritString(vd, "kms-vlc-chroma");
+    if (chroma) {
+        local_vlc_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, chroma);
+
+        if (local_vlc_chroma)
+            msg_Dbg(vd, "forcing VLC to use chroma '%4s'", chroma);
+        else
+            msg_Dbg(vd, "chroma %4s invalid, using default", chroma);
+
+        free(chroma);
+        chroma = NULL;
+        sys->vlc_fourcc = local_vlc_chroma;
+    }
+
+    chroma = var_InheritString(vd, "kms-drm-chroma");
+    if (chroma) {
+        local_drm_chroma = VLC_FOURCC(chroma[0], chroma[1], chroma[2],
+                                      chroma[3]);
+
+        if (local_drm_chroma)
+            msg_Dbg(vd, "Setting DRM chroma to '%4s'", chroma);
+        else
+            msg_Dbg(vd, "chroma %4s invalid, using default", chroma);
+
+        free(chroma);
+        chroma = NULL;
+        sys->drm_fourcc = local_drm_chroma;
+    }
+
+    sys->remote = false;
+
+    if(TtyInit(vd)) {
+        free(sys);
+        return VLC_EGENERIC;
+    }
+
+    if (OpenDisplay(vd)) {
+        Close(VLC_OBJECT(vd));
+        return VLC_EGENERIC;
+    }
+
+    video_format_ApplyRotation(&fmt, &vd->fmt);
+
+    fmt.i_width = fmt.i_visible_width  = sys->width;
+    fmt.i_height = fmt.i_visible_height = sys->height;
+    fmt.i_chroma = sys->vlc_fourcc;
+
+    vd->fmt     = fmt;
+    vd->pool    = Pool;
+    vd->prepare = NULL;
+    vd->display = Display;
+    vd->control = Control;
+
+    vout_display_SendEventDisplaySize(vd, fmt.i_visible_width,
+                                      fmt.i_visible_height);
+    return VLC_SUCCESS;
+}
+
+static void CloseDisplay(vout_display_t *vd)
+{
+    vout_display_sys_t *sys = vd->sys;
+
+    if (sys->stored_crtc != NULL) {
+        drmModeSetCrtc(sys->drm_fd,
+                   sys->stored_crtc->crtc_id,
+                   sys->stored_crtc->buffer_id,
+                   sys->stored_crtc->x,
+                   sys->stored_crtc->y,
+                   &sys->connector_id,
+                   1,
+                   &sys->stored_crtc->mode);
+
+        drmModeFreeCrtc(sys->stored_crtc);
+        sys->stored_crtc = NULL;
+    }
+
+    if (sys->map[HWBUF1] != MAP_FAILED) {
+        DestroyFB(vd, HWBUF1);
+        sys->map[HWBUF1] = MAP_FAILED;
+    }
+
+    if (sys->map[HWBUF2] != MAP_FAILED) {
+        DestroyFB(vd, HWBUF2);
+        sys->map[HWBUF2] = MAP_FAILED;
+    }
+
+    if (sys->drm_fd >= 0) {
+        if(!sys->remote) {
+            ioctl(sys->drm_fd, KDSETMODE, KD_TEXT);
+            tcsetattr(0, 0, &sys->old_termios);
+        }
+
+        drmSetClientCap(sys->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0);
+        drmDropMaster(sys->drm_fd);
+        vlc_close(sys->drm_fd);
+        sys->drm_fd = 0;
+    }
+}
+
+/**
+ * Terminate an output method created by Open
+ */
+static void Close(vlc_object_t *object)
+{
+    vout_display_t *vd = (vout_display_t *)object;
+    CloseDisplay(vd);
+}
-- 
2.7.4



More information about the vlc-devel mailing list