summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/options.rst9
-rw-r--r--video/decode/vdpau.c12
-rw-r--r--video/filter/vf_vdpaupp.c1
-rw-r--r--video/out/opengl/common.c1
-rw-r--r--video/out/opengl/common.h2
-rw-r--r--video/out/opengl/hwdec.h1
-rw-r--r--video/out/opengl/hwdec_vdpau.c125
-rw-r--r--video/out/opengl/video.c50
8 files changed, 161 insertions, 40 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index a8d118023e..a42d87912b 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -641,9 +641,12 @@ Video
not display correctly, and not certain filtering (such as debanding)
can not be applied in an ideal way.
- ``vdpau`` forces RGB conversion. Currently, it does not treat certain
- colorspaces like BT.2020 correctly. This is mostly a mpv-specific
- restriction. This does not apply if the ``vdpauprb`` filter is used.
+ ``vdpau`` is usually safe. If deinterlacing enabled (or the ``vdpaupp``
+ video filter is active in general), it forces RGB conversion. The latter
+ currently does not treat certain colorspaces like BT.2020 correctly
+ (which is mostly a mpv-specific restriction). If the ``vdpauprb``
+ retrieves image data without RGB conversion, but does not work with
+ postprocessing.
``vaapi`` is safe if the ``vaapi-egl`` backend is indicated in the logs.
If ``vaapi-glx`` is indicated, and the video colorspace is either BT.601
diff --git a/video/decode/vdpau.c b/video/decode/vdpau.c
index 2aba10c13b..0003182dcb 100644
--- a/video/decode/vdpau.c
+++ b/video/decode/vdpau.c
@@ -61,6 +61,17 @@ static struct mp_image *allocate_image(struct lavc_ctx *ctx, int w, int h)
return mp_vdpau_get_video_surface(p->mpvdp, chroma, s_w, s_h);
}
+static struct mp_image *update_format(struct lavc_ctx *ctx, struct mp_image *img)
+{
+ VdpChromaType chroma = 0;
+ uint32_t s_w, s_h;
+ if (av_vdpau_get_surface_parameters(ctx->avctx, &chroma, &s_w, &s_h) >= 0) {
+ if (chroma == VDP_CHROMA_TYPE_420)
+ img->params.hw_subfmt = IMGFMT_NV12;
+ }
+ return img;
+}
+
static void uninit(struct lavc_ctx *ctx)
{
struct priv *p = ctx->hwdec_priv;
@@ -99,4 +110,5 @@ const struct vd_lavc_hwdec mp_vd_lavc_vdpau = {
.uninit = uninit,
.init_decoder = init_decoder,
.allocate_image = allocate_image,
+ .process_image = update_format,
};
diff --git a/video/filter/vf_vdpaupp.c b/video/filter/vf_vdpaupp.c
index 077faba0b3..92a40ec8c2 100644
--- a/video/filter/vf_vdpaupp.c
+++ b/video/filter/vf_vdpaupp.c
@@ -132,6 +132,7 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
mp_refqueue_flush(p->queue);
*out = *in;
out->imgfmt = IMGFMT_VDPAU;
+ out->hw_subfmt = 0;
return 0;
}
diff --git a/video/out/opengl/common.c b/video/out/opengl/common.c
index 30321abc86..1c5aa0a2cf 100644
--- a/video/out/opengl/common.c
+++ b/video/out/opengl/common.c
@@ -349,6 +349,7 @@ static const struct gl_functions gl_functions[] = {
DEF_FN(VDPAUInitNV),
DEF_FN(VDPAUFiniNV),
DEF_FN(VDPAURegisterOutputSurfaceNV),
+ DEF_FN(VDPAURegisterVideoSurfaceNV),
DEF_FN(VDPAUUnregisterSurfaceNV),
DEF_FN(VDPAUSurfaceAccessNV),
DEF_FN(VDPAUMapSurfacesNV),
diff --git a/video/out/opengl/common.h b/video/out/opengl/common.h
index 280c1f514b..e3ebd66c41 100644
--- a/video/out/opengl/common.h
+++ b/video/out/opengl/common.h
@@ -203,6 +203,8 @@ struct GL {
void (GLAPIENTRY *VDPAUFiniNV)(void);
GLvdpauSurfaceNV (GLAPIENTRY *VDPAURegisterOutputSurfaceNV)
(GLvoid *, GLenum, GLsizei, const GLuint *);
+ GLvdpauSurfaceNV (GLAPIENTRY *VDPAURegisterVideoSurfaceNV)
+ (GLvoid *, GLenum, GLsizei, const GLuint *);
void (GLAPIENTRY *VDPAUUnregisterSurfaceNV)(GLvdpauSurfaceNV);
void (GLAPIENTRY *VDPAUSurfaceAccessNV)(GLvdpauSurfaceNV, GLenum);
void (GLAPIENTRY *VDPAUMapSurfacesNV)(GLsizei, const GLvdpauSurfaceNV *);
diff --git a/video/out/opengl/hwdec.h b/video/out/opengl/hwdec.h
index 4a074ec4a5..29ccd18a42 100644
--- a/video/out/opengl/hwdec.h
+++ b/video/out/opengl/hwdec.h
@@ -27,6 +27,7 @@ struct gl_hwdec_plane {
struct gl_hwdec_frame {
struct gl_hwdec_plane planes[4];
+ bool vdpau_fields;
};
struct gl_hwdec_driver {
diff --git a/video/out/opengl/hwdec_vdpau.c b/video/out/opengl/hwdec_vdpau.c
index e1bccd7771..83f664a756 100644
--- a/video/out/opengl/hwdec_vdpau.c
+++ b/video/out/opengl/hwdec_vdpau.c
@@ -36,11 +36,12 @@ struct priv {
struct mp_vdpau_ctx *ctx;
uint64_t preemption_counter;
struct mp_image_params image_params;
- GLuint gl_texture;
+ GLuint gl_textures[4];
bool vdpgl_initialized;
GLvdpauSurfaceNV vdpgl_surface;
VdpOutputSurface vdp_surface;
struct mp_vdpau_mixer *mixer;
+ bool direct_mode;
bool mapped;
};
@@ -49,8 +50,13 @@ static void unmap(struct gl_hwdec *hw)
struct priv *p = hw->priv;
GL *gl = hw->gl;
- if (p->mapped)
+ if (p->mapped) {
gl->VDPAUUnmapSurfacesNV(1, &p->vdpgl_surface);
+ if (p->direct_mode) {
+ gl->VDPAUUnregisterSurfaceNV(p->vdpgl_surface);
+ p->vdpgl_surface = 0;
+ }
+ }
p->mapped = false;
}
@@ -75,8 +81,9 @@ static void destroy_objects(struct gl_hwdec *hw)
gl->VDPAUUnregisterSurfaceNV(p->vdpgl_surface);
p->vdpgl_surface = 0;
- glDeleteTextures(1, &p->gl_texture);
- p->gl_texture = 0;
+ glDeleteTextures(4, p->gl_textures);
+ for (int n = 0; n < 4; n++)
+ p->gl_textures[n] = 0;
if (p->vdp_surface != VDP_INVALID_HANDLE) {
vdp_st = vdp->output_surface_destroy(p->vdp_surface);
@@ -151,30 +158,39 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
p->vdpgl_initialized = true;
- vdp_st = vdp->output_surface_create(p->ctx->vdp_device,
- VDP_RGBA_FORMAT_B8G8R8A8,
- params->w, params->h, &p->vdp_surface);
- CHECK_VDP_ERROR(p, "Error when calling vdp_output_surface_create");
-
- gl->GenTextures(1, &p->gl_texture);
- gl->BindTexture(GL_TEXTURE_2D, p->gl_texture);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ p->direct_mode = params->hw_subfmt == IMGFMT_NV12;
+
+ gl->GenTextures(4, p->gl_textures);
+ for (int n = 0; n < 4; n++) {
+ gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]);
+ GLenum filter = p->direct_mode ? GL_NEAREST : GL_LINEAR;
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
gl->BindTexture(GL_TEXTURE_2D, 0);
- p->vdpgl_surface = gl->VDPAURegisterOutputSurfaceNV(BRAINDEATH(p->vdp_surface),
- GL_TEXTURE_2D,
- 1, &p->gl_texture);
- if (!p->vdpgl_surface)
- return -1;
+ if (p->direct_mode) {
+ params->imgfmt = IMGFMT_NV12;
+ } else {
+ vdp_st = vdp->output_surface_create(p->ctx->vdp_device,
+ VDP_RGBA_FORMAT_B8G8R8A8,
+ params->w, params->h, &p->vdp_surface);
+ CHECK_VDP_ERROR(p, "Error when calling vdp_output_surface_create");
+
+ p->vdpgl_surface = gl->VDPAURegisterOutputSurfaceNV(BRAINDEATH(p->vdp_surface),
+ GL_TEXTURE_2D,
+ 1, p->gl_textures);
+ if (!p->vdpgl_surface)
+ return -1;
- gl->VDPAUSurfaceAccessNV(p->vdpgl_surface, GL_READ_ONLY);
+ gl->VDPAUSurfaceAccessNV(p->vdpgl_surface, GL_READ_ONLY);
- gl_check_error(gl, hw->log, "After initializing vdpau OpenGL interop");
+ params->imgfmt = IMGFMT_RGB0;
+ }
- params->imgfmt = IMGFMT_RGB0;
+ gl_check_error(gl, hw->log, "After initializing vdpau OpenGL interop");
return 0;
}
@@ -184,6 +200,8 @@ static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
{
struct priv *p = hw->priv;
GL *gl = hw->gl;
+ struct vdp_functions *vdp = &p->ctx->vdp;
+ VdpStatus vdp_st;
int pe = mp_vdpau_handle_preemption(p->ctx, &p->preemption_counter);
if (pe < 1) {
@@ -194,23 +212,58 @@ static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
return -1;
}
- if (!p->vdpgl_surface)
- return -1;
+ if (p->direct_mode) {
+ VdpVideoSurface surface = (intptr_t)hw_image->planes[3];
+
+ // We need the uncropped size.
+ VdpChromaType s_chroma_type;
+ uint32_t s_w, s_h;
+ vdp_st = vdp->video_surface_get_parameters(surface, &s_chroma_type, &s_w, &s_h);
+ CHECK_VDP_ERROR(hw, "Error when calling vdp_video_surface_get_parameters");
- mp_vdpau_mixer_render(p->mixer, NULL, p->vdp_surface, NULL, hw_image, NULL);
+ p->vdpgl_surface = gl->VDPAURegisterVideoSurfaceNV(BRAINDEATH(surface),
+ GL_TEXTURE_2D,
+ 4, p->gl_textures);
+ if (!p->vdpgl_surface)
+ return -1;
- gl->VDPAUMapSurfacesNV(1, &p->vdpgl_surface);
- p->mapped = true;
- *out_frame = (struct gl_hwdec_frame){
- .planes = {
- {
- .gl_texture = p->gl_texture,
+ gl->VDPAUSurfaceAccessNV(p->vdpgl_surface, GL_READ_ONLY);
+ gl->VDPAUMapSurfacesNV(1, &p->vdpgl_surface);
+
+ p->mapped = true;
+ *out_frame = (struct gl_hwdec_frame){
+ .vdpau_fields = true,
+ };
+ for (int n = 0; n < 4; n++) {
+ bool chroma = n >= 2;
+ out_frame->planes[n] = (struct gl_hwdec_plane){
+ .gl_texture = p->gl_textures[n],
.gl_target = GL_TEXTURE_2D,
- .tex_w = p->image_params.w,
- .tex_h = p->image_params.h,
+ .tex_w = s_w / (chroma ? 2 : 1),
+ .tex_h = s_h / (chroma ? 4 : 2),
+ };
+ };
+ } else {
+ if (!p->vdpgl_surface)
+ return -1;
+
+ mp_vdpau_mixer_render(p->mixer, NULL, p->vdp_surface, NULL, hw_image, NULL);
+
+ gl->VDPAUMapSurfacesNV(1, &p->vdpgl_surface);
+
+ p->mapped = true;
+ *out_frame = (struct gl_hwdec_frame){
+ .planes = {
+ {
+ .gl_texture = p->gl_textures[0],
+ .gl_target = GL_TEXTURE_2D,
+ .tex_w = p->image_params.w,
+ .tex_h = p->image_params.h,
+ },
},
- },
- };
+ };
+ }
+
return 0;
}
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c
index 62bbfd1d05..3b99ba95ca 100644
--- a/video/out/opengl/video.c
+++ b/video/out/opengl/video.c
@@ -217,6 +217,7 @@ struct gl_video {
struct fbotex blend_subs_fbo;
struct fbotex output_fbo;
struct fbosurface surfaces[FBOSURFACES_MAX];
+ struct fbotex vdpau_deinterleave_fbo[2];
int surface_idx;
int surface_now;
@@ -585,6 +586,9 @@ static void uninit_rendering(struct gl_video *p)
for (int n = 0; n < MAX_SAVED_TEXTURES; n++)
fbotex_uninit(&p->hook_fbos[n]);
+ for (int n = 0; n < 2; n++)
+ fbotex_uninit(&p->vdpau_deinterleave_fbo[n]);
+
gl_video_reset_surfaces(p);
gl_video_reset_hooks(p);
@@ -957,7 +961,6 @@ static void render_pass_quad(struct gl_video *p, int vp_w, int vp_h,
debug_check_gl(p, "after rendering");
}
-// flags: see render_pass_quad
static void finish_pass_direct(struct gl_video *p, GLint fbo, int vp_w, int vp_h,
const struct mp_rect *dst)
{
@@ -2917,6 +2920,49 @@ static bool map_image(struct gl_video *p, struct mp_image *mpi)
return true;
}
+// This assumes nv12, with textures set to GL_NEAREST filtering.
+static void reinterleave_vdpau(struct gl_video *p, struct gl_hwdec_frame *frame)
+{
+ struct gl_hwdec_frame res = {0};
+ for (int n = 0; n < 2; n++) {
+ struct fbotex *fbo = &p->vdpau_deinterleave_fbo[n];
+ // This is an array of the 2 to-merge planes.
+ struct gl_hwdec_plane *src = &frame->planes[n * 2];
+ int w = src[0].tex_w;
+ int h = src[0].tex_h;
+ int ids[2];
+ for (int t = 0; t < 2; t++) {
+ ids[t] = pass_bind(p, (struct img_tex){
+ .gl_tex = src[t].gl_texture,
+ .gl_target = src[t].gl_target,
+ .multiplier = 1.0,
+ .transform = identity_trans,
+ .tex_w = w,
+ .tex_h = h,
+ .w = w,
+ .h = h,
+ });
+ }
+
+ GLSLF("color = fract(gl_FragCoord.y / 2) < 0.5\n");
+ GLSLF(" ? texture(texture%d, texcoord%d)\n", ids[0], ids[0]);
+ GLSLF(" : texture(texture%d, texcoord%d);", ids[1], ids[1]);
+
+ fbotex_change(fbo, p->gl, p->log, w, h * 2, n == 0 ? GL_R8 : GL_RG8, 0);
+
+ finish_pass_direct(p, fbo->fbo, fbo->rw, fbo->rh,
+ &(struct mp_rect){0, 0, w, h * 2});
+
+ res.planes[n] = (struct gl_hwdec_plane){
+ .gl_texture = fbo->texture,
+ .gl_target = GL_TEXTURE_2D,
+ .tex_w = w,
+ .tex_h = h * 2,
+ };
+ }
+ *frame = res;
+}
+
// Returns false on failure.
static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi)
{
@@ -2943,6 +2989,8 @@ static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi)
if (ok) {
struct mp_image layout = {0};
mp_image_set_params(&layout, &p->image_params);
+ if (gl_frame.vdpau_fields)
+ reinterleave_vdpau(p, &gl_frame);
for (int n = 0; n < p->plane_count; n++) {
struct gl_hwdec_plane *plane = &gl_frame.planes[n];
vimg->planes[n] = (struct texplane){