Split out generic portal / pipewire code

It will be reused by the video capture portal / pipewire backend.

Bug: webrtc:13177
Change-Id: Ia1a77f1c6e289149cd8a1d54b550754bf192e62e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/263721
Reviewed-by: Mark Foltz <mfoltz@chromium.org>
Commit-Queue: Alexander Cooper <alcooper@chromium.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org>
Reviewed-by: Salman Malik <salmanmalik@google.com>
Cr-Commit-Position: refs/heads/main@{#38487}
diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn
index 34a5704..a496ce5 100644
--- a/modules/desktop_capture/BUILD.gn
+++ b/modules/desktop_capture/BUILD.gn
@@ -7,9 +7,7 @@
 # be found in the AUTHORS file in the root of the source tree.
 
 import("//build/config/linux/gtk/gtk.gni")
-import("//build/config/linux/pkg_config.gni")
 import("//build/config/ui.gni")
-import("//tools/generate_stubs/rules.gni")
 import("../../webrtc.gni")
 
 use_desktop_capture_differ_sse2 = current_cpu == "x86" || current_cpu == "x64"
@@ -84,7 +82,7 @@
       ]
 
       if ((is_linux || is_chromeos) && rtc_use_pipewire) {
-        configs += [ ":gio" ]
+        configs += [ "../portal:gio" ]
       }
 
       public_configs = [ ":x11_config" ]
@@ -105,14 +103,7 @@
         "linux/wayland/test/test_screencast_stream_provider.h",
       ]
 
-      configs += [
-        ":gio",
-        ":pipewire",
-        ":gbm",
-        ":egl",
-        ":epoxy",
-        ":libdrm",
-      ]
+      configs += [ "../portal:pipewire_all" ]
 
       deps = [
         ":desktop_capture",
@@ -122,6 +113,7 @@
         "../../rtc_base:logging",
         "../../rtc_base:random",
         "../../rtc_base:timeutils",
+        "../portal",
 
         # TODO(bugs.webrtc.org/9987): Remove this dep on rtc_base:rtc_base once
         # rtc_base:threading is fully defined.
@@ -135,11 +127,7 @@
         "//rtc_base:rtc_event",
       ]
 
-      if (!rtc_link_pipewire) {
-        deps += [ ":pipewire_stubs" ]
-      }
-
-      public_configs = [ ":pipewire_config" ]
+      public_configs = [ "../portal:pipewire_config" ]
     }
 
     group("pipewire_shared_screencast_stream_test") {
@@ -178,7 +166,7 @@
     ]
 
     if ((is_linux || is_chromeos) && rtc_use_pipewire) {
-      configs += [ ":gio" ]
+      configs += [ "../portal:gio" ]
     }
 
     deps = [
@@ -288,7 +276,7 @@
     ]
 
     if ((is_linux || is_chromeos) && rtc_use_pipewire) {
-      configs += [ ":gio" ]
+      configs += [ "../portal:gio" ]
     }
 
     deps = [
@@ -299,71 +287,10 @@
   }
 }
 
-if (is_linux || is_chromeos) {
-  if (rtc_use_pipewire) {
-    pkg_config("gio") {
-      packages = [
-        "gio-2.0",
-        "gio-unix-2.0",
-      ]
-    }
-
-    pkg_config("pipewire") {
-      packages = [ "libpipewire-0.3" ]
-      if (!rtc_link_pipewire) {
-        ignore_libs = true
-      }
-    }
-
-    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) {
-      # When libpipewire is not directly linked, use stubs to allow for dlopening of
-      # the binary.
-      generate_stubs("pipewire_stubs") {
-        configs = [
-          "../../:common_config",
-          ":pipewire",
-        ]
-        deps = [ "../../rtc_base" ]
-        extra_header = "linux/wayland/pipewire_stub_header.fragment"
-        logging_function = "RTC_LOG(LS_VERBOSE)"
-        logging_include = "rtc_base/logging.h"
-        output_name = "linux/wayland/pipewire_stubs"
-        path_from_source = "modules/desktop_capture/linux/wayland"
-        sigs = [ "linux/wayland/pipewire.sigs" ]
-        if (!build_with_chromium) {
-          macro_include = "rtc_base/system/no_cfi_icall.h"
-          macro_deps = [ "../../rtc_base/system:no_cfi_icall" ]
-        }
-      }
-    }
-
-    config("pipewire_config") {
-      defines = [ "WEBRTC_USE_PIPEWIRE" ]
-      if (!rtc_link_pipewire) {
-        defines += [ "WEBRTC_DLOPEN_PIPEWIRE" ]
-      }
-
-      # Chromecast build config overrides `WEBRTC_USE_PIPEWIRE` even when
-      # `rtc_use_pipewire` is not set, which causes pipewire_config to not be
-      # included in targets. More details in: webrtc:13898
-      if (is_linux && !is_castos) {
-        defines += [ "WEBRTC_USE_GIO" ]
-      }
-    }
+# TODO(bugs.webrtc.org/14187): remove when all users are gone
+if (is_linux && rtc_use_pipewire) {
+  config("pipewire_config") {
+    configs = [ "../portal:pipewire_config" ]
   }
 }
 
@@ -644,12 +571,9 @@
       "linux/wayland/egl_dmabuf.h",
       "linux/wayland/mouse_cursor_monitor_pipewire.cc",
       "linux/wayland/mouse_cursor_monitor_pipewire.h",
-      "linux/wayland/pipewire_utils.cc",
-      "linux/wayland/pipewire_utils.h",
       "linux/wayland/portal_request_response.h",
       "linux/wayland/restore_token_manager.cc",
       "linux/wayland/restore_token_manager.h",
-      "linux/wayland/scoped_glib.cc",
       "linux/wayland/scoped_glib.h",
       "linux/wayland/screen_capture_portal_interface.cc",
       "linux/wayland/screen_capture_portal_interface.h",
@@ -659,27 +583,18 @@
       "linux/wayland/screencast_stream_utils.h",
       "linux/wayland/shared_screencast_stream.cc",
       "linux/wayland/shared_screencast_stream.h",
-      "linux/wayland/xdg_desktop_portal_utils.cc",
       "linux/wayland/xdg_desktop_portal_utils.h",
       "linux/wayland/xdg_session_details.h",
     ]
 
-    configs += [
-      ":gio",
-      ":pipewire",
-      ":gbm",
-      ":egl",
-      ":epoxy",
-      ":libdrm",
+    configs += [ "../portal:pipewire_all" ]
+
+    public_configs += [ "../portal:pipewire_config" ]
+
+    deps += [
+      "../../rtc_base:sanitizer",
+      "../portal",
     ]
-
-    if (!rtc_link_pipewire) {
-      deps += [ ":pipewire_stubs" ]
-    }
-
-    public_configs += [ ":pipewire_config" ]
-
-    deps += [ "../../rtc_base:sanitizer" ]
   }
 
   if (rtc_enable_win_wgc) {
diff --git a/modules/desktop_capture/DEPS b/modules/desktop_capture/DEPS
index 8c894c4..d5f4487 100644
--- a/modules/desktop_capture/DEPS
+++ b/modules/desktop_capture/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+common_video",
   "+system_wrappers",
   "+third_party/libyuv",
 ]
diff --git a/modules/desktop_capture/desktop_capture_metadata.h b/modules/desktop_capture/desktop_capture_metadata.h
index faca156..49a20e7 100644
--- a/modules/desktop_capture/desktop_capture_metadata.h
+++ b/modules/desktop_capture/desktop_capture_metadata.h
@@ -12,7 +12,7 @@
 #define MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_METADATA_H_
 
 #if defined(WEBRTC_USE_GIO)
-#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
+#include "modules/portal/xdg_session_details.h"
 #endif  // defined(WEBRTC_USE_GIO)
 
 namespace webrtc {
diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
index a0af638..e9e45b5 100644
--- a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
+++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
@@ -12,9 +12,9 @@
 
 #include "modules/desktop_capture/desktop_capture_options.h"
 #include "modules/desktop_capture/desktop_capturer.h"
-#include "modules/desktop_capture/linux/wayland/pipewire_utils.h"
 #include "modules/desktop_capture/linux/wayland/restore_token_manager.h"
-#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
+#include "modules/portal/pipewire_utils.h"
+#include "modules/portal/xdg_desktop_portal_utils.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 
diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h
index d84718a..c5c122c 100644
--- a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h
+++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h
@@ -14,12 +14,12 @@
 #include "modules/desktop_capture/delegated_source_list_controller.h"
 #include "modules/desktop_capture/desktop_capture_options.h"
 #include "modules/desktop_capture/desktop_capturer.h"
-#include "modules/desktop_capture/linux/wayland/portal_request_response.h"
 #include "modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h"
 #include "modules/desktop_capture/linux/wayland/screencast_portal.h"
 #include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h"
-#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
-#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
+#include "modules/portal/portal_request_response.h"
+#include "modules/portal/xdg_desktop_portal_utils.h"
+#include "modules/portal/xdg_session_details.h"
 
 namespace webrtc {
 
diff --git a/modules/desktop_capture/linux/wayland/portal_request_response.h b/modules/desktop_capture/linux/wayland/portal_request_response.h
index dde9ac5..25894793 100644
--- a/modules/desktop_capture/linux/wayland/portal_request_response.h
+++ b/modules/desktop_capture/linux/wayland/portal_request_response.h
@@ -11,24 +11,7 @@
 #ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PORTAL_REQUEST_RESPONSE_H_
 #define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PORTAL_REQUEST_RESPONSE_H_
 
-namespace webrtc {
-namespace xdg_portal {
+// TODO(bugs.webrtc.org/14187): remove when all users are gone
+#include "modules/portal/portal_request_response.h"
 
-// Contains type of responses that can be observed when making a request to
-// a desktop portal interface.
-enum class RequestResponse {
-  // Unknown, the initialized status.
-  kUnknown,
-  // Success, the request is carried out.
-  kSuccess,
-  // The user cancelled the interaction.
-  kUserCancelled,
-  // The user interaction was ended in some other way.
-  kError,
-
-  kMaxValue = kError,
-};
-
-}  // namespace xdg_portal
-}  // namespace webrtc
 #endif  // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PORTAL_REQUEST_RESPONSE_H_
diff --git a/modules/desktop_capture/linux/wayland/scoped_glib.h b/modules/desktop_capture/linux/wayland/scoped_glib.h
index 908bd6f..1361f84 100644
--- a/modules/desktop_capture/linux/wayland/scoped_glib.h
+++ b/modules/desktop_capture/linux/wayland/scoped_glib.h
@@ -11,55 +11,7 @@
 #ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCOPED_GLIB_H_
 #define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCOPED_GLIB_H_
 
-#include <gio/gio.h>
-
-#include "rtc_base/checks.h"
-
-namespace webrtc {
-
-template <class T>
-class Scoped {
- public:
-  Scoped() {}
-  explicit Scoped(T* val) { ptr_ = val; }
-  ~Scoped() { RTC_DCHECK_NOTREACHED(); }
-
-  T* operator->() const { return ptr_; }
-
-  explicit operator bool() const { return ptr_ != nullptr; }
-
-  bool operator!() const { return ptr_ == nullptr; }
-
-  T* get() const { return ptr_; }
-
-  T** receive() {
-    RTC_CHECK(!ptr_);
-    return &ptr_;
-  }
-
-  Scoped& operator=(T* val) {
-    RTC_DCHECK(val);
-    ptr_ = val;
-    return *this;
-  }
-
- protected:
-  T* ptr_ = nullptr;
-};
-
-template <>
-Scoped<GError>::~Scoped();
-template <>
-Scoped<char>::~Scoped();
-template <>
-Scoped<GVariant>::~Scoped();
-template <>
-Scoped<GVariantIter>::~Scoped();
-template <>
-Scoped<GDBusMessage>::~Scoped();
-template <>
-Scoped<GUnixFDList>::~Scoped();
-
-}  // namespace webrtc
+// TODO(bugs.webrtc.org/14187): remove when all users are gone
+#include "modules/portal/scoped_glib.h"
 
 #endif  // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCOPED_GLIB_H_
diff --git a/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc b/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc
index 02d9d2e..1c7cc37 100644
--- a/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc
+++ b/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc
@@ -8,10 +8,10 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 #include "modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h"
-#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
 
 #include <string>
 
+#include "modules/portal/xdg_desktop_portal_utils.h"
 #include "rtc_base/logging.h"
 
 namespace webrtc {
diff --git a/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h b/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h
index 59aaf13..deb57a4 100644
--- a/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h
+++ b/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h
@@ -15,10 +15,10 @@
 
 #include <string>
 
-#include "modules/desktop_capture/linux/wayland/portal_request_response.h"
-#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
-#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
-#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
+#include "modules/portal/portal_request_response.h"
+#include "modules/portal/scoped_glib.h"
+#include "modules/portal/xdg_desktop_portal_utils.h"
+#include "modules/portal/xdg_session_details.h"
 
 namespace webrtc {
 namespace xdg_portal {
diff --git a/modules/desktop_capture/linux/wayland/screencast_portal.cc b/modules/desktop_capture/linux/wayland/screencast_portal.cc
index f9cdb08..9848004 100644
--- a/modules/desktop_capture/linux/wayland/screencast_portal.cc
+++ b/modules/desktop_capture/linux/wayland/screencast_portal.cc
@@ -13,8 +13,8 @@
 #include <gio/gunixfdlist.h>
 #include <glib-object.h>
 
-#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
-#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
+#include "modules/portal/scoped_glib.h"
+#include "modules/portal/xdg_desktop_portal_utils.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 
diff --git a/modules/desktop_capture/linux/wayland/screencast_portal.h b/modules/desktop_capture/linux/wayland/screencast_portal.h
index 96e4ba3..c54d482 100644
--- a/modules/desktop_capture/linux/wayland/screencast_portal.h
+++ b/modules/desktop_capture/linux/wayland/screencast_portal.h
@@ -16,10 +16,10 @@
 #include <string>
 
 #include "modules/desktop_capture/desktop_capture_types.h"
-#include "modules/desktop_capture/linux/wayland/portal_request_response.h"
 #include "modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h"
-#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
-#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
+#include "modules/portal/portal_request_response.h"
+#include "modules/portal/xdg_desktop_portal_utils.h"
+#include "modules/portal/xdg_session_details.h"
 
 namespace webrtc {
 
diff --git a/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc b/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc
index dc07847..0c4900d 100644
--- a/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc
+++ b/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc
@@ -27,15 +27,6 @@
 
 namespace webrtc {
 
-PipeWireThreadLoopLock::PipeWireThreadLoopLock(pw_thread_loop* loop)
-    : loop_(loop) {
-  pw_thread_loop_lock(loop_);
-}
-
-PipeWireThreadLoopLock::~PipeWireThreadLoopLock() {
-  pw_thread_loop_unlock(loop_);
-}
-
 PipeWireVersion PipeWireVersion::Parse(const absl::string_view& version) {
   std::vector<absl::string_view> parsed_version = rtc::split(version, '.');
 
diff --git a/modules/desktop_capture/linux/wayland/screencast_stream_utils.h b/modules/desktop_capture/linux/wayland/screencast_stream_utils.h
index 70262c2..e04d7db 100644
--- a/modules/desktop_capture/linux/wayland/screencast_stream_utils.h
+++ b/modules/desktop_capture/linux/wayland/screencast_stream_utils.h
@@ -18,23 +18,12 @@
 
 #include "rtc_base/string_encode.h"
 
-struct pw_thread_loop;
 struct spa_pod;
 struct spa_pod_builder;
 struct spa_rectangle;
 
 namespace webrtc {
 
-// Locks pw_thread_loop in the current scope
-class PipeWireThreadLoopLock {
- public:
-  explicit PipeWireThreadLoopLock(pw_thread_loop* loop);
-  ~PipeWireThreadLoopLock();
-
- private:
-  pw_thread_loop* const loop_;
-};
-
 struct PipeWireVersion {
   static PipeWireVersion Parse(const absl::string_view& version);
 
diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
index 4651486..7a2b9ba 100644
--- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
+++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
@@ -20,8 +20,8 @@
 
 #include "absl/memory/memory.h"
 #include "modules/desktop_capture/linux/wayland/egl_dmabuf.h"
-#include "modules/desktop_capture/linux/wayland/pipewire_utils.h"
 #include "modules/desktop_capture/linux/wayland/screencast_stream_utils.h"
+#include "modules/portal/pipewire_utils.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/sanitizer.h"
diff --git a/modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.cc b/modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.cc
index bf212d0..3b82995 100644
--- a/modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.cc
+++ b/modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.cc
@@ -20,7 +20,7 @@
 #include <utility>
 #include <vector>
 
-#include "modules/desktop_capture/linux/wayland/pipewire_utils.h"
+#include "modules/portal/pipewire_utils.h"
 #include "rtc_base/logging.h"
 
 namespace webrtc {
diff --git a/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h b/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h
index f6ac92b..b213e50 100644
--- a/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h
+++ b/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h
@@ -11,101 +11,7 @@
 #ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_DESKTOP_PORTAL_UTILS_H_
 #define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_DESKTOP_PORTAL_UTILS_H_
 
-#include <gio/gio.h>
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "absl/strings/string_view.h"
-#include "modules/desktop_capture/linux/wayland/portal_request_response.h"
-#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
-#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
-#include "rtc_base/checks.h"
-#include "rtc_base/logging.h"
-
-namespace webrtc {
-namespace xdg_portal {
-
-constexpr char kDesktopBusName[] = "org.freedesktop.portal.Desktop";
-constexpr char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop";
-constexpr char kDesktopRequestObjectPath[] =
-    "/org/freedesktop/portal/desktop/request";
-constexpr char kSessionInterfaceName[] = "org.freedesktop.portal.Session";
-constexpr char kRequestInterfaceName[] = "org.freedesktop.portal.Request";
-constexpr char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast";
-
-using ProxyRequestCallback = void (*)(GObject*, GAsyncResult*, gpointer);
-using SessionRequestCallback = void (*)(GDBusProxy*, GAsyncResult*, gpointer);
-using SessionRequestResponseSignalHandler = void (*)(GDBusConnection*,
-                                                     const char*,
-                                                     const char*,
-                                                     const char*,
-                                                     const char*,
-                                                     GVariant*,
-                                                     gpointer);
-using StartRequestResponseSignalHandler = void (*)(GDBusConnection*,
-                                                   const char*,
-                                                   const char*,
-                                                   const char*,
-                                                   const char*,
-                                                   GVariant*,
-                                                   gpointer);
-using SessionStartRequestedHandler = void (*)(GDBusProxy*,
-                                              GAsyncResult*,
-                                              gpointer);
-
-std::string RequestResponseToString(RequestResponse request);
-
-RequestResponse RequestResponseFromPortalResponse(uint32_t portal_response);
-
-// Returns a string path for signal handle based on the provided connection and
-// token.
-std::string PrepareSignalHandle(absl::string_view token,
-                                GDBusConnection* connection);
-
-// Sets up the callback to execute when a response signal is received for the
-// given object.
-uint32_t SetupRequestResponseSignal(absl::string_view object_path,
-                                    const GDBusSignalCallback callback,
-                                    gpointer user_data,
-                                    GDBusConnection* connection);
-
-void RequestSessionProxy(absl::string_view interface_name,
-                         const ProxyRequestCallback proxy_request_callback,
-                         GCancellable* cancellable,
-                         gpointer user_data);
-
-void SetupSessionRequestHandlers(
-    absl::string_view portal_prefix,
-    const SessionRequestCallback session_request_callback,
-    const SessionRequestResponseSignalHandler request_response_signale_handler,
-    GDBusConnection* connection,
-    GDBusProxy* proxy,
-    GCancellable* cancellable,
-    std::string& portal_handle,
-    guint& session_request_signal_id,
-    gpointer user_data);
-
-void StartSessionRequest(
-    absl::string_view prefix,
-    absl::string_view session_handle,
-    const StartRequestResponseSignalHandler signal_handler,
-    const SessionStartRequestedHandler session_started_handler,
-    GDBusProxy* proxy,
-    GDBusConnection* connection,
-    GCancellable* cancellable,
-    guint& start_request_signal_id,
-    std::string& start_handle,
-    gpointer user_data);
-
-// Tears down the portal session and cleans up related objects.
-void TearDownSession(absl::string_view session_handle,
-                     GDBusProxy* proxy,
-                     GCancellable* cancellable,
-                     GDBusConnection* connection);
-
-}  // namespace xdg_portal
-}  // namespace webrtc
+// TODO(bugs.webrtc.org/14187): remove when all users are gone
+#include "modules/portal/xdg_desktop_portal_utils.h"
 
 #endif  // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_DESKTOP_PORTAL_UTILS_H_
diff --git a/modules/desktop_capture/linux/wayland/xdg_session_details.h b/modules/desktop_capture/linux/wayland/xdg_session_details.h
index b70ac4a..9feff5b 100644
--- a/modules/desktop_capture/linux/wayland/xdg_session_details.h
+++ b/modules/desktop_capture/linux/wayland/xdg_session_details.h
@@ -11,23 +11,7 @@
 #ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_SESSION_DETAILS_H_
 #define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_SESSION_DETAILS_H_
 
-#include <gio/gio.h>
-
-#include <string>
-
-namespace webrtc {
-namespace xdg_portal {
-
-// Details of the session associated with XDG desktop portal session. Portal API
-// calls can be invoked by utilizing the information here.
-struct SessionDetails {
-  GDBusProxy* proxy = nullptr;
-  GCancellable* cancellable = nullptr;
-  std::string session_handle;
-  uint32_t pipewire_stream_node_id = 0;
-};
-
-}  // namespace xdg_portal
-}  // namespace webrtc
+// TODO(bugs.webrtc.org/14187): remove when all users are gone
+#include "modules/portal/xdg_session_details.h"
 
 #endif  // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_SESSION_DETAILS_H_
diff --git a/modules/portal/BUILD.gn b/modules/portal/BUILD.gn
new file mode 100644
index 0000000..730d327
--- /dev/null
+++ b/modules/portal/BUILD.gn
@@ -0,0 +1,124 @@
+# Copyright (c) 2022 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.
+
+import("//build/config/linux/pkg_config.gni")
+import("//tools/generate_stubs/rules.gni")
+import("../../webrtc.gni")
+
+assert(is_linux)
+assert(rtc_use_pipewire)
+
+pkg_config("gio") {
+  packages = [
+    "gio-2.0",
+    "gio-unix-2.0",
+  ]
+}
+
+pkg_config("pipewire") {
+  packages = [ "libpipewire-0.3" ]
+  if (!rtc_link_pipewire) {
+    ignore_libs = true
+  }
+}
+
+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) {
+  # When libpipewire is not directly linked, use stubs to allow for dlopening of
+  # the binary.
+  generate_stubs("pipewire_stubs") {
+    configs = [
+      "../../:common_config",
+      ":pipewire",
+    ]
+    deps = [ "../../rtc_base" ]
+    extra_header = "pipewire_stub_header.fragment"
+    logging_function = "RTC_LOG(LS_VERBOSE)"
+    logging_include = "rtc_base/logging.h"
+    output_name = "pipewire_stubs"
+    path_from_source = "modules/portal"
+    sigs = [ "pipewire.sigs" ]
+    if (!build_with_chromium) {
+      macro_include = "rtc_base/system/no_cfi_icall.h"
+      macro_deps = [ "../../rtc_base/system:no_cfi_icall" ]
+    }
+  }
+}
+
+config("pipewire_base") {
+  configs = [
+    ":gio",
+    ":pipewire",
+  ]
+}
+
+config("pipewire_all") {
+  configs = [
+    ":pipewire_base",
+    ":gbm",
+    ":egl",
+    ":epoxy",
+    ":libdrm",
+  ]
+}
+
+config("pipewire_config") {
+  defines = [ "WEBRTC_USE_PIPEWIRE" ]
+
+  # Chromecast build config overrides `WEBRTC_USE_PIPEWIRE` even when
+  # `rtc_use_pipewire` is not set, which causes pipewire_config to not be
+  # included in targets. More details in: webrtc:13898
+  if (is_linux && !is_castos) {
+    defines += [ "WEBRTC_USE_GIO" ]
+  }
+}
+
+rtc_library("portal") {
+  sources = [
+    "pipewire_utils.cc",
+    "pipewire_utils.h",
+    "portal_request_response.h",
+    "scoped_glib.cc",
+    "scoped_glib.h",
+    "xdg_desktop_portal_utils.cc",
+    "xdg_desktop_portal_utils.h",
+    "xdg_session_details.h",
+  ]
+
+  configs += [
+    ":gio",
+    ":pipewire",
+    ":pipewire_config",
+  ]
+
+  deps = [
+    "../../rtc_base:checks",
+    "../../rtc_base:logging",
+    "../../rtc_base:sanitizer",
+  ]
+  absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
+
+  if (!rtc_link_pipewire) {
+    defines = [ "WEBRTC_DLOPEN_PIPEWIRE" ]
+
+    deps += [ ":pipewire_stubs" ]
+  }
+}
diff --git a/modules/portal/OWNERS b/modules/portal/OWNERS
new file mode 100644
index 0000000..e3bc32e
--- /dev/null
+++ b/modules/portal/OWNERS
@@ -0,0 +1,2 @@
+alcooper@chromium.org
+mfoltz@chromium.org
diff --git a/modules/desktop_capture/linux/wayland/pipewire.sigs b/modules/portal/pipewire.sigs
similarity index 100%
rename from modules/desktop_capture/linux/wayland/pipewire.sigs
rename to modules/portal/pipewire.sigs
diff --git a/modules/desktop_capture/linux/wayland/pipewire_stub_header.fragment b/modules/portal/pipewire_stub_header.fragment
similarity index 100%
rename from modules/desktop_capture/linux/wayland/pipewire_stub_header.fragment
rename to modules/portal/pipewire_stub_header.fragment
diff --git a/modules/desktop_capture/linux/wayland/pipewire_utils.cc b/modules/portal/pipewire_utils.cc
similarity index 68%
rename from modules/desktop_capture/linux/wayland/pipewire_utils.cc
rename to modules/portal/pipewire_utils.cc
index 878e459..fd96b4a 100644
--- a/modules/desktop_capture/linux/wayland/pipewire_utils.cc
+++ b/modules/portal/pipewire_utils.cc
@@ -8,12 +8,14 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "modules/desktop_capture/linux/wayland/pipewire_utils.h"
+#include "modules/portal/pipewire_utils.h"
+
+#include <pipewire/pipewire.h>
 
 #include "rtc_base/sanitizer.h"
 
 #if defined(WEBRTC_DLOPEN_PIPEWIRE)
-#include "modules/desktop_capture/linux/wayland/pipewire_stubs.h"
+#include "modules/portal/pipewire_stubs.h"
 #endif  // defined(WEBRTC_DLOPEN_PIPEWIRE)
 
 namespace webrtc {
@@ -23,10 +25,10 @@
 #if defined(WEBRTC_DLOPEN_PIPEWIRE)
   static constexpr char kPipeWireLib[] = "libpipewire-0.3.so.0";
 
-  using modules_desktop_capture_linux_wayland::InitializeStubs;
-  using modules_desktop_capture_linux_wayland::kModulePipewire;
+  using modules_portal::InitializeStubs;
+  using modules_portal::kModulePipewire;
 
-  modules_desktop_capture_linux_wayland::StubPathMap paths;
+  modules_portal::StubPathMap paths;
 
   // Check if the PipeWire library is available.
   paths[kModulePipewire].push_back(kPipeWireLib);
@@ -39,4 +41,13 @@
 #endif  // defined(WEBRTC_DLOPEN_PIPEWIRE)
 }
 
+PipeWireThreadLoopLock::PipeWireThreadLoopLock(pw_thread_loop* loop)
+    : loop_(loop) {
+  pw_thread_loop_lock(loop_);
+}
+
+PipeWireThreadLoopLock::~PipeWireThreadLoopLock() {
+  pw_thread_loop_unlock(loop_);
+}
+
 }  // namespace webrtc
diff --git a/modules/desktop_capture/linux/wayland/pipewire_utils.h b/modules/portal/pipewire_utils.h
similarity index 65%
rename from modules/desktop_capture/linux/wayland/pipewire_utils.h
rename to modules/portal/pipewire_utils.h
index b785d39..0f5ccf3 100644
--- a/modules/desktop_capture/linux/wayland/pipewire_utils.h
+++ b/modules/portal/pipewire_utils.h
@@ -8,8 +8,10 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PIPEWIRE_UTILS_H_
-#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PIPEWIRE_UTILS_H_
+#ifndef MODULES_PORTAL_PIPEWIRE_UTILS_H_
+#define MODULES_PORTAL_PIPEWIRE_UTILS_H_
+
+struct pw_thread_loop;
 
 namespace webrtc {
 
@@ -18,6 +20,16 @@
 // running nor does it establish a connection to one.
 bool InitializePipeWire();
 
+// Locks pw_thread_loop in the current scope
+class PipeWireThreadLoopLock {
+ public:
+  explicit PipeWireThreadLoopLock(pw_thread_loop* loop);
+  ~PipeWireThreadLoopLock();
+
+ private:
+  pw_thread_loop* const loop_;
+};
+
 }  // namespace webrtc
 
-#endif  // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PIPEWIRE_UTILS_H_
+#endif  // MODULES_PORTAL_PIPEWIRE_UTILS_H_
diff --git a/modules/portal/portal_request_response.h b/modules/portal/portal_request_response.h
new file mode 100644
index 0000000..5fac4eb
--- /dev/null
+++ b/modules/portal/portal_request_response.h
@@ -0,0 +1,34 @@
+/*
+ *  Copyright (c) 2022 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_PORTAL_PORTAL_REQUEST_RESPONSE_H_
+#define MODULES_PORTAL_PORTAL_REQUEST_RESPONSE_H_
+
+namespace webrtc {
+namespace xdg_portal {
+
+// Contains type of responses that can be observed when making a request to
+// a desktop portal interface.
+enum class RequestResponse {
+  // Unknown, the initialized status.
+  kUnknown,
+  // Success, the request is carried out.
+  kSuccess,
+  // The user cancelled the interaction.
+  kUserCancelled,
+  // The user interaction was ended in some other way.
+  kError,
+
+  kMaxValue = kError,
+};
+
+}  // namespace xdg_portal
+}  // namespace webrtc
+#endif  // MODULES_PORTAL_PORTAL_REQUEST_RESPONSE_H_
diff --git a/modules/desktop_capture/linux/wayland/scoped_glib.cc b/modules/portal/scoped_glib.cc
similarity index 93%
rename from modules/desktop_capture/linux/wayland/scoped_glib.cc
rename to modules/portal/scoped_glib.cc
index 0d9a87d..cb4c805 100644
--- a/modules/desktop_capture/linux/wayland/scoped_glib.cc
+++ b/modules/portal/scoped_glib.cc
@@ -8,7 +8,7 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
+#include "modules/portal/scoped_glib.h"
 
 namespace webrtc {
 
diff --git a/modules/portal/scoped_glib.h b/modules/portal/scoped_glib.h
new file mode 100644
index 0000000..b2aaa2e
--- /dev/null
+++ b/modules/portal/scoped_glib.h
@@ -0,0 +1,65 @@
+/*
+ *  Copyright 2022 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_PORTAL_SCOPED_GLIB_H_
+#define MODULES_PORTAL_SCOPED_GLIB_H_
+
+#include <gio/gio.h>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+template <class T>
+class Scoped {
+ public:
+  Scoped() {}
+  explicit Scoped(T* val) { ptr_ = val; }
+  ~Scoped() { RTC_DCHECK_NOTREACHED(); }
+
+  T* operator->() const { return ptr_; }
+
+  explicit operator bool() const { return ptr_ != nullptr; }
+
+  bool operator!() const { return ptr_ == nullptr; }
+
+  T* get() const { return ptr_; }
+
+  T** receive() {
+    RTC_CHECK(!ptr_);
+    return &ptr_;
+  }
+
+  Scoped& operator=(T* val) {
+    RTC_DCHECK(val);
+    ptr_ = val;
+    return *this;
+  }
+
+ protected:
+  T* ptr_ = nullptr;
+};
+
+template <>
+Scoped<GError>::~Scoped();
+template <>
+Scoped<char>::~Scoped();
+template <>
+Scoped<GVariant>::~Scoped();
+template <>
+Scoped<GVariantIter>::~Scoped();
+template <>
+Scoped<GDBusMessage>::~Scoped();
+template <>
+Scoped<GUnixFDList>::~Scoped();
+
+}  // namespace webrtc
+
+#endif  // MODULES_PORTAL_SCOPED_GLIB_H_
diff --git a/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc b/modules/portal/xdg_desktop_portal_utils.cc
similarity index 97%
rename from modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc
rename to modules/portal/xdg_desktop_portal_utils.cc
index 75dbf2b..271e084 100644
--- a/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc
+++ b/modules/portal/xdg_desktop_portal_utils.cc
@@ -7,12 +7,12 @@
  *  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/wayland/xdg_desktop_portal_utils.h"
+#include "modules/portal/xdg_desktop_portal_utils.h"
 
 #include <string>
 
 #include "absl/strings/string_view.h"
-#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
+#include "modules/portal/scoped_glib.h"
 #include "rtc_base/logging.h"
 
 namespace webrtc {
diff --git a/modules/portal/xdg_desktop_portal_utils.h b/modules/portal/xdg_desktop_portal_utils.h
new file mode 100644
index 0000000..8571c64
--- /dev/null
+++ b/modules/portal/xdg_desktop_portal_utils.h
@@ -0,0 +1,111 @@
+/*
+ *  Copyright 2022 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_PORTAL_XDG_DESKTOP_PORTAL_UTILS_H_
+#define MODULES_PORTAL_XDG_DESKTOP_PORTAL_UTILS_H_
+
+#include <gio/gio.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "modules/portal/portal_request_response.h"
+#include "modules/portal/scoped_glib.h"
+#include "modules/portal/xdg_session_details.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace xdg_portal {
+
+constexpr char kDesktopBusName[] = "org.freedesktop.portal.Desktop";
+constexpr char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop";
+constexpr char kDesktopRequestObjectPath[] =
+    "/org/freedesktop/portal/desktop/request";
+constexpr char kSessionInterfaceName[] = "org.freedesktop.portal.Session";
+constexpr char kRequestInterfaceName[] = "org.freedesktop.portal.Request";
+constexpr char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast";
+
+using ProxyRequestCallback = void (*)(GObject*, GAsyncResult*, gpointer);
+using SessionRequestCallback = void (*)(GDBusProxy*, GAsyncResult*, gpointer);
+using SessionRequestResponseSignalHandler = void (*)(GDBusConnection*,
+                                                     const char*,
+                                                     const char*,
+                                                     const char*,
+                                                     const char*,
+                                                     GVariant*,
+                                                     gpointer);
+using StartRequestResponseSignalHandler = void (*)(GDBusConnection*,
+                                                   const char*,
+                                                   const char*,
+                                                   const char*,
+                                                   const char*,
+                                                   GVariant*,
+                                                   gpointer);
+using SessionStartRequestedHandler = void (*)(GDBusProxy*,
+                                              GAsyncResult*,
+                                              gpointer);
+
+std::string RequestResponseToString(RequestResponse request);
+
+RequestResponse RequestResponseFromPortalResponse(uint32_t portal_response);
+
+// Returns a string path for signal handle based on the provided connection and
+// token.
+std::string PrepareSignalHandle(absl::string_view token,
+                                GDBusConnection* connection);
+
+// Sets up the callback to execute when a response signal is received for the
+// given object.
+uint32_t SetupRequestResponseSignal(absl::string_view object_path,
+                                    const GDBusSignalCallback callback,
+                                    gpointer user_data,
+                                    GDBusConnection* connection);
+
+void RequestSessionProxy(absl::string_view interface_name,
+                         const ProxyRequestCallback proxy_request_callback,
+                         GCancellable* cancellable,
+                         gpointer user_data);
+
+void SetupSessionRequestHandlers(
+    absl::string_view portal_prefix,
+    const SessionRequestCallback session_request_callback,
+    const SessionRequestResponseSignalHandler request_response_signale_handler,
+    GDBusConnection* connection,
+    GDBusProxy* proxy,
+    GCancellable* cancellable,
+    std::string& portal_handle,
+    guint& session_request_signal_id,
+    gpointer user_data);
+
+void StartSessionRequest(
+    absl::string_view prefix,
+    absl::string_view session_handle,
+    const StartRequestResponseSignalHandler signal_handler,
+    const SessionStartRequestedHandler session_started_handler,
+    GDBusProxy* proxy,
+    GDBusConnection* connection,
+    GCancellable* cancellable,
+    guint& start_request_signal_id,
+    std::string& start_handle,
+    gpointer user_data);
+
+// Tears down the portal session and cleans up related objects.
+void TearDownSession(absl::string_view session_handle,
+                     GDBusProxy* proxy,
+                     GCancellable* cancellable,
+                     GDBusConnection* connection);
+
+}  // namespace xdg_portal
+}  // namespace webrtc
+
+#endif  // MODULES_PORTAL_XDG_DESKTOP_PORTAL_UTILS_H_
diff --git a/modules/portal/xdg_session_details.h b/modules/portal/xdg_session_details.h
new file mode 100644
index 0000000..ab52508
--- /dev/null
+++ b/modules/portal/xdg_session_details.h
@@ -0,0 +1,33 @@
+/*
+ *  Copyright 2022 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_PORTAL_XDG_SESSION_DETAILS_H_
+#define MODULES_PORTAL_XDG_SESSION_DETAILS_H_
+
+#include <gio/gio.h>
+
+#include <string>
+
+namespace webrtc {
+namespace xdg_portal {
+
+// Details of the session associated with XDG desktop portal session. Portal API
+// calls can be invoked by utilizing the information here.
+struct SessionDetails {
+  GDBusProxy* proxy = nullptr;
+  GCancellable* cancellable = nullptr;
+  std::string session_handle;
+  uint32_t pipewire_stream_node_id = 0;
+};
+
+}  // namespace xdg_portal
+}  // namespace webrtc
+
+#endif  // MODULES_PORTAL_XDG_SESSION_DETAILS_H_