[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