mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-02-16 10:40:59 +00:00
581 lines
23 KiB
C++
581 lines
23 KiB
C++
#include "eglimagefactory.h"
|
|
|
|
#ifdef HAVE_LIBVA
|
|
#include <libavutil/hwcontext_vaapi.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <vector>
|
|
|
|
// Don't take a dependency on libdrm just for these constants
|
|
#ifndef DRM_FORMAT_MOD_INVALID
|
|
#define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
|
|
#endif
|
|
#ifndef DRM_FORMAT_MOD_LINEAR
|
|
#define DRM_FORMAT_MOD_LINEAR 0
|
|
#endif
|
|
#ifndef fourcc_code
|
|
#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
|
|
#endif
|
|
#ifndef DRM_FORMAT_R8
|
|
#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ')
|
|
#endif
|
|
#ifndef DRM_FORMAT_GR88
|
|
#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8')
|
|
#endif
|
|
|
|
EglImageFactory::EglImageFactory(IFFmpegRenderer* renderer) :
|
|
m_Renderer(renderer),
|
|
m_EGLExtDmaBuf(false),
|
|
m_eglCreateImage(nullptr),
|
|
m_eglDestroyImage(nullptr),
|
|
m_eglCreateImageKHR(nullptr),
|
|
m_eglDestroyImageKHR(nullptr),
|
|
m_eglQueryDmaBufFormatsEXT(nullptr),
|
|
m_eglQueryDmaBufModifiersEXT(nullptr)
|
|
{
|
|
}
|
|
|
|
bool EglImageFactory::initializeEGL(EGLDisplay,
|
|
const EGLExtensions &ext)
|
|
{
|
|
if (!ext.isSupported("EGL_EXT_image_dma_buf_import")) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"DRM-EGL: DMABUF unsupported");
|
|
return false;
|
|
}
|
|
|
|
m_EGLExtDmaBuf = ext.isSupported("EGL_EXT_image_dma_buf_import_modifiers");
|
|
if (m_EGLExtDmaBuf) {
|
|
m_eglQueryDmaBufFormatsEXT = (typeof(m_eglQueryDmaBufFormatsEXT))eglGetProcAddress("eglQueryDmaBufFormatsEXT");
|
|
m_eglQueryDmaBufModifiersEXT = (typeof(m_eglQueryDmaBufModifiersEXT))eglGetProcAddress("eglQueryDmaBufModifiersEXT");
|
|
}
|
|
|
|
// NB: eglCreateImage() and eglCreateImageKHR() have slightly different definitions
|
|
m_eglCreateImage = (typeof(m_eglCreateImage))eglGetProcAddress("eglCreateImage");
|
|
m_eglCreateImageKHR = (typeof(m_eglCreateImageKHR))eglGetProcAddress("eglCreateImageKHR");
|
|
m_eglDestroyImage = (typeof(m_eglDestroyImage))eglGetProcAddress("eglDestroyImage");
|
|
m_eglDestroyImageKHR = (typeof(m_eglDestroyImageKHR))eglGetProcAddress("eglDestroyImageKHR");
|
|
|
|
if (!(m_eglCreateImage && m_eglDestroyImage) &&
|
|
!(m_eglCreateImageKHR && m_eglDestroyImageKHR)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"Missing eglCreateImage()/eglDestroyImage() in EGL driver");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void EglImageFactory::resetCache()
|
|
{
|
|
}
|
|
|
|
#ifdef HAVE_DRM
|
|
|
|
ssize_t EglImageFactory::exportDRMImages(AVFrame* frame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES])
|
|
{
|
|
SDL_assert(frame->format == AV_PIX_FMT_DRM_PRIME);
|
|
AVDRMFrameDescriptor* drmFrame = (AVDRMFrameDescriptor*)frame->data[0];
|
|
|
|
// DRM requires composed layers rather than separate layers per plane
|
|
SDL_assert(drmFrame->nb_layers == 1);
|
|
|
|
// Max 33 attributes (1 key + 1 value for each)
|
|
const int MAX_ATTRIB_COUNT = 33 * 2;
|
|
EGLAttrib attribs[MAX_ATTRIB_COUNT] = {
|
|
EGL_LINUX_DRM_FOURCC_EXT, (EGLAttrib)drmFrame->layers[0].format,
|
|
EGL_WIDTH, frame->width,
|
|
EGL_HEIGHT, frame->height,
|
|
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
|
};
|
|
int attribIndex = 8;
|
|
|
|
for (int i = 0; i < drmFrame->layers[0].nb_planes; ++i) {
|
|
const auto &plane = drmFrame->layers[0].planes[i];
|
|
const auto &object = drmFrame->objects[plane.object_index];
|
|
|
|
switch (i) {
|
|
case 0:
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_FD_EXT;
|
|
attribs[attribIndex++] = object.fd;
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
|
|
attribs[attribIndex++] = plane.offset;
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
|
|
attribs[attribIndex++] = plane.pitch;
|
|
if (m_EGLExtDmaBuf && object.format_modifier != DRM_FORMAT_MOD_INVALID) {
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.format_modifier & 0xFFFFFFFF);
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.format_modifier >> 32);
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_FD_EXT;
|
|
attribs[attribIndex++] = object.fd;
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
|
|
attribs[attribIndex++] = plane.offset;
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
|
|
attribs[attribIndex++] = plane.pitch;
|
|
if (m_EGLExtDmaBuf && object.format_modifier != DRM_FORMAT_MOD_INVALID) {
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.format_modifier & 0xFFFFFFFF);
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.format_modifier >> 32);
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_FD_EXT;
|
|
attribs[attribIndex++] = object.fd;
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
|
|
attribs[attribIndex++] = plane.offset;
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
|
|
attribs[attribIndex++] = plane.pitch;
|
|
if (m_EGLExtDmaBuf && object.format_modifier != DRM_FORMAT_MOD_INVALID) {
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.format_modifier & 0xFFFFFFFF);
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.format_modifier >> 32);
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_FD_EXT;
|
|
attribs[attribIndex++] = object.fd;
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT;
|
|
attribs[attribIndex++] = plane.offset;
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_PITCH_EXT;
|
|
attribs[attribIndex++] = plane.pitch;
|
|
if (m_EGLExtDmaBuf && object.format_modifier != DRM_FORMAT_MOD_INVALID) {
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.format_modifier & 0xFFFFFFFF);
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.format_modifier >> 32);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Q_UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
// Add colorspace metadata
|
|
switch (m_Renderer->getFrameColorspace(frame)) {
|
|
case COLORSPACE_REC_601:
|
|
attribs[attribIndex++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_ITU_REC601_EXT;
|
|
break;
|
|
case COLORSPACE_REC_709:
|
|
attribs[attribIndex++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_ITU_REC709_EXT;
|
|
break;
|
|
case COLORSPACE_REC_2020:
|
|
attribs[attribIndex++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_ITU_REC2020_EXT;
|
|
break;
|
|
}
|
|
|
|
// Add color range metadata
|
|
attribs[attribIndex++] = EGL_SAMPLE_RANGE_HINT_EXT;
|
|
attribs[attribIndex++] = m_Renderer->isFrameFullRange(frame) ? EGL_YUV_FULL_RANGE_EXT : EGL_YUV_NARROW_RANGE_EXT;
|
|
|
|
// Add chroma siting metadata
|
|
switch (frame->chroma_location) {
|
|
case AVCHROMA_LOC_LEFT:
|
|
case AVCHROMA_LOC_TOPLEFT:
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_SITING_0_EXT;
|
|
break;
|
|
|
|
case AVCHROMA_LOC_CENTER:
|
|
case AVCHROMA_LOC_TOP:
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_SITING_0_5_EXT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
switch (frame->chroma_location) {
|
|
case AVCHROMA_LOC_TOPLEFT:
|
|
case AVCHROMA_LOC_TOP:
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_SITING_0_EXT;
|
|
break;
|
|
|
|
case AVCHROMA_LOC_LEFT:
|
|
case AVCHROMA_LOC_CENTER:
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_SITING_0_5_EXT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Terminate the attribute list
|
|
attribs[attribIndex++] = EGL_NONE;
|
|
SDL_assert(attribIndex <= MAX_ATTRIB_COUNT);
|
|
|
|
// Our EGLImages are non-planar, so we only populate the first entry
|
|
if (m_eglCreateImage) {
|
|
images[0] = m_eglCreateImage(dpy, EGL_NO_CONTEXT,
|
|
EGL_LINUX_DMA_BUF_EXT,
|
|
nullptr, attribs);
|
|
if (!images[0]) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"eglCreateImage() Failed: %d", eglGetError());
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
// Cast the EGLAttrib array elements to EGLint for the KHR extension
|
|
EGLint intAttribs[MAX_ATTRIB_COUNT];
|
|
for (int i = 0; i < MAX_ATTRIB_COUNT; i++) {
|
|
intAttribs[i] = (EGLint)attribs[i];
|
|
}
|
|
|
|
images[0] = m_eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
|
|
EGL_LINUX_DMA_BUF_EXT,
|
|
nullptr, intAttribs);
|
|
if (!images[0]) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"eglCreateImageKHR() Failed: %d", eglGetError());
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
auto imgCtx = new EglImageContext(dpy, m_eglDestroyImage, m_eglDestroyImageKHR);
|
|
imgCtx->images[0] = images[0];
|
|
imgCtx->count = 1;
|
|
|
|
// Add a buffer reference to the frame to automatically destroy the EGLImages
|
|
// when the frame is no longer referenced.
|
|
frame->opaque_ref = av_buffer_create((uint8_t*)imgCtx, sizeof(*imgCtx),
|
|
freeEglImageContextBuffer,
|
|
frame->opaque_ref, // Chain any existing buffer
|
|
AV_BUFFER_FLAG_READONLY);
|
|
return imgCtx->count;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBVA
|
|
|
|
ssize_t EglImageFactory::exportVAImages(AVFrame *frame, uint32_t exportFlags, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES])
|
|
{
|
|
SDL_assert(frame->format == AV_PIX_FMT_VAAPI);
|
|
auto hwFrameCtx = (AVHWFramesContext*)frame->hw_frames_ctx->data;
|
|
AVVAAPIDeviceContext* vaDeviceContext = (AVVAAPIDeviceContext*)hwFrameCtx->device_ctx->hwctx;
|
|
VASurfaceID surface_id = (VASurfaceID)(uintptr_t)frame->data[3];
|
|
|
|
// Sync the surface before doing anything. We need to do this even if we've got cached EGLImages.
|
|
VAStatus st = vaSyncSurface(vaDeviceContext->display, surface_id);
|
|
if (st != VA_STATUS_SUCCESS) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"vaSyncSurface() failed: %d", st);
|
|
return -1;
|
|
}
|
|
|
|
VADRMPRIMESurfaceDescriptor vaFrame;
|
|
st = vaExportSurfaceHandle(vaDeviceContext->display,
|
|
surface_id,
|
|
VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
|
|
exportFlags,
|
|
&vaFrame);
|
|
if (st != VA_STATUS_SUCCESS) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"vaExportSurfaceHandle failed: %d", st);
|
|
return -1;
|
|
}
|
|
|
|
auto imgCtx = new EglImageContext(dpy, m_eglDestroyImage, m_eglDestroyImageKHR);
|
|
|
|
SDL_assert(vaFrame.num_layers <= EGL_MAX_PLANES);
|
|
|
|
for (size_t i = 0; i < vaFrame.num_layers; ++i) {
|
|
const auto &layer = vaFrame.layers[i];
|
|
|
|
// Max 33 attributes (1 key + 1 value for each)
|
|
const int EGL_ATTRIB_COUNT = 33 * 2;
|
|
EGLAttrib attribs[EGL_ATTRIB_COUNT] = {
|
|
EGL_LINUX_DRM_FOURCC_EXT, (EGLAttrib)layer.drm_format,
|
|
EGL_WIDTH, i == 0 ? frame->width : frame->width / 2,
|
|
EGL_HEIGHT, i == 0 ? frame->height : frame->height / 2,
|
|
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
|
};
|
|
|
|
int attribIndex = 8;
|
|
for (size_t j = 0; j < layer.num_planes; j++) {
|
|
const auto &object = vaFrame.objects[layer.object_index[j]];
|
|
|
|
switch (j) {
|
|
case 0:
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_FD_EXT;
|
|
attribs[attribIndex++] = object.fd;
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
|
|
attribs[attribIndex++] = layer.offset[0];
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
|
|
attribs[attribIndex++] = layer.pitch[0];
|
|
if (m_EGLExtDmaBuf) {
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.drm_format_modifier & 0xFFFFFFFF);
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.drm_format_modifier >> 32);
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_FD_EXT;
|
|
attribs[attribIndex++] = object.fd;
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
|
|
attribs[attribIndex++] = layer.offset[1];
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
|
|
attribs[attribIndex++] = layer.pitch[1];
|
|
if (m_EGLExtDmaBuf) {
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.drm_format_modifier & 0xFFFFFFFF);
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.drm_format_modifier >> 32);
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_FD_EXT;
|
|
attribs[attribIndex++] = object.fd;
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
|
|
attribs[attribIndex++] = layer.offset[2];
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
|
|
attribs[attribIndex++] = layer.pitch[2];
|
|
if (m_EGLExtDmaBuf) {
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.drm_format_modifier & 0xFFFFFFFF);
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.drm_format_modifier >> 32);
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_FD_EXT;
|
|
attribs[attribIndex++] = object.fd;
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT;
|
|
attribs[attribIndex++] = layer.offset[3];
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_PITCH_EXT;
|
|
attribs[attribIndex++] = layer.pitch[3];
|
|
if (m_EGLExtDmaBuf) {
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.drm_format_modifier & 0xFFFFFFFF);
|
|
attribs[attribIndex++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT;
|
|
attribs[attribIndex++] = (EGLint)(object.drm_format_modifier >> 32);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Q_UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
// For composed exports, add the YUV metadata
|
|
if (vaFrame.num_layers == 1) {
|
|
// Add colorspace metadata
|
|
switch (m_Renderer->getFrameColorspace(frame)) {
|
|
case COLORSPACE_REC_601:
|
|
attribs[attribIndex++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_ITU_REC601_EXT;
|
|
break;
|
|
case COLORSPACE_REC_709:
|
|
attribs[attribIndex++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_ITU_REC709_EXT;
|
|
break;
|
|
case COLORSPACE_REC_2020:
|
|
attribs[attribIndex++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_ITU_REC2020_EXT;
|
|
break;
|
|
}
|
|
|
|
// Add color range metadata
|
|
attribs[attribIndex++] = EGL_SAMPLE_RANGE_HINT_EXT;
|
|
attribs[attribIndex++] = m_Renderer->isFrameFullRange(frame) ? EGL_YUV_FULL_RANGE_EXT : EGL_YUV_NARROW_RANGE_EXT;
|
|
|
|
// Add chroma siting metadata
|
|
switch (frame->chroma_location) {
|
|
case AVCHROMA_LOC_LEFT:
|
|
case AVCHROMA_LOC_TOPLEFT:
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_SITING_0_EXT;
|
|
break;
|
|
|
|
case AVCHROMA_LOC_CENTER:
|
|
case AVCHROMA_LOC_TOP:
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_SITING_0_5_EXT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
switch (frame->chroma_location) {
|
|
case AVCHROMA_LOC_TOPLEFT:
|
|
case AVCHROMA_LOC_TOP:
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_SITING_0_EXT;
|
|
break;
|
|
|
|
case AVCHROMA_LOC_LEFT:
|
|
case AVCHROMA_LOC_CENTER:
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT;
|
|
attribs[attribIndex++] = EGL_YUV_CHROMA_SITING_0_5_EXT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Terminate the attribute list
|
|
attribs[attribIndex++] = EGL_NONE;
|
|
SDL_assert(attribIndex <= EGL_ATTRIB_COUNT);
|
|
|
|
if (m_eglCreateImage) {
|
|
imgCtx->images[i] = m_eglCreateImage(dpy, EGL_NO_CONTEXT,
|
|
EGL_LINUX_DMA_BUF_EXT,
|
|
nullptr, attribs);
|
|
if (!imgCtx->images[i]) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"eglCreateImage() Failed: %d", eglGetError());
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
// Cast the EGLAttrib array elements to EGLint for the KHR extension
|
|
EGLint intAttribs[EGL_ATTRIB_COUNT];
|
|
for (int i = 0; i < attribIndex; i++) {
|
|
intAttribs[i] = (EGLint)attribs[i];
|
|
}
|
|
|
|
imgCtx->images[i] = m_eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
|
|
EGL_LINUX_DMA_BUF_EXT,
|
|
nullptr, intAttribs);
|
|
if (!imgCtx->images[i]) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"eglCreateImageKHR() Failed: %d", eglGetError());
|
|
break;
|
|
}
|
|
}
|
|
|
|
imgCtx->count++;
|
|
}
|
|
|
|
// Always close the exported FDs
|
|
for (size_t i = 0; i < vaFrame.num_objects; ++i) {
|
|
close(vaFrame.objects[i].fd);
|
|
}
|
|
|
|
// Check for failure
|
|
if (vaFrame.num_layers != imgCtx->count) {
|
|
delete imgCtx;
|
|
return -1;
|
|
}
|
|
|
|
// Add a buffer reference to the frame to automatically destroy the EGLImages
|
|
// when the frame is no longer referenced.
|
|
frame->opaque_ref = av_buffer_create((uint8_t*)imgCtx, sizeof(*imgCtx),
|
|
freeEglImageContextBuffer,
|
|
frame->opaque_ref, // Chain any existing buffer
|
|
AV_BUFFER_FLAG_READONLY);
|
|
|
|
memcpy(images, imgCtx->images, sizeof(EGLImage) * imgCtx->count);
|
|
return imgCtx->count;
|
|
}
|
|
|
|
#endif
|
|
|
|
bool EglImageFactory::supportsImportingFormat(EGLDisplay dpy, EGLint format)
|
|
{
|
|
if (!m_eglQueryDmaBufFormatsEXT) {
|
|
// These are the standard formats used for importing separate layers of NV12.
|
|
// We will assume all EGL implementations can handle these.
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
"Assuming R8 and GR88 format support because eglQueryDmaBufFormatsEXT() is not supported");
|
|
return format == DRM_FORMAT_R8 || format == DRM_FORMAT_GR88;
|
|
}
|
|
|
|
// Get the number of formats
|
|
EGLint numFormats;
|
|
if (!m_eglQueryDmaBufFormatsEXT(dpy, 0, nullptr, &numFormats)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"eglQueryDmaBufFormatsEXT() #1 failed: %d", eglGetError());
|
|
return false;
|
|
}
|
|
else if (numFormats == 0) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
"eglQueryDmaBufFormatsEXT() returned no supported formats!");
|
|
return false;
|
|
}
|
|
|
|
std::vector<EGLint> formats(numFormats);
|
|
if (!m_eglQueryDmaBufFormatsEXT(dpy, numFormats, formats.data(), &numFormats)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"eglQueryDmaBufFormatsEXT() #2 failed: %d", eglGetError());
|
|
return false;
|
|
}
|
|
|
|
for (EGLint i = 0; i < numFormats; i++) {
|
|
if (format == formats[i]) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool EglImageFactory::supportsImportingModifier(EGLDisplay dpy, EGLint format, EGLuint64KHR modifier)
|
|
{
|
|
// We assume linear and no modifiers are always supported
|
|
if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) {
|
|
return true;
|
|
}
|
|
|
|
if (!m_eglQueryDmaBufModifiersEXT) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
"Assuming linear modifier support because eglQueryDmaBufModifiersEXT() is not supported");
|
|
return false;
|
|
}
|
|
|
|
// Get the number of modifiers
|
|
EGLint numModifiers;
|
|
if (!m_eglQueryDmaBufModifiersEXT(dpy, format, 0, nullptr, nullptr, &numModifiers)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"eglQueryDmaBufModifiersEXT() #1 failed: %d", eglGetError());
|
|
return false;
|
|
}
|
|
else if (numModifiers == 0) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
"eglQueryDmaBufModifiersEXT() returned no supported modifiers!");
|
|
return false;
|
|
}
|
|
|
|
std::vector<EGLuint64KHR> modifiers(numModifiers);
|
|
if (!m_eglQueryDmaBufModifiersEXT(dpy, format, numModifiers, modifiers.data(), nullptr, &numModifiers)) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
"eglQueryDmaBufModifiersEXT() #2 failed: %d", eglGetError());
|
|
return false;
|
|
}
|
|
|
|
for (EGLint i = 0; i < numModifiers; i++) {
|
|
if (modifier == modifiers[i]) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void EglImageFactory::freeEglImageContextBuffer(void* opaque, uint8_t* data)
|
|
{
|
|
auto imgCtx = (EglImageContext*)data;
|
|
delete imgCtx;
|
|
|
|
// Free any chained buffers
|
|
av_buffer_unref((AVBufferRef**)&opaque);
|
|
}
|
|
|