diff --git a/app/streaming/video/ffmpeg-renderers/drm.cpp b/app/streaming/video/ffmpeg-renderers/drm.cpp index 5ffd5ab9..d01e0769 100644 --- a/app/streaming/video/ffmpeg-renderers/drm.cpp +++ b/app/streaming/video/ffmpeg-renderers/drm.cpp @@ -14,10 +14,6 @@ extern "C" { #include -#ifdef HAVE_EGL -#include -#endif - #include DrmRenderer::DrmRenderer() @@ -29,6 +25,12 @@ DrmRenderer::DrmRenderer() m_PlaneId(0), m_CurrentFbId(0) { +#ifdef HAVE_EGL + m_eglCreateImage = nullptr; + m_eglCreateImageKHR = nullptr; + m_eglDestroyImage = nullptr; + m_eglDestroyImageKHR = nullptr; +#endif } DrmRenderer::~DrmRenderer() @@ -388,6 +390,19 @@ bool DrmRenderer::initializeEGL(EGLDisplay, return false; } + // 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; } @@ -406,23 +421,44 @@ ssize_t DrmRenderer::exportEGLImages(AVFrame *frame, EGLDisplay dpy, const auto &plane = drmFrame->layers[0].planes[i]; const auto &object = drmFrame->objects[plane.object_index]; - EGLAttrib attribs[17] = { + const int EGL_ATTRIB_COUNT = 13; + EGLAttrib attribs[EGL_ATTRIB_COUNT] = { EGL_LINUX_DRM_FOURCC_EXT, i == 0 ? DRM_FORMAT_R8 : DRM_FORMAT_GR88, EGL_WIDTH, i == 0 ? frame->width : frame->width / 2, EGL_HEIGHT, i == 0 ? frame->height : frame->height / 2, EGL_DMA_BUF_PLANE0_FD_EXT, object.fd, - EGL_DMA_BUF_PLANE0_OFFSET_EXT, (EGLint)plane.offset, - EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)plane.pitch, - EGL_NONE, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, plane.offset, + EGL_DMA_BUF_PLANE0_PITCH_EXT, plane.pitch, + EGL_NONE }; - images[i] = 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; + + 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 < EGL_ATTRIB_COUNT; 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; } @@ -435,7 +471,12 @@ fail: void DrmRenderer::freeEGLImages(EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) { for (size_t i = 0; i < EGL_MAX_PLANES; ++i) { - eglDestroyImage(dpy, images[i]); + if (m_eglDestroyImage) { + m_eglDestroyImage(dpy, images[i]); + } + else { + m_eglDestroyImageKHR(dpy, images[i]); + } } } diff --git a/app/streaming/video/ffmpeg-renderers/drm.h b/app/streaming/video/ffmpeg-renderers/drm.h index e797a5e8..1421046a 100644 --- a/app/streaming/video/ffmpeg-renderers/drm.h +++ b/app/streaming/video/ffmpeg-renderers/drm.h @@ -5,6 +5,36 @@ #include #include +#ifdef HAVE_EGL +#include + +#ifndef EGL_VERSION_1_5 +typedef intptr_t EGLAttrib; +typedef EGLImage (EGLAPIENTRYP PFNEGLCREATEIMAGEPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEPROC) (EGLDisplay dpy, EGLImage image); +#endif + +#ifndef EGL_KHR_image +// EGL_KHR_image technically uses EGLImageKHR instead of EGLImage, but they're compatible +// so we swap them here to avoid mixing them all over the place +typedef EGLImage (EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGLImage image); +#endif + +#ifndef EGL_EXT_image_dma_buf_import +#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 + +#ifndef EGL_EXT_image_dma_buf_import_modifiers +#define EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 0x3443 +#define EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT 0x3444 +#endif +#endif + class DrmRenderer : public IFFmpegRenderer { public: DrmRenderer(); @@ -32,5 +62,12 @@ private: uint32_t m_PlaneId; uint32_t m_CurrentFbId; SDL_Rect m_OutputRect; + +#ifdef HAVE_EGL + PFNEGLCREATEIMAGEPROC m_eglCreateImage; + PFNEGLDESTROYIMAGEPROC m_eglDestroyImage; + PFNEGLCREATEIMAGEKHRPROC m_eglCreateImageKHR; + PFNEGLDESTROYIMAGEKHRPROC m_eglDestroyImageKHR; +#endif }; diff --git a/app/streaming/video/ffmpeg-renderers/vaapi.cpp b/app/streaming/video/ffmpeg-renderers/vaapi.cpp index 8c73b142..55c42eb3 100644 --- a/app/streaming/video/ffmpeg-renderers/vaapi.cpp +++ b/app/streaming/video/ffmpeg-renderers/vaapi.cpp @@ -5,9 +5,6 @@ #include #include -#ifdef HAVE_EGL -#include -#endif #include #include @@ -20,6 +17,11 @@ VAAPIRenderer::VAAPIRenderer() 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 } @@ -490,6 +492,20 @@ VAAPIRenderer::initializeEGL(EGLDisplay, 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; } @@ -525,33 +541,54 @@ VAAPIRenderer::exportEGLImages(AVFrame *frame, EGLDisplay dpy, const auto &layer = m_PrimeDescriptor.layers[i]; const auto &object = m_PrimeDescriptor.objects[layer.object_index[0]]; - EGLAttrib attribs[17] = { - EGL_LINUX_DRM_FOURCC_EXT, (EGLint)layer.drm_format, + const int EGL_ATTRIB_COUNT = 17; + 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_DMA_BUF_PLANE0_FD_EXT, object.fd, - EGL_DMA_BUF_PLANE0_OFFSET_EXT, (EGLint)layer.offset[0], - EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)layer.pitch[0], - EGL_NONE, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, layer.offset[0], + EGL_DMA_BUF_PLANE0_PITCH_EXT, layer.pitch[0], + EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, (EGLint)object.drm_format_modifier, + EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, (EGLint)(object.drm_format_modifier >> 32), + EGL_NONE }; - if (m_EGLExtDmaBuf) { - const EGLAttrib extra[] = { - EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, - (EGLint)object.drm_format_modifier, - EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, - (EGLint)(object.drm_format_modifier >> 32), - EGL_NONE, - }; - memcpy((void *)(&attribs[12]), (void *)extra, sizeof (extra)); + + // Cut off the attribute array before the modifiers if they aren't supported + if (!m_EGLExtDmaBuf) { + SDL_assert(attribs[12] == EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT); + SDL_assert(attribs[14] == EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT); + SDL_assert(attribs[16] == EGL_NONE); + attribs[12] = EGL_NONE; } - images[i] = 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; + + 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 < EGL_ATTRIB_COUNT; 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; } return count; @@ -566,7 +603,12 @@ sync_fail: void VAAPIRenderer::freeEGLImages(EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) { for (size_t i = 0; i < m_PrimeDescriptor.num_layers; ++i) { - eglDestroyImage(dpy, images[i]); + if (m_eglDestroyImage) { + m_eglDestroyImage(dpy, images[i]); + } + else { + m_eglDestroyImageKHR(dpy, images[i]); + } } 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 9435a9ce..bd574b2c 100644 --- a/app/streaming/video/ffmpeg-renderers/vaapi.h +++ b/app/streaming/video/ffmpeg-renderers/vaapi.h @@ -31,6 +31,36 @@ extern "C" { #endif } +#ifdef HAVE_EGL +#include + +#ifndef EGL_VERSION_1_5 +typedef intptr_t EGLAttrib; +typedef EGLImage (EGLAPIENTRYP PFNEGLCREATEIMAGEPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEPROC) (EGLDisplay dpy, EGLImage image); +#endif + +#ifndef EGL_KHR_image +// EGL_KHR_image technically uses EGLImageKHR instead of EGLImage, but they're compatible +// so we swap them here to avoid mixing them all over the place +typedef EGLImage (EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGLImage image); +#endif + +#ifndef EGL_EXT_image_dma_buf_import +#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 + +#ifndef EGL_EXT_image_dma_buf_import_modifiers +#define EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 0x3443 +#define EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT 0x3444 +#endif +#endif + class VAAPIRenderer : public IFFmpegRenderer { public: @@ -68,5 +98,9 @@ private: #ifdef HAVE_EGL VADRMPRIMESurfaceDescriptor m_PrimeDescriptor; bool m_EGLExtDmaBuf; + PFNEGLCREATEIMAGEPROC m_eglCreateImage; + PFNEGLDESTROYIMAGEPROC m_eglDestroyImage; + PFNEGLCREATEIMAGEKHRPROC m_eglCreateImageKHR; + PFNEGLDESTROYIMAGEKHRPROC m_eglDestroyImageKHR; #endif };