2009-02-08 03:27:30 +00:00
|
|
|
/*
|
|
|
|
* X11 Xv interface
|
|
|
|
*
|
2015-04-13 07:36:54 +00:00
|
|
|
* This file is part of mpv.
|
2009-02-08 03:27:30 +00:00
|
|
|
*
|
2015-04-13 07:36:54 +00:00
|
|
|
* mpv is free software; you can redistribute it and/or modify
|
2009-02-08 03:27:30 +00:00
|
|
|
* 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.
|
|
|
|
*
|
2015-04-13 07:36:54 +00:00
|
|
|
* mpv is distributed in the hope that it will be useful,
|
2009-02-08 03:27:30 +00:00
|
|
|
* 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
|
2015-04-13 07:36:54 +00:00
|
|
|
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
2009-02-08 03:27:30 +00:00
|
|
|
*/
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2020-03-13 15:49:39 +00:00
|
|
|
#include <float.h>
|
2001-02-24 20:28:24 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2008-04-04 05:04:11 +00:00
|
|
|
#include <stdint.h>
|
2008-12-01 17:53:57 +00:00
|
|
|
#include <stdbool.h>
|
2012-03-19 23:02:13 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xutil.h>
|
|
|
|
|
|
|
|
#include <libavutil/common.h>
|
2001-02-24 20:28:24 +00:00
|
|
|
|
|
|
|
#include "config.h"
|
2012-03-19 23:02:13 +00:00
|
|
|
|
2013-12-26 15:57:21 +00:00
|
|
|
#include <sys/types.h>
|
2012-03-19 23:02:13 +00:00
|
|
|
#include <sys/ipc.h>
|
|
|
|
#include <sys/shm.h>
|
|
|
|
#include <X11/extensions/XShm.h>
|
|
|
|
|
|
|
|
// Note: depends on the inclusion of X11/extensions/XShm.h
|
|
|
|
#include <X11/extensions/Xv.h>
|
|
|
|
#include <X11/extensions/Xvlib.h>
|
|
|
|
|
2013-12-17 01:02:25 +00:00
|
|
|
#include "options/options.h"
|
2016-01-11 18:03:40 +00:00
|
|
|
#include "mpv_talloc.h"
|
2013-12-17 01:39:45 +00:00
|
|
|
#include "common/msg.h"
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "vo.h"
|
|
|
|
#include "video/mp_image.h"
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
#include "present_sync.h"
|
2001-03-03 21:46:39 +00:00
|
|
|
#include "x11_common.h"
|
2013-11-24 11:58:06 +00:00
|
|
|
#include "sub/osd.h"
|
2012-11-21 16:59:24 +00:00
|
|
|
#include "sub/draw_bmp.h"
|
2012-11-09 00:06:43 +00:00
|
|
|
#include "video/csputils.h"
|
2013-12-17 01:02:25 +00:00
|
|
|
#include "options/m_option.h"
|
2014-07-27 19:33:11 +00:00
|
|
|
#include "input/input.h"
|
2013-01-02 11:55:52 +00:00
|
|
|
#include "osdep/timer.h"
|
2005-02-20 22:43:25 +00:00
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
#define CK_METHOD_NONE 0 // no colorkey drawing
|
|
|
|
#define CK_METHOD_BACKGROUND 1 // set colorkey as window background
|
|
|
|
#define CK_METHOD_AUTOPAINT 2 // let xv draw the colorkey
|
|
|
|
#define CK_METHOD_MANUALFILL 3 // manually draw the colorkey
|
|
|
|
#define CK_SRC_USE 0 // use specified / default colorkey
|
|
|
|
#define CK_SRC_SET 1 // use and set specified / default colorkey
|
|
|
|
#define CK_SRC_CUR 2 // use current colorkey (get it from xv)
|
|
|
|
|
2015-05-20 21:07:47 +00:00
|
|
|
#define MAX_BUFFERS 10
|
|
|
|
|
2008-04-04 05:04:11 +00:00
|
|
|
struct xvctx {
|
2013-01-26 21:37:47 +00:00
|
|
|
struct xv_ck_info_s {
|
|
|
|
int method; // CK_METHOD_* constants
|
|
|
|
int source; // CK_SRC_* constants
|
|
|
|
} xv_ck_info;
|
2013-07-21 23:03:22 +00:00
|
|
|
int colorkey;
|
2013-01-26 21:37:47 +00:00
|
|
|
unsigned long xv_colorkey;
|
2013-07-21 22:59:00 +00:00
|
|
|
int xv_port;
|
|
|
|
int cfg_xv_adaptor;
|
2015-05-20 21:07:47 +00:00
|
|
|
int cfg_buffers;
|
2008-04-04 05:04:11 +00:00
|
|
|
XvAdaptorInfo *ai;
|
|
|
|
XvImageFormatValues *fo;
|
|
|
|
unsigned int formats, adaptors, xv_format;
|
|
|
|
int current_buf;
|
|
|
|
int current_ip_buf;
|
|
|
|
int num_buffers;
|
2015-05-20 21:07:47 +00:00
|
|
|
XvImage *xvimage[MAX_BUFFERS];
|
2012-12-22 16:50:15 +00:00
|
|
|
struct mp_image *original_image;
|
2008-04-04 05:04:11 +00:00
|
|
|
uint32_t image_width;
|
|
|
|
uint32_t image_height;
|
|
|
|
uint32_t image_format;
|
2014-03-28 23:25:08 +00:00
|
|
|
int cached_csp;
|
2012-10-27 20:10:32 +00:00
|
|
|
struct mp_rect src_rect;
|
|
|
|
struct mp_rect dst_rect;
|
2008-04-04 05:04:11 +00:00
|
|
|
uint32_t max_width, max_height; // zero means: not set
|
2015-07-01 21:55:10 +00:00
|
|
|
GC f_gc; // used to paint background
|
|
|
|
GC vo_gc; // used to paint video
|
2013-01-02 11:55:52 +00:00
|
|
|
int Shmem_Flag;
|
2015-05-20 21:07:47 +00:00
|
|
|
XShmSegmentInfo Shminfo[MAX_BUFFERS];
|
2013-01-02 11:55:52 +00:00
|
|
|
int Shm_Warned_Slow;
|
2008-04-04 05:04:11 +00:00
|
|
|
};
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2017-06-18 13:00:36 +00:00
|
|
|
#define MP_FOURCC(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((unsigned)(d)<<24))
|
|
|
|
|
|
|
|
#define MP_FOURCC_YV12 MP_FOURCC('Y', 'V', '1', '2')
|
|
|
|
#define MP_FOURCC_I420 MP_FOURCC('I', '4', '2', '0')
|
|
|
|
#define MP_FOURCC_IYUV MP_FOURCC('I', 'Y', 'U', 'V')
|
|
|
|
#define MP_FOURCC_UYVY MP_FOURCC('U', 'Y', 'V', 'Y')
|
|
|
|
|
video: decouple internal pixel formats from FourCCs
mplayer's video chain traditionally used FourCCs for pixel formats. For
example, it used IMGFMT_YV12 for 4:2:0 YUV, which was defined to the
string 'YV12' interpreted as unsigned int. Additionally, it used to
encode information into the numeric values of some formats. The RGB
formats had their bit depth and endian encoded into the least
significant byte. Extended planar formats (420P10 etc.) had chroma
shift, endian, and component bit depth encoded. (This has been removed
in recent commits.)
Replace the FourCC mess with a simple enum. Remove all the redundant
formats like YV12/I420/IYUV. Replace some image format names by
something more intuitive, most importantly IMGFMT_YV12 -> IMGFMT_420P.
Add img_fourcc.h, which contains the old IDs for code that actually uses
FourCCs. Change the way demuxers, that output raw video, identify the
video format: they set either MP_FOURCC_RAWVIDEO or MP_FOURCC_IMGFMT to
request the rawvideo decoder, and sh_video->imgfmt specifies the pixel
format. Like the previous hack, this is supposed to avoid the need for
a complete codecs.cfg entry per format, or other lookup tables. (Note
that the RGB raw video FourCCs mostly rely on ffmpeg's mappings for NUT
raw video, but this is still considered better than adding a raw video
decoder - even if trivial, it would be full of annoying lookup tables.)
The TV code has not been tested.
Some corrective changes regarding endian and other image format flags
creep in.
2012-12-23 19:03:30 +00:00
|
|
|
struct fmt_entry {
|
|
|
|
int imgfmt;
|
|
|
|
int fourcc;
|
|
|
|
};
|
|
|
|
static const struct fmt_entry fmt_table[] = {
|
|
|
|
{IMGFMT_420P, MP_FOURCC_YV12},
|
|
|
|
{IMGFMT_420P, MP_FOURCC_I420},
|
|
|
|
{IMGFMT_UYVY, MP_FOURCC_UYVY},
|
|
|
|
{0}
|
|
|
|
};
|
|
|
|
|
2013-10-27 16:58:06 +00:00
|
|
|
static bool allocate_xvimage(struct vo *, int);
|
2008-04-04 05:04:11 +00:00
|
|
|
static void deallocate_xvimage(struct vo *vo, int foo);
|
2012-11-30 16:45:40 +00:00
|
|
|
static struct mp_image get_xv_buffer(struct vo *vo, int buf_index);
|
2002-10-16 19:31:07 +00:00
|
|
|
|
video: decouple internal pixel formats from FourCCs
mplayer's video chain traditionally used FourCCs for pixel formats. For
example, it used IMGFMT_YV12 for 4:2:0 YUV, which was defined to the
string 'YV12' interpreted as unsigned int. Additionally, it used to
encode information into the numeric values of some formats. The RGB
formats had their bit depth and endian encoded into the least
significant byte. Extended planar formats (420P10 etc.) had chroma
shift, endian, and component bit depth encoded. (This has been removed
in recent commits.)
Replace the FourCC mess with a simple enum. Remove all the redundant
formats like YV12/I420/IYUV. Replace some image format names by
something more intuitive, most importantly IMGFMT_YV12 -> IMGFMT_420P.
Add img_fourcc.h, which contains the old IDs for code that actually uses
FourCCs. Change the way demuxers, that output raw video, identify the
video format: they set either MP_FOURCC_RAWVIDEO or MP_FOURCC_IMGFMT to
request the rawvideo decoder, and sh_video->imgfmt specifies the pixel
format. Like the previous hack, this is supposed to avoid the need for
a complete codecs.cfg entry per format, or other lookup tables. (Note
that the RGB raw video FourCCs mostly rely on ffmpeg's mappings for NUT
raw video, but this is still considered better than adding a raw video
decoder - even if trivial, it would be full of annoying lookup tables.)
The TV code has not been tested.
Some corrective changes regarding endian and other image format flags
creep in.
2012-12-23 19:03:30 +00:00
|
|
|
static int find_xv_format(int imgfmt)
|
|
|
|
{
|
|
|
|
for (int n = 0; fmt_table[n].imgfmt; n++) {
|
|
|
|
if (fmt_table[n].imgfmt == imgfmt)
|
|
|
|
return fmt_table[n].fourcc;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
static int xv_find_atom(struct vo *vo, uint32_t xv_port, const char *name,
|
|
|
|
bool get, int *min, int *max)
|
|
|
|
{
|
|
|
|
Atom atom = None;
|
|
|
|
int howmany = 0;
|
|
|
|
XvAttribute *attributes = XvQueryPortAttributes(vo->x11->display, xv_port,
|
|
|
|
&howmany);
|
|
|
|
for (int i = 0; i < howmany && attributes; i++) {
|
|
|
|
int flag = get ? XvGettable : XvSettable;
|
|
|
|
if (attributes[i].flags & flag) {
|
|
|
|
atom = XInternAtom(vo->x11->display, attributes[i].name, True);
|
|
|
|
*min = attributes[i].min_value;
|
|
|
|
*max = attributes[i].max_value;
|
|
|
|
/* since we have SET_DEFAULTS first in our list, we can check if it's available
|
|
|
|
then trigger it if it's ok so that the other values are at default upon query */
|
|
|
|
if (atom != None) {
|
|
|
|
if (!strcmp(attributes[i].name, "XV_BRIGHTNESS") &&
|
2014-07-10 06:26:44 +00:00
|
|
|
(!strcmp(name, "brightness")))
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
else if (!strcmp(attributes[i].name, "XV_CONTRAST") &&
|
2014-07-10 06:26:44 +00:00
|
|
|
(!strcmp(name, "contrast")))
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
else if (!strcmp(attributes[i].name, "XV_SATURATION") &&
|
2014-07-10 06:26:44 +00:00
|
|
|
(!strcmp(name, "saturation")))
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
else if (!strcmp(attributes[i].name, "XV_HUE") &&
|
2014-07-10 06:26:44 +00:00
|
|
|
(!strcmp(name, "hue")))
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
if (!strcmp(attributes[i].name, "XV_RED_INTENSITY") &&
|
2014-07-10 06:26:44 +00:00
|
|
|
(!strcmp(name, "red_intensity")))
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
else if (!strcmp(attributes[i].name, "XV_GREEN_INTENSITY")
|
2014-07-10 06:26:44 +00:00
|
|
|
&& (!strcmp(name, "green_intensity")))
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
else if (!strcmp(attributes[i].name, "XV_BLUE_INTENSITY")
|
2014-07-10 06:26:44 +00:00
|
|
|
&& (!strcmp(name, "blue_intensity")))
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
else if ((!strcmp(attributes[i].name, "XV_ITURBT_709") //NVIDIA
|
|
|
|
|| !strcmp(attributes[i].name, "XV_COLORSPACE")) //ATI
|
2014-07-10 06:26:44 +00:00
|
|
|
&& (!strcmp(name, "bt_709")))
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
atom = None;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
XFree(attributes);
|
|
|
|
return atom;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xv_set_eq(struct vo *vo, uint32_t xv_port, const char *name,
|
|
|
|
int value)
|
|
|
|
{
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "xv_set_eq called! (%s, %d)\n", name, value);
|
2013-01-26 21:37:47 +00:00
|
|
|
|
|
|
|
int min, max;
|
|
|
|
int atom = xv_find_atom(vo, xv_port, name, false, &min, &max);
|
|
|
|
if (atom != None) {
|
|
|
|
// -100 -> min
|
|
|
|
// 0 -> (max+min)/2
|
|
|
|
// +100 -> max
|
|
|
|
int port_value = (value + 100) * (max - min) / 200 + min;
|
|
|
|
XvSetPortAttribute(vo->x11->display, xv_port, atom, port_value);
|
|
|
|
return VO_TRUE;
|
|
|
|
}
|
|
|
|
return VO_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xv_get_eq(struct vo *vo, uint32_t xv_port, const char *name,
|
|
|
|
int *value)
|
|
|
|
{
|
|
|
|
int min, max;
|
|
|
|
int atom = xv_find_atom(vo, xv_port, name, true, &min, &max);
|
|
|
|
if (atom != None) {
|
|
|
|
int port_value = 0;
|
|
|
|
XvGetPortAttribute(vo->x11->display, xv_port, atom, &port_value);
|
|
|
|
|
|
|
|
*value = (port_value - min) * 200 / (max - min) - 100;
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "xv_get_eq called! (%s, %d)\n", name, *value);
|
2013-01-26 21:37:47 +00:00
|
|
|
return VO_TRUE;
|
|
|
|
}
|
|
|
|
return VO_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Atom xv_intern_atom_if_exists(struct vo *vo, char const *atom_name)
|
|
|
|
{
|
|
|
|
struct xvctx *ctx = vo->priv;
|
|
|
|
XvAttribute *attributes;
|
|
|
|
int attrib_count, i;
|
|
|
|
Atom xv_atom = None;
|
|
|
|
|
|
|
|
attributes = XvQueryPortAttributes(vo->x11->display, ctx->xv_port,
|
|
|
|
&attrib_count);
|
|
|
|
if (attributes != NULL) {
|
|
|
|
for (i = 0; i < attrib_count; ++i) {
|
|
|
|
if (strcmp(attributes[i].name, atom_name) == 0) {
|
|
|
|
xv_atom = XInternAtom(vo->x11->display, atom_name, False);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
XFree(attributes);
|
|
|
|
}
|
|
|
|
|
|
|
|
return xv_atom;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to enable vsync for xv.
|
|
|
|
// Returns -1 if not available, 0 on failure and 1 on success.
|
|
|
|
static int xv_enable_vsync(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct xvctx *ctx = vo->priv;
|
|
|
|
Atom xv_atom = xv_intern_atom_if_exists(vo, "XV_SYNC_TO_VBLANK");
|
|
|
|
if (xv_atom == None)
|
|
|
|
return -1;
|
|
|
|
return XvSetPortAttribute(vo->x11->display, ctx->xv_port, xv_atom, 1)
|
|
|
|
== Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get maximum supported source image dimensions.
|
|
|
|
// If querying the dimensions fails, don't change *width and *height.
|
|
|
|
static void xv_get_max_img_dim(struct vo *vo, uint32_t *width, uint32_t *height)
|
|
|
|
{
|
|
|
|
struct xvctx *ctx = vo->priv;
|
|
|
|
XvEncodingInfo *encodings;
|
|
|
|
unsigned int num_encodings, idx;
|
|
|
|
|
|
|
|
XvQueryEncodings(vo->x11->display, ctx->xv_port, &num_encodings, &encodings);
|
|
|
|
|
|
|
|
if (encodings) {
|
|
|
|
for (idx = 0; idx < num_encodings; ++idx) {
|
|
|
|
if (strcmp(encodings[idx].name, "XV_IMAGE") == 0) {
|
|
|
|
*width = encodings[idx].width;
|
|
|
|
*height = encodings[idx].height;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "Maximum source image dimensions: %ux%u\n", *width, *height);
|
2013-01-26 21:37:47 +00:00
|
|
|
|
|
|
|
XvFreeEncodingInfo(encodings);
|
|
|
|
}
|
|
|
|
|
2013-08-23 21:30:09 +00:00
|
|
|
static void xv_print_ck_info(struct vo *vo)
|
2013-01-26 21:37:47 +00:00
|
|
|
{
|
2013-08-23 21:30:09 +00:00
|
|
|
struct xvctx *xv = vo->priv;
|
2013-01-26 21:37:47 +00:00
|
|
|
|
|
|
|
switch (xv->xv_ck_info.method) {
|
|
|
|
case CK_METHOD_NONE:
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "Drawing no colorkey.\n");
|
2013-01-26 21:37:47 +00:00
|
|
|
return;
|
|
|
|
case CK_METHOD_AUTOPAINT:
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "Colorkey is drawn by Xv.\n");
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
case CK_METHOD_MANUALFILL:
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "Drawing colorkey manually.\n");
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
case CK_METHOD_BACKGROUND:
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "Colorkey is drawn as window background.\n");
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (xv->xv_ck_info.source) {
|
|
|
|
case CK_SRC_CUR:
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "Using colorkey from Xv (0x%06lx).\n", xv->xv_colorkey);
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
case CK_SRC_USE:
|
|
|
|
if (xv->xv_ck_info.method == CK_METHOD_AUTOPAINT) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "Ignoring colorkey from mpv (0x%06lx).\n",
|
|
|
|
xv->xv_colorkey);
|
2013-01-26 21:37:47 +00:00
|
|
|
} else {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "Using colorkey from mpv (0x%06lx). Use -colorkey to change.\n",
|
|
|
|
xv->xv_colorkey);
|
2013-01-26 21:37:47 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CK_SRC_SET:
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "Setting and using colorkey from mpv (0x%06lx)."
|
|
|
|
" Use -colorkey to change.\n", xv->xv_colorkey);
|
2013-01-26 21:37:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-04 16:40:21 +00:00
|
|
|
/* NOTE: If vo.colorkey has bits set after the first 3 low order bytes
|
2013-01-26 21:37:47 +00:00
|
|
|
* we don't draw anything as this means it was forced to off. */
|
|
|
|
static int xv_init_colorkey(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct xvctx *ctx = vo->priv;
|
|
|
|
Display *display = vo->x11->display;
|
|
|
|
Atom xv_atom;
|
|
|
|
int rez;
|
|
|
|
|
|
|
|
/* check if colorkeying is needed */
|
|
|
|
xv_atom = xv_intern_atom_if_exists(vo, "XV_COLORKEY");
|
2016-08-31 11:22:32 +00:00
|
|
|
if (xv_atom != None && ctx->xv_ck_info.method != CK_METHOD_NONE) {
|
2013-01-26 21:37:47 +00:00
|
|
|
if (ctx->xv_ck_info.source == CK_SRC_CUR) {
|
|
|
|
int colorkey_ret;
|
|
|
|
|
|
|
|
rez = XvGetPortAttribute(display, ctx->xv_port, xv_atom,
|
|
|
|
&colorkey_ret);
|
|
|
|
if (rez == Success)
|
|
|
|
ctx->xv_colorkey = colorkey_ret;
|
|
|
|
else {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_FATAL(vo, "Couldn't get colorkey! "
|
|
|
|
"Maybe the selected Xv port has no overlay.\n");
|
2013-01-26 21:37:47 +00:00
|
|
|
return 0; // error getting colorkey
|
|
|
|
}
|
|
|
|
} else {
|
2013-07-21 23:03:22 +00:00
|
|
|
ctx->xv_colorkey = ctx->colorkey;
|
2013-01-26 21:37:47 +00:00
|
|
|
|
|
|
|
/* check if we have to set the colorkey too */
|
|
|
|
if (ctx->xv_ck_info.source == CK_SRC_SET) {
|
|
|
|
xv_atom = XInternAtom(display, "XV_COLORKEY", False);
|
|
|
|
|
|
|
|
rez = XvSetPortAttribute(display, ctx->xv_port, xv_atom,
|
2013-07-21 23:03:22 +00:00
|
|
|
ctx->colorkey);
|
2013-01-26 21:37:47 +00:00
|
|
|
if (rez != Success) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_FATAL(vo, "Couldn't set colorkey!\n");
|
2013-01-26 21:37:47 +00:00
|
|
|
return 0; // error setting colorkey
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
xv_atom = xv_intern_atom_if_exists(vo, "XV_AUTOPAINT_COLORKEY");
|
|
|
|
|
|
|
|
/* should we draw the colorkey ourselves or activate autopainting? */
|
|
|
|
if (ctx->xv_ck_info.method == CK_METHOD_AUTOPAINT) {
|
|
|
|
rez = !Success;
|
|
|
|
|
|
|
|
if (xv_atom != None) // autopaint is supported
|
|
|
|
rez = XvSetPortAttribute(display, ctx->xv_port, xv_atom, 1);
|
|
|
|
|
|
|
|
if (rez != Success)
|
|
|
|
ctx->xv_ck_info.method = CK_METHOD_MANUALFILL;
|
|
|
|
} else {
|
|
|
|
// disable colorkey autopainting if supported
|
|
|
|
if (xv_atom != None)
|
|
|
|
XvSetPortAttribute(display, ctx->xv_port, xv_atom, 0);
|
|
|
|
}
|
2016-08-31 11:22:32 +00:00
|
|
|
} else { // do no colorkey drawing at all
|
2013-01-26 21:37:47 +00:00
|
|
|
ctx->xv_ck_info.method = CK_METHOD_NONE;
|
2016-08-31 11:22:32 +00:00
|
|
|
ctx->colorkey = 0xFF000000;
|
|
|
|
}
|
2013-01-26 21:37:47 +00:00
|
|
|
|
2013-08-23 21:30:09 +00:00
|
|
|
xv_print_ck_info(vo);
|
2013-01-26 21:37:47 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Draw the colorkey on the video window.
|
|
|
|
*
|
|
|
|
* Draws the colorkey depending on the set method ( colorkey_handling ).
|
|
|
|
*
|
|
|
|
* Also draws the black bars ( when the video doesn't fit the display in
|
|
|
|
* fullscreen ) separately, so they don't overlap with the video area. */
|
2013-03-17 22:01:33 +00:00
|
|
|
static void xv_draw_colorkey(struct vo *vo, const struct mp_rect *rc)
|
2012-11-11 17:40:43 +00:00
|
|
|
{
|
|
|
|
struct xvctx *ctx = vo->priv;
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2013-01-26 21:37:47 +00:00
|
|
|
if (ctx->xv_ck_info.method == CK_METHOD_MANUALFILL ||
|
|
|
|
ctx->xv_ck_info.method == CK_METHOD_BACKGROUND)
|
|
|
|
{
|
2015-07-01 21:55:10 +00:00
|
|
|
if (!ctx->vo_gc)
|
2015-02-26 08:26:28 +00:00
|
|
|
return;
|
2013-01-26 21:37:47 +00:00
|
|
|
//less tearing than XClearWindow()
|
2015-07-01 21:55:10 +00:00
|
|
|
XSetForeground(x11->display, ctx->vo_gc, ctx->xv_colorkey);
|
|
|
|
XFillRectangle(x11->display, x11->window, ctx->vo_gc, rc->x0, rc->y0,
|
2013-03-17 22:01:33 +00:00
|
|
|
rc->x1 - rc->x0, rc->y1 - rc->y0);
|
2013-01-26 21:37:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void read_xv_csp(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct xvctx *ctx = vo->priv;
|
2014-03-28 23:25:08 +00:00
|
|
|
ctx->cached_csp = 0;
|
2012-11-11 17:40:43 +00:00
|
|
|
int bt709_enabled;
|
2013-01-26 21:37:47 +00:00
|
|
|
if (xv_get_eq(vo, ctx->xv_port, "bt_709", &bt709_enabled))
|
2014-03-28 23:25:08 +00:00
|
|
|
ctx->cached_csp = bt709_enabled == 100 ? MP_CSP_BT_709 : MP_CSP_BT_601;
|
2012-11-11 17:40:43 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 21:55:10 +00:00
|
|
|
|
|
|
|
static void fill_rect(struct vo *vo, GC gc, int x0, int y0, int x1, int y1)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
|
|
|
x0 = MPMAX(x0, 0);
|
|
|
|
y0 = MPMAX(y0, 0);
|
|
|
|
x1 = MPMIN(x1, vo->dwidth);
|
|
|
|
y1 = MPMIN(y1, vo->dheight);
|
|
|
|
|
2015-10-07 11:47:36 +00:00
|
|
|
if (x11->window && gc && x1 > x0 && y1 > y0)
|
2015-07-01 21:55:10 +00:00
|
|
|
XFillRectangle(x11->display, x11->window, gc, x0, y0, x1 - x0, y1 - y0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear everything outside of rc with the background color
|
|
|
|
static void vo_x11_clear_background(struct vo *vo, const struct mp_rect *rc)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
struct xvctx *ctx = vo->priv;
|
|
|
|
GC gc = ctx->f_gc;
|
|
|
|
|
|
|
|
int w = vo->dwidth;
|
|
|
|
int h = vo->dheight;
|
|
|
|
|
|
|
|
fill_rect(vo, gc, 0, 0, w, rc->y0); // top
|
|
|
|
fill_rect(vo, gc, 0, rc->y1, w, h); // bottom
|
|
|
|
fill_rect(vo, gc, 0, rc->y0, rc->x0, rc->y1); // left
|
|
|
|
fill_rect(vo, gc, rc->x1, rc->y0, w, rc->y1); // right
|
|
|
|
|
|
|
|
XFlush(x11->display);
|
|
|
|
}
|
|
|
|
|
2009-02-13 01:52:51 +00:00
|
|
|
static void resize(struct vo *vo)
|
2009-02-12 17:40:53 +00:00
|
|
|
{
|
2009-02-13 01:52:51 +00:00
|
|
|
struct xvctx *ctx = vo->priv;
|
|
|
|
|
2012-10-27 20:10:32 +00:00
|
|
|
// Can't be used, because the function calculates screen-space coordinates,
|
|
|
|
// while we need video-space.
|
|
|
|
struct mp_osd_res unused;
|
|
|
|
|
|
|
|
vo_get_src_dst_rects(vo, &ctx->src_rect, &ctx->dst_rect, &unused);
|
|
|
|
|
2013-03-17 22:01:33 +00:00
|
|
|
vo_x11_clear_background(vo, &ctx->dst_rect);
|
|
|
|
xv_draw_colorkey(vo, &ctx->dst_rect);
|
2012-11-11 17:40:43 +00:00
|
|
|
read_xv_csp(vo);
|
2013-06-15 15:14:14 +00:00
|
|
|
|
2014-07-27 19:33:11 +00:00
|
|
|
mp_input_set_mouse_transform(vo->input_ctx, &ctx->dst_rect, &ctx->src_rect);
|
|
|
|
|
2013-06-15 15:14:14 +00:00
|
|
|
vo->want_redraw = true;
|
2009-02-12 17:40:53 +00:00
|
|
|
}
|
|
|
|
|
2001-03-21 19:10:55 +00:00
|
|
|
/*
|
2014-01-24 20:22:25 +00:00
|
|
|
* create and map window,
|
2001-02-24 20:28:24 +00:00
|
|
|
* allocate colors and (shared) memory
|
|
|
|
*/
|
2015-10-03 16:20:16 +00:00
|
|
|
static int reconfig(struct vo *vo, struct mp_image_params *params)
|
2001-02-24 20:28:24 +00:00
|
|
|
{
|
2008-04-20 06:34:27 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2008-04-04 05:04:11 +00:00
|
|
|
struct xvctx *ctx = vo->priv;
|
|
|
|
int i;
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2012-12-22 16:50:15 +00:00
|
|
|
mp_image_unrefp(&ctx->original_image);
|
|
|
|
|
2014-01-24 20:22:25 +00:00
|
|
|
ctx->image_height = params->h;
|
|
|
|
ctx->image_width = params->w;
|
|
|
|
ctx->image_format = params->imgfmt;
|
2001-10-18 02:42:20 +00:00
|
|
|
|
2008-12-01 14:15:17 +00:00
|
|
|
if ((ctx->max_width != 0 && ctx->max_height != 0)
|
|
|
|
&& (ctx->image_width > ctx->max_width
|
|
|
|
|| ctx->image_height > ctx->max_height)) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "Source image dimensions are too high: %ux%u (maximum is %ux%u)\n",
|
2008-12-01 14:15:17 +00:00
|
|
|
ctx->image_width, ctx->image_height, ctx->max_width,
|
|
|
|
ctx->max_height);
|
2005-11-10 02:39:31 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-06-14 04:53:03 +00:00
|
|
|
/* check image formats */
|
2008-04-04 05:42:51 +00:00
|
|
|
ctx->xv_format = 0;
|
|
|
|
for (i = 0; i < ctx->formats; i++) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "Xvideo image format: 0x%x (%4.4s) %s\n",
|
|
|
|
ctx->fo[i].id, (char *) &ctx->fo[i].id,
|
|
|
|
(ctx->fo[i].format == XvPacked) ? "packed" : "planar");
|
2014-01-24 20:22:25 +00:00
|
|
|
if (ctx->fo[i].id == find_xv_format(ctx->image_format))
|
2008-04-04 05:42:51 +00:00
|
|
|
ctx->xv_format = ctx->fo[i].id;
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
2008-04-04 05:42:51 +00:00
|
|
|
if (!ctx->xv_format)
|
|
|
|
return -1;
|
2001-10-18 02:42:20 +00:00
|
|
|
|
2015-09-30 21:31:34 +00:00
|
|
|
vo_x11_config_vo_window(vo);
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2015-07-01 21:55:10 +00:00
|
|
|
if (!ctx->f_gc && !ctx->vo_gc) {
|
|
|
|
ctx->f_gc = XCreateGC(x11->display, x11->window, 0, 0);
|
|
|
|
ctx->vo_gc = XCreateGC(x11->display, x11->window, 0, NULL);
|
|
|
|
XSetForeground(x11->display, ctx->f_gc, 0);
|
|
|
|
}
|
|
|
|
|
2013-01-27 11:01:08 +00:00
|
|
|
if (ctx->xv_ck_info.method == CK_METHOD_BACKGROUND)
|
|
|
|
XSetWindowBackground(x11->display, x11->window, ctx->xv_colorkey);
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "using Xvideo port %d for hw scaling\n", ctx->xv_port);
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2008-04-04 06:09:14 +00:00
|
|
|
// In case config has been called before
|
2013-03-14 14:53:59 +00:00
|
|
|
for (i = 0; i < ctx->num_buffers; i++)
|
2008-04-04 06:09:14 +00:00
|
|
|
deallocate_xvimage(vo, i);
|
|
|
|
|
2015-05-20 21:07:47 +00:00
|
|
|
ctx->num_buffers = ctx->cfg_buffers;
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2013-10-27 16:58:06 +00:00
|
|
|
for (i = 0; i < ctx->num_buffers; i++) {
|
|
|
|
if (!allocate_xvimage(vo, i)) {
|
|
|
|
MP_FATAL(vo, "could not allocate Xv image data\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2008-04-04 05:04:11 +00:00
|
|
|
ctx->current_buf = 0;
|
|
|
|
ctx->current_ip_buf = 0;
|
2001-03-21 19:10:55 +00:00
|
|
|
|
2016-06-29 07:16:13 +00:00
|
|
|
int is_709 = params->color.space == MP_CSP_BT_709;
|
2014-03-28 23:25:08 +00:00
|
|
|
xv_set_eq(vo, ctx->xv_port, "bt_709", is_709 * 200 - 100);
|
|
|
|
read_xv_csp(vo);
|
2002-06-10 18:40:19 +00:00
|
|
|
|
2009-02-13 01:52:51 +00:00
|
|
|
resize(vo);
|
2003-11-30 16:36:10 +00:00
|
|
|
|
2004-06-14 04:53:03 +00:00
|
|
|
return 0;
|
2001-02-24 20:28:24 +00:00
|
|
|
}
|
|
|
|
|
2013-10-27 16:58:06 +00:00
|
|
|
static bool allocate_xvimage(struct vo *vo, int foo)
|
2001-02-24 20:28:24 +00:00
|
|
|
{
|
2008-04-04 05:04:11 +00:00
|
|
|
struct xvctx *ctx = vo->priv;
|
2008-04-20 06:34:27 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2013-01-06 20:44:15 +00:00
|
|
|
// align it for faster OSD rendering (draw_bmp.c swscale usage)
|
|
|
|
int aligned_w = FFALIGN(ctx->image_width, 32);
|
2016-06-25 10:44:42 +00:00
|
|
|
// round up the height to next chroma boundary too
|
|
|
|
int aligned_h = FFALIGN(ctx->image_height, 2);
|
2013-01-02 11:55:52 +00:00
|
|
|
if (x11->display_is_local && XShmQueryExtension(x11->display)) {
|
2008-04-04 05:04:11 +00:00
|
|
|
ctx->Shmem_Flag = 1;
|
2013-01-02 11:55:52 +00:00
|
|
|
x11->ShmCompletionEvent = XShmGetEventBase(x11->display)
|
|
|
|
+ ShmCompletion;
|
|
|
|
} else {
|
2008-04-04 05:04:11 +00:00
|
|
|
ctx->Shmem_Flag = 0;
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_INFO(vo, "Shared memory not supported\nReverting to normal Xv.\n");
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
2008-12-01 14:15:17 +00:00
|
|
|
if (ctx->Shmem_Flag) {
|
2008-04-04 05:04:11 +00:00
|
|
|
ctx->xvimage[foo] =
|
2013-01-26 21:37:47 +00:00
|
|
|
(XvImage *) XvShmCreateImage(x11->display, ctx->xv_port,
|
2008-12-01 14:15:17 +00:00
|
|
|
ctx->xv_format, NULL,
|
2016-06-25 10:44:42 +00:00
|
|
|
aligned_w, aligned_h,
|
2008-04-04 05:04:11 +00:00
|
|
|
&ctx->Shminfo[foo]);
|
2013-10-27 16:58:06 +00:00
|
|
|
if (!ctx->xvimage[foo])
|
|
|
|
return false;
|
2008-04-04 05:04:11 +00:00
|
|
|
|
2008-12-01 14:15:17 +00:00
|
|
|
ctx->Shminfo[foo].shmid = shmget(IPC_PRIVATE,
|
|
|
|
ctx->xvimage[foo]->data_size,
|
|
|
|
IPC_CREAT | 0777);
|
2015-03-19 23:46:56 +00:00
|
|
|
ctx->Shminfo[foo].shmaddr = shmat(ctx->Shminfo[foo].shmid, 0, 0);
|
2013-10-27 16:58:06 +00:00
|
|
|
if (ctx->Shminfo[foo].shmaddr == (void *)-1)
|
|
|
|
return false;
|
2008-04-04 05:04:11 +00:00
|
|
|
ctx->Shminfo[foo].readOnly = False;
|
|
|
|
|
|
|
|
ctx->xvimage[foo]->data = ctx->Shminfo[foo].shmaddr;
|
2008-04-20 06:34:27 +00:00
|
|
|
XShmAttach(x11->display, &ctx->Shminfo[foo]);
|
|
|
|
XSync(x11->display, False);
|
2008-04-04 05:04:11 +00:00
|
|
|
shmctl(ctx->Shminfo[foo].shmid, IPC_RMID, 0);
|
2017-12-02 22:19:13 +00:00
|
|
|
} else {
|
2008-04-04 05:04:11 +00:00
|
|
|
ctx->xvimage[foo] =
|
2013-01-26 21:37:47 +00:00
|
|
|
(XvImage *) XvCreateImage(x11->display, ctx->xv_port,
|
2012-11-22 18:37:20 +00:00
|
|
|
ctx->xv_format, NULL, aligned_w,
|
2016-06-25 10:44:42 +00:00
|
|
|
aligned_h);
|
2013-10-27 16:58:06 +00:00
|
|
|
if (!ctx->xvimage[foo])
|
|
|
|
return false;
|
2012-11-30 16:45:40 +00:00
|
|
|
ctx->xvimage[foo]->data = av_malloc(ctx->xvimage[foo]->data_size);
|
2013-10-27 16:58:06 +00:00
|
|
|
if (!ctx->xvimage[foo]->data)
|
|
|
|
return false;
|
2008-04-20 06:34:27 +00:00
|
|
|
XSync(x11->display, False);
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
2016-05-23 23:21:18 +00:00
|
|
|
|
2016-06-25 10:44:42 +00:00
|
|
|
if ((ctx->xvimage[foo]->width < aligned_w) ||
|
|
|
|
(ctx->xvimage[foo]->height < aligned_h)) {
|
|
|
|
MP_ERR(vo, "Got XvImage with too small size: %ux%u (expected %ux%u)\n",
|
2016-05-23 23:21:18 +00:00
|
|
|
ctx->xvimage[foo]->width, ctx->xvimage[foo]->height,
|
|
|
|
aligned_w, ctx->image_height);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-11-30 16:45:40 +00:00
|
|
|
struct mp_image img = get_xv_buffer(vo, foo);
|
2016-06-25 10:44:42 +00:00
|
|
|
mp_image_set_size(&img, aligned_w, aligned_h);
|
2012-12-19 11:04:57 +00:00
|
|
|
mp_image_clear(&img, 0, 0, img.w, img.h);
|
2013-10-27 16:58:06 +00:00
|
|
|
return true;
|
2001-02-24 20:28:24 +00:00
|
|
|
}
|
|
|
|
|
2008-04-04 05:04:11 +00:00
|
|
|
static void deallocate_xvimage(struct vo *vo, int foo)
|
2001-08-31 10:06:32 +00:00
|
|
|
{
|
2008-04-04 05:04:11 +00:00
|
|
|
struct xvctx *ctx = vo->priv;
|
2008-12-01 14:15:17 +00:00
|
|
|
if (ctx->Shmem_Flag) {
|
2008-04-20 06:34:27 +00:00
|
|
|
XShmDetach(vo->x11->display, &ctx->Shminfo[foo]);
|
2008-04-04 05:04:11 +00:00
|
|
|
shmdt(ctx->Shminfo[foo].shmaddr);
|
2017-12-02 22:19:13 +00:00
|
|
|
} else {
|
2012-11-30 16:45:40 +00:00
|
|
|
av_free(ctx->xvimage[foo]->data);
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
2013-10-27 16:58:06 +00:00
|
|
|
if (ctx->xvimage[foo])
|
|
|
|
XFree(ctx->xvimage[foo]);
|
|
|
|
|
|
|
|
ctx->xvimage[foo] = NULL;
|
|
|
|
ctx->Shminfo[foo] = (XShmSegmentInfo){0};
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2008-04-20 06:34:27 +00:00
|
|
|
XSync(vo->x11->display, False);
|
2004-06-14 04:53:03 +00:00
|
|
|
return;
|
2001-08-31 10:06:32 +00:00
|
|
|
}
|
|
|
|
|
2008-04-04 05:04:11 +00:00
|
|
|
static inline void put_xvimage(struct vo *vo, XvImage *xvi)
|
2005-02-23 12:23:44 +00:00
|
|
|
{
|
2008-04-04 05:04:11 +00:00
|
|
|
struct xvctx *ctx = vo->priv;
|
2008-04-20 06:34:27 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2012-10-27 20:10:32 +00:00
|
|
|
struct mp_rect *src = &ctx->src_rect;
|
|
|
|
struct mp_rect *dst = &ctx->dst_rect;
|
|
|
|
int dw = dst->x1 - dst->x0, dh = dst->y1 - dst->y0;
|
|
|
|
int sw = src->x1 - src->x0, sh = src->y1 - src->y0;
|
2017-12-02 22:19:13 +00:00
|
|
|
|
2008-12-01 14:15:17 +00:00
|
|
|
if (ctx->Shmem_Flag) {
|
2015-07-01 21:55:10 +00:00
|
|
|
XvShmPutImage(x11->display, ctx->xv_port, x11->window, ctx->vo_gc, xvi,
|
2012-10-27 20:10:32 +00:00
|
|
|
src->x0, src->y0, sw, sh,
|
|
|
|
dst->x0, dst->y0, dw, dh,
|
2013-01-02 11:55:52 +00:00
|
|
|
True);
|
|
|
|
x11->ShmCompletionWaitCount++;
|
2017-12-02 22:19:13 +00:00
|
|
|
} else {
|
2015-07-01 21:55:10 +00:00
|
|
|
XvPutImage(x11->display, ctx->xv_port, x11->window, ctx->vo_gc, xvi,
|
2012-10-27 20:10:32 +00:00
|
|
|
src->x0, src->y0, sw, sh,
|
|
|
|
dst->x0, dst->y0, dw, dh);
|
2005-02-23 12:23:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-07 01:28:40 +00:00
|
|
|
static struct mp_image get_xv_buffer(struct vo *vo, int buf_index)
|
2008-12-01 17:53:57 +00:00
|
|
|
{
|
|
|
|
struct xvctx *ctx = vo->priv;
|
2012-10-07 01:28:40 +00:00
|
|
|
XvImage *xv_image = ctx->xvimage[buf_index];
|
|
|
|
|
|
|
|
struct mp_image img = {0};
|
2012-11-10 01:02:24 +00:00
|
|
|
mp_image_set_size(&img, ctx->image_width, ctx->image_height);
|
2012-10-07 01:28:40 +00:00
|
|
|
mp_image_setfmt(&img, ctx->image_format);
|
|
|
|
|
video: decouple internal pixel formats from FourCCs
mplayer's video chain traditionally used FourCCs for pixel formats. For
example, it used IMGFMT_YV12 for 4:2:0 YUV, which was defined to the
string 'YV12' interpreted as unsigned int. Additionally, it used to
encode information into the numeric values of some formats. The RGB
formats had their bit depth and endian encoded into the least
significant byte. Extended planar formats (420P10 etc.) had chroma
shift, endian, and component bit depth encoded. (This has been removed
in recent commits.)
Replace the FourCC mess with a simple enum. Remove all the redundant
formats like YV12/I420/IYUV. Replace some image format names by
something more intuitive, most importantly IMGFMT_YV12 -> IMGFMT_420P.
Add img_fourcc.h, which contains the old IDs for code that actually uses
FourCCs. Change the way demuxers, that output raw video, identify the
video format: they set either MP_FOURCC_RAWVIDEO or MP_FOURCC_IMGFMT to
request the rawvideo decoder, and sh_video->imgfmt specifies the pixel
format. Like the previous hack, this is supposed to avoid the need for
a complete codecs.cfg entry per format, or other lookup tables. (Note
that the RGB raw video FourCCs mostly rely on ffmpeg's mappings for NUT
raw video, but this is still considered better than adding a raw video
decoder - even if trivial, it would be full of annoying lookup tables.)
The TV code has not been tested.
Some corrective changes regarding endian and other image format flags
creep in.
2012-12-23 19:03:30 +00:00
|
|
|
bool swapuv = ctx->xv_format == MP_FOURCC_YV12;
|
2012-10-07 01:28:40 +00:00
|
|
|
for (int n = 0; n < img.num_planes; n++) {
|
|
|
|
int sn = n > 0 && swapuv ? (n == 1 ? 2 : 1) : n;
|
|
|
|
img.planes[n] = xv_image->data + xv_image->offsets[sn];
|
|
|
|
img.stride[n] = xv_image->pitches[sn];
|
|
|
|
}
|
2008-12-01 17:53:57 +00:00
|
|
|
|
2014-03-28 23:25:08 +00:00
|
|
|
if (vo->params) {
|
|
|
|
struct mp_image_params params = *vo->params;
|
|
|
|
if (ctx->cached_csp)
|
2016-06-29 07:16:13 +00:00
|
|
|
params.color.space = ctx->cached_csp;
|
2014-03-28 23:25:08 +00:00
|
|
|
mp_image_set_attributes(&img, ¶ms);
|
|
|
|
}
|
2012-10-27 16:06:09 +00:00
|
|
|
|
2012-10-07 01:28:40 +00:00
|
|
|
return img;
|
|
|
|
}
|
|
|
|
|
2013-01-02 11:55:52 +00:00
|
|
|
static void wait_for_completion(struct vo *vo, int max_outstanding)
|
|
|
|
{
|
|
|
|
struct xvctx *ctx = vo->priv;
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
if (ctx->Shmem_Flag) {
|
|
|
|
while (x11->ShmCompletionWaitCount > max_outstanding) {
|
|
|
|
if (!ctx->Shm_Warned_Slow) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_WARN(vo, "X11 can't keep up! Waiting"
|
|
|
|
" for XShm completion events...\n");
|
2013-01-02 11:55:52 +00:00
|
|
|
ctx->Shm_Warned_Slow = 1;
|
|
|
|
}
|
2013-05-25 17:56:52 +00:00
|
|
|
mp_sleep_us(1000);
|
2013-05-16 12:02:28 +00:00
|
|
|
vo_x11_check_events(vo);
|
2013-01-02 11:55:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-04 05:04:11 +00:00
|
|
|
static void flip_page(struct vo *vo)
|
2001-02-24 20:28:24 +00:00
|
|
|
{
|
2008-04-04 05:04:11 +00:00
|
|
|
struct xvctx *ctx = vo->priv;
|
|
|
|
put_xvimage(vo, ctx->xvimage[ctx->current_buf]);
|
2005-02-23 12:23:44 +00:00
|
|
|
|
|
|
|
/* remember the currently visible buffer */
|
2012-03-19 23:02:13 +00:00
|
|
|
ctx->current_buf = (ctx->current_buf + 1) % ctx->num_buffers;
|
2013-01-02 11:55:52 +00:00
|
|
|
|
|
|
|
if (!ctx->Shmem_Flag)
|
|
|
|
XSync(vo->x11->display, False);
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
|
2022-06-22 04:13:44 +00:00
|
|
|
if (vo->x11->use_present) {
|
|
|
|
vo_x11_present(vo);
|
|
|
|
present_sync_swap(vo->x11->present);
|
|
|
|
}
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void get_vsync(struct vo *vo, struct vo_vsync_info *info)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2022-06-22 04:13:44 +00:00
|
|
|
if (x11->use_present)
|
|
|
|
present_sync_get_info(x11->present, info);
|
2001-02-24 20:28:24 +00:00
|
|
|
}
|
|
|
|
|
2014-06-18 18:04:59 +00:00
|
|
|
// Note: REDRAW_FRAME can call this with NULL.
|
2012-11-04 14:56:04 +00:00
|
|
|
static void draw_image(struct vo *vo, mp_image_t *mpi)
|
2004-06-14 04:53:03 +00:00
|
|
|
{
|
2008-04-04 05:04:11 +00:00
|
|
|
struct xvctx *ctx = vo->priv;
|
2008-12-01 17:53:57 +00:00
|
|
|
|
2013-01-02 11:55:52 +00:00
|
|
|
wait_for_completion(vo, ctx->num_buffers - 1);
|
x11: avoid wasteful rendering when possible
Because wayland is a special snowflake, mpv wound up incorporating a lot
of logic into its render loop where visibilty checks are performed
before rendering anything (in the name of efficiency of course). Only
wayland actually uses this, but there's no reason why other backends
(x11 in this commit) can't be smarter. It's far easier on xorg since we
can just query _NET_WM_STATE_HIDDEN directly and not have to do silly
callback dances.
The function, vo_x11_check_net_wm_state_change, already tracks net wm
changes, including _NET_WM_STATE_HIDDEN. There is an already existing
window_hidden variable but that is actually just for checking if the
window was mapped and has nothing to do with this particular atom. mpv
also currently assumes that a _NET_WM_STATE_HIDDEN is exactly the same
as being minimized but according to the spec, that's not neccesarily
true (in practice, it's likely that these are the same though). Anyways,
just keep track of this state in a new variable (hidden) and use that
for determing if mpv should render or not.
There is one catch though: this cannot work if a display sync mode is
used. This is why the previous commit is needed. The display sync modes
in mpv require a blocking vsync implementation since its render loop is
directly driven by vsync. In xorg, if nothing is actually rendered, then
there's nothing for eglSwapBuffers (or FIFO for vulkan) to block on so
it returns immediately. This, of course, results in completely broken
video. We just need to check to make sure that we aren't in a display
sync mode before trying to be smart about rendering. Display sync is
power inefficient anyways, so no one is really being hurt here. As an
aside, this happens to work in wayland because there's basically a
custom (and ugly) vsync blocking function + timeout but that's off
topic.
2022-04-07 03:36:30 +00:00
|
|
|
bool render = vo_x11_check_visible(vo);
|
|
|
|
if (!render)
|
|
|
|
return;
|
2013-01-02 11:55:52 +00:00
|
|
|
|
2012-12-22 16:25:28 +00:00
|
|
|
struct mp_image xv_buffer = get_xv_buffer(vo, ctx->current_buf);
|
2013-10-06 21:03:30 +00:00
|
|
|
if (mpi) {
|
|
|
|
mp_image_copy(&xv_buffer, mpi);
|
|
|
|
} else {
|
|
|
|
mp_image_clear(&xv_buffer, 0, 0, xv_buffer.w, xv_buffer.h);
|
|
|
|
}
|
2008-12-01 17:53:57 +00:00
|
|
|
|
2014-06-15 18:46:57 +00:00
|
|
|
struct mp_osd_res res = osd_res_from_image_params(vo->params);
|
|
|
|
osd_draw_on_image(vo->osd, res, mpi ? mpi->pts : 0, 0, &xv_buffer);
|
|
|
|
|
2014-06-17 21:05:50 +00:00
|
|
|
if (mpi != ctx->original_image) {
|
|
|
|
talloc_free(ctx->original_image);
|
|
|
|
ctx->original_image = mpi;
|
|
|
|
}
|
2002-07-20 16:27:17 +00:00
|
|
|
}
|
|
|
|
|
2015-01-21 21:08:24 +00:00
|
|
|
static int query_format(struct vo *vo, int format)
|
2001-02-24 20:28:24 +00:00
|
|
|
{
|
2012-11-04 15:24:18 +00:00
|
|
|
struct xvctx *ctx = vo->priv;
|
2003-09-12 15:50:35 +00:00
|
|
|
uint32_t i;
|
2004-06-14 04:53:03 +00:00
|
|
|
|
video: decouple internal pixel formats from FourCCs
mplayer's video chain traditionally used FourCCs for pixel formats. For
example, it used IMGFMT_YV12 for 4:2:0 YUV, which was defined to the
string 'YV12' interpreted as unsigned int. Additionally, it used to
encode information into the numeric values of some formats. The RGB
formats had their bit depth and endian encoded into the least
significant byte. Extended planar formats (420P10 etc.) had chroma
shift, endian, and component bit depth encoded. (This has been removed
in recent commits.)
Replace the FourCC mess with a simple enum. Remove all the redundant
formats like YV12/I420/IYUV. Replace some image format names by
something more intuitive, most importantly IMGFMT_YV12 -> IMGFMT_420P.
Add img_fourcc.h, which contains the old IDs for code that actually uses
FourCCs. Change the way demuxers, that output raw video, identify the
video format: they set either MP_FOURCC_RAWVIDEO or MP_FOURCC_IMGFMT to
request the rawvideo decoder, and sh_video->imgfmt specifies the pixel
format. Like the previous hack, this is supposed to avoid the need for
a complete codecs.cfg entry per format, or other lookup tables. (Note
that the RGB raw video FourCCs mostly rely on ffmpeg's mappings for NUT
raw video, but this is still considered better than adding a raw video
decoder - even if trivial, it would be full of annoying lookup tables.)
The TV code has not been tested.
Some corrective changes regarding endian and other image format flags
creep in.
2012-12-23 19:03:30 +00:00
|
|
|
int fourcc = find_xv_format(format);
|
|
|
|
if (fourcc) {
|
|
|
|
for (i = 0; i < ctx->formats; i++) {
|
|
|
|
if (ctx->fo[i].id == fourcc)
|
2015-01-21 21:08:24 +00:00
|
|
|
return 1;
|
video: decouple internal pixel formats from FourCCs
mplayer's video chain traditionally used FourCCs for pixel formats. For
example, it used IMGFMT_YV12 for 4:2:0 YUV, which was defined to the
string 'YV12' interpreted as unsigned int. Additionally, it used to
encode information into the numeric values of some formats. The RGB
formats had their bit depth and endian encoded into the least
significant byte. Extended planar formats (420P10 etc.) had chroma
shift, endian, and component bit depth encoded. (This has been removed
in recent commits.)
Replace the FourCC mess with a simple enum. Remove all the redundant
formats like YV12/I420/IYUV. Replace some image format names by
something more intuitive, most importantly IMGFMT_YV12 -> IMGFMT_420P.
Add img_fourcc.h, which contains the old IDs for code that actually uses
FourCCs. Change the way demuxers, that output raw video, identify the
video format: they set either MP_FOURCC_RAWVIDEO or MP_FOURCC_IMGFMT to
request the rawvideo decoder, and sh_video->imgfmt specifies the pixel
format. Like the previous hack, this is supposed to avoid the need for
a complete codecs.cfg entry per format, or other lookup tables. (Note
that the RGB raw video FourCCs mostly rely on ffmpeg's mappings for NUT
raw video, but this is still considered better than adding a raw video
decoder - even if trivial, it would be full of annoying lookup tables.)
The TV code has not been tested.
Some corrective changes regarding endian and other image format flags
creep in.
2012-12-23 19:03:30 +00:00
|
|
|
}
|
2004-06-14 04:53:03 +00:00
|
|
|
}
|
|
|
|
return 0;
|
2001-02-24 20:28:24 +00:00
|
|
|
}
|
|
|
|
|
2008-04-04 05:04:11 +00:00
|
|
|
static void uninit(struct vo *vo)
|
2001-09-04 15:21:00 +00:00
|
|
|
{
|
2008-04-04 05:04:11 +00:00
|
|
|
struct xvctx *ctx = vo->priv;
|
2004-06-14 04:53:03 +00:00
|
|
|
int i;
|
|
|
|
|
2012-12-22 16:50:15 +00:00
|
|
|
talloc_free(ctx->original_image);
|
|
|
|
|
2008-04-04 06:57:58 +00:00
|
|
|
if (ctx->ai)
|
|
|
|
XvFreeAdaptorInfo(ctx->ai);
|
2008-04-04 05:04:11 +00:00
|
|
|
ctx->ai = NULL;
|
|
|
|
if (ctx->fo) {
|
|
|
|
XFree(ctx->fo);
|
|
|
|
ctx->fo = NULL;
|
2004-11-15 14:56:18 +00:00
|
|
|
}
|
2013-03-14 14:53:59 +00:00
|
|
|
for (i = 0; i < ctx->num_buffers; i++)
|
2008-04-04 05:04:11 +00:00
|
|
|
deallocate_xvimage(vo, i);
|
2015-07-01 21:55:10 +00:00
|
|
|
if (ctx->f_gc != None)
|
|
|
|
XFreeGC(vo->x11->display, ctx->f_gc);
|
|
|
|
if (ctx->vo_gc != None)
|
|
|
|
XFreeGC(vo->x11->display, ctx->vo_gc);
|
2008-04-04 06:57:58 +00:00
|
|
|
// uninit() shouldn't get called unless initialization went past vo_init()
|
2008-04-20 06:34:27 +00:00
|
|
|
vo_x11_uninit(vo);
|
2001-04-09 22:27:27 +00:00
|
|
|
}
|
2001-03-21 19:10:55 +00:00
|
|
|
|
2013-07-22 20:52:42 +00:00
|
|
|
static int preinit(struct vo *vo)
|
2002-01-26 16:01:26 +00:00
|
|
|
{
|
2002-04-07 02:10:39 +00:00
|
|
|
XvPortID xv_p;
|
2004-06-14 04:53:03 +00:00
|
|
|
int busy_ports = 0;
|
2003-09-12 15:50:35 +00:00
|
|
|
unsigned int i;
|
2013-07-21 22:59:00 +00:00
|
|
|
struct xvctx *ctx = vo->priv;
|
|
|
|
int xv_adaptor = ctx->cfg_xv_adaptor;
|
2005-02-20 22:43:25 +00:00
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
if (!vo_x11_init(vo))
|
2012-08-03 03:55:02 +00:00
|
|
|
return -1;
|
|
|
|
|
2015-09-30 21:31:34 +00:00
|
|
|
if (!vo_x11_create_vo_window(vo, NULL, "xv"))
|
|
|
|
goto error;
|
|
|
|
|
2012-08-03 03:55:02 +00:00
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
2004-06-14 04:53:03 +00:00
|
|
|
/* check for Xvideo extension */
|
2008-04-04 05:04:11 +00:00
|
|
|
unsigned int ver, rel, req, ev, err;
|
2008-12-01 14:15:17 +00:00
|
|
|
if (Success != XvQueryExtension(x11->display, &ver, &rel, &req, &ev, &err)) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "Xv not supported by this X11 version/driver\n");
|
2008-04-04 06:57:58 +00:00
|
|
|
goto error;
|
2002-04-07 02:10:39 +00:00
|
|
|
}
|
2004-06-14 04:53:03 +00:00
|
|
|
|
|
|
|
/* check for Xvideo support */
|
|
|
|
if (Success !=
|
2008-12-01 14:15:17 +00:00
|
|
|
XvQueryAdaptors(x11->display, DefaultRootWindow(x11->display),
|
|
|
|
&ctx->adaptors, &ctx->ai)) {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_ERR(vo, "XvQueryAdaptors failed.\n");
|
2008-04-04 06:57:58 +00:00
|
|
|
goto error;
|
2002-04-07 02:10:39 +00:00
|
|
|
}
|
|
|
|
|
2004-06-14 04:53:03 +00:00
|
|
|
/* check adaptors */
|
2013-01-26 21:37:47 +00:00
|
|
|
if (ctx->xv_port) {
|
2003-09-07 18:58:56 +00:00
|
|
|
int port_found;
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2008-12-01 14:15:17 +00:00
|
|
|
for (port_found = 0, i = 0; !port_found && i < ctx->adaptors; i++) {
|
|
|
|
if ((ctx->ai[i].type & XvInputMask)
|
|
|
|
&& (ctx->ai[i].type & XvImageMask)) {
|
2008-04-04 05:04:11 +00:00
|
|
|
for (xv_p = ctx->ai[i].base_id;
|
2008-12-01 14:15:17 +00:00
|
|
|
xv_p < ctx->ai[i].base_id + ctx->ai[i].num_ports;
|
|
|
|
++xv_p) {
|
2013-01-26 21:37:47 +00:00
|
|
|
if (xv_p == ctx->xv_port) {
|
2003-09-07 18:58:56 +00:00
|
|
|
port_found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-12-01 14:15:17 +00:00
|
|
|
if (port_found) {
|
2013-01-26 21:37:47 +00:00
|
|
|
if (XvGrabPort(x11->display, ctx->xv_port, CurrentTime))
|
|
|
|
ctx->xv_port = 0;
|
2008-12-01 14:15:17 +00:00
|
|
|
} else {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_WARN(vo, "Invalid port parameter, overriding with port 0.\n");
|
2013-01-26 21:37:47 +00:00
|
|
|
ctx->xv_port = 0;
|
2003-09-07 18:58:56 +00:00
|
|
|
}
|
2003-09-06 13:40:00 +00:00
|
|
|
}
|
2004-06-14 04:53:03 +00:00
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
for (i = 0; i < ctx->adaptors && ctx->xv_port == 0; i++) {
|
2008-05-13 17:52:25 +00:00
|
|
|
/* check if adaptor number has been specified */
|
|
|
|
if (xv_adaptor != -1 && xv_adaptor != i)
|
2008-12-01 14:15:17 +00:00
|
|
|
continue;
|
2008-05-15 18:19:35 +00:00
|
|
|
|
2008-12-01 14:15:17 +00:00
|
|
|
if ((ctx->ai[i].type & XvInputMask) && (ctx->ai[i].type & XvImageMask)) {
|
2008-04-04 05:04:11 +00:00
|
|
|
for (xv_p = ctx->ai[i].base_id;
|
|
|
|
xv_p < ctx->ai[i].base_id + ctx->ai[i].num_ports; ++xv_p)
|
2008-12-01 14:15:17 +00:00
|
|
|
if (!XvGrabPort(x11->display, xv_p, CurrentTime)) {
|
2013-01-26 21:37:47 +00:00
|
|
|
ctx->xv_port = xv_p;
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_VERBOSE(vo, "Using Xv Adapter #%d (%s)\n",
|
|
|
|
i, ctx->ai[i].name);
|
2004-06-14 04:53:03 +00:00
|
|
|
break;
|
2008-12-01 14:15:17 +00:00
|
|
|
} else {
|
2013-08-23 21:30:09 +00:00
|
|
|
MP_WARN(vo, "Could not grab port %i.\n", (int) xv_p);
|
2004-06-14 04:53:03 +00:00
|
|
|
++busy_ports;
|
|
|
|
}
|
|
|
|
}
|
2002-04-07 02:10:39 +00:00
|
|
|
}
|
2013-01-26 21:37:47 +00:00
|
|
|
if (!ctx->xv_port) {
|
2004-06-14 04:53:03 +00:00
|
|
|
if (busy_ports)
|
2014-06-20 21:04:29 +00:00
|
|
|
MP_ERR(vo, "Xvideo ports busy.\n");
|
2004-06-14 04:53:03 +00:00
|
|
|
else
|
2014-06-20 21:04:29 +00:00
|
|
|
MP_ERR(vo, "No Xvideo support found.\n");
|
2008-04-04 06:57:58 +00:00
|
|
|
goto error;
|
2002-04-07 02:10:39 +00:00
|
|
|
}
|
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
if (!xv_init_colorkey(vo)) {
|
2008-12-01 14:15:17 +00:00
|
|
|
goto error; // bail out, colorkey setup failed
|
2002-10-23 00:36:55 +00:00
|
|
|
}
|
2013-01-26 21:37:47 +00:00
|
|
|
xv_enable_vsync(vo);
|
|
|
|
xv_get_max_img_dim(vo, &ctx->max_width, &ctx->max_height);
|
2002-10-23 00:36:55 +00:00
|
|
|
|
2013-01-26 21:37:47 +00:00
|
|
|
ctx->fo = XvListImageFormats(x11->display, ctx->xv_port,
|
2008-12-01 14:15:17 +00:00
|
|
|
(int *) &ctx->formats);
|
2002-04-11 21:07:00 +00:00
|
|
|
|
2016-03-06 18:22:15 +00:00
|
|
|
MP_WARN(vo, "Warning: this legacy VO has bad quality and performance, "
|
|
|
|
"and will in particular result in blurry OSD and subtitles. "
|
2016-07-09 12:19:50 +00:00
|
|
|
"You should fix your graphics drivers, or not force the xv VO.\n");
|
2002-02-17 08:24:43 +00:00
|
|
|
return 0;
|
2008-04-04 06:57:58 +00:00
|
|
|
|
2008-12-01 14:15:17 +00:00
|
|
|
error:
|
|
|
|
uninit(vo); // free resources
|
2008-04-04 06:57:58 +00:00
|
|
|
return -1;
|
2002-01-26 16:01:26 +00:00
|
|
|
}
|
2001-02-24 20:28:24 +00:00
|
|
|
|
2008-04-04 05:04:11 +00:00
|
|
|
static int control(struct vo *vo, uint32_t request, void *data)
|
2002-02-09 00:47:26 +00:00
|
|
|
{
|
2008-04-04 05:04:11 +00:00
|
|
|
struct xvctx *ctx = vo->priv;
|
2008-12-01 14:15:17 +00:00
|
|
|
switch (request) {
|
|
|
|
case VOCTRL_SET_PANSCAN:
|
2009-09-04 16:49:35 +00:00
|
|
|
resize(vo);
|
2008-12-01 14:15:17 +00:00
|
|
|
return VO_TRUE;
|
2011-12-05 03:24:18 +00:00
|
|
|
case VOCTRL_REDRAW_FRAME:
|
2014-06-18 18:04:59 +00:00
|
|
|
draw_image(vo, ctx->original_image);
|
video/out: always support redrawing VO window at any point
Before, a VO could easily refuse to respond to VOCTRL_REDRAW_FRAME,
which means the VO wouldn't redraw OSD and window contents, and the
player would appear frozen to the user. This was a bit stupid, and makes
dealing with some corner cases much harder (think of --keep-open, which
was hard to implement, because the VO gets into this state if there are
no new video frames after a seek reset).
Change this, and require VOs to always react to VOCTRL_REDRAW_FRAME.
There are two aspects of this: First, behavior after a (successful)
vo_reconfig() call, but before any video frame has been displayed.
Second, behavior after a vo_seek_reset().
For the first issue, we define that sending VOCTRL_REDRAW_FRAME after
vo_reconfig() should clear the window with black. This requires minor
changes to some VOs. In particular vaapi makes this horribly
complicated, because OSD rendering is bound to a video surface. We
create a black dummy surface for this purpose.
The second issue is much simpler and works already with most VOs: they
simply redraw whatever has been uploaded previously. The exception is
vdpau, which has a complicated mechanism to track and filter video
frames. The state associated with this mechanism is completely cleared
with vo_seek_reset(), so implementing this to work as expected is not
trivial. For now, we just clear the window with black.
2013-10-01 21:35:51 +00:00
|
|
|
return true;
|
2011-10-06 18:46:01 +00:00
|
|
|
}
|
2013-05-16 12:02:28 +00:00
|
|
|
int events = 0;
|
|
|
|
int r = vo_x11_control(vo, &events, request, data);
|
2013-06-15 15:14:14 +00:00
|
|
|
if (events & (VO_EVENT_EXPOSE | VO_EVENT_RESIZE))
|
2013-05-16 12:02:28 +00:00
|
|
|
resize(vo);
|
2014-11-02 19:26:51 +00:00
|
|
|
vo_event(vo, events);
|
2013-05-16 12:02:28 +00:00
|
|
|
return r;
|
2002-02-09 00:47:26 +00:00
|
|
|
}
|
2008-04-04 05:04:11 +00:00
|
|
|
|
2013-07-21 22:59:00 +00:00
|
|
|
#define OPT_BASE_STRUCT struct xvctx
|
|
|
|
|
2008-04-04 05:04:11 +00:00
|
|
|
const struct vo_driver video_out_xv = {
|
2013-10-23 17:06:14 +00:00
|
|
|
.description = "X11/Xv",
|
|
|
|
.name = "xv",
|
2008-04-04 05:04:11 +00:00
|
|
|
.preinit = preinit,
|
2012-11-04 15:24:18 +00:00
|
|
|
.query_format = query_format,
|
2014-01-24 20:22:25 +00:00
|
|
|
.reconfig = reconfig,
|
2008-04-04 05:04:11 +00:00
|
|
|
.control = control,
|
2012-11-04 14:56:04 +00:00
|
|
|
.draw_image = draw_image,
|
2008-04-04 05:04:11 +00:00
|
|
|
.flip_page = flip_page,
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
.get_vsync = get_vsync,
|
2016-07-20 18:52:08 +00:00
|
|
|
.wakeup = vo_x11_wakeup,
|
|
|
|
.wait_events = vo_x11_wait_events,
|
2013-07-21 22:59:00 +00:00
|
|
|
.uninit = uninit,
|
|
|
|
.priv_size = sizeof(struct xvctx),
|
|
|
|
.priv_defaults = &(const struct xvctx) {
|
|
|
|
.cfg_xv_adaptor = -1,
|
|
|
|
.xv_ck_info = {CK_METHOD_MANUALFILL, CK_SRC_CUR},
|
2013-07-21 23:03:22 +00:00
|
|
|
.colorkey = 0x0000ff00, // default colorkey is green
|
|
|
|
// (0xff000000 means that colorkey has been disabled)
|
2015-05-20 21:07:47 +00:00
|
|
|
.cfg_buffers = 2,
|
2013-07-21 22:59:00 +00:00
|
|
|
},
|
|
|
|
.options = (const struct m_option[]) {
|
options: change option macros and all option declarations
Change all OPT_* macros such that they don't define the entire m_option
initializer, and instead expand only to a part of it, which sets certain
fields. This requires changing almost every option declaration, because
they all use these macros. A declaration now always starts with
{"name", ...
followed by designated initializers only (possibly wrapped in macros).
The OPT_* macros now initialize the .offset and .type fields only,
sometimes also .priv and others.
I think this change makes the option macros less tricky. The old code
had to stuff everything into macro arguments (and attempted to allow
setting arbitrary fields by letting the user pass designated
initializers in the vararg parts). Some of this was made messy due to
C99 and C11 not allowing 0-sized varargs with ',' removal. It's also
possible that this change is pointless, other than cosmetic preferences.
Not too happy about some things. For example, the OPT_CHOICE()
indentation I applied looks a bit ugly.
Much of this change was done with regex search&replace, but some places
required manual editing. In particular, code in "obscure" areas (which I
didn't include in compilation) might be broken now.
In wayland_common.c the author of some option declarations confused the
flags parameter with the default value (though the default value was
also properly set below). I fixed this with this change.
2020-03-14 20:28:01 +00:00
|
|
|
{"port", OPT_INT(xv_port), M_RANGE(0, DBL_MAX)},
|
|
|
|
{"adaptor", OPT_INT(cfg_xv_adaptor), M_RANGE(-1, DBL_MAX)},
|
|
|
|
{"ck", OPT_CHOICE(xv_ck_info.source,
|
|
|
|
{"use", CK_SRC_USE},
|
|
|
|
{"set", CK_SRC_SET},
|
|
|
|
{"cur", CK_SRC_CUR})},
|
|
|
|
{"ck-method", OPT_CHOICE(xv_ck_info.method,
|
|
|
|
{"none", CK_METHOD_NONE},
|
|
|
|
{"bg", CK_METHOD_BACKGROUND},
|
|
|
|
{"man", CK_METHOD_MANUALFILL},
|
|
|
|
{"auto", CK_METHOD_AUTOPAINT})},
|
|
|
|
{"colorkey", OPT_INT(colorkey)},
|
|
|
|
{"buffers", OPT_INT(cfg_buffers), M_RANGE(1, MAX_BUFFERS)},
|
|
|
|
{"no-colorkey", OPT_REMOVED("use ck-method=none instead")},
|
2013-07-21 22:59:00 +00:00
|
|
|
{0}
|
|
|
|
},
|
2016-11-25 20:00:39 +00:00
|
|
|
.options_prefix = "xv",
|
2008-04-04 05:04:11 +00:00
|
|
|
};
|