mpv/video/out/opengl/hwdec_vaegl.c

558 lines
17 KiB
C
Raw Normal View History

/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <va/va_drmcommon.h>
#include <libavutil/common.h>
#include <libavutil/hwcontext.h>
#include <libavutil/hwcontext_vaapi.h>
#include "config.h"
vo_opengl: refactor into vo_gpu This is done in several steps: 1. refactor MPGLContext -> struct ra_ctx 2. move GL-specific stuff in vo_opengl into opengl/context.c 3. generalize context creation to support other APIs, and add --gpu-api 4. rename all of the --opengl- options that are no longer opengl-specific 5. move all of the stuff from opengl/* that isn't GL-specific into gpu/ (note: opengl/gl_utils.h became opengl/utils.h) 6. rename vo_opengl to vo_gpu 7. to handle window screenshots, the short-term approach was to just add it to ra_swchain_fns. Long term (and for vulkan) this has to be moved to ra itself (and vo_gpu altered to compensate), but this was a stop-gap measure to prevent this commit from getting too big 8. move ra->fns->flush to ra_gl_ctx instead 9. some other minor changes that I've probably already forgotten Note: This is one half of a major refactor, the other half of which is provided by rossy's following commit. This commit enables support for all linux platforms, while his version enables support for all non-linux platforms. Note 2: vo_opengl_cb.c also re-uses ra_gl_ctx so it benefits from the --opengl- options like --opengl-early-flush, --opengl-finish etc. Should be a strict superset of the old functionality. Disclaimer: Since I have no way of compiling mpv on all platforms, some of these ports were done blindly. Specifically, the blind ports included context_mali_fbdev.c and context_rpi.c. Since they're both based on egl_helpers, the port should have gone smoothly without any major changes required. But if somebody complains about a compile error on those platforms (assuming anybody actually uses them), you know where to complain.
2017-09-14 06:04:55 +00:00
#include "video/out/gpu/hwdec.h"
#include "video/mp_image_pool.h"
vo_opengl: refactor into vo_gpu This is done in several steps: 1. refactor MPGLContext -> struct ra_ctx 2. move GL-specific stuff in vo_opengl into opengl/context.c 3. generalize context creation to support other APIs, and add --gpu-api 4. rename all of the --opengl- options that are no longer opengl-specific 5. move all of the stuff from opengl/* that isn't GL-specific into gpu/ (note: opengl/gl_utils.h became opengl/utils.h) 6. rename vo_opengl to vo_gpu 7. to handle window screenshots, the short-term approach was to just add it to ra_swchain_fns. Long term (and for vulkan) this has to be moved to ra itself (and vo_gpu altered to compensate), but this was a stop-gap measure to prevent this commit from getting too big 8. move ra->fns->flush to ra_gl_ctx instead 9. some other minor changes that I've probably already forgotten Note: This is one half of a major refactor, the other half of which is provided by rossy's following commit. This commit enables support for all linux platforms, while his version enables support for all non-linux platforms. Note 2: vo_opengl_cb.c also re-uses ra_gl_ctx so it benefits from the --opengl- options like --opengl-early-flush, --opengl-finish etc. Should be a strict superset of the old functionality. Disclaimer: Since I have no way of compiling mpv on all platforms, some of these ports were done blindly. Specifically, the blind ports included context_mali_fbdev.c and context_rpi.c. Since they're both based on egl_helpers, the port should have gone smoothly without any major changes required. But if somebody complains about a compile error on those platforms (assuming anybody actually uses them), you know where to complain.
2017-09-14 06:04:55 +00:00
#include "video/vaapi.h"
#include "common.h"
#include "ra_gl.h"
#ifndef GL_OES_EGL_image
typedef void* GLeglImageOES;
#endif
#ifndef EGL_KHR_image
typedef void *EGLImageKHR;
#endif
#ifndef EGL_LINUX_DMA_BUF_EXT
#define EGL_LINUX_DMA_BUF_EXT 0x3270
#define EGL_LINUX_DRM_FOURCC_EXT 0x3271
#define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272
#define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273
#define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274
#endif
#if HAVE_VAAPI_X11
#include <va/va_x11.h>
static VADisplay *create_x11_va_display(GL *gl)
{
Display *x11 = mpgl_get_native_display(gl, "x11");
return x11 ? vaGetDisplay(x11) : NULL;
}
#endif
#if HAVE_VAAPI_WAYLAND
#include <va/va_wayland.h>
static VADisplay *create_wayland_va_display(GL *gl)
{
struct wl_display *wl = mpgl_get_native_display(gl, "wl");
return wl ? vaGetDisplayWl(wl) : NULL;
}
#endif
#if HAVE_VAAPI_DRM
#include <va/va_drm.h>
static VADisplay *create_drm_va_display(GL *gl)
{
int drm_fd = (intptr_t)mpgl_get_native_display(gl, "drm");
// Note: yes, drm_fd==0 could be valid - but it's rare and doesn't fit with
// our slightly crappy way of passing it through, so consider 0 not
// valid.
return drm_fd ? vaGetDisplayDRM(drm_fd) : NULL;
}
#endif
struct va_create_native {
const char *name;
VADisplay *(*create)(GL *gl);
};
static const struct va_create_native create_native_cbs[] = {
#if HAVE_VAAPI_X11
{"x11", create_x11_va_display},
#endif
#if HAVE_VAAPI_WAYLAND
{"wayland", create_wayland_va_display},
#endif
#if HAVE_VAAPI_DRM
{"drm", create_drm_va_display},
#endif
};
static VADisplay *create_native_va_display(GL *gl, struct mp_log *log)
{
for (int n = 0; n < MP_ARRAY_SIZE(create_native_cbs); n++) {
const struct va_create_native *disp = &create_native_cbs[n];
mp_verbose(log, "Trying to open a %s VA display...\n", disp->name);
VADisplay *display = disp->create(gl);
if (display)
return display;
}
return NULL;
}
struct priv_owner {
struct mp_vaapi_ctx *ctx;
VADisplay *display;
int *formats;
bool probing_formats; // temporary during init
};
struct priv {
int num_planes;
struct ra_tex *tex[4];
GLuint gl_textures[4];
EGLImageKHR images[4];
VAImage current_image;
bool buffer_acquired;
#if VA_CHECK_VERSION(1, 1, 0)
bool esh_not_implemented;
VADRMPRIMESurfaceDescriptor desc;
bool surface_acquired;
#endif
EGLImageKHR (EGLAPIENTRY *CreateImageKHR)(EGLDisplay, EGLContext,
EGLenum, EGLClientBuffer,
const EGLint *);
EGLBoolean (EGLAPIENTRY *DestroyImageKHR)(EGLDisplay, EGLImageKHR);
void (EGLAPIENTRY *EGLImageTargetTexture2DOES)(GLenum, GLeglImageOES);
};
static void determine_working_formats(struct ra_hwdec *hw);
static void uninit(struct ra_hwdec *hw)
{
struct priv_owner *p = hw->priv;
if (p->ctx)
hwdec_devices_remove(hw->devs, &p->ctx->hwctx);
va_destroy(p->ctx);
}
static int init(struct ra_hwdec *hw)
{
struct priv_owner *p = hw->priv;
if (!ra_is_gl(hw->ra) || !eglGetCurrentContext())
return -1;
const char *exts = eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS);
if (!exts)
return -1;
GL *gl = ra_gl_get(hw->ra);
if (!strstr(exts, "EXT_image_dma_buf_import") ||
!strstr(exts, "EGL_KHR_image_base") ||
!strstr(gl->extensions, "GL_OES_EGL_image") ||
!(gl->mpgl_caps & MPGL_CAP_TEX_RG))
return -1;
p->display = create_native_va_display(gl, hw->log);
if (!p->display) {
MP_VERBOSE(hw, "Could not create a VA display.\n");
return -1;
}
p->ctx = va_initialize(p->display, hw->log, true);
if (!p->ctx) {
vaTerminate(p->display);
return -1;
}
if (!p->ctx->av_device_ref) {
MP_VERBOSE(hw, "libavutil vaapi code rejected the driver?\n");
return -1;
}
if (hw->probing && va_guess_if_emulated(p->ctx)) {
return -1;
}
MP_VERBOSE(hw, "using VAAPI EGL interop\n");
determine_working_formats(hw);
if (!p->formats || !p->formats[0]) {
return -1;
}
p->ctx->hwctx.supported_formats = p->formats;
p->ctx->hwctx.driver_name = hw->driver->name;
hwdec_devices_add(hw->devs, &p->ctx->hwctx);
return 0;
}
static void mapper_unmap(struct ra_hwdec_mapper *mapper)
{
struct priv_owner *p_owner = mapper->owner->priv;
VADisplay *display = p_owner->display;
struct priv *p = mapper->priv;
VAStatus status;
for (int n = 0; n < 4; n++) {
if (p->images[n])
p->DestroyImageKHR(eglGetCurrentDisplay(), p->images[n]);
p->images[n] = 0;
}
#if VA_CHECK_VERSION(1, 1, 0)
if (p->surface_acquired) {
for (int n = 0; n < p->desc.num_objects; n++)
close(p->desc.objects[n].fd);
p->surface_acquired = false;
}
#endif
if (p->buffer_acquired) {
status = vaReleaseBufferHandle(display, p->current_image.buf);
CHECK_VA_STATUS(mapper, "vaReleaseBufferHandle()");
p->buffer_acquired = false;
}
if (p->current_image.image_id != VA_INVALID_ID) {
status = vaDestroyImage(display, p->current_image.image_id);
CHECK_VA_STATUS(mapper, "vaDestroyImage()");
p->current_image.image_id = VA_INVALID_ID;
}
}
static void mapper_uninit(struct ra_hwdec_mapper *mapper)
{
struct priv *p = mapper->priv;
GL *gl = ra_gl_get(mapper->ra);
gl->DeleteTextures(4, p->gl_textures);
for (int n = 0; n < 4; n++) {
p->gl_textures[n] = 0;
ra_tex_free(mapper->ra, &p->tex[n]);
}
}
static bool check_fmt(struct ra_hwdec_mapper *mapper, int fmt)
{
struct priv_owner *p_owner = mapper->owner->priv;
for (int n = 0; p_owner->formats && p_owner->formats[n]; n++) {
if (p_owner->formats[n] == fmt)
return true;
}
return false;
}
static int mapper_init(struct ra_hwdec_mapper *mapper)
{
struct priv_owner *p_owner = mapper->owner->priv;
struct priv *p = mapper->priv;
GL *gl = ra_gl_get(mapper->ra);
p->current_image.buf = p->current_image.image_id = VA_INVALID_ID;
// EGL_KHR_image_base
p->CreateImageKHR = (void *)eglGetProcAddress("eglCreateImageKHR");
p->DestroyImageKHR = (void *)eglGetProcAddress("eglDestroyImageKHR");
// GL_OES_EGL_image
p->EGLImageTargetTexture2DOES =
(void *)eglGetProcAddress("glEGLImageTargetTexture2DOES");
if (!p->CreateImageKHR || !p->DestroyImageKHR ||
!p->EGLImageTargetTexture2DOES)
return -1;
mapper->dst_params = mapper->src_params;
mapper->dst_params.imgfmt = mapper->src_params.hw_subfmt;
mapper->dst_params.hw_subfmt = 0;
struct ra_imgfmt_desc desc = {0};
struct mp_image layout = {0};
if (!ra_get_imgfmt_desc(mapper->ra, mapper->dst_params.imgfmt, &desc))
return -1;
p->num_planes = desc.num_planes;
mp_image_set_params(&layout, &mapper->dst_params);
gl->GenTextures(4, p->gl_textures);
for (int n = 0; n < desc.num_planes; n++) {
gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]);
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);
gl->BindTexture(GL_TEXTURE_2D, 0);
struct ra_tex_params params = {
.dimensions = 2,
.w = mp_image_plane_w(&layout, n),
.h = mp_image_plane_h(&layout, n),
.d = 1,
.format = desc.planes[n],
.render_src = true,
.src_linear = true,
};
if (params.format->ctype != RA_CTYPE_UNORM)
return -1;
p->tex[n] = ra_create_wrapped_tex(mapper->ra, &params,
p->gl_textures[n]);
if (!p->tex[n])
return -1;
vaapi: determine surface format in decoder, not in renderer Until now, we have made the assumption that a driver will use only 1 hardware surface format. the format is dictated by the driver (you don't create surfaces with a specific format - you just pass a rt_format and get a surface that will be in a specific driver-chosen format). In particular, the renderer created a dummy surface to probe the format, and hoped the decoder would produce the same format. Due to a driver bug this required a workaround to actually get the same format as the driver did. Change this so that the format is determined in the decoder. The format is then passed down as hw_subfmt, which allows the renderer to configure itself with the correct format. If the hardware surface changes its format midstream, the renderer can be reconfigured using the normal mechanisms. This calls va_surface_init_subformat() each time after the decoder returns a surface. Since libavcodec/AVFrame has no concept of sub- formats, this is unavoidable. It creates and destroys a derived VAImage, but this shouldn't have any bad performance effects (at least I didn't notice any measurable effects). Note that vaDeriveImage() failures are silently ignored as some drivers (the vdpau wrapper) support neither vaDeriveImage, nor EGL interop. In addition, we still probe whether we can map an image in the EGL interop code. This is important as it's the only way to determine whether EGL interop is supported at all. With respect to the driver bug mentioned above, it doesn't matter which format the test surface has. In vf_vavpp, also remove the rt_format guessing business. I think the existing logic was a bit meaningless anyway. It's not even a given that vavpp produces the same rt_format for output.
2016-04-11 18:46:05 +00:00
}
if (!p_owner->probing_formats && !check_fmt(mapper, mapper->dst_params.imgfmt))
{
MP_FATAL(mapper, "unsupported VA image format %s\n",
mp_imgfmt_to_name(mapper->dst_params.imgfmt));
return -1;
}
vaapi: determine surface format in decoder, not in renderer Until now, we have made the assumption that a driver will use only 1 hardware surface format. the format is dictated by the driver (you don't create surfaces with a specific format - you just pass a rt_format and get a surface that will be in a specific driver-chosen format). In particular, the renderer created a dummy surface to probe the format, and hoped the decoder would produce the same format. Due to a driver bug this required a workaround to actually get the same format as the driver did. Change this so that the format is determined in the decoder. The format is then passed down as hw_subfmt, which allows the renderer to configure itself with the correct format. If the hardware surface changes its format midstream, the renderer can be reconfigured using the normal mechanisms. This calls va_surface_init_subformat() each time after the decoder returns a surface. Since libavcodec/AVFrame has no concept of sub- formats, this is unavoidable. It creates and destroys a derived VAImage, but this shouldn't have any bad performance effects (at least I didn't notice any measurable effects). Note that vaDeriveImage() failures are silently ignored as some drivers (the vdpau wrapper) support neither vaDeriveImage, nor EGL interop. In addition, we still probe whether we can map an image in the EGL interop code. This is important as it's the only way to determine whether EGL interop is supported at all. With respect to the driver bug mentioned above, it doesn't matter which format the test surface has. In vf_vavpp, also remove the rt_format guessing business. I think the existing logic was a bit meaningless anyway. It's not even a given that vavpp produces the same rt_format for output.
2016-04-11 18:46:05 +00:00
return 0;
}
#define ADD_ATTRIB(name, value) \
do { \
assert(num_attribs + 3 < MP_ARRAY_SIZE(attribs)); \
attribs[num_attribs++] = (name); \
attribs[num_attribs++] = (value); \
attribs[num_attribs] = EGL_NONE; \
} while(0)
static int mapper_map(struct ra_hwdec_mapper *mapper)
{
struct priv_owner *p_owner = mapper->owner->priv;
struct priv *p = mapper->priv;
GL *gl = ra_gl_get(mapper->ra);
VAStatus status;
VAImage *va_image = &p->current_image;
VADisplay *display = p_owner->display;
#if VA_CHECK_VERSION(1, 1, 0)
if (p->esh_not_implemented)
goto esh_failed;
status = vaExportSurfaceHandle(display, va_surface_id(mapper->src),
VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
VA_EXPORT_SURFACE_READ_ONLY |
VA_EXPORT_SURFACE_SEPARATE_LAYERS,
&p->desc);
if (!CHECK_VA_STATUS(mapper, "vaAcquireSurfaceHandle()")) {
if (status == VA_STATUS_ERROR_UNIMPLEMENTED)
p->esh_not_implemented = true;
goto esh_failed;
}
p->surface_acquired = true;
for (int n = 0; n < p->num_planes; n++) {
int attribs[20] = {EGL_NONE};
int num_attribs = 0;
ADD_ATTRIB(EGL_LINUX_DRM_FOURCC_EXT, p->desc.layers[n].drm_format);
ADD_ATTRIB(EGL_WIDTH, p->tex[n]->params.w);
ADD_ATTRIB(EGL_HEIGHT, p->tex[n]->params.h);
#define ADD_PLANE_ATTRIBS(plane) do { \
ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _FD_EXT, \
p->desc.objects[p->desc.layers[n].object_index[plane]].fd); \
ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _OFFSET_EXT, \
p->desc.layers[n].offset[plane]); \
ADD_ATTRIB(EGL_DMA_BUF_PLANE ## plane ## _PITCH_EXT, \
p->desc.layers[n].pitch[plane]); \
} while (0)
ADD_PLANE_ATTRIBS(0);
if (p->desc.layers[n].num_planes > 1)
ADD_PLANE_ATTRIBS(1);
if (p->desc.layers[n].num_planes > 2)
ADD_PLANE_ATTRIBS(2);
if (p->desc.layers[n].num_planes > 3)
ADD_PLANE_ATTRIBS(3);
p->images[n] = p->CreateImageKHR(eglGetCurrentDisplay(),
EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
if (!p->images[n])
goto esh_failed;
gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]);
p->EGLImageTargetTexture2DOES(GL_TEXTURE_2D, p->images[n]);
mapper->tex[n] = p->tex[n];
}
gl->BindTexture(GL_TEXTURE_2D, 0);
if (p->desc.fourcc == VA_FOURCC_YV12)
MPSWAP(struct ra_tex*, mapper->tex[1], mapper->tex[2]);
return 0;
esh_failed:
if (p->surface_acquired) {
for (int n = 0; n < p->desc.num_objects; n++)
close(p->desc.objects[n].fd);
p->surface_acquired = false;
}
#endif
status = vaDeriveImage(display, va_surface_id(mapper->src), va_image);
if (!CHECK_VA_STATUS(mapper, "vaDeriveImage()"))
goto err;
VABufferInfo buffer_info = {.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME};
status = vaAcquireBufferHandle(display, va_image->buf, &buffer_info);
if (!CHECK_VA_STATUS(mapper, "vaAcquireBufferHandle()"))
goto err;
p->buffer_acquired = true;
int drm_fmts[8] = {
// 1 bytes per component, 1-4 components
MKTAG('R', '8', ' ', ' '), // DRM_FORMAT_R8
MKTAG('G', 'R', '8', '8'), // DRM_FORMAT_GR88
0, // untested (DRM_FORMAT_RGB888?)
0, // untested (DRM_FORMAT_RGBA8888?)
// 2 bytes per component, 1-4 components
MKTAG('R', '1', '6', ' '), // proposed DRM_FORMAT_R16
MKTAG('G', 'R', '3', '2'), // proposed DRM_FORMAT_GR32
0, // N/A
0, // N/A
};
for (int n = 0; n < p->num_planes; n++) {
int attribs[20] = {EGL_NONE};
int num_attribs = 0;
const struct ra_format *fmt = p->tex[n]->params.format;
int n_comp = fmt->num_components;
int comp_s = fmt->component_size[n] / 8;
if (n_comp < 1 || n_comp > 3 || comp_s < 1 || comp_s > 2)
goto err;
int drm_fmt = drm_fmts[n_comp - 1 + (comp_s - 1) * 4];
if (!drm_fmt)
goto err;
ADD_ATTRIB(EGL_LINUX_DRM_FOURCC_EXT, drm_fmt);
ADD_ATTRIB(EGL_WIDTH, p->tex[n]->params.w);
ADD_ATTRIB(EGL_HEIGHT, p->tex[n]->params.h);
ADD_ATTRIB(EGL_DMA_BUF_PLANE0_FD_EXT, buffer_info.handle);
ADD_ATTRIB(EGL_DMA_BUF_PLANE0_OFFSET_EXT, va_image->offsets[n]);
ADD_ATTRIB(EGL_DMA_BUF_PLANE0_PITCH_EXT, va_image->pitches[n]);
p->images[n] = p->CreateImageKHR(eglGetCurrentDisplay(),
EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
if (!p->images[n])
goto err;
gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]);
p->EGLImageTargetTexture2DOES(GL_TEXTURE_2D, p->images[n]);
mapper->tex[n] = p->tex[n];
}
gl->BindTexture(GL_TEXTURE_2D, 0);
if (va_image->format.fourcc == VA_FOURCC_YV12)
MPSWAP(struct ra_tex*, mapper->tex[1], mapper->tex[2]);
return 0;
err:
if (!p_owner->probing_formats)
MP_FATAL(mapper, "mapping VAAPI EGL image failed\n");
return -1;
}
static bool try_format(struct ra_hwdec *hw, struct mp_image *surface)
{
bool ok = false;
struct ra_hwdec_mapper *mapper = ra_hwdec_mapper_create(hw, &surface->params);
if (mapper)
ok = ra_hwdec_mapper_map(mapper, surface) >= 0;
ra_hwdec_mapper_free(&mapper);
return ok;
}
static void determine_working_formats(struct ra_hwdec *hw)
{
struct priv_owner *p = hw->priv;
int num_formats = 0;
int *formats = NULL;
p->probing_formats = true;
AVHWFramesConstraints *fc =
av_hwdevice_get_hwframe_constraints(p->ctx->av_device_ref, NULL);
if (!fc) {
2017-10-23 08:53:28 +00:00
MP_WARN(hw, "failed to retrieve libavutil frame constraints\n");
goto done;
}
for (int n = 0; fc->valid_sw_formats[n] != AV_PIX_FMT_NONE; n++) {
AVBufferRef *fref = NULL;
struct mp_image *s = NULL;
AVFrame *frame = NULL;
fref = av_hwframe_ctx_alloc(p->ctx->av_device_ref);
if (!fref)
goto err;
AVHWFramesContext *fctx = (void *)fref->data;
fctx->format = AV_PIX_FMT_VAAPI;
fctx->sw_format = fc->valid_sw_formats[n];
fctx->width = 128;
fctx->height = 128;
if (av_hwframe_ctx_init(fref) < 0)
goto err;
frame = av_frame_alloc();
if (!frame)
goto err;
if (av_hwframe_get_buffer(fref, frame, 0) < 0)
goto err;
s = mp_image_from_av_frame(frame);
if (!s || !mp_image_params_valid(&s->params))
goto err;
if (try_format(hw, s))
MP_TARRAY_APPEND(p, formats, num_formats, s->params.hw_subfmt);
err:
talloc_free(s);
av_frame_free(&frame);
av_buffer_unref(&fref);
}
av_hwframe_constraints_free(&fc);
done:
MP_TARRAY_APPEND(p, formats, num_formats, 0); // terminate it
p->formats = formats;
p->probing_formats = false;
MP_VERBOSE(hw, "Supported formats:\n");
for (int n = 0; formats[n]; n++)
MP_VERBOSE(hw, " %s\n", mp_imgfmt_to_name(formats[n]));
}
const struct ra_hwdec_driver ra_hwdec_vaegl = {
.name = "vaapi-egl",
.priv_size = sizeof(struct priv_owner),
.imgfmts = {IMGFMT_VAAPI, 0},
.init = init,
.uninit = uninit,
.mapper = &(const struct ra_hwdec_mapper_driver){
.priv_size = sizeof(struct priv),
.init = mapper_init,
.uninit = mapper_uninit,
.map = mapper_map,
.unmap = mapper_unmap,
},
};