mirror of
https://github.com/mpv-player/mpv.git
synced 2024-09-20 12:02:23 +02:00
parent
f51952441e
commit
f3c8c613ba
@ -22,12 +22,19 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <linux/vt.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <libswscale/swscale.h>
|
#include <libswscale/swscale.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
#include <xf86drmMode.h>
|
#include <xf86drmMode.h>
|
||||||
|
|
||||||
|
#include "osdep/io.h"
|
||||||
|
#include "osdep/timer.h"
|
||||||
#include "common/msg.h"
|
#include "common/msg.h"
|
||||||
#include "sub/osd.h"
|
#include "sub/osd.h"
|
||||||
#include "video/fmt-conversion.h"
|
#include "video/fmt-conversion.h"
|
||||||
@ -35,8 +42,15 @@
|
|||||||
#include "video/sws_utils.h"
|
#include "video/sws_utils.h"
|
||||||
#include "vo.h"
|
#include "vo.h"
|
||||||
|
|
||||||
|
#define USE_MASTER 0
|
||||||
|
#define EVT_RELEASE 1
|
||||||
|
#define EVT_ACQUIRE 2
|
||||||
|
#define EVT_INTERRUPT 255
|
||||||
#define BUF_COUNT 2
|
#define BUF_COUNT 2
|
||||||
|
|
||||||
|
static int setup_vo_crtc(struct vo *vo);
|
||||||
|
static void release_vo_crtc(struct vo *vo);
|
||||||
|
|
||||||
struct modeset_buf {
|
struct modeset_buf {
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
@ -55,11 +69,18 @@ struct modeset_dev {
|
|||||||
int front_buf;
|
int front_buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct vt_switcher {
|
||||||
|
int tty_fd;
|
||||||
|
struct vo *vo;
|
||||||
|
};
|
||||||
|
|
||||||
struct priv {
|
struct priv {
|
||||||
int fd;
|
int fd;
|
||||||
|
struct vt_switcher vt_switcher;
|
||||||
struct modeset_dev *dev;
|
struct modeset_dev *dev;
|
||||||
drmModeCrtc *old_crtc;
|
drmModeCrtc *old_crtc;
|
||||||
|
|
||||||
|
bool active;
|
||||||
char *device_path;
|
char *device_path;
|
||||||
int connector_id;
|
int connector_id;
|
||||||
|
|
||||||
@ -76,6 +97,8 @@ struct priv {
|
|||||||
|
|
||||||
static int modeset_open(struct vo *vo, int *out, const char *node)
|
static int modeset_open(struct vo *vo, int *out, const char *node)
|
||||||
{
|
{
|
||||||
|
*out = -1;
|
||||||
|
|
||||||
int fd = open(node, O_RDWR | O_CLOEXEC);
|
int fd = open(node, O_RDWR | O_CLOEXEC);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
MP_ERR(vo, "Cannot open \"%s\": %s.\n", node, mp_strerror(errno));
|
MP_ERR(vo, "Cannot open \"%s\": %s.\n", node, mp_strerror(errno));
|
||||||
@ -325,6 +348,162 @@ end:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vt_switcher_interrupt(struct vt_switcher *s)
|
||||||
|
{
|
||||||
|
unsigned char event = EVT_INTERRUPT;
|
||||||
|
write(vt_switcher_pipe[1], &event, sizeof(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vt_switcher_init(struct vt_switcher *s, struct vo *vo)
|
||||||
|
{
|
||||||
|
s->tty_fd = -1;
|
||||||
|
vt_switcher_pipe[0] = -1;
|
||||||
|
vt_switcher_pipe[1] = -1;
|
||||||
|
|
||||||
|
s->vo = vo;
|
||||||
|
if (mp_make_cloexec_pipe(vt_switcher_pipe)) {
|
||||||
|
MP_ERR(vo, "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(vo, "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(vo, "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(vo, "VT_SETMODE failed: %s", mp_strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vt_switcher_destroy(struct vt_switcher *s)
|
||||||
|
{
|
||||||
|
close(s->tty_fd);
|
||||||
|
close(vt_switcher_pipe[0]);
|
||||||
|
close(vt_switcher_pipe[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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:
|
||||||
|
release_vo_crtc(s->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 = s->vo->priv;
|
||||||
|
if (drmDropMaster(p->fd)) {
|
||||||
|
MP_WARN(s->vo, "Failed to drop DRM master: %s\n", mp_strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(s->tty_fd, VT_RELDISP, 1) < 0) {
|
||||||
|
MP_ERR(s->vo, "Failed to release virtual terminal\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVT_ACQUIRE:
|
||||||
|
if (USE_MASTER) {
|
||||||
|
struct priv *p = s->vo->priv;
|
||||||
|
if (drmSetMaster(p->fd)) {
|
||||||
|
MP_WARN(s->vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_vo_crtc(s->vo);
|
||||||
|
if (ioctl(s->tty_fd, VT_RELDISP, VT_ACKACQ) < 0) {
|
||||||
|
MP_ERR(s->vo, "Failed to acquire virtual terminal\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVT_INTERRUPT:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int setup_vo_crtc(struct vo *vo)
|
||||||
|
{
|
||||||
|
struct priv *p = vo->priv;
|
||||||
|
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;
|
||||||
|
p->active = false;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(&p->vt_switcher);
|
||||||
|
}
|
||||||
|
|
||||||
static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
|
static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
|
||||||
{
|
{
|
||||||
struct priv *p = vo->priv;
|
struct priv *p = vo->priv;
|
||||||
@ -375,23 +554,23 @@ static void draw_image(struct vo *vo, mp_image_t *mpi)
|
|||||||
{
|
{
|
||||||
struct priv *p = vo->priv;
|
struct priv *p = vo->priv;
|
||||||
|
|
||||||
struct mp_rect src_rc = p->src;
|
if (p->active) {
|
||||||
src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x);
|
struct mp_rect src_rc = p->src;
|
||||||
src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y);
|
src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x);
|
||||||
mp_image_crop_rc(mpi, src_rc);
|
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);
|
||||||
|
|
||||||
mp_sws_scale(p->sws, p->cur_frame, mpi);
|
struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf];
|
||||||
|
int32_t shift = (p->device_w * p->y + p->x) * 4;
|
||||||
osd_draw_on_image(vo->osd, p->osd, mpi ? mpi->pts : 0, 0, p->cur_frame);
|
memcpy_pic(front_buf->map + shift,
|
||||||
|
p->cur_frame->planes[0],
|
||||||
struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf];
|
(p->dst.x1 - p->dst.x0) * 4,
|
||||||
int32_t shift = (p->device_w * p->y + p->x) * 4;
|
p->dst.y1 - p->dst.y0,
|
||||||
memcpy_pic(front_buf->map + shift,
|
p->device_w * 4,
|
||||||
p->cur_frame->planes[0],
|
p->cur_frame->stride[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) {
|
if (mpi != p->last_input) {
|
||||||
talloc_free(p->last_input);
|
talloc_free(p->last_input);
|
||||||
@ -402,6 +581,7 @@ static void draw_image(struct vo *vo, mp_image_t *mpi)
|
|||||||
static void flip_page(struct vo *vo)
|
static void flip_page(struct vo *vo)
|
||||||
{
|
{
|
||||||
struct priv *p = vo->priv;
|
struct priv *p = vo->priv;
|
||||||
|
if (!p->active) return;
|
||||||
|
|
||||||
int ret = drmModeSetCrtc(p->fd, p->dev->crtc,
|
int ret = drmModeSetCrtc(p->fd, p->dev->crtc,
|
||||||
p->dev->bufs[p->dev->front_buf].fb,
|
p->dev->bufs[p->dev->front_buf].fb,
|
||||||
@ -414,62 +594,54 @@ static void flip_page(struct vo *vo)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int preinit(struct vo *vo)
|
|
||||||
{
|
|
||||||
struct priv *p = vo->priv;
|
|
||||||
p->sws = mp_sws_alloc(vo);
|
|
||||||
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = modeset_open(vo, &p->fd, p->device_path);
|
|
||||||
if (ret)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
ret = modeset_prepare_dev(vo, p->fd, p->connector_id, &p->dev);
|
|
||||||
if (ret)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
assert(p->dev);
|
|
||||||
p->device_w = p->dev->bufs[0].width;
|
|
||||||
p->device_h = p->dev->bufs[0].height;
|
|
||||||
|
|
||||||
p->old_crtc = drmModeGetCrtc(p->fd, p->dev->crtc);
|
|
||||||
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);
|
|
||||||
if (ret) {
|
|
||||||
MP_ERR(vo, "Cannot set CRTC for connector %u: %s\n", p->connector_id,
|
|
||||||
mp_strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uninit(struct vo *vo)
|
static void uninit(struct vo *vo)
|
||||||
{
|
{
|
||||||
struct priv *p = vo->priv;
|
struct priv *p = vo->priv;
|
||||||
|
|
||||||
if (p->dev) {
|
if (p->dev) {
|
||||||
if (p->old_crtc) {
|
release_vo_crtc(vo);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
modeset_destroy_fb(p->fd, &p->dev->bufs[1]);
|
modeset_destroy_fb(p->fd, &p->dev->bufs[1]);
|
||||||
modeset_destroy_fb(p->fd, &p->dev->bufs[0]);
|
modeset_destroy_fb(p->fd, &p->dev->bufs[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vt_switcher_destroy(&p->vt_switcher);
|
||||||
talloc_free(p->last_input);
|
talloc_free(p->last_input);
|
||||||
talloc_free(p->cur_frame);
|
talloc_free(p->cur_frame);
|
||||||
talloc_free(p->dev);
|
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;
|
||||||
|
|
||||||
|
if (vt_switcher_init(&p->vt_switcher, vo))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
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)
|
static int query_format(struct vo *vo, int format)
|
||||||
@ -500,6 +672,8 @@ const struct vo_driver video_out_drm = {
|
|||||||
.draw_image = draw_image,
|
.draw_image = draw_image,
|
||||||
.flip_page = flip_page,
|
.flip_page = flip_page,
|
||||||
.uninit = uninit,
|
.uninit = uninit,
|
||||||
|
.wait_events = wait_events,
|
||||||
|
.wakeup = wakeup,
|
||||||
.priv_size = sizeof(struct priv),
|
.priv_size = sizeof(struct priv),
|
||||||
.options = (const struct m_option[]) {
|
.options = (const struct m_option[]) {
|
||||||
OPT_STRING("devpath", device_path, 0),
|
OPT_STRING("devpath", device_path, 0),
|
||||||
|
Loading…
Reference in New Issue
Block a user