[vlc-devel] [PATCH 2/3] vout: add drm-gbm window

Victorien Le Couviour--Tuffet victorien.lecouviour.tuffet at gmail.com
Thu Jun 8 11:51:18 CEST 2017


---
 configure.ac                     |  40 +++++
 modules/video_output/Makefile.am |  11 ++
 modules/video_output/drm_gbm.c   | 350 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 401 insertions(+)
 create mode 100644 modules/video_output/drm_gbm.c

diff --git a/configure.ac b/configure.ac
index 43ce7a3e1a..83d1549eee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3105,6 +3105,46 @@ AC_SUBST([WAYLAND_SCANNER])
 AM_CONDITIONAL([HAVE_WAYLAND], [test "${have_wayland}" = "yes"])
 AM_CONDITIONAL([HAVE_WAYLAND_EGL], [test "${have_wayland_egl}" = "yes"])
 
+dnl
+dnl  Direct Rendering Manager
+dnl
+AC_ARG_ENABLE(drm,
+  [AS_HELP_STRING([--enable-drm], [DRM support (default auto)])])
+have_libdrm="no"
+
+AS_IF([test "${enable_drm}" != "no"], [
+  PKG_CHECK_MODULES([LIBDRM], [libdrm >= 2.4.58], [
+  have_libdrm="yes"
+  enable_gbm="yes"
+  ], [
+    AS_IF([test -n "${enable_drm}"], [
+      AC_MSG_ERROR([${LIBDRM_PKG_ERRORS}.])
+    ])
+  ])
+])
+AM_CONDITIONAL([HAVE_LIBDRM], [test "${have_libdrm}" = "yes"])
+
+dnl
+dnl  Generic Buffer Management
+dnl
+AC_ARG_ENABLE(gbm,
+  [AS_HELP_STRING([--enable-gbm], [GBM support (default auto)])])
+have_gbm="no"
+
+AS_IF([test "${enable_gbm}" != "no"], [
+  PKG_CHECK_MODULES([GBM], [gbm >= 10.3.2], [
+    AS_IF([test "${have_egl}" = "yes"], [
+      have_gbm="yes"
+    ], [
+      AC_MSG_ERROR([egl is required])
+    ])
+  ], [
+    AS_IF([test -n "${enable_gbm}"], [
+      AC_MSG_ERROR([${GBM_PKG_ERRORS}.])
+    ])
+  ])
+])
+AM_CONDITIONAL([HAVE_GBM], [test "${have_gbm}" = "yes"])
 
 dnl
 dnl  SDL module
diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index dae0183d96..750e903292 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -349,6 +349,17 @@ vout_LTLIBRARIES += libegl_android_plugin.la
 endif
 endif
 
+### Direct Rendering Manager ###
+
+libdrm_gbm_window_plugin_la_SOURCES = video_output/drm_gbm.c
+libdrm_gbm_window_plugin_la_CFLAGS = $(AM_CFLAGS) $(LIBDRM_CFLAGS) $(GBM_CFLAGS)
+libdrm_gbm_window_plugin_la_LIBADD = $(LIBDRM_LIBS) $(GBM_LIBS)
+
+if HAVE_GBM
+if HAVE_LIBDRM
+vout_LTLIBRARIES += libdrm_gbm_window_plugin.la
+endif
+endif
 
 ### FrameBuffer ###
 
diff --git a/modules/video_output/drm_gbm.c b/modules/video_output/drm_gbm.c
new file mode 100644
index 0000000000..2342a844ac
--- /dev/null
+++ b/modules/video_output/drm_gbm.c
@@ -0,0 +1,350 @@
+/**
+ * @file drm_gbm.c
+ * @brief DRM-GBM surface provider module for VLC media player
+ */
+/*****************************************************************************
+ * Copyright © 2017 VLC authors, VideoLAN and VideoLabs
+ *
+ * Author: Victorien Le Couviour--Tuffet <victorien.lecouviour.tuffet at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <fcntl.h>
+
+#include <gbm.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include <vlc_common.h>
+#include <vlc_fs.h>
+#include <vlc_plugin.h>
+#include <vlc_vout_window.h>
+
+struct vout_window_sys_t
+{
+    struct
+    {
+        int                     fd;
+        drmModeConnector *      connector;
+        drmModeModeInfo *       mode;
+        uint32_t                crtc_id;
+        uint32_t                fb;
+    } drm;
+
+    struct
+    {
+        struct gbm_device *     device;
+        struct gbm_surface *    surface;
+        struct gbm_bo *         bo;
+    } gbm;
+};
+
+static int
+SwapFrameBuffersCallback(vlc_object_t * obj, char const * name,
+                         vlc_value_t prev, vlc_value_t cur,
+                         void * data)
+{ VLC_UNUSED(obj); VLC_UNUSED(name); VLC_UNUSED(prev); VLC_UNUSED(cur);
+    vout_window_sys_t *const    sys = data;
+    struct gbm_bo *const        next_bo =
+        gbm_surface_lock_front_buffer(sys->gbm.surface);
+    uint32_t const              bo_handle = gbm_bo_get_handle(next_bo).u32;
+    uint32_t const              bo_pitch = gbm_bo_get_stride(next_bo);
+    uint32_t                    next_fb;
+
+    if (drmModeAddFB(sys->drm.fd,
+                     sys->drm.mode->hdisplay, sys->drm.mode->vdisplay,
+                     24, 32, bo_pitch, bo_handle, &next_fb) ||
+        drmModeSetCrtc(sys->drm.fd, sys->drm.crtc_id, next_fb, 0, 0,
+                       &sys->drm.connector->connector_id, 1, sys->drm.mode))
+        return VLC_EGENERIC;
+
+    if (sys->gbm.bo)
+    {
+        drmModeRmFB(sys->drm.fd, sys->drm.fb);
+        gbm_surface_release_buffer(sys->gbm.surface, sys->gbm.bo);
+    }
+    sys->drm.fb = next_fb;
+    sys->gbm.bo = next_bo;
+
+    return VLC_SUCCESS;
+}
+
+static int
+Control(vout_window_t * wnd, int cmd, va_list ap)
+{ VLC_UNUSED(wnd); VLC_UNUSED(cmd); VLC_UNUSED(ap);
+    return VLC_SUCCESS;
+}
+
+static int
+drm_open_device(vout_window_t * wnd)
+{
+    vout_window_sys_t *const    sys = wnd->sys;
+    char *const                 usr_drm_device_path =
+        var_InheritString(wnd, "drm-device");
+    static char const *const    drm_device_paths[] =
+        { DRM_DIR_NAME "/card0", DRM_DIR_NAME "/renderD128" };
+
+    sys->drm.fd = -1;
+
+    if (usr_drm_device_path)
+        sys->drm.fd = vlc_open(usr_drm_device_path, O_RDWR);
+    if (sys->drm.fd != -1)
+    {
+        msg_Dbg(wnd, "using %s as DRM device", usr_drm_device_path);
+        free(usr_drm_device_path);
+    }
+    else
+        for (unsigned int i = 0;
+             i < ARRAY_SIZE(drm_device_paths) && sys->drm.fd == -1; ++i)
+        {
+            sys->drm.fd = vlc_open(drm_device_paths[i], O_RDWR);
+            if (sys->drm.fd != -1)
+                msg_Dbg(wnd, "using %s as DRM device", drm_device_paths[i]);
+        }
+
+    return sys->drm.fd != -1 ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+static drmModeConnector *
+drm_find_connector(int drm_fd, drmModeRes * resources)
+{
+    for (int i = 0; i < resources->count_connectors; ++i)
+    {
+        drmModeConnector *const connector =
+            drmModeGetConnector(drm_fd, resources->connectors[i]);
+
+        if (connector->connection == DRM_MODE_CONNECTED)
+            return connector;
+        drmModeFreeConnector(connector);
+    }
+    return NULL;
+}
+
+static drmModeModeInfo *
+drm_find_mode(drmModeConnector * connector)
+{
+    drmModeModeInfo *   mode = NULL;
+    uint32_t            area = 0;
+
+    for (int i = 0; i < connector->count_modes; ++i)
+    {
+        drmModeModeInfo *const  cur_mode = connector->modes + i;
+        uint32_t const          cur_area =
+            cur_mode->hdisplay * cur_mode->vdisplay;
+
+        if (cur_mode->type & DRM_MODE_TYPE_PREFERRED)
+            mode = cur_mode;
+        if (cur_area > area)
+        {
+            mode = cur_mode;
+            area = cur_area;
+        }
+    }
+
+    return mode;
+}
+
+static drmModeEncoder *
+drm_find_encoder(int drm_fd,
+                 drmModeRes * resources,
+                 drmModeConnector * connector)
+{
+    for (int i = 0; i < resources->count_encoders; ++i)
+    {
+        drmModeEncoder *const   encoder =
+            drmModeGetEncoder(drm_fd, resources->encoders[i]);
+
+        if (encoder->encoder_id == connector->encoder_id)
+            return encoder;
+        drmModeFreeEncoder(encoder);
+    }
+    return NULL;
+}
+
+static int
+drm_find_crtc(int drm_fd, drmModeRes * resources,
+              drmModeConnector * connector, uint32_t * p_crtc_id)
+{
+    int ret = VLC_EGENERIC;
+
+    for (int i = 0; i < connector->count_encoders; ++i)
+    {
+        drmModeEncoder *const   encoder =
+            drmModeGetEncoder(drm_fd, connector->encoders[i]);
+        if (!encoder)
+            continue;
+
+        for (int j = 0; j < resources->count_crtcs; ++j)
+            if (encoder->possible_crtcs & (1 << j))
+            {
+                *p_crtc_id = resources->crtcs[j];
+                ret = VLC_SUCCESS;
+                break;
+            }
+        drmModeFreeEncoder(encoder);
+
+        if (!ret)
+            break;
+    }
+
+    return ret;
+}
+
+static int
+init_drm(vout_window_t * wnd)
+{
+    vout_window_sys_t *const    sys = wnd->sys;
+    drmModeRes *                resources;
+    drmModeEncoder *            encoder = NULL;
+
+    if (drm_open_device(wnd))
+        return VLC_EGENERIC;
+
+    resources = drmModeGetResources(sys->drm.fd);
+    if (!resources)
+        goto error;
+
+    sys->drm.connector = drm_find_connector(sys->drm.fd, resources);
+    if (!sys->drm.connector)
+        goto error;
+
+    sys->drm.mode = drm_find_mode(sys->drm.connector);
+    if (!sys->drm.mode)
+        goto error;
+    else
+        msg_Dbg(wnd, "display mode is %ux%u",
+                sys->drm.mode->hdisplay, sys->drm.mode->vdisplay);
+
+    encoder = drm_find_encoder(sys->drm.fd, resources, sys->drm.connector);
+    if (encoder)
+        sys->drm.crtc_id = encoder->crtc_id;
+    else if (drm_find_crtc(sys->drm.fd, resources,
+                           sys->drm.connector, &sys->drm.crtc_id))
+        goto error;
+
+    drmModeFreeEncoder(encoder);
+    drmModeFreeResources(resources);
+    return VLC_SUCCESS;
+
+error:
+    if (encoder)
+        drmModeFreeEncoder(encoder);
+    if (resources)
+        drmModeFreeResources(resources);
+    return VLC_EGENERIC;
+}
+
+static int
+init_gbm(vout_window_t * wnd)
+{
+    vout_window_sys_t *const    sys = wnd->sys;
+
+    sys->gbm.device = gbm_create_device(sys->drm.fd);
+    if (!sys->gbm.device)
+        return VLC_EGENERIC;
+
+    sys->gbm.surface =
+        gbm_surface_create(sys->gbm.device,
+                           sys->drm.mode->hdisplay, sys->drm.mode->vdisplay,
+                           GBM_FORMAT_XRGB8888,
+                           GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+    if (!sys->gbm.surface)
+    {
+        gbm_device_destroy(sys->gbm.device);
+        return VLC_EGENERIC;
+    }
+
+    return VLC_SUCCESS;
+}
+
+static int
+Open(vout_window_t * wnd, vout_window_cfg_t const * cfg)
+{ VLC_UNUSED(cfg);
+    if (cfg->type != VOUT_WINDOW_TYPE_INVALID &&
+        cfg->type != VOUT_WINDOW_TYPE_DRM_GBM)
+        return VLC_EGENERIC;
+
+    wnd->sys = calloc(1, sizeof(vout_window_sys_t));
+    if (!wnd->sys)
+        return VLC_ENOMEM;
+
+    if (init_drm(wnd) || init_gbm(wnd))
+        goto error;
+
+    vout_window_ReportSize(wnd, wnd->sys->drm.mode->hdisplay,
+                           wnd->sys->drm.mode->vdisplay);
+
+    var_Create(wnd, "drm-gbm-swap_frame_buffers", VLC_VAR_VOID);
+
+    /* This callback is to be triggered rigth after a call to
+       eglSwapBuffers. */
+    var_AddCallback(wnd, "drm-gbm-swap_frame_buffers",
+                    SwapFrameBuffersCallback, wnd->sys);
+
+    wnd->type = VOUT_WINDOW_TYPE_DRM_GBM;
+    wnd->handle.gbm = wnd->sys->gbm.surface;
+    wnd->display.gbm = wnd->sys->gbm.device;
+    wnd->control = Control;
+
+    return VLC_SUCCESS;
+
+error:
+    if (wnd->sys->drm.connector)
+        drmModeFreeConnector(wnd->sys->drm.connector);
+    if (wnd->sys->drm.fd)
+        vlc_close(wnd->sys->drm.fd);
+    free(wnd->sys);
+    return VLC_EGENERIC;
+}
+
+static void
+Close(vout_window_t * wnd)
+{
+    vout_window_sys_t *const    sys = wnd->sys;
+
+    var_DelCallback(wnd, "drm-gbm-swap_frame_buffers",
+                    SwapFrameBuffersCallback, wnd->sys);
+    var_Destroy(wnd, "drm-gbm-swap_frame_buffers");
+    if (sys->gbm.bo)
+        gbm_surface_release_buffer(sys->gbm.surface, sys->gbm.bo);
+    if (sys->drm.fb)
+        drmModeRmFB(sys->drm.fd, sys->drm.fb);
+    gbm_surface_destroy(sys->gbm.surface);
+    gbm_device_destroy(sys->gbm.device);
+    drmModeFreeConnector(sys->drm.connector);
+    vlc_close(sys->drm.fd);
+    free(sys);
+}
+
+#define DEVICE_TEXT     N_("DRM device")
+#define DEVICE_LONGTEXT N_(                                     \
+        "Video will be rendered to this device. "               \
+        "If empty, one of the default devices will be used.")
+
+vlc_module_begin()
+    set_shortname(N_("DRM-GBM window"))
+    set_description(N_("DRM-GBM window surface"))
+    set_category(CAT_VIDEO)
+    set_subcategory(SUBCAT_VIDEO_VOUT)
+    set_capability("vout window", 10)
+    set_callbacks(Open, Close)
+
+    add_string("drm-device", NULL, DEVICE_TEXT, DEVICE_LONGTEXT, true)
+vlc_module_end()
-- 
2.13.0



More information about the vlc-devel mailing list