• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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