1 /*
2 * Copyright 2021 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "modules/desktop_capture/linux/wayland/egl_dmabuf.h"
12
13 #include <asm/ioctl.h>
14 #include <dlfcn.h>
15 #include <fcntl.h>
16 #include <libdrm/drm_fourcc.h>
17 #include <linux/types.h>
18 #include <spa/param/video/format-utils.h>
19 #include <unistd.h>
20 #include <xf86drm.h>
21
22 #include "absl/memory/memory.h"
23 #include "absl/types/optional.h"
24 #include "rtc_base/checks.h"
25 #include "rtc_base/logging.h"
26 #include "rtc_base/sanitizer.h"
27 #include "rtc_base/string_encode.h"
28
29 namespace webrtc {
30
31 // EGL
32 typedef EGLBoolean (*eglBindAPI_func)(EGLenum api);
33 typedef EGLContext (*eglCreateContext_func)(EGLDisplay dpy,
34 EGLConfig config,
35 EGLContext share_context,
36 const EGLint* attrib_list);
37 typedef EGLBoolean (*eglDestroyContext_func)(EGLDisplay display,
38 EGLContext context);
39 typedef EGLBoolean (*eglTerminate_func)(EGLDisplay display);
40 typedef EGLImageKHR (*eglCreateImageKHR_func)(EGLDisplay dpy,
41 EGLContext ctx,
42 EGLenum target,
43 EGLClientBuffer buffer,
44 const EGLint* attrib_list);
45 typedef EGLBoolean (*eglDestroyImageKHR_func)(EGLDisplay dpy,
46 EGLImageKHR image);
47 typedef EGLint (*eglGetError_func)(void);
48 typedef void* (*eglGetProcAddress_func)(const char*);
49 typedef EGLDisplay (*eglGetPlatformDisplayEXT_func)(EGLenum platform,
50 void* native_display,
51 const EGLint* attrib_list);
52 typedef EGLDisplay (*eglGetPlatformDisplay_func)(EGLenum platform,
53 void* native_display,
54 const EGLAttrib* attrib_list);
55
56 typedef EGLBoolean (*eglInitialize_func)(EGLDisplay dpy,
57 EGLint* major,
58 EGLint* minor);
59 typedef EGLBoolean (*eglMakeCurrent_func)(EGLDisplay dpy,
60 EGLSurface draw,
61 EGLSurface read,
62 EGLContext ctx);
63 typedef EGLBoolean (*eglQueryDmaBufFormatsEXT_func)(EGLDisplay dpy,
64 EGLint max_formats,
65 EGLint* formats,
66 EGLint* num_formats);
67 typedef EGLBoolean (*eglQueryDmaBufModifiersEXT_func)(EGLDisplay dpy,
68 EGLint format,
69 EGLint max_modifiers,
70 EGLuint64KHR* modifiers,
71 EGLBoolean* external_only,
72 EGLint* num_modifiers);
73 typedef const char* (*eglQueryString_func)(EGLDisplay dpy, EGLint name);
74 typedef void (*glEGLImageTargetTexture2DOES_func)(GLenum target,
75 GLeglImageOES image);
76
77 // This doesn't follow naming conventions in WebRTC, where the naming
78 // should look like e.g. egl_bind_api instead of EglBindAPI, however
79 // we named them according to the exported functions they map to for
80 // consistency.
81 eglBindAPI_func EglBindAPI = nullptr;
82 eglCreateContext_func EglCreateContext = nullptr;
83 eglDestroyContext_func EglDestroyContext = nullptr;
84 eglTerminate_func EglTerminate = nullptr;
85 eglCreateImageKHR_func EglCreateImageKHR = nullptr;
86 eglDestroyImageKHR_func EglDestroyImageKHR = nullptr;
87 eglGetError_func EglGetError = nullptr;
88 eglGetProcAddress_func EglGetProcAddress = nullptr;
89 eglGetPlatformDisplayEXT_func EglGetPlatformDisplayEXT = nullptr;
90 eglGetPlatformDisplay_func EglGetPlatformDisplay = nullptr;
91 eglInitialize_func EglInitialize = nullptr;
92 eglMakeCurrent_func EglMakeCurrent = nullptr;
93 eglQueryDmaBufFormatsEXT_func EglQueryDmaBufFormatsEXT = nullptr;
94 eglQueryDmaBufModifiersEXT_func EglQueryDmaBufModifiersEXT = nullptr;
95 eglQueryString_func EglQueryString = nullptr;
96 glEGLImageTargetTexture2DOES_func GlEGLImageTargetTexture2DOES = nullptr;
97
98 // GL
99 typedef void (*glBindTexture_func)(GLenum target, GLuint texture);
100 typedef void (*glDeleteTextures_func)(GLsizei n, const GLuint* textures);
101 typedef void (*glGenTextures_func)(GLsizei n, GLuint* textures);
102 typedef GLenum (*glGetError_func)(void);
103 typedef const GLubyte* (*glGetString_func)(GLenum name);
104 typedef void (*glGetTexImage_func)(GLenum target,
105 GLint level,
106 GLenum format,
107 GLenum type,
108 void* pixels);
109 typedef void (*glTexParameteri_func)(GLenum target, GLenum pname, GLint param);
110 typedef void* (*glXGetProcAddressARB_func)(const char*);
111
112 // This doesn't follow naming conventions in WebRTC, where the naming
113 // should look like e.g. egl_bind_api instead of EglBindAPI, however
114 // we named them according to the exported functions they map to for
115 // consistency.
116 glBindTexture_func GlBindTexture = nullptr;
117 glDeleteTextures_func GlDeleteTextures = nullptr;
118 glGenTextures_func GlGenTextures = nullptr;
119 glGetError_func GlGetError = nullptr;
120 glGetString_func GlGetString = nullptr;
121 glGetTexImage_func GlGetTexImage = nullptr;
122 glTexParameteri_func GlTexParameteri = nullptr;
123 glXGetProcAddressARB_func GlXGetProcAddressARB = nullptr;
124
FormatGLError(GLenum err)125 static const std::string FormatGLError(GLenum err) {
126 switch (err) {
127 case GL_NO_ERROR:
128 return "GL_NO_ERROR";
129 case GL_INVALID_ENUM:
130 return "GL_INVALID_ENUM";
131 case GL_INVALID_VALUE:
132 return "GL_INVALID_VALUE";
133 case GL_INVALID_OPERATION:
134 return "GL_INVALID_OPERATION";
135 case GL_STACK_OVERFLOW:
136 return "GL_STACK_OVERFLOW";
137 case GL_STACK_UNDERFLOW:
138 return "GL_STACK_UNDERFLOW";
139 case GL_OUT_OF_MEMORY:
140 return "GL_OUT_OF_MEMORY";
141 default:
142 return "GL error code: " + std::to_string(err);
143 }
144 }
145
FormatEGLError(EGLint err)146 static const std::string FormatEGLError(EGLint err) {
147 switch (err) {
148 case EGL_NOT_INITIALIZED:
149 return "EGL_NOT_INITIALIZED";
150 case EGL_BAD_ACCESS:
151 return "EGL_BAD_ACCESS";
152 case EGL_BAD_ALLOC:
153 return "EGL_BAD_ALLOC";
154 case EGL_BAD_ATTRIBUTE:
155 return "EGL_BAD_ATTRIBUTE";
156 case EGL_BAD_CONTEXT:
157 return "EGL_BAD_CONTEXT";
158 case EGL_BAD_CONFIG:
159 return "EGL_BAD_CONFIG";
160 case EGL_BAD_CURRENT_SURFACE:
161 return "EGL_BAD_CURRENT_SURFACE";
162 case EGL_BAD_DISPLAY:
163 return "EGL_BAD_DISPLAY";
164 case EGL_BAD_SURFACE:
165 return "EGL_BAD_SURFACE";
166 case EGL_BAD_MATCH:
167 return "EGL_BAD_MATCH";
168 case EGL_BAD_PARAMETER:
169 return "EGL_BAD_PARAMETER";
170 case EGL_BAD_NATIVE_PIXMAP:
171 return "EGL_BAD_NATIVE_PIXMAP";
172 case EGL_BAD_NATIVE_WINDOW:
173 return "EGL_BAD_NATIVE_WINDOW";
174 case EGL_CONTEXT_LOST:
175 return "EGL_CONTEXT_LOST";
176 default:
177 return "EGL error code: " + std::to_string(err);
178 }
179 }
180
SpaPixelFormatToDrmFormat(uint32_t spa_format)181 static uint32_t SpaPixelFormatToDrmFormat(uint32_t spa_format) {
182 switch (spa_format) {
183 case SPA_VIDEO_FORMAT_RGBA:
184 return DRM_FORMAT_ABGR8888;
185 case SPA_VIDEO_FORMAT_RGBx:
186 return DRM_FORMAT_XBGR8888;
187 case SPA_VIDEO_FORMAT_BGRA:
188 return DRM_FORMAT_ARGB8888;
189 case SPA_VIDEO_FORMAT_BGRx:
190 return DRM_FORMAT_XRGB8888;
191 default:
192 return DRM_FORMAT_INVALID;
193 }
194 }
195
CloseLibrary(void * library)196 static void CloseLibrary(void* library) {
197 if (library) {
198 dlclose(library);
199 library = nullptr;
200 }
201 }
202
203 static void* g_lib_egl = nullptr;
204
205 RTC_NO_SANITIZE("cfi-icall")
OpenEGL()206 static bool OpenEGL() {
207 g_lib_egl = dlopen("libEGL.so.1", RTLD_NOW | RTLD_GLOBAL);
208 if (g_lib_egl) {
209 EglGetProcAddress =
210 (eglGetProcAddress_func)dlsym(g_lib_egl, "eglGetProcAddress");
211 return EglGetProcAddress;
212 }
213
214 return false;
215 }
216
217 RTC_NO_SANITIZE("cfi-icall")
LoadEGL()218 static bool LoadEGL() {
219 if (OpenEGL()) {
220 EglBindAPI = (eglBindAPI_func)EglGetProcAddress("eglBindAPI");
221 EglCreateContext =
222 (eglCreateContext_func)EglGetProcAddress("eglCreateContext");
223 EglDestroyContext =
224 (eglDestroyContext_func)EglGetProcAddress("eglDestroyContext");
225 EglTerminate = (eglTerminate_func)EglGetProcAddress("eglTerminate");
226 EglCreateImageKHR =
227 (eglCreateImageKHR_func)EglGetProcAddress("eglCreateImageKHR");
228 EglDestroyImageKHR =
229 (eglDestroyImageKHR_func)EglGetProcAddress("eglDestroyImageKHR");
230 EglGetError = (eglGetError_func)EglGetProcAddress("eglGetError");
231 EglGetPlatformDisplayEXT = (eglGetPlatformDisplayEXT_func)EglGetProcAddress(
232 "eglGetPlatformDisplayEXT");
233 EglGetPlatformDisplay =
234 (eglGetPlatformDisplay_func)EglGetProcAddress("eglGetPlatformDisplay");
235 EglInitialize = (eglInitialize_func)EglGetProcAddress("eglInitialize");
236 EglMakeCurrent = (eglMakeCurrent_func)EglGetProcAddress("eglMakeCurrent");
237 EglQueryString = (eglQueryString_func)EglGetProcAddress("eglQueryString");
238 GlEGLImageTargetTexture2DOES =
239 (glEGLImageTargetTexture2DOES_func)EglGetProcAddress(
240 "glEGLImageTargetTexture2DOES");
241
242 return EglBindAPI && EglCreateContext && EglCreateImageKHR &&
243 EglTerminate && EglDestroyContext && EglDestroyImageKHR &&
244 EglGetError && EglGetPlatformDisplayEXT && EglGetPlatformDisplay &&
245 EglInitialize && EglMakeCurrent && EglQueryString &&
246 GlEGLImageTargetTexture2DOES;
247 }
248
249 return false;
250 }
251
252 static void* g_lib_gl = nullptr;
253
254 RTC_NO_SANITIZE("cfi-icall")
OpenGL()255 static bool OpenGL() {
256 std::vector<std::string> names = {"libGL.so.1", "libGL.so"};
257 for (const std::string& name : names) {
258 g_lib_gl = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL);
259 if (g_lib_gl) {
260 GlXGetProcAddressARB =
261 (glXGetProcAddressARB_func)dlsym(g_lib_gl, "glXGetProcAddressARB");
262 return GlXGetProcAddressARB;
263 }
264 }
265
266 return false;
267 }
268
269 RTC_NO_SANITIZE("cfi-icall")
LoadGL()270 static bool LoadGL() {
271 if (OpenGL()) {
272 GlGetString = (glGetString_func)GlXGetProcAddressARB("glGetString");
273 if (!GlGetString) {
274 return false;
275 }
276
277 GlBindTexture = (glBindTexture_func)GlXGetProcAddressARB("glBindTexture");
278 GlDeleteTextures =
279 (glDeleteTextures_func)GlXGetProcAddressARB("glDeleteTextures");
280 GlGenTextures = (glGenTextures_func)GlXGetProcAddressARB("glGenTextures");
281 GlGetError = (glGetError_func)GlXGetProcAddressARB("glGetError");
282 GlGetTexImage = (glGetTexImage_func)GlXGetProcAddressARB("glGetTexImage");
283 GlTexParameteri =
284 (glTexParameteri_func)GlXGetProcAddressARB("glTexParameteri");
285
286 return GlBindTexture && GlDeleteTextures && GlGenTextures && GlGetError &&
287 GlGetTexImage && GlTexParameteri;
288 }
289
290 return false;
291 }
292
293 RTC_NO_SANITIZE("cfi-icall")
EglDmaBuf()294 EglDmaBuf::EglDmaBuf() {
295 if (!LoadEGL()) {
296 RTC_LOG(LS_ERROR) << "Unable to load EGL entry functions.";
297 CloseLibrary(g_lib_egl);
298 return;
299 }
300
301 if (!LoadGL()) {
302 RTC_LOG(LS_ERROR) << "Failed to load OpenGL entry functions.";
303 CloseLibrary(g_lib_gl);
304 return;
305 }
306
307 if (!GetClientExtensions(EGL_NO_DISPLAY, EGL_EXTENSIONS)) {
308 return;
309 }
310
311 bool has_platform_base_ext = false;
312 bool has_platform_gbm_ext = false;
313 bool has_khr_platform_gbm_ext = false;
314
315 for (const auto& extension : egl_.extensions) {
316 if (extension == "EGL_EXT_platform_base") {
317 has_platform_base_ext = true;
318 continue;
319 } else if (extension == "EGL_MESA_platform_gbm") {
320 has_platform_gbm_ext = true;
321 continue;
322 } else if (extension == "EGL_KHR_platform_gbm") {
323 has_khr_platform_gbm_ext = true;
324 continue;
325 }
326 }
327
328 if (!has_platform_base_ext || !has_platform_gbm_ext ||
329 !has_khr_platform_gbm_ext) {
330 RTC_LOG(LS_ERROR) << "One of required EGL extensions is missing";
331 return;
332 }
333
334 egl_.display = EglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR,
335 (void*)EGL_DEFAULT_DISPLAY, nullptr);
336
337 if (egl_.display == EGL_NO_DISPLAY) {
338 RTC_LOG(LS_ERROR) << "Failed to obtain default EGL display: "
339 << FormatEGLError(EglGetError()) << "\n"
340 << "Defaulting to using first available render node";
341 absl::optional<std::string> render_node = GetRenderNode();
342 if (!render_node) {
343 return;
344 }
345
346 drm_fd_ = open(render_node->c_str(), O_RDWR);
347
348 if (drm_fd_ < 0) {
349 RTC_LOG(LS_ERROR) << "Failed to open drm render node: "
350 << strerror(errno);
351 return;
352 }
353
354 gbm_device_ = gbm_create_device(drm_fd_);
355
356 if (!gbm_device_) {
357 RTC_LOG(LS_ERROR) << "Cannot create GBM device: " << strerror(errno);
358 close(drm_fd_);
359 return;
360 }
361
362 // Use eglGetPlatformDisplayEXT() to get the display pointer
363 // if the implementation supports it.
364 egl_.display =
365 EglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, gbm_device_, nullptr);
366 }
367
368 if (egl_.display == EGL_NO_DISPLAY) {
369 RTC_LOG(LS_ERROR) << "Error during obtaining EGL display: "
370 << FormatEGLError(EglGetError());
371 return;
372 }
373
374 EGLint major, minor;
375 if (EglInitialize(egl_.display, &major, &minor) == EGL_FALSE) {
376 RTC_LOG(LS_ERROR) << "Error during eglInitialize: "
377 << FormatEGLError(EglGetError());
378 return;
379 }
380
381 if (EglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
382 RTC_LOG(LS_ERROR) << "bind OpenGL API failed";
383 return;
384 }
385
386 egl_.context =
387 EglCreateContext(egl_.display, nullptr, EGL_NO_CONTEXT, nullptr);
388
389 if (egl_.context == EGL_NO_CONTEXT) {
390 RTC_LOG(LS_ERROR) << "Couldn't create EGL context: "
391 << FormatGLError(EglGetError());
392 return;
393 }
394
395 if (!GetClientExtensions(egl_.display, EGL_EXTENSIONS)) {
396 return;
397 }
398
399 bool has_image_dma_buf_import_modifiers_ext = false;
400
401 for (const auto& extension : egl_.extensions) {
402 if (extension == "EGL_EXT_image_dma_buf_import") {
403 has_image_dma_buf_import_ext_ = true;
404 continue;
405 } else if (extension == "EGL_EXT_image_dma_buf_import_modifiers") {
406 has_image_dma_buf_import_modifiers_ext = true;
407 continue;
408 }
409 }
410
411 if (has_image_dma_buf_import_ext_ && has_image_dma_buf_import_modifiers_ext) {
412 EglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func)EglGetProcAddress(
413 "eglQueryDmaBufFormatsEXT");
414 EglQueryDmaBufModifiersEXT =
415 (eglQueryDmaBufModifiersEXT_func)EglGetProcAddress(
416 "eglQueryDmaBufModifiersEXT");
417 }
418
419 RTC_LOG(LS_INFO) << "Egl initialization succeeded";
420 egl_initialized_ = true;
421 }
422
423 RTC_NO_SANITIZE("cfi-icall")
~EglDmaBuf()424 EglDmaBuf::~EglDmaBuf() {
425 if (gbm_device_) {
426 gbm_device_destroy(gbm_device_);
427 close(drm_fd_);
428 }
429
430 if (egl_.context != EGL_NO_CONTEXT) {
431 EglDestroyContext(egl_.display, egl_.context);
432 }
433
434 if (egl_.display != EGL_NO_DISPLAY) {
435 EglTerminate(egl_.display);
436 }
437
438 // BUG: crbug.com/1290566
439 // Closing libEGL.so.1 when using NVidia drivers causes a crash
440 // when EglGetPlatformDisplayEXT() is used, at least this one is enough
441 // to be called to make it crash.
442 // It also looks that libepoxy and glad don't dlclose it either
443 // CloseLibrary(g_lib_egl);
444 // CloseLibrary(g_lib_gl);
445 }
446
447 RTC_NO_SANITIZE("cfi-icall")
GetClientExtensions(EGLDisplay dpy,EGLint name)448 bool EglDmaBuf::GetClientExtensions(EGLDisplay dpy, EGLint name) {
449 // Get the list of client extensions
450 const char* client_extensions_cstring = EglQueryString(dpy, name);
451 if (!client_extensions_cstring) {
452 // If eglQueryString() returned NULL, the implementation doesn't support
453 // EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error.
454 RTC_LOG(LS_ERROR) << "No client extensions defined! "
455 << FormatEGLError(EglGetError());
456 return false;
457 }
458
459 std::vector<absl::string_view> client_extensions =
460 rtc::split(client_extensions_cstring, ' ');
461 for (const auto& extension : client_extensions) {
462 egl_.extensions.push_back(std::string(extension));
463 }
464
465 return true;
466 }
467
468 RTC_NO_SANITIZE("cfi-icall")
ImageFromDmaBuf(const DesktopSize & size,uint32_t format,const std::vector<PlaneData> & plane_datas,uint64_t modifier)469 std::unique_ptr<uint8_t[]> EglDmaBuf::ImageFromDmaBuf(
470 const DesktopSize& size,
471 uint32_t format,
472 const std::vector<PlaneData>& plane_datas,
473 uint64_t modifier) {
474 std::unique_ptr<uint8_t[]> src;
475
476 if (!egl_initialized_) {
477 return src;
478 }
479
480 if (plane_datas.size() <= 0) {
481 RTC_LOG(LS_ERROR) << "Failed to process buffer: invalid number of planes";
482 return src;
483 }
484
485 EGLint attribs[47];
486 int atti = 0;
487
488 attribs[atti++] = EGL_WIDTH;
489 attribs[atti++] = static_cast<EGLint>(size.width());
490 attribs[atti++] = EGL_HEIGHT;
491 attribs[atti++] = static_cast<EGLint>(size.height());
492 attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
493 attribs[atti++] = SpaPixelFormatToDrmFormat(format);
494
495 if (plane_datas.size() > 0) {
496 attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
497 attribs[atti++] = plane_datas[0].fd;
498 attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
499 attribs[atti++] = plane_datas[0].offset;
500 attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
501 attribs[atti++] = plane_datas[0].stride;
502
503 if (modifier != DRM_FORMAT_MOD_INVALID) {
504 attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
505 attribs[atti++] = modifier & 0xFFFFFFFF;
506 attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
507 attribs[atti++] = modifier >> 32;
508 }
509 }
510
511 if (plane_datas.size() > 1) {
512 attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT;
513 attribs[atti++] = plane_datas[1].fd;
514 attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
515 attribs[atti++] = plane_datas[1].offset;
516 attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
517 attribs[atti++] = plane_datas[1].stride;
518
519 if (modifier != DRM_FORMAT_MOD_INVALID) {
520 attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
521 attribs[atti++] = modifier & 0xFFFFFFFF;
522 attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
523 attribs[atti++] = modifier >> 32;
524 }
525 }
526
527 if (plane_datas.size() > 2) {
528 attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT;
529 attribs[atti++] = plane_datas[2].fd;
530 attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
531 attribs[atti++] = plane_datas[2].offset;
532 attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
533 attribs[atti++] = plane_datas[2].stride;
534
535 if (modifier != DRM_FORMAT_MOD_INVALID) {
536 attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
537 attribs[atti++] = modifier & 0xFFFFFFFF;
538 attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
539 attribs[atti++] = modifier >> 32;
540 }
541 }
542
543 if (plane_datas.size() > 3) {
544 attribs[atti++] = EGL_DMA_BUF_PLANE3_FD_EXT;
545 attribs[atti++] = plane_datas[3].fd;
546 attribs[atti++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT;
547 attribs[atti++] = plane_datas[3].offset;
548 attribs[atti++] = EGL_DMA_BUF_PLANE3_PITCH_EXT;
549 attribs[atti++] = plane_datas[3].stride;
550
551 if (modifier != DRM_FORMAT_MOD_INVALID) {
552 attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT;
553 attribs[atti++] = modifier & 0xFFFFFFFF;
554 attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT;
555 attribs[atti++] = modifier >> 32;
556 }
557 }
558
559 attribs[atti++] = EGL_NONE;
560
561 // bind context to render thread
562 EglMakeCurrent(egl_.display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_.context);
563
564 // create EGL image from attribute list
565 EGLImageKHR image = EglCreateImageKHR(
566 egl_.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs);
567
568 if (image == EGL_NO_IMAGE) {
569 RTC_LOG(LS_ERROR) << "Failed to record frame: Error creating EGLImage - "
570 << FormatEGLError(EglGetError());
571 return src;
572 }
573
574 // create GL 2D texture for framebuffer
575 GLuint texture;
576 GlGenTextures(1, &texture);
577 GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
578 GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
579 GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
580 GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
581 GlBindTexture(GL_TEXTURE_2D, texture);
582 GlEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
583
584 src = std::make_unique<uint8_t[]>(plane_datas[0].stride * size.height());
585
586 GLenum gl_format = GL_BGRA;
587 switch (format) {
588 case SPA_VIDEO_FORMAT_RGBx:
589 gl_format = GL_RGBA;
590 break;
591 case SPA_VIDEO_FORMAT_RGBA:
592 gl_format = GL_RGBA;
593 break;
594 case SPA_VIDEO_FORMAT_BGRx:
595 gl_format = GL_BGRA;
596 break;
597 default:
598 gl_format = GL_BGRA;
599 break;
600 }
601 GlGetTexImage(GL_TEXTURE_2D, 0, gl_format, GL_UNSIGNED_BYTE, src.get());
602
603 if (GlGetError()) {
604 RTC_LOG(LS_ERROR) << "Failed to get image from DMA buffer.";
605 return src;
606 }
607
608 GlDeleteTextures(1, &texture);
609 EglDestroyImageKHR(egl_.display, image);
610
611 return src;
612 }
613
614 RTC_NO_SANITIZE("cfi-icall")
QueryDmaBufModifiers(uint32_t format)615 std::vector<uint64_t> EglDmaBuf::QueryDmaBufModifiers(uint32_t format) {
616 if (!egl_initialized_) {
617 return {};
618 }
619
620 // Explicit modifiers not supported, return just DRM_FORMAT_MOD_INVALID as we
621 // can still use modifier-less DMA-BUFs if we have required extension
622 if (EglQueryDmaBufFormatsEXT == nullptr ||
623 EglQueryDmaBufModifiersEXT == nullptr) {
624 return has_image_dma_buf_import_ext_
625 ? std::vector<uint64_t>{DRM_FORMAT_MOD_INVALID}
626 : std::vector<uint64_t>{};
627 }
628
629 uint32_t drm_format = SpaPixelFormatToDrmFormat(format);
630 // Should never happen as it's us who controls the list of supported formats
631 RTC_DCHECK(drm_format != DRM_FORMAT_INVALID);
632
633 EGLint count = 0;
634 EGLBoolean success =
635 EglQueryDmaBufFormatsEXT(egl_.display, 0, nullptr, &count);
636
637 if (!success || !count) {
638 RTC_LOG(LS_WARNING) << "Cannot query the number of formats.";
639 return {DRM_FORMAT_MOD_INVALID};
640 }
641
642 std::vector<uint32_t> formats(count);
643 if (!EglQueryDmaBufFormatsEXT(egl_.display, count,
644 reinterpret_cast<EGLint*>(formats.data()),
645 &count)) {
646 RTC_LOG(LS_WARNING) << "Cannot query a list of formats.";
647 return {DRM_FORMAT_MOD_INVALID};
648 }
649
650 if (std::find(formats.begin(), formats.end(), drm_format) == formats.end()) {
651 RTC_LOG(LS_WARNING) << "Format " << drm_format
652 << " not supported for modifiers.";
653 return {DRM_FORMAT_MOD_INVALID};
654 }
655
656 success = EglQueryDmaBufModifiersEXT(egl_.display, drm_format, 0, nullptr,
657 nullptr, &count);
658
659 if (!success || !count) {
660 RTC_LOG(LS_WARNING) << "Cannot query the number of modifiers.";
661 return {DRM_FORMAT_MOD_INVALID};
662 }
663
664 std::vector<uint64_t> modifiers(count);
665 if (!EglQueryDmaBufModifiersEXT(egl_.display, drm_format, count,
666 modifiers.data(), nullptr, &count)) {
667 RTC_LOG(LS_WARNING) << "Cannot query a list of modifiers.";
668 }
669
670 // Support modifier-less buffers
671 modifiers.push_back(DRM_FORMAT_MOD_INVALID);
672 return modifiers;
673 }
674
GetRenderNode()675 absl::optional<std::string> EglDmaBuf::GetRenderNode() {
676 int max_devices = drmGetDevices2(0, nullptr, 0);
677 if (max_devices <= 0) {
678 RTC_LOG(LS_ERROR) << "drmGetDevices2() has not found any devices (errno="
679 << -max_devices << ")";
680 return absl::nullopt;
681 }
682
683 std::vector<drmDevicePtr> devices(max_devices);
684 int ret = drmGetDevices2(0, devices.data(), max_devices);
685 if (ret < 0) {
686 RTC_LOG(LS_ERROR) << "drmGetDevices2() returned an error " << ret;
687 return absl::nullopt;
688 }
689
690 std::string render_node;
691
692 for (const drmDevicePtr& device : devices) {
693 if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
694 render_node = device->nodes[DRM_NODE_RENDER];
695 break;
696 }
697 }
698
699 drmFreeDevices(devices.data(), ret);
700 return render_node;
701 }
702
703 } // namespace webrtc
704