diff --git a/app/app.pro b/app/app.pro index 440ddb9b..be16d837 100644 --- a/app/app.pro +++ b/app/app.pro @@ -314,8 +314,11 @@ config_EGL { DEFINES += HAVE_EGL SOURCES += \ streaming/video/ffmpeg-renderers/eglvid.cpp \ - streaming/video/ffmpeg-renderers/egl_extensions.cpp - HEADERS += streaming/video/ffmpeg-renderers/eglvid.h + streaming/video/ffmpeg-renderers/egl_extensions.cpp \ + streaming/video/ffmpeg-renderers/eglimagefactory.cpp + HEADERS += \ + streaming/video/ffmpeg-renderers/eglvid.h \ + streaming/video/ffmpeg-renderers/eglimagefactory.h } config_SL { message(Steam Link build configuration selected) diff --git a/app/streaming/video/ffmpeg-renderers/drm.cpp b/app/streaming/video/ffmpeg-renderers/drm.cpp index e32977a0..3353cd0e 100644 --- a/app/streaming/video/ffmpeg-renderers/drm.cpp +++ b/app/streaming/video/ffmpeg-renderers/drm.cpp @@ -83,15 +83,10 @@ DrmRenderer::DrmRenderer(bool hwaccel, IFFmpegRenderer *backendRenderer) m_HdrOutputMetadataBlobId(0), m_SwFrameMapper(this), m_CurrentSwFrameIdx(0) -{ #ifdef HAVE_EGL - m_EGLExtDmaBuf = false; - m_eglCreateImage = nullptr; - m_eglCreateImageKHR = nullptr; - m_eglDestroyImage = nullptr; - m_eglDestroyImageKHR = nullptr; + , m_EglImageFactory(this) #endif - +{ SDL_zero(m_SwFrame); } @@ -1153,223 +1148,20 @@ AVPixelFormat DrmRenderer::getEGLImagePixelFormat() { return AV_PIX_FMT_DRM_PRIME; } -bool DrmRenderer::initializeEGL(EGLDisplay, +bool DrmRenderer::initializeEGL(EGLDisplay display, 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"); - - // 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; + return m_EglImageFactory.initializeEGL(display, ext); } ssize_t DrmRenderer::exportEGLImages(AVFrame *frame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) { AVDRMFrameDescriptor* drmFrame = (AVDRMFrameDescriptor*)frame->data[0]; - memset(images, 0, sizeof(EGLImage) * EGL_MAX_PLANES); - - // 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 (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++] = 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()); - goto fail; - } - } - 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()); - goto fail; - } - } - - return 1; - -fail: - freeEGLImages(dpy, images); - return -1; + return m_EglImageFactory.exportDRMImages(frame, drmFrame, dpy, images); } void DrmRenderer::freeEGLImages(EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) { - if (m_eglDestroyImage) { - m_eglDestroyImage(dpy, images[0]); - } - else { - m_eglDestroyImageKHR(dpy, images[0]); - } - - // Our EGLImages are non-planar - SDL_assert(images[1] == 0); - SDL_assert(images[2] == 0); + m_EglImageFactory.freeEGLImages(dpy, images); } #endif diff --git a/app/streaming/video/ffmpeg-renderers/drm.h b/app/streaming/video/ffmpeg-renderers/drm.h index 2cd58c8e..5608e30d 100644 --- a/app/streaming/video/ffmpeg-renderers/drm.h +++ b/app/streaming/video/ffmpeg-renderers/drm.h @@ -3,6 +3,10 @@ #include "renderer.h" #include "swframemapper.h" +#ifdef HAVE_EGL +#include "eglimagefactory.h" +#endif + #include #include @@ -105,11 +109,7 @@ private: } m_SwFrame[k_SwFrameCount]; #ifdef HAVE_EGL - bool m_EGLExtDmaBuf; - PFNEGLCREATEIMAGEPROC m_eglCreateImage; - PFNEGLDESTROYIMAGEPROC m_eglDestroyImage; - PFNEGLCREATEIMAGEKHRPROC m_eglCreateImageKHR; - PFNEGLDESTROYIMAGEKHRPROC m_eglDestroyImageKHR; + EglImageFactory m_EglImageFactory; #endif }; diff --git a/app/streaming/video/ffmpeg-renderers/eglimagefactory.cpp b/app/streaming/video/ffmpeg-renderers/eglimagefactory.cpp new file mode 100644 index 00000000..fad875dd --- /dev/null +++ b/app/streaming/video/ffmpeg-renderers/eglimagefactory.cpp @@ -0,0 +1,364 @@ +#include "eglimagefactory.h" + +// Don't take a dependency on libdrm just for this constant +#ifndef DRM_FORMAT_MOD_INVALID +#define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1) +#endif + +EglImageFactory::EglImageFactory(IFFmpegRenderer* renderer) : + m_Renderer(renderer), + m_EGLExtDmaBuf(false), + m_eglCreateImage(nullptr), + m_eglDestroyImage(nullptr), + m_eglCreateImageKHR(nullptr), + m_eglDestroyImageKHR(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"); + + // 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; +} + +ssize_t EglImageFactory::exportDRMImages(AVFrame* frame, AVDRMFrameDescriptor* drmFrame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) +{ + memset(images, 0, sizeof(EGLImage) * EGL_MAX_PLANES); + + // 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; + } + } + + return 1; +} + +#ifdef HAVE_LIBVA + +ssize_t EglImageFactory::exportVAImages(AVFrame *frame, VADRMPRIMESurfaceDescriptor *vaFrame, EGLDisplay dpy, EGLImage images[]) +{ + ssize_t count = 0; + + memset(images, 0, sizeof(EGLImage) * EGL_MAX_PLANES); + + 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 31 attributes (1 key + 1 value for each) + const int EGL_ATTRIB_COUNT = 31 * 2; + EGLAttrib attribs[EGL_ATTRIB_COUNT] = { + EGL_LINUX_DRM_FOURCC_EXT, 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(); + } + } + + // Terminate the attribute list + attribs[attribIndex++] = EGL_NONE; + SDL_assert(attribIndex <= EGL_ATTRIB_COUNT); + + if (m_eglCreateImage) { + images[i] = m_eglCreateImage(dpy, EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + nullptr, attribs); + if (!images[i]) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "eglCreateImage() Failed: %d", eglGetError()); + goto fail; + } + } + 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]; + } + + images[i] = m_eglCreateImageKHR(dpy, EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + nullptr, intAttribs); + if (!images[i]) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "eglCreateImageKHR() Failed: %d", eglGetError()); + goto fail; + } + } + + ++count; + } + return count; + +fail: + freeEGLImages(dpy, images); + return -1; +} + +#endif + +void EglImageFactory::freeEGLImages(EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) { + for (size_t i = 0; i < EGL_MAX_PLANES; ++i) { + if (images[i] != nullptr) { + if (m_eglDestroyImage) { + m_eglDestroyImage(dpy, images[i]); + } + else { + m_eglDestroyImageKHR(dpy, images[i]); + } + } + } +} diff --git a/app/streaming/video/ffmpeg-renderers/eglimagefactory.h b/app/streaming/video/ffmpeg-renderers/eglimagefactory.h new file mode 100644 index 00000000..7a4dc4d3 --- /dev/null +++ b/app/streaming/video/ffmpeg-renderers/eglimagefactory.h @@ -0,0 +1,32 @@ +#pragma once + +#include "renderer.h" + +#define SDL_USE_BUILTIN_OPENGL_DEFINITIONS 1 +#include + +#ifdef HAVE_LIBVA +#include +#endif + +class EglImageFactory +{ +public: + EglImageFactory(IFFmpegRenderer* renderer); + bool initializeEGL(EGLDisplay, const EGLExtensions &ext); + ssize_t exportDRMImages(AVFrame* frame, AVDRMFrameDescriptor* drmFrame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]); + +#ifdef HAVE_LIBVA + ssize_t exportVAImages(AVFrame* frame, VADRMPRIMESurfaceDescriptor* vaFrame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]); +#endif + + void freeEGLImages(EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]); + +private: + IFFmpegRenderer* m_Renderer; + bool m_EGLExtDmaBuf; + PFNEGLCREATEIMAGEPROC m_eglCreateImage; + PFNEGLDESTROYIMAGEPROC m_eglDestroyImage; + PFNEGLCREATEIMAGEKHRPROC m_eglCreateImageKHR; + PFNEGLDESTROYIMAGEKHRPROC m_eglDestroyImageKHR; +}; diff --git a/app/streaming/video/ffmpeg-renderers/vaapi.cpp b/app/streaming/video/ffmpeg-renderers/vaapi.cpp index 880fc531..34359a98 100644 --- a/app/streaming/video/ffmpeg-renderers/vaapi.cpp +++ b/app/streaming/video/ffmpeg-renderers/vaapi.cpp @@ -17,16 +17,13 @@ VAAPIRenderer::VAAPIRenderer(int decoderSelectionPass) m_HwContext(nullptr), m_BlacklistedForDirectRendering(false), m_OverlayMutex(nullptr) +#ifdef HAVE_EGL + , m_EglImageFactory(this) +#endif { #ifdef HAVE_EGL m_PrimeDescriptor.num_layers = 0; m_PrimeDescriptor.num_objects = 0; - m_EGLExtDmaBuf = false; - - m_eglCreateImage = nullptr; - m_eglCreateImageKHR = nullptr; - m_eglDestroyImage = nullptr; - m_eglDestroyImageKHR = nullptr; #endif SDL_zero(m_OverlayImage); @@ -887,35 +884,15 @@ AVPixelFormat VAAPIRenderer::getEGLImagePixelFormat() { } bool -VAAPIRenderer::initializeEGL(EGLDisplay, +VAAPIRenderer::initializeEGL(EGLDisplay dpy, const EGLExtensions &ext) { - if (!ext.isSupported("EGL_EXT_image_dma_buf_import")) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "VAAPI-EGL: DMABUF unsupported"); - return false; - } - m_EGLExtDmaBuf = ext.isSupported("EGL_EXT_image_dma_buf_import_modifiers"); - - // 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; + return m_EglImageFactory.initializeEGL(dpy, ext); } ssize_t VAAPIRenderer::exportEGLImages(AVFrame *frame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) { - ssize_t count = 0; + ssize_t count; auto hwFrameCtx = (AVHWFramesContext*)frame->hw_frames_ctx->data; AVVAAPIDeviceContext* vaDeviceContext = (AVVAAPIDeviceContext*)hwFrameCtx->device_ctx->hwctx; @@ -931,149 +908,32 @@ VAAPIRenderer::exportEGLImages(AVFrame *frame, EGLDisplay dpy, return -1; } - SDL_assert(m_PrimeDescriptor.num_layers <= EGL_MAX_PLANES); - st = vaSyncSurface(vaDeviceContext->display, surface_id); if (st != VA_STATUS_SUCCESS) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "vaSyncSurface failed: %d", st); - goto sync_fail; + "vaSyncSurface() failed: %d", st); + goto fail; } - for (size_t i = 0; i < m_PrimeDescriptor.num_layers; ++i) { - const auto &layer = m_PrimeDescriptor.layers[i]; - - // Max 31 attributes (1 key + 1 value for each) - const int EGL_ATTRIB_COUNT = 31 * 2; - EGLAttrib attribs[EGL_ATTRIB_COUNT] = { - EGL_LINUX_DRM_FOURCC_EXT, 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 = m_PrimeDescriptor.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(); - } - } - - // Terminate the attribute list - attribs[attribIndex++] = EGL_NONE; - SDL_assert(attribIndex <= EGL_ATTRIB_COUNT); - - if (m_eglCreateImage) { - images[i] = m_eglCreateImage(dpy, EGL_NO_CONTEXT, - EGL_LINUX_DMA_BUF_EXT, - nullptr, attribs); - if (!images[i]) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "eglCreateImage() Failed: %d", eglGetError()); - goto create_image_fail; - } - } - 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]; - } - - images[i] = m_eglCreateImageKHR(dpy, EGL_NO_CONTEXT, - EGL_LINUX_DMA_BUF_EXT, - nullptr, intAttribs); - if (!images[i]) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "eglCreateImageKHR() Failed: %d", eglGetError()); - goto create_image_fail; - } - } - - ++count; + count = m_EglImageFactory.exportVAImages(frame, &m_PrimeDescriptor, dpy, images); + if (count < 0) { + goto fail; } + return count; -create_image_fail: - m_PrimeDescriptor.num_layers = count; -sync_fail: - freeEGLImages(dpy, images); +fail: + for (size_t i = 0; i < m_PrimeDescriptor.num_objects; ++i) { + close(m_PrimeDescriptor.objects[i].fd); + } + m_PrimeDescriptor.num_layers = 0; + m_PrimeDescriptor.num_objects = 0; return -1; } void VAAPIRenderer::freeEGLImages(EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) { - for (size_t i = 0; i < m_PrimeDescriptor.num_layers; ++i) { - if (m_eglDestroyImage) { - m_eglDestroyImage(dpy, images[i]); - } - else { - m_eglDestroyImageKHR(dpy, images[i]); - } - } + m_EglImageFactory.freeEGLImages(dpy, images); for (size_t i = 0; i < m_PrimeDescriptor.num_objects; ++i) { close(m_PrimeDescriptor.objects[i].fd); } diff --git a/app/streaming/video/ffmpeg-renderers/vaapi.h b/app/streaming/video/ffmpeg-renderers/vaapi.h index a0671c14..b7fce359 100644 --- a/app/streaming/video/ffmpeg-renderers/vaapi.h +++ b/app/streaming/video/ffmpeg-renderers/vaapi.h @@ -14,6 +14,10 @@ #undef HAVE_LIBVA_WAYLAND #endif +#ifdef HAVE_EGL +#include "eglimagefactory.h" +#endif + extern "C" { #include #ifdef HAVE_LIBVA_X11 @@ -93,10 +97,6 @@ private: #ifdef HAVE_EGL VADRMPRIMESurfaceDescriptor m_PrimeDescriptor; - bool m_EGLExtDmaBuf; - PFNEGLCREATEIMAGEPROC m_eglCreateImage; - PFNEGLDESTROYIMAGEPROC m_eglDestroyImage; - PFNEGLCREATEIMAGEKHRPROC m_eglCreateImageKHR; - PFNEGLDESTROYIMAGEKHRPROC m_eglDestroyImageKHR; + EglImageFactory m_EglImageFactory; #endif };