1 /*****************************************************************************
2 * converter_vaapi.c: OpenGL VAAPI opaque converter
3 *****************************************************************************
4 * Copyright (C) 2017 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
25 #include "converter.h"
26 #include "../../hw/vaapi/vlc_vaapi.h"
27 #include <vlc_vout_window.h>
32 #include <EGL/eglext.h>
33 #include <va/va_drmcommon.h>
36 # include <va/va_wayland.h>
40 # include <va/va_x11.h>
41 # include <vlc_xlib.h>
45 # include <va/va_drm.h>
50 #if defined(USE_OPENGL_ES2)
51 # include <GLES2/gl2ext.h>
56 struct vlc_vaapi_instance
*vainst
;
58 VASurfaceID
*va_surface_ids
;
59 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES
;
62 EGLint drm_fourccs
[3];
67 VABufferInfo va_buffer_info
;
73 vaegl_image_create(const opengl_tex_converter_t
*tc
, EGLint w
, EGLint h
,
74 EGLint fourcc
, EGLint fd
, EGLint offset
, EGLint pitch
)
79 EGL_LINUX_DRM_FOURCC_EXT
, fourcc
,
80 EGL_DMA_BUF_PLANE0_FD_EXT
, fd
,
81 EGL_DMA_BUF_PLANE0_OFFSET_EXT
, offset
,
82 EGL_DMA_BUF_PLANE0_PITCH_EXT
, pitch
,
86 return tc
->gl
->egl
.createImageKHR(tc
->gl
, EGL_LINUX_DMA_BUF_EXT
, NULL
,
91 vaegl_image_destroy(const opengl_tex_converter_t
*tc
, EGLImageKHR image
)
93 tc
->gl
->egl
.destroyImageKHR(tc
->gl
, image
);
97 vaegl_release_last_pic(const opengl_tex_converter_t
*tc
, struct priv
*priv
)
99 vlc_object_t
*o
= VLC_OBJECT(tc
->gl
);
101 for (unsigned i
= 0; i
< priv
->last
.va_image
.num_planes
; ++i
)
102 vaegl_image_destroy(tc
, priv
->last
.egl_images
[i
]);
104 vlc_vaapi_ReleaseBufferHandle(o
, priv
->vadpy
, priv
->last
.va_image
.buf
);
106 vlc_vaapi_DestroyImage(o
, priv
->vadpy
, priv
->last
.va_image
.image_id
);
108 picture_Release(priv
->last
.pic
);
112 vaegl_init_fourcc(const opengl_tex_converter_t
*tc
, struct priv
*priv
,
119 priv
->drm_fourccs
[0] = VLC_FOURCC('R', '8', ' ', ' ');
120 priv
->drm_fourccs
[1] = VLC_FOURCC('G', 'R', '8', '8');
123 priv
->drm_fourccs
[0] = VLC_FOURCC('R', '1', '6', ' ');
124 priv
->drm_fourccs
[1] = VLC_FOURCC('G', 'R', '3', '2');
127 /* TODO: the following fourcc are not handled for now */
129 priv
->drm_fourccs
[0] = VLC_FOURCC('G', 'R', '3', '2');
132 priv
->drm_fourccs
[0] = VLC_FOURCC('G', 'R', '3', '2');
135 priv
->drm_fourccs
[0] = VLC_FOURCC('R', '8', ' ', ' ');
136 priv
->drm_fourccs
[1] = VLC_FOURCC('R', '8', ' ', ' ');
137 priv
->drm_fourccs
[2] = VLC_FOURCC('R', '8', ' ', ' ');
140 priv
->drm_fourccs
[0] = VLC_FOURCC('R', '8', ' ', ' ');
141 priv
->drm_fourccs
[1] = VLC_FOURCC('R', '8', ' ', ' ');
142 priv
->drm_fourccs
[2] = VLC_FOURCC('R', '8', ' ', ' ');
145 priv
->drm_fourccs
[0] = VLC_FOURCC('R', '1', '6', ' ');
148 priv
->drm_fourccs
[0] = VLC_FOURCC('R', '1', '6', ' ');
149 priv
->drm_fourccs
[1] = VLC_FOURCC('R', '1', '6', ' ');
150 priv
->drm_fourccs
[2] = VLC_FOURCC('R', '1', '6', ' ');
153 default: return VLC_EGENERIC
;
155 priv
->fourcc
= va_fourcc
;
160 tc_vaegl_update(const opengl_tex_converter_t
*tc
, GLuint
*textures
,
161 const GLsizei
*tex_width
, const GLsizei
*tex_height
,
162 picture_t
*pic
, const size_t *plane_offset
)
165 struct priv
*priv
= tc
->priv
;
166 vlc_object_t
*o
= VLC_OBJECT(tc
->gl
);
168 VABufferInfo va_buffer_info
;
169 EGLImageKHR egl_images
[3] = { };
170 bool release_image
= false, release_buffer_info
= false;
172 if (pic
== priv
->last
.pic
)
174 va_image
= priv
->last
.va_image
;
175 va_buffer_info
= priv
->last
.va_buffer_info
;
176 for (unsigned i
= 0; i
< priv
->last
.va_image
.num_planes
; ++i
)
177 egl_images
[i
] = priv
->last
.egl_images
[i
];
181 if (vlc_vaapi_DeriveImage(o
, priv
->vadpy
, vlc_vaapi_PicGetSurface(pic
),
184 release_image
= true;
186 assert(va_image
.format
.fourcc
== priv
->fourcc
);
188 va_buffer_info
= (VABufferInfo
) {
189 .mem_type
= VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME
191 if (vlc_vaapi_AcquireBufferHandle(o
, priv
->vadpy
, va_image
.buf
,
194 release_buffer_info
= true;
197 for (unsigned i
= 0; i
< va_image
.num_planes
; ++i
)
200 vaegl_image_create(tc
, tex_width
[i
], tex_height
[i
],
201 priv
->drm_fourccs
[i
], va_buffer_info
.handle
,
202 va_image
.offsets
[i
], va_image
.pitches
[i
]);
203 if (egl_images
[i
] == NULL
)
206 tc
->vt
->BindTexture(tc
->tex_target
, textures
[i
]);
208 priv
->glEGLImageTargetTexture2DOES(tc
->tex_target
, egl_images
[i
]);
211 if (pic
!= priv
->last
.pic
)
213 if (priv
->last
.pic
!= NULL
)
214 vaegl_release_last_pic(tc
, priv
);
215 priv
->last
.pic
= picture_Hold(pic
);
216 priv
->last
.va_image
= va_image
;
217 priv
->last
.va_buffer_info
= va_buffer_info
;
218 for (unsigned i
= 0; i
< va_image
.num_planes
; ++i
)
219 priv
->last
.egl_images
[i
] = egl_images
[i
];
227 if (release_buffer_info
)
228 vlc_vaapi_ReleaseBufferHandle(o
, priv
->vadpy
, va_image
.buf
);
230 for (unsigned i
= 0; i
< 3 && egl_images
[i
] != NULL
; ++i
)
231 vaegl_image_destroy(tc
, egl_images
[i
]);
233 vlc_vaapi_DestroyImage(o
, priv
->vadpy
, va_image
.image_id
);
238 static picture_pool_t
*
239 tc_vaegl_get_pool(const opengl_tex_converter_t
*tc
, unsigned requested_count
)
241 vlc_object_t
*o
= VLC_OBJECT(tc
->gl
);
242 struct priv
*priv
= tc
->priv
;
244 picture_pool_t
*pool
=
245 vlc_vaapi_PoolNew(VLC_OBJECT(tc
->gl
), priv
->vainst
, priv
->vadpy
,
246 requested_count
, &priv
->va_surface_ids
, &tc
->fmt
,
251 /* Check if a surface from the pool can be derived and displayed via dmabuf
253 bool success
= false;
254 VAImage va_image
= { .image_id
= VA_INVALID_ID
};
255 if (vlc_vaapi_DeriveImage(o
, priv
->vadpy
, priv
->va_surface_ids
[0],
259 assert(va_image
.format
.fourcc
== priv
->fourcc
);
261 VABufferInfo va_buffer_info
= (VABufferInfo
) {
262 .mem_type
= VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME
264 if (vlc_vaapi_AcquireBufferHandle(o
,priv
->vadpy
, va_image
.buf
,
268 for (unsigned i
= 0; i
< va_image
.num_planes
; ++i
)
270 EGLint w
= (va_image
.width
* tc
->texs
[i
].w
.num
) / tc
->texs
[i
].w
.den
;
271 EGLint h
= (va_image
.height
* tc
->texs
[i
].h
.num
) / tc
->texs
[i
].h
.den
;
272 EGLImageKHR egl_image
=
273 vaegl_image_create(tc
, w
, h
, priv
->drm_fourccs
[i
], va_buffer_info
.handle
,
274 va_image
.offsets
[i
], va_image
.pitches
[i
]);
275 if (egl_image
== NULL
)
277 msg_Warn(o
, "Can't create Image KHR: kernel too old ?");
280 vaegl_image_destroy(tc
, egl_image
);
285 if (va_image
.image_id
!= VA_INVALID_ID
)
287 if (va_image
.buf
!= VA_INVALID_ID
)
288 vlc_vaapi_ReleaseBufferHandle(o
, priv
->vadpy
, va_image
.buf
);
289 vlc_vaapi_DestroyImage(o
, priv
->vadpy
, va_image
.image_id
);
293 picture_pool_Release(pool
);
300 Close(vlc_object_t
*obj
)
302 opengl_tex_converter_t
*tc
= (void *)obj
;
303 struct priv
*priv
= tc
->priv
;
305 if (priv
->last
.pic
!= NULL
)
306 vaegl_release_last_pic(tc
, priv
);
308 vlc_vaapi_ReleaseInstance(priv
->vainst
);
314 tc_va_check_interop_blacklist(opengl_tex_converter_t
*tc
, VADisplay
*vadpy
)
316 const char *vendor
= vaQueryVendorString(vadpy
);
320 #define BL_SIZE_MAX 19
321 static const char blacklist_prefix
[][BL_SIZE_MAX
] = {
322 /* XXX: case insensitive and alphabetical order */
323 "mesa gallium vaapi",
326 char vendor_prefix
[BL_SIZE_MAX
];
327 strncpy(vendor_prefix
, vendor
, BL_SIZE_MAX
);
328 vendor_prefix
[BL_SIZE_MAX
- 1] = '\0';
330 const char *found
= bsearch(vendor_prefix
, blacklist_prefix
,
331 ARRAY_SIZE(blacklist_prefix
),
332 BL_SIZE_MAX
, (void *) strcasecmp
);
335 msg_Warn(tc
->gl
, "The '%s' driver is blacklisted: no interop", found
);
343 tc_vaegl_init(opengl_tex_converter_t
*tc
, VADisplay
*vadpy
,
344 VANativeDisplay native
,
345 vlc_vaapi_native_destroy_cb native_destroy_cb
)
347 int ret
= VLC_EGENERIC
;
348 struct priv
*priv
= NULL
;
354 priv
= tc
->priv
= calloc(1, sizeof(struct priv
));
355 if (unlikely(tc
->priv
== NULL
))
364 switch (tc
->fmt
.i_chroma
)
366 case VLC_CODEC_VAAPI_420
:
367 va_fourcc
= VA_FOURCC_NV12
;
368 vlc_sw_chroma
= VLC_CODEC_NV12
;
370 case VLC_CODEC_VAAPI_420_10BPP
:
371 va_fourcc
= VA_FOURCC_P010
;
372 vlc_sw_chroma
= VLC_CODEC_P010
;
375 vlc_assert_unreachable();
378 if (vaegl_init_fourcc(tc
, priv
, va_fourcc
))
381 priv
->glEGLImageTargetTexture2DOES
=
382 vlc_gl_GetProcAddress(tc
->gl
, "glEGLImageTargetTexture2DOES");
383 if (priv
->glEGLImageTargetTexture2DOES
== NULL
)
386 tc
->pf_update
= tc_vaegl_update
;
387 tc
->pf_get_pool
= tc_vaegl_get_pool
;
389 priv
->vainst
= vlc_vaapi_InitializeInstance(VLC_OBJECT(tc
->gl
), priv
->vadpy
,
390 native
, native_destroy_cb
);
391 if (priv
->vainst
== NULL
)
393 /* Already released by vlc_vaapi_InitializeInstance */
395 native_destroy_cb
= NULL
;
399 if (tc_va_check_interop_blacklist(tc
, priv
->vadpy
))
402 tc
->fshader
= opengl_fragment_shader_init(tc
, GL_TEXTURE_2D
, vlc_sw_chroma
,
404 if (tc
->fshader
== 0)
407 /* Fix the UV plane texture scale factor for GR */
408 if (vlc_sw_chroma
== VLC_CODEC_NV12
|| vlc_sw_chroma
== VLC_CODEC_P010
)
409 tc
->texs
[1].h
= (vlc_rational_t
) { 1, 2 };
414 if (priv
&& priv
->vainst
)
415 vlc_vaapi_ReleaseInstance(priv
->vainst
);
420 if (native
!= NULL
&& native_destroy_cb
!= NULL
)
421 native_destroy_cb(native
);
429 x11_native_destroy_cb(VANativeDisplay native
)
431 XCloseDisplay(native
);
436 drm_native_destroy_cb(VANativeDisplay native
)
438 vlc_close((intptr_t) native
);
443 Open(vlc_object_t
*obj
)
445 opengl_tex_converter_t
*tc
= (void *) obj
;
447 if (!vlc_vaapi_IsChromaOpaque(tc
->fmt
.i_chroma
)
448 || tc
->gl
->ext
!= VLC_GL_EXT_EGL
449 || tc
->gl
->egl
.createImageKHR
== NULL
450 || tc
->gl
->egl
.destroyImageKHR
== NULL
)
453 if (!HasExtension(tc
->glexts
, "GL_OES_EGL_image"))
456 const char *eglexts
= tc
->gl
->egl
.queryString(tc
->gl
, EGL_EXTENSIONS
);
457 if (eglexts
== NULL
|| !HasExtension(eglexts
, "EGL_EXT_image_dma_buf_import"))
460 int ret
= VLC_EGENERIC
;
461 #if defined (HAVE_VA_X11)
462 if (tc
->gl
->surface
->type
== VOUT_WINDOW_TYPE_XID
)
464 if (!vlc_xlib_init(VLC_OBJECT(tc
->gl
)))
466 Display
*x11dpy
= XOpenDisplay(tc
->gl
->surface
->display
.x11
);
470 ret
= tc_vaegl_init(tc
, vaGetDisplay(x11dpy
), x11dpy
,
471 x11_native_destroy_cb
);
473 #elif defined(HAVE_VA_WL)
474 if (tc
->gl
->surface
->type
== VOUT_WINDOW_TYPE_WAYLAND
)
475 ret
= tc_vaegl_init(tc
, vaGetDisplayWl(tc
->gl
->surface
->display
.wl
),
477 #elif defined (HAVE_VA_DRM)
478 static const char *const drm_device_paths
[] = {
479 "/dev/dri/renderD128",
483 for (size_t i
= 0; i
< ARRAY_SIZE(drm_device_paths
); i
++)
485 int drm_fd
= vlc_open(drm_device_paths
[i
], O_RDWR
);
489 VADisplay dpy
= vaGetDisplayDRM(drm_fd
);
492 ret
= tc_vaegl_init(tc
, dpy
, (VANativeDisplay
) (intptr_t) drm_fd
,
493 drm_native_destroy_cb
);
494 if (ret
== VLC_SUCCESS
)
505 #if defined (HAVE_VA_X11)
507 # define SHORTCUT "vaapi_x11"
508 # define DESCRIPTION_SUFFIX "X11"
509 #elif defined(HAVE_VA_WL)
511 # define SHORTCUT "vaapi_wl"
512 # define DESCRIPTION_SUFFIX "Wayland"
513 #elif defined (HAVE_VA_DRM)
515 # define SHORTCUT "vaapi_drm"
516 # define DESCRIPTION_SUFFIX "DRM"
520 set_description("VA-API OpenGL surface converter for " DESCRIPTION_SUFFIX
)
521 set_capability("glconv", PRIORITY
)
522 set_callbacks(Open
, Close
)
523 set_category(CAT_VIDEO
)
524 set_subcategory(SUBCAT_VIDEO_VOUT
)
525 add_shortcut("vaapi", SHORTCUT
)