diff options
Diffstat (limited to 'video/out')
-rw-r--r-- | video/out/drm_common.c | 149 | ||||
-rw-r--r-- | video/out/drm_common.h | 36 | ||||
-rw-r--r-- | video/out/vo.c | 50 | ||||
-rw-r--r-- | video/out/vo_drm.c | 641 | ||||
-rw-r--r-- | video/out/vo_opengl.c | 3 | ||||
-rw-r--r-- | video/out/vo_rpi.c | 8 | ||||
-rw-r--r-- | video/out/w32_common.c | 26 | ||||
-rw-r--r-- | video/out/win_state.c | 2 | ||||
-rw-r--r-- | video/out/x11_common.c | 67 | ||||
-rw-r--r-- | video/out/x11_common.h | 6 |
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 | |