Reland "PipeWire capturer: implement proper DMA-BUFs support""

This is a reland of f2177f6612079ccce9c320ea7e77bc934c684f5c

Original change's description:
> PipeWire capturer: implement proper DMA-BUFs support
>
> Currently both KWin (KDE) and Mutter (GNOME) window managers don't
> use DMA-BUFs by default, but only when client asks specifically for
> them (KWin) or when experimental DMA-BUF support is enabled (Mutter).
> While current implementation works just fine on integrated graphics
> cards, it causes issues on dedicated GPUs (AMD and NVidia) where the
> code either crashes or screensharing is slow and unusable.
>
> To fix this, DMA-BUFs has to be opened using OpenGL context and not
> being directly mmaped(). This implementation requires to use DMA-BUF
> modifiers, as they are now mandatory for DMA-BUFs usage.
>
> Documentation for this behavior can be found here:
> https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/doc/dma-buf.dox
>
> Bug: chromium:1233417
> Change-Id: I0cecf16d6bb0f576954b9e8f071cab526f7baf2c
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227022
> Commit-Queue: Tommi <tommi@webrtc.org>
> Reviewed-by: Tommi <tommi@webrtc.org>
> Reviewed-by: Erik Språng <sprang@webrtc.org>
> Cr-Commit-Position: refs/heads/main@{#34889}

Bug: chromium:1233417
Change-Id: I308501d86ec18ab6df9bcee569c4b72df7926549
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/231180
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Tommi <tommi@webrtc.org>
Commit-Queue: Tommi <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35152}
diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn
index 91c8d0f..787a5c8 100644
--- a/modules/desktop_capture/BUILD.gn
+++ b/modules/desktop_capture/BUILD.gn
@@ -222,6 +222,23 @@
       }
     }
 
+    pkg_config("gbm") {
+      packages = [ "gbm" ]
+    }
+    pkg_config("egl") {
+      packages = [ "egl" ]
+    }
+    pkg_config("epoxy") {
+      packages = [ "epoxy" ]
+      ignore_libs = true
+    }
+    pkg_config("libdrm") {
+      packages = [ "libdrm" ]
+      if (!rtc_link_pipewire) {
+        ignore_libs = true
+      }
+    }
+
     if (!rtc_link_pipewire) {
       # When libpipewire is not directly linked, use stubs to allow for dlopening of
       # the binary.
@@ -229,6 +246,7 @@
         configs = [
           "../../:common_config",
           ":pipewire",
+          ":libdrm",
         ]
         deps = [ "../../rtc_base" ]
         extra_header = "linux/pipewire_stub_header.fragment"
@@ -236,7 +254,10 @@
         logging_include = "rtc_base/logging.h"
         output_name = "linux/pipewire_stubs"
         path_from_source = "modules/desktop_capture/linux"
-        sigs = [ "linux/pipewire03.sigs" ]
+        sigs = [
+          "linux/pipewire.sigs",
+          "linux/drm.sigs",
+        ]
       }
     }
 
@@ -542,17 +563,25 @@
     sources += [
       "linux/base_capturer_pipewire.cc",
       "linux/base_capturer_pipewire.h",
+      "linux/egl_dmabuf.cc",
+      "linux/egl_dmabuf.h",
     ]
 
     configs += [
       ":pipewire_config",
       ":gio",
       ":pipewire",
+      ":gbm",
+      ":egl",
+      ":epoxy",
+      ":libdrm",
     ]
 
     if (!rtc_link_pipewire) {
       deps += [ ":pipewire_stubs" ]
     }
+
+    deps += [ "../../rtc_base:sanitizer" ]
   }
 
   if (rtc_enable_win_wgc) {
diff --git a/modules/desktop_capture/linux/base_capturer_pipewire.cc b/modules/desktop_capture/linux/base_capturer_pipewire.cc
index 8c39772..2d5e973 100644
--- a/modules/desktop_capture/linux/base_capturer_pipewire.cc
+++ b/modules/desktop_capture/linux/base_capturer_pipewire.cc
@@ -14,12 +14,13 @@
 #include <glib-object.h>
 #include <spa/param/format-utils.h>
 #include <spa/param/props.h>
-
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/syscall.h>
+#include <unistd.h>
 
 #include <memory>
+#include <string>
 #include <utility>
 
 #include "absl/memory/memory.h"
@@ -27,11 +28,13 @@
 #include "modules/desktop_capture/desktop_capturer.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
+#include "rtc_base/string_encode.h"
 
 #if defined(WEBRTC_DLOPEN_PIPEWIRE)
 #include "modules/desktop_capture/linux/pipewire_stubs.h"
 using modules_desktop_capture_linux::InitializeStubs;
-using modules_desktop_capture_linux::kModulePipewire03;
+using modules_desktop_capture_linux::kModuleDrm;
+using modules_desktop_capture_linux::kModulePipewire;
 using modules_desktop_capture_linux::StubPathMap;
 #endif  // defined(WEBRTC_DLOPEN_PIPEWIRE)
 
@@ -49,67 +52,116 @@
 
 #if defined(WEBRTC_DLOPEN_PIPEWIRE)
 const char kPipeWireLib[] = "libpipewire-0.3.so.0";
+const char kDrmLib[] = "libdrm.so.2";
 #endif
 
-// static
-struct dma_buf_sync {
-  uint64_t flags;
+#if !PW_CHECK_VERSION(0, 3, 29)
+#define SPA_POD_PROP_FLAG_MANDATORY (1u << 3)
+#endif
+#if !PW_CHECK_VERSION(0, 3, 33)
+#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4)
+#endif
+
+struct pw_version {
+  int major = 0;
+  int minor = 0;
+  int micro = 0;
 };
-#define DMA_BUF_SYNC_READ (1 << 0)
-#define DMA_BUF_SYNC_START (0 << 2)
-#define DMA_BUF_SYNC_END (1 << 2)
-#define DMA_BUF_BASE 'b'
-#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
 
-static void SyncDmaBuf(int fd, uint64_t start_or_end) {
-  struct dma_buf_sync sync = {0};
+bool CheckPipeWireVersion(pw_version required_version) {
+  std::vector<std::string> parsed_version;
+  std::string version_string = pw_get_library_version();
+  rtc::split(version_string, '.', &parsed_version);
 
-  sync.flags = start_or_end | DMA_BUF_SYNC_READ;
-
-  while (true) {
-    int ret;
-    ret = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
-    if (ret == -1 && errno == EINTR) {
-      continue;
-    } else if (ret == -1) {
-      RTC_LOG(LS_ERROR) << "Failed to synchronize DMA buffer: "
-                        << g_strerror(errno);
-      break;
-    } else {
-      break;
-    }
+  if (parsed_version.size() != 3) {
+    return false;
   }
+
+  pw_version current_version = {std::stoi(parsed_version.at(0)),
+                                std::stoi(parsed_version.at(1)),
+                                std::stoi(parsed_version.at(2))};
+
+  return (current_version.major > required_version.major) ||
+         (current_version.major == required_version.major &&
+          current_version.minor > required_version.minor) ||
+         (current_version.major == required_version.major &&
+          current_version.minor == required_version.minor &&
+          current_version.micro >= required_version.micro);
+}
+
+spa_pod* BuildFormat(spa_pod_builder* builder,
+                     uint32_t format,
+                     const std::vector<uint64_t>& modifiers) {
+  bool first = true;
+  spa_pod_frame frames[2];
+  spa_rectangle pw_min_screen_bounds = spa_rectangle{1, 1};
+  spa_rectangle pw_max_screen_bounds = spa_rectangle{UINT32_MAX, UINT32_MAX};
+
+  spa_pod_builder_push_object(builder, &frames[0], SPA_TYPE_OBJECT_Format,
+                              SPA_PARAM_EnumFormat);
+  spa_pod_builder_add(builder, SPA_FORMAT_mediaType,
+                      SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
+  spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype,
+                      SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0);
+  spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
+
+  if (modifiers.size()) {
+    // SPA_POD_PROP_FLAG_DONT_FIXATE can be used with PipeWire >= 0.3.33
+    if (CheckPipeWireVersion(pw_version{0, 3, 33})) {
+      spa_pod_builder_prop(
+          builder, SPA_FORMAT_VIDEO_modifier,
+          SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
+    } else {
+      spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier,
+                           SPA_POD_PROP_FLAG_MANDATORY);
+    }
+    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) {
+        spa_pod_builder_long(builder, val);
+        first = false;
+      }
+    }
+    spa_pod_builder_pop(builder, &frames[1]);
+  }
+
+  spa_pod_builder_add(
+      builder, SPA_FORMAT_VIDEO_size,
+      SPA_POD_CHOICE_RANGE_Rectangle(
+          &pw_min_screen_bounds, &pw_min_screen_bounds, &pw_max_screen_bounds),
+      0);
+
+  return static_cast<spa_pod*>(spa_pod_builder_pop(builder, &frames[0]));
 }
 
 class ScopedBuf {
  public:
   ScopedBuf() {}
-  ScopedBuf(unsigned char* map, int map_size, bool is_dma_buf, int fd)
-      : map_(map), map_size_(map_size), is_dma_buf_(is_dma_buf), fd_(fd) {}
+  ScopedBuf(uint8_t* map, int map_size, int fd)
+      : map_(map), map_size_(map_size), fd_(fd) {}
   ~ScopedBuf() {
     if (map_ != MAP_FAILED) {
-      if (is_dma_buf_) {
-        SyncDmaBuf(fd_, DMA_BUF_SYNC_END);
-      }
       munmap(map_, map_size_);
     }
   }
 
   operator bool() { return map_ != MAP_FAILED; }
 
-  void initialize(unsigned char* map, int map_size, bool is_dma_buf, int fd) {
+  void initialize(uint8_t* map, int map_size, int fd) {
     map_ = map;
     map_size_ = map_size;
-    is_dma_buf_ = is_dma_buf;
     fd_ = fd;
   }
 
-  unsigned char* get() { return map_; }
+  uint8_t* get() { return map_; }
 
  protected:
-  unsigned char* map_ = nullptr;
+  uint8_t* map_ = static_cast<uint8_t*>(MAP_FAILED);
   int map_size_;
-  bool is_dma_buf_;
   int fd_;
 };
 
@@ -234,17 +286,26 @@
   auto size = height * stride;
 
   that->desktop_size_ = DesktopSize(width, height);
+#if PW_CHECK_VERSION(0, 3, 0)
+  that->modifier_ = that->spa_video_format_.modifier;
+#endif
 
   uint8_t buffer[1024] = {};
   auto builder = spa_pod_builder{buffer, sizeof(buffer)};
 
   // Setup buffers and meta header for new format.
   const struct spa_pod* params[3];
+  const int buffer_types =
+      spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier)
+          ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) |
+                (1 << SPA_DATA_MemPtr)
+          : (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr);
   params[0] = reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
       &builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
       SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), SPA_PARAM_BUFFERS_stride,
       SPA_POD_Int(stride), SPA_PARAM_BUFFERS_buffers,
-      SPA_POD_CHOICE_RANGE_Int(8, 1, 32)));
+      SPA_POD_CHOICE_RANGE_Int(8, 1, 32), SPA_PARAM_BUFFERS_dataType,
+      SPA_POD_CHOICE_FLAGS_Int(buffer_types)));
   params[1] = reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
       &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type,
       SPA_POD_Id(SPA_META_Header), SPA_PARAM_META_size,
@@ -363,12 +424,13 @@
       reinterpret_cast<GAsyncReadyCallback>(OnProxyRequested), this);
 }
 
-void BaseCapturerPipeWire::InitPipeWire() {
+void BaseCapturerPipeWire::Init() {
 #if defined(WEBRTC_DLOPEN_PIPEWIRE)
   StubPathMap paths;
 
-  // Check if the PipeWire library is available.
-  paths[kModulePipewire03].push_back(kPipeWireLib);
+  // 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.";
     portal_init_failed_ = true;
@@ -376,6 +438,8 @@
   }
 #endif  // defined(WEBRTC_DLOPEN_PIPEWIRE)
 
+  egl_dmabuf_ = std::make_unique<EglDmaBuf>();
+
   pw_init(/*argc=*/nullptr, /*argc=*/nullptr);
 
   pw_main_loop_ = pw_thread_loop_new("pipewire-main-loop", nullptr);
@@ -423,34 +487,37 @@
 }
 
 pw_stream* BaseCapturerPipeWire::CreateReceivingStream() {
-  spa_rectangle pwMinScreenBounds = spa_rectangle{1, 1};
-  spa_rectangle pwMaxScreenBounds = spa_rectangle{UINT32_MAX, UINT32_MAX};
-
   pw_properties* reuseProps =
       pw_properties_new_string("pipewire.client.reuse=1");
   auto stream = pw_stream_new(pw_core_, "webrtc-consume-stream", reuseProps);
 
-  uint8_t buffer[1024] = {};
-  const spa_pod* params[1];
+  uint8_t buffer[2048] = {};
+  std::vector<uint64_t> modifiers;
+
   spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)};
 
-  params[0] = reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
-      &builder, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
-      SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
-      SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
-      SPA_FORMAT_VIDEO_format,
-      SPA_POD_CHOICE_ENUM_Id(5, SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx,
-                             SPA_VIDEO_FORMAT_RGBA, SPA_VIDEO_FORMAT_BGRx,
-                             SPA_VIDEO_FORMAT_BGRA),
-      SPA_FORMAT_VIDEO_size,
-      SPA_POD_CHOICE_RANGE_Rectangle(&pwMinScreenBounds, &pwMinScreenBounds,
-                                     &pwMaxScreenBounds),
-      0));
+  std::vector<const spa_pod*> params;
+  const bool has_required_pw_version =
+      CheckPipeWireVersion(pw_version{0, 3, 29});
+  for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA,
+                          SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) {
+    // Modifiers can be used with PipeWire >= 0.3.29
+    if (has_required_pw_version) {
+      modifiers = egl_dmabuf_->QueryDmaBufModifiers(format);
+
+      if (!modifiers.empty()) {
+        params.push_back(BuildFormat(&builder, format, modifiers));
+      }
+    }
+
+    params.push_back(BuildFormat(&builder, format, /*modifiers=*/{}));
+  }
 
   pw_stream_add_listener(stream, &spa_stream_listener_, &pw_stream_events_,
                          this);
   if (pw_stream_connect(stream, PW_DIRECTION_INPUT, pw_stream_node_id_,
-                        PW_STREAM_FLAG_AUTOCONNECT, params, 1) != 0) {
+                        PW_STREAM_FLAG_AUTOCONNECT, params.data(),
+                        params.size()) != 0) {
     RTC_LOG(LS_ERROR) << "Could not connect receiving stream.";
     portal_init_failed_ = true;
     return nullptr;
@@ -460,25 +527,26 @@
 }
 
 void BaseCapturerPipeWire::HandleBuffer(pw_buffer* buffer) {
-  spa_buffer* spaBuffer = buffer->buffer;
+  spa_buffer* spa_buffer = buffer->buffer;
   ScopedBuf map;
+  std::unique_ptr<uint8_t[]> src_unique_ptr;
   uint8_t* src = nullptr;
 
-  if (spaBuffer->datas[0].chunk->size == 0) {
+  if (spa_buffer->datas[0].chunk->size == 0) {
     RTC_LOG(LS_ERROR) << "Failed to get video stream: Zero size.";
     return;
   }
 
-  if (spaBuffer->datas[0].type == SPA_DATA_MemFd ||
-      spaBuffer->datas[0].type == SPA_DATA_DmaBuf) {
+  std::function<void()> cleanup;
+  const int32_t src_stride = spa_buffer->datas[0].chunk->stride;
+  if (spa_buffer->datas[0].type == SPA_DATA_MemFd) {
     map.initialize(
         static_cast<uint8_t*>(
             mmap(nullptr,
-                 spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
-                 PROT_READ, MAP_PRIVATE, spaBuffer->datas[0].fd, 0)),
-        spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
-        spaBuffer->datas[0].type == SPA_DATA_DmaBuf,
-        spaBuffer->datas[0].fd);
+                 spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset,
+                 PROT_READ, MAP_PRIVATE, spa_buffer->datas[0].fd, 0)),
+        spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset,
+        spa_buffer->datas[0].fd);
 
     if (!map) {
       RTC_LOG(LS_ERROR) << "Failed to mmap the memory: "
@@ -486,13 +554,25 @@
       return;
     }
 
-    if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf) {
-      SyncDmaBuf(spaBuffer->datas[0].fd, DMA_BUF_SYNC_START);
+    src = SPA_MEMBER(map.get(), spa_buffer->datas[0].mapoffset, uint8_t);
+  } else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf) {
+    const uint n_planes = spa_buffer->n_datas;
+    int fds[n_planes];
+    uint32_t offsets[n_planes];
+    uint32_t strides[n_planes];
+
+    for (uint32_t i = 0; i < n_planes; ++i) {
+      fds[i] = spa_buffer->datas[i].fd;
+      offsets[i] = spa_buffer->datas[i].chunk->offset;
+      strides[i] = spa_buffer->datas[i].chunk->stride;
     }
 
-    src = SPA_MEMBER(map.get(), spaBuffer->datas[0].mapoffset, uint8_t);
-  } else if (spaBuffer->datas[0].type == SPA_DATA_MemPtr) {
-    src = static_cast<uint8_t*>(spaBuffer->datas[0].data);
+    src_unique_ptr = egl_dmabuf_->ImageFromDmaBuf(
+        desktop_size_, spa_video_format_.format, n_planes, fds, strides,
+        offsets, modifier_);
+    src = src_unique_ptr.get();
+  } else if (spa_buffer->datas[0].type == SPA_DATA_MemPtr) {
+    src = static_cast<uint8_t*>(spa_buffer->datas[0].data);
   }
 
   if (!src) {
@@ -501,7 +581,7 @@
 
   struct spa_meta_region* video_metadata =
       static_cast<struct spa_meta_region*>(spa_buffer_find_meta_data(
-          spaBuffer, SPA_META_VideoCrop, sizeof(*video_metadata)));
+          spa_buffer, SPA_META_VideoCrop, sizeof(*video_metadata)));
 
   // Video size from metadata is bigger than an actual video stream size.
   // The metadata are wrong or we should up-scale the video...in both cases
@@ -517,7 +597,6 @@
   // Use video metadata when video size from metadata is set and smaller than
   // video stream size, so we need to adjust it.
   bool video_metadata_use = false;
-
   const struct spa_rectangle* video_metadata_size =
       video_metadata ? &video_metadata->region.size : nullptr;
 
@@ -544,7 +623,6 @@
   }
 
   const int32_t dst_stride = video_size_.width() * kBytesPerPixel;
-  const int32_t src_stride = spaBuffer->datas[0].chunk->stride;
 
   if (src_stride != (desktop_size_.width() * kBytesPerPixel)) {
     RTC_LOG(LS_ERROR) << "Got buffer with stride different from screen stride: "
@@ -632,7 +710,7 @@
                                                  const gchar* token) {
   Scoped<gchar> sender(
       g_strdup(g_dbus_connection_get_unique_name(connection) + 1));
-  for (int i = 0; sender.get()[i]; i++) {
+  for (int i = 0; sender.get()[i]; ++i) {
     if (sender.get()[i] == '.') {
       sender.get()[i] = '_';
     }
@@ -1005,7 +1083,7 @@
     return;
   }
 
-  that->InitPipeWire();
+  that->Init();
 }
 
 void BaseCapturerPipeWire::Start(Callback* callback) {
diff --git a/modules/desktop_capture/linux/base_capturer_pipewire.h b/modules/desktop_capture/linux/base_capturer_pipewire.h
index 7ec5ea6..787ee5e 100644
--- a/modules/desktop_capture/linux/base_capturer_pipewire.h
+++ b/modules/desktop_capture/linux/base_capturer_pipewire.h
@@ -19,6 +19,7 @@
 #include "absl/types/optional.h"
 #include "modules/desktop_capture/desktop_capture_options.h"
 #include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/linux/egl_dmabuf.h"
 #include "rtc_base/constructor_magic.h"
 #include "rtc_base/synchronization/mutex.h"
 
@@ -88,6 +89,7 @@
   guint sources_request_signal_id_ = 0;
   guint start_request_signal_id_ = 0;
 
+  int64_t modifier_;
   DesktopSize video_size_;
   DesktopSize desktop_size_ = {};
   DesktopCaptureOptions options_ = {};
@@ -98,8 +100,10 @@
 
   bool portal_init_failed_ = false;
 
+  std::unique_ptr<EglDmaBuf> egl_dmabuf_;
+
+  void Init();
   void InitPortal();
-  void InitPipeWire();
   void InitPipeWireTypes();
 
   pw_stream* CreateReceivingStream();
diff --git a/modules/desktop_capture/linux/drm.sigs b/modules/desktop_capture/linux/drm.sigs
new file mode 100644
index 0000000..226979f
--- /dev/null
+++ b/modules/desktop_capture/linux/drm.sigs
@@ -0,0 +1,11 @@
+// Copyright 2021 The WebRTC project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//------------------------------------------------
+// Functions from DRM used in capturer code.
+//--------
+
+// xf86drm.h
+int drmGetDevices2(uint32_t flags, drmDevicePtr devices[], int max_devices);
+void drmFreeDevices(drmDevicePtr devices[], int count);
diff --git a/modules/desktop_capture/linux/egl_dmabuf.cc b/modules/desktop_capture/linux/egl_dmabuf.cc
new file mode 100644
index 0000000..ae34b69
--- /dev/null
+++ b/modules/desktop_capture/linux/egl_dmabuf.cc
@@ -0,0 +1,584 @@
+/*
+ *  Copyright 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/egl_dmabuf.h"
+
+#include <asm/ioctl.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <libdrm/drm_fourcc.h>
+#include <linux/types.h>
+#include <spa/param/video/format-utils.h>
+#include <unistd.h>
+#include <xf86drm.h>
+
+#include "absl/memory/memory.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/sanitizer.h"
+#include "rtc_base/string_encode.h"
+
+namespace webrtc {
+
+// EGL
+typedef EGLBoolean (*eglBindAPI_func)(EGLenum api);
+typedef EGLContext (*eglCreateContext_func)(EGLDisplay dpy,
+                                            EGLConfig config,
+                                            EGLContext share_context,
+                                            const EGLint* attrib_list);
+typedef EGLImageKHR (*eglCreateImageKHR_func)(EGLDisplay dpy,
+                                              EGLContext ctx,
+                                              EGLenum target,
+                                              EGLClientBuffer buffer,
+                                              const EGLint* attrib_list);
+typedef EGLBoolean (*eglDestroyImageKHR_func)(EGLDisplay dpy,
+                                              EGLImageKHR image);
+typedef EGLint (*eglGetError_func)(void);
+typedef void* (*eglGetProcAddress_func)(const char*);
+typedef EGLDisplay (*eglGetPlatformDisplayEXT_func)(EGLenum platform,
+                                                    void* native_display,
+                                                    const EGLint* attrib_list);
+typedef EGLBoolean (*eglInitialize_func)(EGLDisplay dpy,
+                                         EGLint* major,
+                                         EGLint* minor);
+typedef EGLBoolean (*eglMakeCurrent_func)(EGLDisplay dpy,
+                                          EGLSurface draw,
+                                          EGLSurface read,
+                                          EGLContext ctx);
+typedef EGLBoolean (*eglQueryDmaBufFormatsEXT_func)(EGLDisplay dpy,
+                                                    EGLint max_formats,
+                                                    EGLint* formats,
+                                                    EGLint* num_formats);
+typedef EGLBoolean (*eglQueryDmaBufModifiersEXT_func)(EGLDisplay dpy,
+                                                      EGLint format,
+                                                      EGLint max_modifiers,
+                                                      EGLuint64KHR* modifiers,
+                                                      EGLBoolean* external_only,
+                                                      EGLint* num_modifiers);
+typedef const char* (*eglQueryString_func)(EGLDisplay dpy, EGLint name);
+typedef void (*glEGLImageTargetTexture2DOES_func)(GLenum target,
+                                                  GLeglImageOES image);
+
+// This doesn't follow naming conventions in WebRTC, where the naming
+// should look like e.g. egl_bind_api instead of EglBindAPI, however
+// we named them according to the exported functions they map to for
+// consistency.
+eglBindAPI_func EglBindAPI = nullptr;
+eglCreateContext_func EglCreateContext = nullptr;
+eglCreateImageKHR_func EglCreateImageKHR = nullptr;
+eglDestroyImageKHR_func EglDestroyImageKHR = nullptr;
+eglGetError_func EglGetError = nullptr;
+eglGetProcAddress_func EglGetProcAddress = nullptr;
+eglGetPlatformDisplayEXT_func EglGetPlatformDisplayEXT = nullptr;
+eglInitialize_func EglInitialize = nullptr;
+eglMakeCurrent_func EglMakeCurrent = nullptr;
+eglQueryDmaBufFormatsEXT_func EglQueryDmaBufFormatsEXT = nullptr;
+eglQueryDmaBufModifiersEXT_func EglQueryDmaBufModifiersEXT = nullptr;
+eglQueryString_func EglQueryString = nullptr;
+glEGLImageTargetTexture2DOES_func GlEGLImageTargetTexture2DOES = nullptr;
+
+// GL
+typedef void (*glBindTexture_func)(GLenum target, GLuint texture);
+typedef void (*glDeleteTextures_func)(GLsizei n, const GLuint* textures);
+typedef void (*glGenTextures_func)(GLsizei n, GLuint* textures);
+typedef GLenum (*glGetError_func)(void);
+typedef const GLubyte* (*glGetString_func)(GLenum name);
+typedef void (*glGetTexImage_func)(GLenum target,
+                                   GLint level,
+                                   GLenum format,
+                                   GLenum type,
+                                   void* pixels);
+typedef void (*glTexParameteri_func)(GLenum target, GLenum pname, GLint param);
+typedef void* (*glXGetProcAddressARB_func)(const char*);
+
+// This doesn't follow naming conventions in WebRTC, where the naming
+// should look like e.g. egl_bind_api instead of EglBindAPI, however
+// we named them according to the exported functions they map to for
+// consistency.
+glBindTexture_func GlBindTexture = nullptr;
+glDeleteTextures_func GlDeleteTextures = nullptr;
+glGenTextures_func GlGenTextures = nullptr;
+glGetError_func GlGetError = nullptr;
+glGetString_func GlGetString = nullptr;
+glGetTexImage_func GlGetTexImage = nullptr;
+glTexParameteri_func GlTexParameteri = nullptr;
+glXGetProcAddressARB_func GlXGetProcAddressARB = nullptr;
+
+static const std::string FormatGLError(GLenum err) {
+  switch (err) {
+    case GL_NO_ERROR:
+      return "GL_NO_ERROR";
+    case GL_INVALID_ENUM:
+      return "GL_INVALID_ENUM";
+    case GL_INVALID_VALUE:
+      return "GL_INVALID_VALUE";
+    case GL_INVALID_OPERATION:
+      return "GL_INVALID_OPERATION";
+    case GL_STACK_OVERFLOW:
+      return "GL_STACK_OVERFLOW";
+    case GL_STACK_UNDERFLOW:
+      return "GL_STACK_UNDERFLOW";
+    case GL_OUT_OF_MEMORY:
+      return "GL_OUT_OF_MEMORY";
+    default:
+      return std::string("0x") + std::to_string(err);
+  }
+}
+
+static uint32_t SpaPixelFormatToDrmFormat(uint32_t spa_format) {
+  switch (spa_format) {
+    case SPA_VIDEO_FORMAT_RGBA:
+      return DRM_FORMAT_ABGR8888;
+    case SPA_VIDEO_FORMAT_RGBx:
+      return DRM_FORMAT_XBGR8888;
+    case SPA_VIDEO_FORMAT_BGRA:
+      return DRM_FORMAT_ARGB8888;
+    case SPA_VIDEO_FORMAT_BGRx:
+      return DRM_FORMAT_XRGB8888;
+    default:
+      return DRM_FORMAT_INVALID;
+  }
+}
+
+static void CloseLibrary(void* library) {
+  if (library) {
+    dlclose(library);
+    library = nullptr;
+  }
+}
+
+static void* g_lib_egl = nullptr;
+
+static bool OpenEGL() {
+  g_lib_egl = dlopen("libEGL.so.1", RTLD_NOW | RTLD_GLOBAL);
+  if (g_lib_egl) {
+    EglGetProcAddress =
+        (eglGetProcAddress_func)dlsym(g_lib_egl, "eglGetProcAddress");
+    return EglGetProcAddress;
+  }
+
+  return false;
+}
+
+static bool LoadEGL() {
+  if (OpenEGL()) {
+    EglBindAPI = (eglBindAPI_func)EglGetProcAddress("eglBindAPI");
+    EglCreateContext =
+        (eglCreateContext_func)EglGetProcAddress("eglCreateContext");
+    EglCreateImageKHR =
+        (eglCreateImageKHR_func)EglGetProcAddress("eglCreateImageKHR");
+    EglDestroyImageKHR =
+        (eglDestroyImageKHR_func)EglGetProcAddress("eglDestroyImageKHR");
+    EglGetError = (eglGetError_func)EglGetProcAddress("eglGetError");
+    EglGetPlatformDisplayEXT = (eglGetPlatformDisplayEXT_func)EglGetProcAddress(
+        "eglGetPlatformDisplayEXT");
+    EglInitialize = (eglInitialize_func)EglGetProcAddress("eglInitialize");
+    EglMakeCurrent = (eglMakeCurrent_func)EglGetProcAddress("eglMakeCurrent");
+    EglQueryString = (eglQueryString_func)EglGetProcAddress("eglQueryString");
+    GlEGLImageTargetTexture2DOES =
+        (glEGLImageTargetTexture2DOES_func)EglGetProcAddress(
+            "glEGLImageTargetTexture2DOES");
+
+    return EglBindAPI && EglCreateContext && EglCreateImageKHR &&
+           EglDestroyImageKHR && EglGetError && EglGetPlatformDisplayEXT &&
+           EglInitialize && EglMakeCurrent && EglQueryString &&
+           GlEGLImageTargetTexture2DOES;
+  }
+
+  return false;
+}
+
+static void* g_lib_gl = nullptr;
+
+static bool OpenGL() {
+  std::vector<std::string> names = {"libGL.so.1", "libGL.so"};
+  for (const std::string& name : names) {
+    g_lib_gl = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL);
+    if (g_lib_gl) {
+      GlXGetProcAddressARB =
+          (glXGetProcAddressARB_func)dlsym(g_lib_gl, "glXGetProcAddressARB");
+      return GlXGetProcAddressARB;
+    }
+  }
+
+  return false;
+}
+
+static bool LoadGL() {
+  if (OpenGL()) {
+    GlGetString = (glGetString_func)GlXGetProcAddressARB("glGetString");
+    if (!GlGetString) {
+      return false;
+    }
+
+    GlBindTexture = (glBindTexture_func)GlXGetProcAddressARB("glBindTexture");
+    GlDeleteTextures =
+        (glDeleteTextures_func)GlXGetProcAddressARB("glDeleteTextures");
+    GlGenTextures = (glGenTextures_func)GlXGetProcAddressARB("glGenTextures");
+    GlGetError = (glGetError_func)GlXGetProcAddressARB("glGetError");
+    GlGetTexImage = (glGetTexImage_func)GlXGetProcAddressARB("glGetTexImage");
+    GlTexParameteri =
+        (glTexParameteri_func)GlXGetProcAddressARB("glTexParameteri");
+
+    return GlBindTexture && GlDeleteTextures && GlGenTextures && GlGetError &&
+           GlGetTexImage && GlTexParameteri;
+  }
+
+  return false;
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+EglDmaBuf::EglDmaBuf() {
+  absl::optional<std::string> 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.";
+    return;
+  }
+
+  if (!LoadGL()) {
+    RTC_LOG(LS_ERROR) << "Failed to load OpenGL entry functions.";
+    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! "
+                      << FormatGLError(EglGetError());
+    return;
+  }
+
+  std::vector<std::string> client_extensions_no_display;
+  rtc::split(client_extensions_cstring_no_display, ' ',
+             &client_extensions_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;
+
+  for (const auto& extension : egl_.extensions) {
+    if (extension == "EGL_EXT_platform_base") {
+      has_platform_base_ext = true;
+      continue;
+    } else if (extension == "EGL_MESA_platform_gbm") {
+      has_platform_gbm_ext = true;
+      continue;
+    }
+  }
+
+  if (!has_platform_base_ext || !has_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);
+
+  if (egl_.display == EGL_NO_DISPLAY) {
+    RTC_LOG(LS_ERROR) << "Error during obtaining EGL display: "
+                      << FormatGLError(EglGetError());
+    return;
+  }
+
+  EGLint major, minor;
+  if (EglInitialize(egl_.display, &major, &minor) == EGL_FALSE) {
+    RTC_LOG(LS_ERROR) << "Error during eglInitialize: "
+                      << FormatGLError(EglGetError());
+    return;
+  }
+
+  if (EglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
+    RTC_LOG(LS_ERROR) << "bind OpenGL API failed";
+    return;
+  }
+
+  egl_.context =
+      EglCreateContext(egl_.display, nullptr, EGL_NO_CONTEXT, nullptr);
+
+  if (egl_.context == EGL_NO_CONTEXT) {
+    RTC_LOG(LS_ERROR) << "Couldn't create EGL context: "
+                      << FormatGLError(EglGetError());
+    return;
+  }
+
+  const char* client_extensions_cstring_display =
+      EglQueryString(egl_.display, EGL_EXTENSIONS);
+  client_extensions_string = client_extensions_cstring_display;
+
+  std::vector<std::string> client_extensions;
+  rtc::split(client_extensions_string, ' ', &client_extensions);
+  for (const auto& extension : client_extensions) {
+    egl_.extensions.push_back(std::string(extension));
+  }
+
+  bool has_image_dma_buf_import_ext = false;
+  bool has_image_dma_buf_import_modifiers_ext = false;
+
+  for (const auto& extension : egl_.extensions) {
+    if (extension == "EGL_EXT_image_dma_buf_import") {
+      has_image_dma_buf_import_ext = true;
+      continue;
+    } else if (extension == "EGL_EXT_image_dma_buf_import_modifiers") {
+      has_image_dma_buf_import_modifiers_ext = true;
+      continue;
+    }
+  }
+
+  if (has_image_dma_buf_import_ext && has_image_dma_buf_import_modifiers_ext) {
+    EglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func)EglGetProcAddress(
+        "eglQueryDmaBufFormatsEXT");
+    EglQueryDmaBufModifiersEXT =
+        (eglQueryDmaBufModifiersEXT_func)EglGetProcAddress(
+            "eglQueryDmaBufModifiersEXT");
+  }
+
+  RTC_LOG(LS_INFO) << "Egl initialization succeeded";
+  egl_initialized_ = true;
+}
+
+EglDmaBuf::~EglDmaBuf() {
+  if (gbm_device_) {
+    gbm_device_destroy(gbm_device_);
+  }
+
+  CloseLibrary(g_lib_egl);
+  CloseLibrary(g_lib_gl);
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+std::unique_ptr<uint8_t[]> EglDmaBuf::ImageFromDmaBuf(const DesktopSize& size,
+                                                      uint32_t format,
+                                                      uint32_t n_planes,
+                                                      const int32_t* fds,
+                                                      const uint32_t* strides,
+                                                      const uint32_t* offsets,
+                                                      uint64_t modifier) {
+  std::unique_ptr<uint8_t[]> src;
+
+  if (!egl_initialized_) {
+    return src;
+  }
+
+  if (n_planes <= 0) {
+    RTC_LOG(LS_ERROR) << "Failed to process buffer: invalid number of planes";
+    return src;
+  }
+
+  gbm_bo* imported;
+  if (modifier == DRM_FORMAT_MOD_INVALID) {
+    gbm_import_fd_data import_info = {fds[0],
+                                      static_cast<uint32_t>(size.width()),
+                                      static_cast<uint32_t>(size.height()),
+                                      strides[0], GBM_BO_FORMAT_ARGB8888};
+
+    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<uint32_t>(size.width());
+    import_info.height = static_cast<uint32_t>(size.height());
+    import_info.num_fds = n_planes;
+    import_info.modifier = modifier;
+    for (uint32_t i = 0; i < n_planes; i++) {
+      import_info.fds[i] = fds[i];
+      import_info.offsets[i] = offsets[i];
+      import_info.strides[i] = strides[i];
+    }
+
+    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;
+  }
+
+  // bind context to render thread
+  EglMakeCurrent(egl_.display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_.context);
+
+  // create EGL image from imported BO
+  EGLImageKHR image = EglCreateImageKHR(
+      egl_.display, nullptr, EGL_NATIVE_PIXMAP_KHR, imported, nullptr);
+
+  if (image == EGL_NO_IMAGE_KHR) {
+    RTC_LOG(LS_ERROR) << "Failed to record frame: Error creating EGLImageKHR - "
+                      << FormatGLError(GlGetError());
+    gbm_bo_destroy(imported);
+    return src;
+  }
+
+  // create GL 2D texture for framebuffer
+  GLuint texture;
+  GlGenTextures(1, &texture);
+  GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+  GlBindTexture(GL_TEXTURE_2D, texture);
+  GlEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+
+  src = std::make_unique<uint8_t[]>(strides[0] * size.height());
+
+  GLenum gl_format = GL_BGRA;
+  switch (format) {
+    case SPA_VIDEO_FORMAT_RGBx:
+      gl_format = GL_RGBA;
+      break;
+    case SPA_VIDEO_FORMAT_RGBA:
+      gl_format = GL_RGBA;
+      break;
+    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;
+  }
+  GlGetTexImage(GL_TEXTURE_2D, 0, gl_format, GL_UNSIGNED_BYTE, src.get());
+
+  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;
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+std::vector<uint64_t> EglDmaBuf::QueryDmaBufModifiers(uint32_t format) {
+  if (!egl_initialized_) {
+    return {};
+  }
+
+  // Modifiers not supported, return just DRM_FORMAT_MOD_INVALID as we can still
+  // use modifier-less DMA-BUFs
+  if (EglQueryDmaBufFormatsEXT == nullptr ||
+      EglQueryDmaBufModifiersEXT == nullptr) {
+    return {DRM_FORMAT_MOD_INVALID};
+  }
+
+  uint32_t drm_format = SpaPixelFormatToDrmFormat(format);
+  if (drm_format == DRM_FORMAT_INVALID) {
+    RTC_LOG(LS_ERROR) << "Failed to find matching DRM format.";
+    return {DRM_FORMAT_MOD_INVALID};
+  }
+
+  EGLint count = 0;
+  EGLBoolean success =
+      EglQueryDmaBufFormatsEXT(egl_.display, 0, nullptr, &count);
+
+  if (!success || !count) {
+    RTC_LOG(LS_ERROR) << "Failed to query DMA-BUF formats.";
+    return {DRM_FORMAT_MOD_INVALID};
+  }
+
+  std::vector<uint32_t> formats(count);
+  if (!EglQueryDmaBufFormatsEXT(egl_.display, count,
+                                reinterpret_cast<EGLint*>(formats.data()),
+                                &count)) {
+    RTC_LOG(LS_ERROR) << "Failed to query DMA-BUF formats.";
+    return {DRM_FORMAT_MOD_INVALID};
+  }
+
+  if (std::find(formats.begin(), formats.end(), drm_format) == formats.end()) {
+    RTC_LOG(LS_ERROR) << "Format " << drm_format
+                      << " not supported for modifiers.";
+    return {DRM_FORMAT_MOD_INVALID};
+  }
+
+  success = EglQueryDmaBufModifiersEXT(egl_.display, drm_format, 0, nullptr,
+                                       nullptr, &count);
+
+  if (!success || !count) {
+    RTC_LOG(LS_ERROR) << "Failed to query DMA-BUF modifiers.";
+    return {DRM_FORMAT_MOD_INVALID};
+  }
+
+  std::vector<uint64_t> modifiers(count);
+  if (!EglQueryDmaBufModifiersEXT(egl_.display, drm_format, count,
+                                  modifiers.data(), nullptr, &count)) {
+    RTC_LOG(LS_ERROR) << "Failed to query DMA-BUF modifiers.";
+  }
+
+  // Support modifier-less buffers
+  modifiers.push_back(DRM_FORMAT_MOD_INVALID);
+  return modifiers;
+}
+
+absl::optional<std::string> EglDmaBuf::GetRenderNode() {
+  int max_devices = drmGetDevices2(0, nullptr, 0);
+  if (max_devices <= 0) {
+    RTC_LOG(LS_ERROR) << "drmGetDevices2() has not found any devices (errno="
+                      << -max_devices << ")";
+    return absl::nullopt;
+  }
+
+  std::vector<drmDevicePtr> devices(max_devices);
+  int ret = drmGetDevices2(0, devices.data(), max_devices);
+  if (ret < 0) {
+    RTC_LOG(LS_ERROR) << "drmGetDevices2() returned an error " << ret;
+    return absl::nullopt;
+  }
+
+  std::string render_node;
+
+  for (const drmDevicePtr& device : devices) {
+    if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
+      render_node = device->nodes[DRM_NODE_RENDER];
+      break;
+    }
+  }
+
+  drmFreeDevices(devices.data(), ret);
+  return render_node;
+}
+
+}  // namespace webrtc
diff --git a/modules/desktop_capture/linux/egl_dmabuf.h b/modules/desktop_capture/linux/egl_dmabuf.h
new file mode 100644
index 0000000..f7ced99
--- /dev/null
+++ b/modules/desktop_capture/linux/egl_dmabuf.h
@@ -0,0 +1,61 @@
+/*
+ *  Copyright 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_EGL_DMABUF_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_EGL_DMABUF_H_
+
+#include <epoxy/egl.h>
+#include <epoxy/gl.h>
+#include <gbm.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+class EglDmaBuf {
+ public:
+  struct EGLStruct {
+    std::vector<std::string> extensions;
+    EGLDisplay display = EGL_NO_DISPLAY;
+    EGLContext context = EGL_NO_CONTEXT;
+  };
+
+  EglDmaBuf();
+  ~EglDmaBuf();
+
+  std::unique_ptr<uint8_t[]> ImageFromDmaBuf(const DesktopSize& size,
+                                             uint32_t format,
+                                             uint32_t n_planes,
+                                             const int32_t* fds,
+                                             const uint32_t* strides,
+                                             const uint32_t* offsets,
+                                             uint64_t modifiers);
+  std::vector<uint64_t> QueryDmaBufModifiers(uint32_t format);
+
+  bool IsEglInitialized() const { return egl_initialized_; }
+
+ private:
+  bool egl_initialized_ = false;
+  int32_t drm_fd_ = -1;               // for GBM buffer mmap
+  gbm_device* gbm_device_ = nullptr;  // for passed GBM buffer retrieval
+
+  EGLStruct egl_;
+
+  absl::optional<std::string> GetRenderNode();
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_DESKTOP_CAPTURE_LINUX_EGL_DMABUF_H_
diff --git a/modules/desktop_capture/linux/pipewire03.sigs b/modules/desktop_capture/linux/pipewire.sigs
similarity index 97%
rename from modules/desktop_capture/linux/pipewire03.sigs
rename to modules/desktop_capture/linux/pipewire.sigs
index 44e4100..ffcd077 100644
--- a/modules/desktop_capture/linux/pipewire03.sigs
+++ b/modules/desktop_capture/linux/pipewire.sigs
@@ -16,6 +16,7 @@
 
 // pipewire.h
 void pw_init(int *argc, char **argv[]);
+const char* pw_get_library_version();
 
 // properties.h
 pw_properties * pw_properties_new_string(const char *args);
@@ -39,7 +40,6 @@
 void pw_thread_loop_unlock(pw_thread_loop *loop);
 pw_loop * pw_thread_loop_get_loop(pw_thread_loop *loop);
 
-
 // context.h
 void pw_context_destroy(pw_context *context);
 pw_context *pw_context_new(pw_loop *main_loop, pw_properties *props, size_t user_data_size);
diff --git a/modules/desktop_capture/linux/pipewire_stub_header.fragment b/modules/desktop_capture/linux/pipewire_stub_header.fragment
index 9d7dbd27..06ae18d 100644
--- a/modules/desktop_capture/linux/pipewire_stub_header.fragment
+++ b/modules/desktop_capture/linux/pipewire_stub_header.fragment
@@ -5,4 +5,5 @@
 
 #include <pipewire/pipewire.h>
 
+#include <xf86drm.h>
 }
diff --git a/webrtc.gni b/webrtc.gni
index 3d8c538..90f69f0 100644
--- a/webrtc.gni
+++ b/webrtc.gni
@@ -132,7 +132,7 @@
   # supported Ubuntu and Debian distributions.
   rtc_use_pipewire = is_linux && use_sysroot
 
-  # Set this to link PipeWire directly instead of using the dlopen.
+  # Set this to link PipeWire and required libraries directly instead of using the dlopen.
   rtc_link_pipewire = false
 
   # Enable to use the Mozilla internal settings.