diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn index 8701c295b3..63a0bb3412 100644 --- a/modules/desktop_capture/BUILD.gn +++ b/modules/desktop_capture/BUILD.gn @@ -228,10 +228,6 @@ if (is_linux || is_chromeos) { pkg_config("egl") { packages = [ "egl" ] } - pkg_config("epoxy") { - packages = [ "epoxy" ] - ignore_libs = true - } pkg_config("libdrm") { packages = [ "libdrm" ] if (!rtc_link_pipewire) { @@ -578,7 +574,6 @@ rtc_library("desktop_capture_generic") { ":pipewire", ":gbm", ":egl", - ":epoxy", ":libdrm", ] diff --git a/modules/desktop_capture/linux/wayland/egl_dmabuf.cc b/modules/desktop_capture/linux/wayland/egl_dmabuf.cc index 088d1c4ea4..1d364da036 100644 --- a/modules/desktop_capture/linux/wayland/egl_dmabuf.cc +++ b/modules/desktop_capture/linux/wayland/egl_dmabuf.cc @@ -49,6 +49,10 @@ typedef void* (*eglGetProcAddress_func)(const char*); typedef EGLDisplay (*eglGetPlatformDisplayEXT_func)(EGLenum platform, void* native_display, const EGLint* attrib_list); +typedef EGLDisplay (*eglGetPlatformDisplay_func)(EGLenum platform, + void* native_display, + const EGLAttrib* attrib_list); + typedef EGLBoolean (*eglInitialize_func)(EGLDisplay dpy, EGLint* major, EGLint* minor); @@ -83,6 +87,7 @@ eglDestroyImageKHR_func EglDestroyImageKHR = nullptr; eglGetError_func EglGetError = nullptr; eglGetProcAddress_func EglGetProcAddress = nullptr; eglGetPlatformDisplayEXT_func EglGetPlatformDisplayEXT = nullptr; +eglGetPlatformDisplay_func EglGetPlatformDisplay = nullptr; eglInitialize_func EglInitialize = nullptr; eglMakeCurrent_func EglMakeCurrent = nullptr; eglQueryDmaBufFormatsEXT_func EglQueryDmaBufFormatsEXT = nullptr; @@ -225,6 +230,8 @@ static bool LoadEGL() { EglGetError = (eglGetError_func)EglGetProcAddress("eglGetError"); EglGetPlatformDisplayEXT = (eglGetPlatformDisplayEXT_func)EglGetProcAddress( "eglGetPlatformDisplayEXT"); + EglGetPlatformDisplay = + (eglGetPlatformDisplay_func)EglGetProcAddress("eglGetPlatformDisplay"); EglInitialize = (eglInitialize_func)EglGetProcAddress("eglInitialize"); EglMakeCurrent = (eglMakeCurrent_func)EglGetProcAddress("eglMakeCurrent"); EglQueryString = (eglQueryString_func)EglGetProcAddress("eglQueryString"); @@ -234,8 +241,9 @@ static bool LoadEGL() { return EglBindAPI && EglCreateContext && EglCreateImageKHR && EglTerminate && EglDestroyContext && EglDestroyImageKHR && - EglGetError && EglGetPlatformDisplayEXT && EglInitialize && - EglMakeCurrent && EglQueryString && GlEGLImageTargetTexture2DOES; + EglGetError && EglGetPlatformDisplayEXT && EglGetPlatformDisplay && + EglInitialize && EglMakeCurrent && EglQueryString && + GlEGLImageTargetTexture2DOES; } return false; @@ -284,25 +292,6 @@ static bool LoadGL() { RTC_NO_SANITIZE("cfi-icall") EglDmaBuf::EglDmaBuf() { - absl::optional render_node = GetRenderNode(); - if (!render_node) { - return; - } - - drm_fd_ = open(render_node->c_str(), O_RDWR); - - if (drm_fd_ < 0) { - RTC_LOG(LS_ERROR) << "Failed to open drm render node: " << strerror(errno); - return; - } - - gbm_device_ = gbm_create_device(drm_fd_); - - if (!gbm_device_) { - RTC_LOG(LS_ERROR) << "Cannot create GBM device: " << strerror(errno); - return; - } - if (!LoadEGL()) { RTC_LOG(LS_ERROR) << "Unable to load EGL entry functions."; CloseLibrary(g_lib_egl); @@ -315,26 +304,13 @@ EglDmaBuf::EglDmaBuf() { return; } - // Get the list of client extensions - const char* client_extensions_cstring_no_display = - EglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); - std::string client_extensions_string = client_extensions_cstring_no_display; - if (!client_extensions_cstring_no_display) { - // If eglQueryString() returned NULL, the implementation doesn't support - // EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error. - RTC_LOG(LS_ERROR) << "No client extensions defined! " - << FormatEGLError(EglGetError()); + if (!GetClientExtensions(EGL_NO_DISPLAY, EGL_EXTENSIONS)) { return; } - std::vector client_extensions_no_display = - rtc::split(client_extensions_cstring_no_display, ' '); - for (const auto& extension : client_extensions_no_display) { - egl_.extensions.push_back(std::string(extension)); - } - bool has_platform_base_ext = false; bool has_platform_gbm_ext = false; + bool has_khr_platform_gbm_ext = false; for (const auto& extension : egl_.extensions) { if (extension == "EGL_EXT_platform_base") { @@ -343,18 +319,51 @@ EglDmaBuf::EglDmaBuf() { } else if (extension == "EGL_MESA_platform_gbm") { has_platform_gbm_ext = true; continue; + } else if (extension == "EGL_KHR_platform_gbm") { + has_khr_platform_gbm_ext = true; + continue; } } - if (!has_platform_base_ext || !has_platform_gbm_ext) { + if (!has_platform_base_ext || !has_platform_gbm_ext || + !has_khr_platform_gbm_ext) { RTC_LOG(LS_ERROR) << "One of required EGL extensions is missing"; return; } - // Use eglGetPlatformDisplayEXT() to get the display pointer - // if the implementation supports it. - egl_.display = - EglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, gbm_device_, nullptr); + egl_.display = EglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, + (void*)EGL_DEFAULT_DISPLAY, nullptr); + + if (egl_.display == EGL_NO_DISPLAY) { + RTC_LOG(LS_ERROR) << "Failed to obtain default EGL display: " + << FormatEGLError(EglGetError()) << "\n" + << "Defaulting to using first available render node"; + absl::optional render_node = GetRenderNode(); + if (!render_node) { + return; + } + + drm_fd_ = open(render_node->c_str(), O_RDWR); + + if (drm_fd_ < 0) { + RTC_LOG(LS_ERROR) << "Failed to open drm render node: " + << strerror(errno); + return; + } + + gbm_device_ = gbm_create_device(drm_fd_); + + if (!gbm_device_) { + RTC_LOG(LS_ERROR) << "Cannot create GBM device: " << strerror(errno); + close(drm_fd_); + return; + } + + // Use eglGetPlatformDisplayEXT() to get the display pointer + // if the implementation supports it. + egl_.display = + EglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, gbm_device_, nullptr); + } if (egl_.display == EGL_NO_DISPLAY) { RTC_LOG(LS_ERROR) << "Error during obtaining EGL display: " @@ -383,14 +392,8 @@ EglDmaBuf::EglDmaBuf() { return; } - const char* client_extensions_cstring_display = - EglQueryString(egl_.display, EGL_EXTENSIONS); - client_extensions_string = client_extensions_cstring_display; - - std::vector client_extensions = - rtc::split(client_extensions_string, ' '); - for (const auto& extension : client_extensions) { - egl_.extensions.push_back(std::string(extension)); + if (!GetClientExtensions(egl_.display, EGL_EXTENSIONS)) { + return; } bool has_image_dma_buf_import_modifiers_ext = false; @@ -421,6 +424,7 @@ RTC_NO_SANITIZE("cfi-icall") EglDmaBuf::~EglDmaBuf() { if (gbm_device_) { gbm_device_destroy(gbm_device_); + close(drm_fd_); } if (egl_.context != EGL_NO_CONTEXT) { @@ -440,6 +444,27 @@ EglDmaBuf::~EglDmaBuf() { // CloseLibrary(g_lib_gl); } +RTC_NO_SANITIZE("cfi-icall") +bool EglDmaBuf::GetClientExtensions(EGLDisplay dpy, EGLint name) { + // Get the list of client extensions + const char* client_extensions_cstring = EglQueryString(dpy, name); + if (!client_extensions_cstring) { + // If eglQueryString() returned NULL, the implementation doesn't support + // EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error. + RTC_LOG(LS_ERROR) << "No client extensions defined! " + << FormatEGLError(EglGetError()); + return false; + } + + std::vector client_extensions = + rtc::split(client_extensions_cstring, ' '); + for (const auto& extension : client_extensions) { + egl_.extensions.push_back(std::string(extension)); + } + + return true; +} + RTC_NO_SANITIZE("cfi-icall") std::unique_ptr EglDmaBuf::ImageFromDmaBuf( const DesktopSize& size, @@ -457,50 +482,92 @@ std::unique_ptr EglDmaBuf::ImageFromDmaBuf( return src; } - gbm_bo* imported; + EGLint attribs[47]; + int atti = 0; - if (modifier == DRM_FORMAT_MOD_INVALID) { - gbm_import_fd_data import_info = { - plane_datas[0].fd, static_cast(size.width()), - static_cast(size.height()), plane_datas[0].stride, - GBM_BO_FORMAT_ARGB8888}; + attribs[atti++] = EGL_WIDTH; + attribs[atti++] = static_cast(size.width()); + attribs[atti++] = EGL_HEIGHT; + attribs[atti++] = static_cast(size.height()); + attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; + attribs[atti++] = SpaPixelFormatToDrmFormat(format); - imported = gbm_bo_import(gbm_device_, GBM_BO_IMPORT_FD, &import_info, 0); - } else { - gbm_import_fd_modifier_data import_info = {}; - import_info.format = GBM_BO_FORMAT_ARGB8888; - import_info.width = static_cast(size.width()); - import_info.height = static_cast(size.height()); - import_info.num_fds = plane_datas.size(); - import_info.modifier = modifier; - for (uint32_t i = 0; i < plane_datas.size(); i++) { - import_info.fds[i] = plane_datas[i].fd; - import_info.offsets[i] = plane_datas[i].offset; - import_info.strides[i] = plane_datas[i].stride; + if (plane_datas.size() > 0) { + attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; + attribs[atti++] = plane_datas[0].fd; + attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; + attribs[atti++] = plane_datas[0].offset; + attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; + attribs[atti++] = plane_datas[0].stride; + + if (modifier != DRM_FORMAT_MOD_INVALID) { + attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; + attribs[atti++] = modifier & 0xFFFFFFFF; + attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; + attribs[atti++] = modifier >> 32; } - - imported = - gbm_bo_import(gbm_device_, GBM_BO_IMPORT_FD_MODIFIER, &import_info, 0); } - if (!imported) { - RTC_LOG(LS_ERROR) - << "Failed to process buffer: Cannot import passed GBM fd - " - << strerror(errno); - return src; + if (plane_datas.size() > 1) { + attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT; + attribs[atti++] = plane_datas[1].fd; + attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; + attribs[atti++] = plane_datas[1].offset; + attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; + attribs[atti++] = plane_datas[1].stride; + + if (modifier != DRM_FORMAT_MOD_INVALID) { + attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; + attribs[atti++] = modifier & 0xFFFFFFFF; + attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; + attribs[atti++] = modifier >> 32; + } } + if (plane_datas.size() > 2) { + attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT; + attribs[atti++] = plane_datas[2].fd; + attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; + attribs[atti++] = plane_datas[2].offset; + attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; + attribs[atti++] = plane_datas[2].stride; + + if (modifier != DRM_FORMAT_MOD_INVALID) { + attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; + attribs[atti++] = modifier & 0xFFFFFFFF; + attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; + attribs[atti++] = modifier >> 32; + } + } + + if (plane_datas.size() > 3) { + attribs[atti++] = EGL_DMA_BUF_PLANE3_FD_EXT; + attribs[atti++] = plane_datas[3].fd; + attribs[atti++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT; + attribs[atti++] = plane_datas[3].offset; + attribs[atti++] = EGL_DMA_BUF_PLANE3_PITCH_EXT; + attribs[atti++] = plane_datas[3].stride; + + if (modifier != DRM_FORMAT_MOD_INVALID) { + attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT; + attribs[atti++] = modifier & 0xFFFFFFFF; + attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT; + attribs[atti++] = modifier >> 32; + } + } + + attribs[atti++] = EGL_NONE; + // bind context to render thread EglMakeCurrent(egl_.display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_.context); - // create EGL image from imported BO + // create EGL image from attribute list EGLImageKHR image = EglCreateImageKHR( - egl_.display, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, imported, nullptr); + egl_.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs); if (image == EGL_NO_IMAGE) { RTC_LOG(LS_ERROR) << "Failed to record frame: Error creating EGLImage - " << FormatEGLError(EglGetError()); - gbm_bo_destroy(imported); return src; } @@ -527,12 +594,6 @@ std::unique_ptr EglDmaBuf::ImageFromDmaBuf( case SPA_VIDEO_FORMAT_BGRx: gl_format = GL_BGRA; break; - case SPA_VIDEO_FORMAT_RGB: - gl_format = GL_RGB; - break; - case SPA_VIDEO_FORMAT_BGR: - gl_format = GL_BGR; - break; default: gl_format = GL_BGRA; break; @@ -541,15 +602,12 @@ std::unique_ptr EglDmaBuf::ImageFromDmaBuf( if (GlGetError()) { RTC_LOG(LS_ERROR) << "Failed to get image from DMA buffer."; - gbm_bo_destroy(imported); return src; } GlDeleteTextures(1, &texture); EglDestroyImageKHR(egl_.display, image); - gbm_bo_destroy(imported); - return src; } diff --git a/modules/desktop_capture/linux/wayland/egl_dmabuf.h b/modules/desktop_capture/linux/wayland/egl_dmabuf.h index 75a8d81250..f1d96b2f80 100644 --- a/modules/desktop_capture/linux/wayland/egl_dmabuf.h +++ b/modules/desktop_capture/linux/wayland/egl_dmabuf.h @@ -51,6 +51,8 @@ class EglDmaBuf { bool IsEglInitialized() const { return egl_initialized_; } private: + bool GetClientExtensions(EGLDisplay dpy, EGLint name); + bool egl_initialized_ = false; bool has_image_dma_buf_import_ext_ = false; int32_t drm_fd_ = -1; // for GBM buffer mmap diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc index 92dbbc8c62..f49bf1e325 100644 --- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc +++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc @@ -104,21 +104,28 @@ spa_pod* BuildFormat(spa_pod_builder* builder, spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); if (modifiers.size()) { - spa_pod_builder_prop( - builder, SPA_FORMAT_VIDEO_modifier, - SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); - spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0); - // modifiers from the array - for (int64_t val : modifiers) { - spa_pod_builder_long(builder, val); - // Add the first modifier twice as the very first value is the default - // option - if (first) { + if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) { + spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY); + spa_pod_builder_long(builder, modifiers[0]); + } else { + spa_pod_builder_prop( + builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); + spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0); + + // modifiers from the array + for (int64_t val : modifiers) { spa_pod_builder_long(builder, val); - first = false; + // Add the first modifier twice as the very first value is the default + // option + if (first) { + spa_pod_builder_long(builder, val); + first = false; + } } + spa_pod_builder_pop(builder, &frames[1]); } - spa_pod_builder_pop(builder, &frames[1]); } spa_pod_builder_add( @@ -471,8 +478,12 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream( // Check if the PipeWire and DRM libraries are available. paths[kModulePipewire].push_back(kPipeWireLib); paths[kModuleDrm].push_back(kDrmLib); + if (!InitializeStubs(paths)) { - RTC_LOG(LS_ERROR) << "Failed to load the PipeWire library and symbols."; + RTC_LOG(LS_ERROR) + << "One of following libraries is missing on your system:\n" + << " - PipeWire (" << kPipeWireLib << ")\n" + << " - drm (" << kDrmLib << ")"; return false; } #endif // defined(WEBRTC_DLOPEN_PIPEWIRE)