summaryrefslogtreecommitdiffstats
path: root/video/out
diff options
context:
space:
mode:
Diffstat (limited to 'video/out')
-rw-r--r--video/out/drm_common.c149
-rw-r--r--video/out/drm_common.h36
-rw-r--r--video/out/vo.c50
-rw-r--r--video/out/vo_drm.c641
-rw-r--r--video/out/vo_opengl.c3
-rw-r--r--video/out/vo_rpi.c8
-rw-r--r--video/out/w32_common.c26
-rw-r--r--video/out/win_state.c2
-rw-r--r--video/out/x11_common.c67
-rw-r--r--video/out/x11_common.h6
10 files changed, 942 insertions, 46 deletions
diff --git a/video/out/drm_common.c b/video/out/drm_common.c
new file mode 100644
index 0000000000..c61ad69eba
--- /dev/null
+++ b/video/out/drm_common.c
@@ -0,0 +1,149 @@
+/*
+ * This file is part of mpv.
+ *
+ * by rr- <rr-@sakuya.pl>
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/vt.h>
+#include <unistd.h>
+
+#include "drm_common.h"
+
+#include "common/common.h"
+#include "common/msg.h"
+#include "osdep/io.h"
+
+#define EVT_RELEASE 1
+#define EVT_ACQUIRE 2
+#define EVT_INTERRUPT 255
+#define HANDLER_ACQUIRE 0
+#define HANDLER_RELEASE 1
+
+static int vt_switcher_pipe[2];
+
+static void vt_switcher_sighandler(int sig)
+{
+ unsigned char event = sig == SIGUSR1 ? EVT_RELEASE : EVT_ACQUIRE;
+ write(vt_switcher_pipe[1], &event, sizeof(event));
+}
+
+int vt_switcher_init(struct vt_switcher *s, struct mp_log *log)
+{
+ s->log = log;
+ s->tty_fd = -1;
+ vt_switcher_pipe[0] = -1;
+ vt_switcher_pipe[1] = -1;
+
+ if (mp_make_cloexec_pipe(vt_switcher_pipe)) {
+ MP_ERR(s, "Creating pipe failed: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ s->tty_fd = open("/dev/tty", O_RDWR | O_CLOEXEC);
+ if (s->tty_fd < 0) {
+ MP_ERR(s, "Can't open TTY for VT control: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ struct sigaction act;
+ act.sa_handler = vt_switcher_sighandler;
+ act.sa_flags = SA_RESTART;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGUSR1, &act, 0);
+ sigaction(SIGUSR2, &act, 0);
+
+ struct vt_mode vt_mode;
+ if (ioctl(s->tty_fd, VT_GETMODE, &vt_mode) < 0) {
+ MP_ERR(s, "VT_GETMODE failed: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ vt_mode.mode = VT_PROCESS;
+ vt_mode.relsig = SIGUSR1;
+ vt_mode.acqsig = SIGUSR2;
+ if (ioctl(s->tty_fd, VT_SETMODE, &vt_mode) < 0) {
+ MP_ERR(s, "VT_SETMODE failed: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+void vt_switcher_acquire(struct vt_switcher *s,
+ void (*handler)(void*), void *user_data)
+{
+ s->handlers[HANDLER_ACQUIRE] = handler;
+ s->handler_data[HANDLER_ACQUIRE] = user_data;
+}
+
+void vt_switcher_release(struct vt_switcher *s,
+ void (*handler)(void*), void *user_data)
+{
+ s->handlers[HANDLER_RELEASE] = handler;
+ s->handler_data[HANDLER_RELEASE] = user_data;
+}
+
+void vt_switcher_interrupt_poll(struct vt_switcher *s)
+{
+ unsigned char event = EVT_INTERRUPT;
+ write(vt_switcher_pipe[1], &event, sizeof(event));
+}
+
+void vt_switcher_destroy(struct vt_switcher *s)
+{
+ close(s->tty_fd);
+ close(vt_switcher_pipe[0]);
+ close(vt_switcher_pipe[1]);
+}
+
+void vt_switcher_poll(struct vt_switcher *s, int timeout_ms)
+{
+ struct pollfd fds[1] = {
+ { .events = POLLIN, .fd = vt_switcher_pipe[0] },
+ };
+ poll(fds, 1, timeout_ms);
+ if (!fds[0].revents)
+ return;
+
+ unsigned char event;
+ if (read(fds[0].fd, &event, sizeof(event)) != sizeof(event))
+ return;
+
+ switch (event) {
+ case EVT_RELEASE:
+ s->handlers[HANDLER_RELEASE](s->handler_data[HANDLER_RELEASE]);
+
+ if (ioctl(s->tty_fd, VT_RELDISP, 1) < 0) {
+ MP_ERR(s, "Failed to release virtual terminal\n");
+ }
+ break;
+
+ case EVT_ACQUIRE:
+ s->handlers[HANDLER_ACQUIRE](s->handler_data[HANDLER_ACQUIRE]);
+
+ if (ioctl(s->tty_fd, VT_RELDISP, VT_ACKACQ) < 0) {
+ MP_ERR(s, "Failed to acquire virtual terminal\n");
+ }
+ break;
+
+ case EVT_INTERRUPT:
+ break;
+ }
+}
diff --git a/video/out/drm_common.h b/video/out/drm_common.h
new file mode 100644
index 0000000000..5e6c1915ba
--- /dev/null
+++ b/video/out/drm_common.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MP_VT_SWITCHER_H
+#define MP_VT_SWITCHER_H
+
+struct vt_switcher {
+ int tty_fd;
+ struct mp_log *log;
+ void (*handlers[2])(void*);
+ void *handler_data[2];
+};
+
+int vt_switcher_init(struct vt_switcher *s, struct mp_log *log);
+void vt_switcher_destroy(struct vt_switcher *s);
+void vt_switcher_poll(struct vt_switcher *s, int timeout_ms);
+void vt_switcher_interrupt_poll(struct vt_switcher *s);
+
+void vt_switcher_acquire(struct vt_switcher *s, void (*handler)(void*), void *user_data);
+void vt_switcher_release(struct vt_switcher *s, void (*handler)(void*), void *user_data);
+
+#endif
diff --git a/video/out/vo.c b/video/out/vo.c
index 4600205cb7..70f682263e 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -57,6 +57,7 @@ extern const struct vo_driver video_out_null;
extern const struct vo_driver video_out_image;
extern const struct vo_driver video_out_lavc;
extern const struct vo_driver video_out_caca;
+extern const struct vo_driver video_out_drm;
extern const struct vo_driver video_out_direct3d;
extern const struct vo_driver video_out_direct3d_shaders;
extern const struct vo_driver video_out_sdl;
@@ -67,47 +68,50 @@ extern const struct vo_driver video_out_rpi;
const struct vo_driver *const video_out_drivers[] =
{
#if HAVE_RPI
- &video_out_rpi,
+ &video_out_rpi,
#endif
#if HAVE_GL
- &video_out_opengl,
+ &video_out_opengl,
#endif
#if HAVE_VDPAU
- &video_out_vdpau,
+ &video_out_vdpau,
#endif
#if HAVE_DIRECT3D
- &video_out_direct3d_shaders,
- &video_out_direct3d,
+ &video_out_direct3d_shaders,
+ &video_out_direct3d,
#endif
#if HAVE_XV
- &video_out_xv,
+ &video_out_xv,
#endif
#if HAVE_SDL2
- &video_out_sdl,
+ &video_out_sdl,
#endif
#if HAVE_VAAPI
- &video_out_vaapi,
+ &video_out_vaapi,
#endif
#if HAVE_X11
- &video_out_x11,
+ &video_out_x11,
#endif
- &video_out_null,
- // should not be auto-selected
- &video_out_image,
+ &video_out_null,
+ // should not be auto-selected
+ &video_out_image,
#if HAVE_CACA
- &video_out_caca,
+ &video_out_caca,
+#endif
+#if HAVE_DRM
+ &video_out_drm,
#endif
#if HAVE_ENCODING
- &video_out_lavc,
+ &video_out_lavc,
#endif
#if HAVE_GL
- &video_out_opengl_hq,
- &video_out_opengl_cb,
+ &video_out_opengl_hq,
+ &video_out_opengl_cb,
#endif
#if HAVE_WAYLAND
- &video_out_wayland,
+ &video_out_wayland,
#endif
- NULL
+ NULL
};
struct vo_internal {
@@ -751,7 +755,7 @@ static void *vo_thread(void *ptr)
return NULL;
update_display_fps(vo);
- vo_event(vo, VO_WIN_STATE_MINIMIZED);
+ vo_event(vo, VO_EVENT_WIN_STATE);
while (1) {
mp_dispatch_queue_process(vo->in->dispatch, 0);
@@ -983,10 +987,10 @@ struct mp_image *vo_get_current_frame(struct vo *vo)
return r;
}
-/**
- * \brief lookup an integer in a table, table must have 0 as the last key
- * \param key key to search for
- * \result translation corresponding to key or "to" value of last mapping
+/*
+ * lookup an integer in a table, table must have 0 as the last key
+ * param: key key to search for
+ * returns translation corresponding to key or "to" value of last mapping
* if not found.
*/
int lookup_keymap_table(const struct mp_keymap *map, int key)
diff --git a/video/out/vo_drm.c b/video/out/vo_drm.c
new file mode 100644
index 0000000000..64fdadb271
--- /dev/null
+++ b/video/out/vo_drm.c
@@ -0,0 +1,641 @@
+/*
+ * video output driver for libdrm
+ *
+ * by rr- <rr-@sakuya.pl>
+ *
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <unistd.h>
+
+#include <libswscale/swscale.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "drm_common.h"
+
+#include "common/msg.h"
+#include "osdep/timer.h"
+#include "sub/osd.h"
+#include "video/fmt-conversion.h"
+#include "video/mp_image.h"
+#include "video/sws_utils.h"
+#include "vo.h"
+
+#define USE_MASTER 0
+#define BUF_COUNT 2
+
+struct modeset_buf {
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ uint32_t size;
+ uint32_t handle;
+ uint8_t *map;
+ uint32_t fb;
+};
+
+struct modeset_dev {
+ struct modeset_buf bufs[BUF_COUNT];
+ drmModeModeInfo mode;
+ drmModeEncoder *enc;
+ uint32_t conn;
+ uint32_t crtc;
+ int front_buf;
+};
+
+struct priv {
+ char *device_path;
+ int connector_id;
+
+ int fd;
+ struct vt_switcher vt_switcher;
+ struct modeset_dev *dev;
+ drmModeCrtc *old_crtc;
+ drmEventContext ev;
+
+ bool active;
+ bool pflip_happening;
+
+ int32_t device_w;
+ int32_t device_h;
+ int32_t x, y;
+ struct mp_image *last_input;
+ struct mp_image *cur_frame;
+ struct mp_rect src;
+ struct mp_rect dst;
+ struct mp_osd_res osd;
+ struct mp_sws_context *sws;
+};
+
+static int modeset_open(struct vo *vo, int *out, const char *node)
+{
+ *out = -1;
+
+ int fd = open(node, O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ MP_ERR(vo, "Cannot open \"%s\": %s.\n", node, mp_strerror(errno));
+ return -errno;
+ }
+
+ uint64_t has_dumb;
+ if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0) {
+ MP_ERR(vo, "Device \"%s\" does not support dumb buffers.\n", node);
+ return -EOPNOTSUPP;
+ }
+
+ *out = fd;
+ return 0;
+}
+
+static void modeset_destroy_fb(int fd, struct modeset_buf *buf)
+{
+ if (buf->map) {
+ munmap(buf->map, buf->size);
+ }
+ if (buf->fb) {
+ drmModeRmFB(fd, buf->fb);
+ }
+ if (buf->handle) {
+ struct drm_mode_destroy_dumb dreq = {
+ .handle = buf->handle,
+ };
+ drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
+ }
+}
+
+static int modeset_create_fb(struct vo *vo, int fd, struct modeset_buf *buf)
+{
+ int ret = 0;
+
+ buf->handle = 0;
+
+ // create dumb buffer
+ struct drm_mode_create_dumb creq = {
+ .width = buf->width,
+ .height = buf->height,
+ .bpp = 32,
+ };
+ ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
+ if (ret < 0) {
+ MP_ERR(vo, "Cannot create dumb buffer: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+ buf->stride = creq.pitch;
+ buf->size = creq.size;
+ buf->handle = creq.handle;
+
+ // create framebuffer object for the dumb-buffer
+ ret = drmModeAddFB(fd, buf->width, buf->height, 24, 32, buf->stride,
+ buf->handle, &buf->fb);
+ if (ret) {
+ MP_ERR(vo, "Cannot create framebuffer: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+
+ // prepare buffer for memory mapping
+ struct drm_mode_map_dumb mreq = {
+ .handle = buf->handle,
+ };
+ ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
+ if (ret) {
+ MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+
+ // perform actual memory mapping
+ buf->map = mmap(0, buf->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, mreq.offset);
+ if (buf->map == MAP_FAILED) {
+ MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+
+ memset(buf->map, 0, buf->size);
+
+end:
+ if (ret == 0) {
+ return 0;
+ }
+
+ modeset_destroy_fb(fd, buf);
+ return ret;
+}
+
+static int modeset_find_crtc(struct vo *vo, int fd, drmModeRes *res,
+ drmModeConnector *conn, struct modeset_dev *dev)
+{
+ for (unsigned int i = 0; i < conn->count_encoders; ++i) {
+ drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]);
+ if (!enc) {
+ MP_WARN(vo, "Cannot retrieve encoder %u:%u: %s\n",
+ i, conn->encoders[i], mp_strerror(errno));
+ continue;
+ }
+
+ // iterate all global CRTCs
+ for (unsigned int j = 0; j < res->count_crtcs; ++j) {
+ // check whether this CRTC works with the encoder
+ if (!(enc->possible_crtcs & (1 << j)))
+ continue;
+
+ dev->enc = enc;
+ dev->crtc = enc->crtc_id;
+ return 0;
+ }
+
+ drmModeFreeEncoder(enc);
+ }
+
+ MP_ERR(vo, "Connector %u has no suitable CRTC\n", conn->connector_id);
+ return -ENOENT;
+}
+
+static bool is_connector_valid(struct vo *vo, int conn_id,
+ drmModeConnector *conn, bool silent)
+{
+ if (!conn) {
+ if (!silent) {
+ MP_ERR(vo, "Cannot get connector %d: %s\n", conn_id,
+ mp_strerror(errno));
+ }
+ return false;
+ }
+
+ if (conn->connection != DRM_MODE_CONNECTED) {
+ if (!silent) {
+ MP_ERR(vo, "Connector %d is disconnected\n", conn_id);
+ }
+ return false;
+ }
+
+ if (conn->count_modes == 0) {
+ if (!silent) {
+ MP_ERR(vo, "Connector %d has no valid modes\n", conn_id);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+static int modeset_prepare_dev(struct vo *vo, int fd, int conn_id,
+ struct modeset_dev **out)
+{
+ struct modeset_dev *dev = NULL;
+ drmModeConnector *conn = NULL;
+
+ int ret = 0;
+ *out = NULL;
+
+ drmModeRes *res = drmModeGetResources(fd);
+ if (!res) {
+ MP_ERR(vo, "Cannot retrieve DRM resources: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+
+ if (conn_id == -1) {
+ // get the first connected connector
+ for (int i = 0; i < res->count_connectors; i++) {
+ conn = drmModeGetConnector(fd, res->connectors[i]);
+ if (is_connector_valid(vo, i, conn, true)) {
+ conn_id = i;
+ break;
+ }
+ if (conn) {
+ drmModeFreeConnector(conn);
+ conn = NULL;
+ }
+ }
+ if (conn_id == -1) {
+ MP_ERR(vo, "No connected connectors found\n");
+ ret = -ENODEV;
+ goto end;
+ }
+ }
+
+ if (conn_id < 0 || conn_id >= res->count_connectors) {
+ MP_ERR(vo, "Bad connector ID. Max valid connector ID = %u\n",
+ res->count_connectors);
+ ret = -ENODEV;
+ goto end;
+ }
+
+ conn = drmModeGetConnector(fd, res->connectors[conn_id]);
+ if (!is_connector_valid(vo, conn_id, conn, false)) {
+ ret = -ENODEV;
+ goto end;
+ }
+
+ dev = talloc_zero(vo->priv, struct modeset_dev);
+ dev->conn = conn->connector_id;
+ dev->front_buf = 0;
+ dev->mode = conn->modes[0];
+ dev->bufs[0].width = conn->modes[0].hdisplay;
+ dev->bufs[0].height = conn->modes[0].vdisplay;
+ dev->bufs[1].width = conn->modes[0].hdisplay;
+ dev->bufs[1].height = conn->modes[0].vdisplay;
+
+ MP_INFO(vo, "Connector using mode %ux%u\n",
+ dev->bufs[0].width, dev->bufs[0].height);
+
+ ret = modeset_find_crtc(vo, fd, res, conn, dev);
+ if (ret) {
+ MP_ERR(vo, "Connector %d has no valid CRTC\n", conn_id);
+ goto end;
+ }
+
+ for (unsigned int i = 0; i < BUF_COUNT; i++) {
+ ret = modeset_create_fb(vo, fd, &dev->bufs[i]);
+ if (ret) {
+ MP_ERR(vo, "Cannot create framebuffer for connector %d\n",
+ conn_id);
+ for (unsigned int j = 0; j < i; j++) {
+ modeset_destroy_fb(fd, &dev->bufs[j]);
+ }
+ goto end;
+ }
+ }
+
+end:
+ if (conn) {
+ drmModeFreeConnector(conn);
+ conn = NULL;
+ }
+ if (res) {
+ drmModeFreeResources(res);
+ res = NULL;
+ }
+ if (ret == 0) {
+ *out = dev;
+ } else {
+ talloc_free(dev);
+ }
+ return ret;
+}
+
+static void modeset_page_flipped(int fd, unsigned int frame, unsigned int sec,
+ unsigned int usec, void *data)
+{
+ struct priv *p = data;
+ p->pflip_happening = false;
+}
+
+
+
+static int setup_vo_crtc(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ if (p->active)
+ return 0;
+ p->old_crtc = drmModeGetCrtc(p->fd, p->dev->crtc);
+ int ret = drmModeSetCrtc(p->fd, p->dev->crtc,
+ p->dev->bufs[p->dev->front_buf + BUF_COUNT - 1].fb,
+ 0, 0, &p->dev->conn, 1, &p->dev->mode);
+ p->active = true;
+ return ret;
+}
+
+static void release_vo_crtc(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ if (!p->active)
+ return;
+ p->active = false;
+
+ // wait for current page flip
+ while (p->pflip_happening) {
+ int ret = drmHandleEvent(p->fd, &p->ev);
+ if (ret) {
+ MP_ERR(vo, "drmHandleEvent failed: %i\n", ret);
+ break;
+ }
+ }
+
+ if (p->old_crtc) {
+ drmModeSetCrtc(p->fd,
+ p->old_crtc->crtc_id,
+ p->old_crtc->buffer_id,
+ p->old_crtc->x,
+ p->old_crtc->y,
+ &p->dev->conn,
+ 1,
+ &p->dev->mode);
+ drmModeFreeCrtc(p->old_crtc);
+ p->old_crtc = NULL;
+ }
+}
+
+static void release_vt(void *data)
+{
+ struct vo *vo = data;
+ release_vo_crtc(vo);
+ if (USE_MASTER) {
+ //this function enables support for switching to x, weston etc.
+ //however, for whatever reason, it can be called only by root users.
+ //until things change, this is commented.
+ struct priv *p = vo->priv;
+ if (drmDropMaster(p->fd)) {
+ MP_WARN(vo, "Failed to drop DRM master: %s\n", mp_strerror(errno));
+ }
+ }
+}
+
+static void acquire_vt(void *data)
+{
+ struct vo *vo = data;
+ if (USE_MASTER) {
+ struct priv *p = vo->priv;
+ if (drmSetMaster(p->fd)) {
+ MP_WARN(vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno));
+ }
+ }
+
+ setup_vo_crtc(vo);
+}
+
+
+
+static int wait_events(struct vo *vo, int64_t until_time_us)
+{
+ struct priv *p = vo->priv;
+ int64_t wait_us = until_time_us - mp_time_us();
+ int timeout_ms = MPCLAMP((wait_us + 500) / 1000, 0, 10000);
+ vt_switcher_poll(&p->vt_switcher, timeout_ms);
+ return 0;
+}
+
+static void wakeup(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ vt_switcher_interrupt_poll(&p->vt_switcher);
+}
+
+static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
+{
+ struct priv *p = vo->priv;
+
+ vo->dwidth = p->device_w;
+ vo->dheight = p->device_h;
+ vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd);
+
+ int32_t w = p->dst.x1 - p->dst.x0;
+ int32_t h = p->dst.y1 - p->dst.y0;
+
+ // p->osd contains the parameters assuming OSD rendering in window
+ // coordinates, but OSD can only be rendered in the intersection
+ // between window and video rectangle (i.e. not into panscan borders).
+ p->osd.w = w;
+ p->osd.h = h;
+ p->osd.mt = MPMIN(0, p->osd.mt);
+ p->osd.mb = MPMIN(0, p->osd.mb);
+ p->osd.mr = MPMIN(0, p->osd.mr);
+ p->osd.ml = MPMIN(0, p->osd.ml);
+
+ p->x = (p->device_w - w) >> 1;
+ p->y = (p->device_h - h) >> 1;
+
+ mp_sws_set_from_cmdline(p->sws, vo->opts->sws_opts);
+ p->sws->src = *params;
+ p->sws->dst = (struct mp_image_params) {
+ .imgfmt = IMGFMT_BGR0,
+ .w = w,
+ .h = h,
+ .d_w = w,
+ .d_h = h,
+ };
+
+ talloc_free(p->cur_frame);
+ p->cur_frame = mp_image_alloc(IMGFMT_BGR0, p->device_w, p->device_h);
+ mp_image_params_guess_csp(&p->sws->dst);
+ mp_image_set_params(p->cur_frame, &p->sws->dst);
+
+ if (mp_sws_reinit(p->sws) < 0)
+ return -1;
+
+ vo->want_redraw = true;
+ return 0;
+}
+
+static void draw_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct priv *p = vo->priv;
+
+ if (p->active) {
+ struct mp_rect src_rc = p->src;
+ src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x);
+ src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y);
+ mp_image_crop_rc(mpi, src_rc);
+ mp_sws_scale(p->sws, p->cur_frame, mpi);
+ osd_draw_on_image(vo->osd, p->osd, mpi ? mpi->pts : 0, 0, p->cur_frame);
+
+ struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf];
+ int32_t shift = (p->device_w * p->y + p->x) * 4;
+ memcpy_pic(front_buf->map + shift,
+ p->cur_frame->planes[0],
+ (p->dst.x1 - p->dst.x0) * 4,
+ p->dst.y1 - p->dst.y0,
+ p->device_w * 4,
+ p->cur_frame->stride[0]);
+ }
+
+ if (mpi != p->last_input) {
+ talloc_free(p->last_input);
+ p->last_input = mpi;
+ }
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ if (!p->active || p->pflip_happening)
+ return;
+
+ int ret = drmModePageFlip(p->fd, p->dev->crtc,
+ p->dev->bufs[p->dev->front_buf].fb,
+ DRM_MODE_PAGE_FLIP_EVENT, p);
+ if (ret) {
+ MP_WARN(vo, "Cannot flip page for connector\n");
+ } else {
+ p->dev->front_buf++;
+ p->dev->front_buf %= BUF_COUNT;
+ p->pflip_happening = true;
+ }
+
+ // poll page flip finish event
+ const int timeout_ms = 3000;
+ struct pollfd fds[1] = {
+ { .events = POLLIN, .fd = p->fd },
+ };
+ poll(fds, 1, timeout_ms);
+ if (fds[0].revents & POLLIN) {
+ ret = drmHandleEvent(p->fd, &p->ev);
+ if (ret != 0) {
+ MP_ERR(vo, "drmHandleEvent failed: %i\n", ret);
+ return;
+ }
+ }
+}
+
+static void uninit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ if (p->dev) {
+ release_vo_crtc(vo);
+
+ modeset_destroy_fb(p->fd, &p->dev->bufs[1]);
+ modeset_destroy_fb(p->fd, &p->dev->bufs[0]);
+ drmModeFreeEncoder(p->dev->enc);
+ }
+
+ vt_switcher_destroy(&p->vt_switcher);
+ talloc_free(p->last_input);
+ talloc_free(p->cur_frame);
+ talloc_free(p->dev);
+ close(p->fd);
+}
+
+static int preinit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ p->sws = mp_sws_alloc(vo);
+ p->fd = -1;
+ p->ev.version = DRM_EVENT_CONTEXT_VERSION;
+ p->ev.page_flip_handler = modeset_page_flipped;
+
+ if (vt_switcher_init(&p->vt_switcher, vo->log))
+ goto err;
+
+ vt_switcher_acquire(&p->vt_switcher, acquire_vt, vo);
+ vt_switcher_release(&p->vt_switcher, release_vt, vo);
+
+ if (modeset_open(vo, &p->fd, p->device_path))
+ goto err;
+
+ if (modeset_prepare_dev(vo, p->fd, p->connector_id, &p->dev))
+ goto err;
+
+ assert(p->dev);
+ p->device_w = p->dev->bufs[0].width;
+ p->device_h = p->dev->bufs[0].height;
+
+ if (setup_vo_crtc(vo)) {
+ MP_ERR(vo, "Cannot set CRTC for connector %u: %s\n", p->connector_id,
+ mp_strerror(errno));
+ goto err;
+ }
+
+ return 0;
+
+err:
+ uninit(vo);
+ return -1;
+}
+
+static int query_format(struct vo *vo, int format)
+{
+ return sws_isSupportedInput(imgfmt2pixfmt(format));
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct priv *p = vo->priv;
+ switch (request) {
+ case VOCTRL_REDRAW_FRAME:
+ draw_image(vo, p->last_input);
+ return VO_TRUE;
+ }
+ return VO_NOTIMPL;
+}
+
+#define OPT_BASE_STRUCT struct priv
+
+const struct vo_driver video_out_drm = {
+ .name = "drm",
+ .description = "Direct Rendering Manager",
+ .preinit = preinit,
+ .query_format = query_format,
+ .reconfig = reconfig,
+ .control = control,
+ .draw_image = draw_image,
+ .flip_page = flip_page,
+ .uninit = uninit,
+ .wait_events = wait_events,
+ .wakeup = wakeup,
+ .priv_size = sizeof(struct priv),
+ .options = (const struct m_option[]) {
+ OPT_STRING("devpath", device_path, 0),
+ OPT_INT("connector", connector_id, 0),
+ {0},
+ },
+ .priv_defaults = &(const struct priv) {
+ .device_path = "/dev/dri/card0",
+ .connector_id = -1,
+ },
+};
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c
index 136ad03c9f..e409eaa18d 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_opengl.c
@@ -507,7 +507,8 @@ static const struct m_option options[] = {
OPT_FLAG("glfinish", use_glFinish, 0),
OPT_FLAG("waitvsync", waitvsync, 0),
OPT_INT("swapinterval", swap_interval, 0, OPTDEF_INT(1)),
- OPT_INT("dwmflush", dwm_flush, 0, OPTDEF_INT(0)),
+ OPT_CHOICE("dwmflush", dwm_flush, 0,
+ ({"no", 0}, {"windowed", 1}, {"yes", 2})),
OPT_FLAG("debug", use_gl_debug, 0),
OPT_STRING_VALIDATE("backend", backend, 0, mpgl_validate_backend_opt),
OPT_FLAG("sw", allow_sw, 0),
diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c
index 48827b8a67..c60218d575 100644
--- a/video/out/vo_rpi.c
+++ b/video/out/vo_rpi.c
@@ -26,6 +26,7 @@
#include <interface/mmal/mmal.h>
#include <interface/mmal/util/mmal_util.h>
#include <interface/mmal/util/mmal_default_components.h>
+#include <interface/mmal/vc/mmal_vc_api.h>
#include <libavutil/rational.h>
@@ -488,6 +489,8 @@ static void uninit(struct vo *vo)
if (p->display)
vc_dispmanx_display_close(p->display);
+
+ mmal_vc_deinit();
}
static int preinit(struct vo *vo)
@@ -500,6 +503,11 @@ static int preinit(struct vo *vo)
bcm_host_init();
+ if (mmal_vc_init()) {
+ MP_FATAL(vo, "Could not initialize MMAL.\n");
+ return -1;
+ }
+
p->display = vc_dispmanx_display_open(p->display_nr);
p->update = vc_dispmanx_update_start(0);
if (!p->display || !p->update) {
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
index e1ec3ad8e9..075bda586e 100644
--- a/video/out/w32_common.c
+++ b/video/out/w32_common.c
@@ -565,12 +565,15 @@ static void wakeup_gui_thread(void *ctx)
PostMessage(w32->window, WM_USER, 0, 0);
}
-static double vo_w32_get_display_fps(void)
+static double vo_w32_get_display_fps(struct vo_w32_state *w32)
{
- DEVMODE dm;
- dm.dmSize = sizeof(DEVMODE);
- dm.dmDriverExtra = 0;
- if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm))
+ // Get the device name of the monitor containing the window
+ HMONITOR mon = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
+ MONITORINFOEXW mi = { .cbSize = sizeof mi };
+ GetMonitorInfoW(mon, (MONITORINFO*)&mi);
+
+ DEVMODE dm = { .dmSize = sizeof dm };
+ if (!EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm))
return -1;
// May return 0 or 1 which "represent the display hardware's default refresh rate"
@@ -586,20 +589,22 @@ static double vo_w32_get_display_fps(void)
switch (dm.dmDisplayFrequency) {
case 23:
case 29:
+ case 47:
case 59:
case 71:
+ case 89:
+ case 95:
case 119:
+ case 143:
rv = (rv + 1) / 1.001;
}
return rv;
}
-static void update_display_fps(void *ctx)
+static void update_display_fps(struct vo_w32_state *w32)
{
- struct vo_w32_state *w32 = ctx;
-
- double fps = vo_w32_get_display_fps();
+ double fps = vo_w32_get_display_fps(w32);
if (fps != w32->display_fps) {
w32->display_fps = fps;
signal_events(w32, VO_EVENT_WIN_STATE);
@@ -1316,7 +1321,8 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
return VO_TRUE;
case VOCTRL_KILL_SCREENSAVER:
w32->disable_screensaver = true;
- SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
+ SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED |