Add support for PipeWire 0.3

Adds a new "rtc_pipewire_version" build option to specify version of
PipeWire we want to build against. We use version "0.2" by default
which is version of PipeWire we currently have in sysroot and which
is supported even on older systems like RHEL7 and Debian.

Bug: chromium:1146942
Change-Id: Ib74b52fa87623a3f960e419916b01586aaeba47f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/195441
Commit-Queue: Jamie Walch <jamiewalch@chromium.org>
Reviewed-by: Jamie Walch <jamiewalch@chromium.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32768}
diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn
index 72273f9..3773fac 100644
--- a/modules/desktop_capture/BUILD.gn
+++ b/modules/desktop_capture/BUILD.gn
@@ -11,6 +11,11 @@
 import("//tools/generate_stubs/rules.gni")
 import("../../webrtc.gni")
 
+if (rtc_use_pipewire) {
+  assert(rtc_pipewire_version == "0.2" || rtc_pipewire_version == "0.3",
+         "Unsupported PipeWire version")
+}
+
 use_desktop_capture_differ_sse2 = current_cpu == "x86" || current_cpu == "x64"
 
 config("x11_config") {
@@ -206,22 +211,41 @@
       ]
     }
 
-    if (rtc_link_pipewire) {
+    if (rtc_pipewire_version == "0.3") {
       pkg_config("pipewire") {
-        packages = [ "libpipewire-0.2" ]
+        packages = [ "libpipewire-0.3" ]
+        if (!rtc_link_pipewire) {
+          ignore_libs = true
+        }
       }
     } else {
+      pkg_config("pipewire") {
+        packages = [ "libpipewire-0.2" ]
+        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.
       generate_stubs("pipewire_stubs") {
-        configs = [ "../../:common_config" ]
+        configs = [
+          "../../:common_config",
+          ":pipewire",
+        ]
         deps = [ "../../rtc_base" ]
         extra_header = "linux/pipewire_stub_header.fragment"
         logging_function = "RTC_LOG(LS_VERBOSE)"
         logging_include = "rtc_base/logging.h"
         output_name = "linux/pipewire_stubs"
         path_from_source = "modules/desktop_capture/linux"
-        sigs = [ "linux/pipewire.sigs" ]
+        if (rtc_pipewire_version == "0.3") {
+          sigs = [ "linux/pipewire03.sigs" ]
+        } else {
+          sigs = [ "linux/pipewire02.sigs" ]
+        }
       }
     }
 
@@ -526,11 +550,10 @@
     configs += [
       ":pipewire_config",
       ":gio",
+      ":pipewire",
     ]
 
-    if (rtc_link_pipewire) {
-      configs += [ ":pipewire" ]
-    } else {
+    if (!rtc_link_pipewire) {
       deps += [ ":pipewire_stubs" ]
     }
   }
diff --git a/modules/desktop_capture/linux/base_capturer_pipewire.cc b/modules/desktop_capture/linux/base_capturer_pipewire.cc
index bc96535..c302a08 100644
--- a/modules/desktop_capture/linux/base_capturer_pipewire.cc
+++ b/modules/desktop_capture/linux/base_capturer_pipewire.cc
@@ -14,8 +14,10 @@
 #include <glib-object.h>
 #include <spa/param/format-utils.h>
 #include <spa/param/props.h>
+#if !PW_CHECK_VERSION(0, 3, 0)
 #include <spa/param/video/raw-utils.h>
 #include <spa/support/type-map.h>
+#endif
 
 #include <sys/ioctl.h>
 #include <sys/mman.h>
@@ -34,7 +36,11 @@
 #include "modules/desktop_capture/linux/pipewire_stubs.h"
 
 using modules_desktop_capture_linux::InitializeStubs;
-using modules_desktop_capture_linux::kModulePipewire;
+#if PW_CHECK_VERSION(0, 3, 0)
+using modules_desktop_capture_linux::kModulePipewire03;
+#else
+using modules_desktop_capture_linux::kModulePipewire02;
+#endif
 using modules_desktop_capture_linux::StubPathMap;
 #endif  // defined(WEBRTC_DLOPEN_PIPEWIRE)
 
@@ -51,8 +57,12 @@
 const int kBytesPerPixel = 4;
 
 #if defined(WEBRTC_DLOPEN_PIPEWIRE)
+#if PW_CHECK_VERSION(0, 3, 0)
+const char kPipeWireLib[] = "libpipewire-0.3.so.0";
+#else
 const char kPipeWireLib[] = "libpipewire-0.2.so.1";
 #endif
+#endif
 
 // static
 struct dma_buf_sync {
@@ -185,6 +195,18 @@
   }
 }
 
+#if PW_CHECK_VERSION(0, 3, 0)
+void BaseCapturerPipeWire::OnCoreError(void* data,
+                                       uint32_t id,
+                                       int seq,
+                                       int res,
+                                       const char* message) {
+  BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(data);
+  RTC_DCHECK(that);
+
+  RTC_LOG(LS_ERROR) << "PipeWire remote error: " << message;
+}
+#else
 // static
 void BaseCapturerPipeWire::OnStateChanged(void* data,
                                           pw_remote_state old_state,
@@ -199,7 +221,7 @@
       break;
     case PW_REMOTE_STATE_CONNECTED:
       RTC_LOG(LS_INFO) << "PipeWire remote state: connected.";
-      that->CreateReceivingStream();
+      that->pw_stream_ = that->CreateReceivingStream();
       break;
     case PW_REMOTE_STATE_CONNECTING:
       RTC_LOG(LS_INFO) << "PipeWire remote state: connecting.";
@@ -209,6 +231,7 @@
       break;
   }
 }
+#endif
 
 // static
 void BaseCapturerPipeWire::OnStreamStateChanged(void* data,
@@ -218,6 +241,18 @@
   BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(data);
   RTC_DCHECK(that);
 
+#if PW_CHECK_VERSION(0, 3, 0)
+  switch (state) {
+    case PW_STREAM_STATE_ERROR:
+      RTC_LOG(LS_ERROR) << "PipeWire stream state error: " << error_message;
+      break;
+    case PW_STREAM_STATE_PAUSED:
+    case PW_STREAM_STATE_STREAMING:
+    case PW_STREAM_STATE_UNCONNECTED:
+    case PW_STREAM_STATE_CONNECTING:
+      break;
+  }
+#else
   switch (state) {
     case PW_STREAM_STATE_ERROR:
       RTC_LOG(LS_ERROR) << "PipeWire stream state error: " << error_message;
@@ -232,28 +267,48 @@
     case PW_STREAM_STATE_STREAMING:
       break;
   }
+#endif
 }
 
 // static
+#if PW_CHECK_VERSION(0, 3, 0)
+void BaseCapturerPipeWire::OnStreamParamChanged(void* data,
+                                                uint32_t id,
+                                                const struct spa_pod* format) {
+#else
 void BaseCapturerPipeWire::OnStreamFormatChanged(void* data,
                                                  const struct spa_pod* format) {
+#endif
   BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(data);
   RTC_DCHECK(that);
 
   RTC_LOG(LS_INFO) << "PipeWire stream format changed.";
 
+#if PW_CHECK_VERSION(0, 3, 0)
+  if (!format || id != SPA_PARAM_Format) {
+#else
   if (!format) {
     pw_stream_finish_format(that->pw_stream_, /*res=*/0, /*params=*/nullptr,
                             /*n_params=*/0);
+#endif
     return;
   }
 
+#if PW_CHECK_VERSION(0, 3, 0)
+  spa_format_video_raw_parse(format, &that->spa_video_format_);
+#else
   that->spa_video_format_ = new spa_video_info_raw();
   spa_format_video_raw_parse(format, that->spa_video_format_,
                              &that->pw_type_->format_video);
+#endif
 
+#if PW_CHECK_VERSION(0, 3, 0)
+  auto width = that->spa_video_format_.size.width;
+  auto height = that->spa_video_format_.size.height;
+#else
   auto width = that->spa_video_format_->size.width;
   auto height = that->spa_video_format_->size.height;
+#endif
   auto stride = SPA_ROUND_UP_N(width * kBytesPerPixel, 4);
   auto size = height * stride;
 
@@ -264,6 +319,22 @@
 
   // Setup buffers and meta header for new format.
   const struct spa_pod* params[3];
+#if PW_CHECK_VERSION(0, 3, 0)
+  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)));
+  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,
+      SPA_POD_Int(sizeof(struct spa_meta_header))));
+  params[2] = 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_VideoCrop), SPA_PARAM_META_size,
+      SPA_POD_Int(sizeof(struct spa_meta_region))));
+  pw_stream_update_params(that->pw_stream_, params, 3);
+#else
   params[0] = reinterpret_cast<spa_pod*>(spa_pod_builder_object(
       &builder,
       // id to enumerate buffer requirements
@@ -303,6 +374,7 @@
       ":", that->pw_core_type_->param_meta.size, "i",
       sizeof(struct spa_meta_video_crop)));
   pw_stream_finish_format(that->pw_stream_, /*res=*/0, params, /*n_params=*/3);
+#endif
 }
 
 // static
@@ -340,6 +412,7 @@
     pw_thread_loop_stop(pw_main_loop_);
   }
 
+#if !PW_CHECK_VERSION(0, 3, 0)
   if (pw_type_) {
     delete pw_type_;
   }
@@ -347,26 +420,41 @@
   if (spa_video_format_) {
     delete spa_video_format_;
   }
+#endif
 
   if (pw_stream_) {
     pw_stream_destroy(pw_stream_);
   }
 
+#if !PW_CHECK_VERSION(0, 3, 0)
   if (pw_remote_) {
     pw_remote_destroy(pw_remote_);
   }
+#endif
 
+#if PW_CHECK_VERSION(0, 3, 0)
+  if (pw_core_) {
+    pw_core_disconnect(pw_core_);
+  }
+
+  if (pw_context_) {
+    pw_context_destroy(pw_context_);
+  }
+#else
   if (pw_core_) {
     pw_core_destroy(pw_core_);
   }
+#endif
 
   if (pw_main_loop_) {
     pw_thread_loop_destroy(pw_main_loop_);
   }
 
+#if !PW_CHECK_VERSION(0, 3, 0)
   if (pw_loop_) {
     pw_loop_destroy(pw_loop_);
   }
+#endif
 
   if (start_request_signal_id_) {
     g_dbus_connection_signal_unsubscribe(connection_, start_request_signal_id_);
@@ -425,7 +513,11 @@
   StubPathMap paths;
 
   // Check if the PipeWire library is available.
-  paths[kModulePipewire].push_back(kPipeWireLib);
+#if PW_CHECK_VERSION(0, 3, 0)
+  paths[kModulePipewire03].push_back(kPipeWireLib);
+#else
+  paths[kModulePipewire02].push_back(kPipeWireLib);
+#endif
   if (!InitializeStubs(paths)) {
     RTC_LOG(LS_ERROR) << "Failed to load the PipeWire library and symbols.";
     portal_init_failed_ = true;
@@ -435,6 +527,24 @@
 
   pw_init(/*argc=*/nullptr, /*argc=*/nullptr);
 
+#if PW_CHECK_VERSION(0, 3, 0)
+  pw_main_loop_ = pw_thread_loop_new("pipewire-main-loop", nullptr);
+
+  pw_thread_loop_lock(pw_main_loop_);
+
+  pw_context_ =
+      pw_context_new(pw_thread_loop_get_loop(pw_main_loop_), nullptr, 0);
+  if (!pw_context_) {
+    RTC_LOG(LS_ERROR) << "Failed to create PipeWire context";
+    return;
+  }
+
+  pw_core_ = pw_context_connect(pw_context_, nullptr, 0);
+  if (!pw_core_) {
+    RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context";
+    return;
+  }
+#else
   pw_loop_ = pw_loop_new(/*properties=*/nullptr);
   pw_main_loop_ = pw_thread_loop_new(pw_loop_, "pipewire-main-loop");
 
@@ -445,8 +555,18 @@
   pw_remote_ = pw_remote_new(pw_core_, nullptr, /*user_data_size=*/0);
 
   InitPipeWireTypes();
+#endif
 
   // Initialize event handlers, remote end and stream-related.
+#if PW_CHECK_VERSION(0, 3, 0)
+  pw_core_events_.version = PW_VERSION_CORE_EVENTS;
+  pw_core_events_.error = &OnCoreError;
+
+  pw_stream_events_.version = PW_VERSION_STREAM_EVENTS;
+  pw_stream_events_.state_changed = &OnStreamStateChanged;
+  pw_stream_events_.param_changed = &OnStreamParamChanged;
+  pw_stream_events_.process = &OnStreamProcess;
+#else
   pw_remote_events_.version = PW_VERSION_REMOTE_EVENTS;
   pw_remote_events_.state_changed = &OnStateChanged;
 
@@ -454,10 +574,21 @@
   pw_stream_events_.state_changed = &OnStreamStateChanged;
   pw_stream_events_.format_changed = &OnStreamFormatChanged;
   pw_stream_events_.process = &OnStreamProcess;
+#endif
 
+#if PW_CHECK_VERSION(0, 3, 0)
+  pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this);
+
+  pw_stream_ = CreateReceivingStream();
+  if (!pw_stream_) {
+    RTC_LOG(LS_ERROR) << "Failed to create PipeWire stream";
+    return;
+  }
+#else
   pw_remote_add_listener(pw_remote_, &spa_remote_listener_, &pw_remote_events_,
                          this);
   pw_remote_connect_fd(pw_remote_, pw_fd_);
+#endif
 
   if (pw_thread_loop_start(pw_main_loop_) < 0) {
     RTC_LOG(LS_ERROR) << "Failed to start main PipeWire loop";
@@ -469,6 +600,7 @@
   RTC_LOG(LS_INFO) << "PipeWire remote opened.";
 }
 
+#if !PW_CHECK_VERSION(0, 3, 0)
 void BaseCapturerPipeWire::InitPipeWireTypes() {
   spa_type_map* map = pw_core_type_->map;
   pw_type_ = new PipeWireType();
@@ -478,18 +610,44 @@
   spa_type_format_video_map(map, &pw_type_->format_video);
   spa_type_video_format_map(map, &pw_type_->video_format);
 }
+#endif
 
-void BaseCapturerPipeWire::CreateReceivingStream() {
+pw_stream* BaseCapturerPipeWire::CreateReceivingStream() {
+#if !PW_CHECK_VERSION(0, 3, 0)
+  if (pw_remote_get_state(pw_remote_, nullptr) != PW_REMOTE_STATE_CONNECTED) {
+    RTC_LOG(LS_ERROR) << "Cannot create pipewire stream";
+    return nullptr;
+  }
+#endif
   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");
-  pw_stream_ = pw_stream_new(pw_remote_, "webrtc-consume-stream", reuseProps);
+#if PW_CHECK_VERSION(0, 3, 0)
+  auto stream = pw_stream_new(pw_core_, "webrtc-consume-stream", reuseProps);
+#else
+  auto stream = pw_stream_new(pw_remote_, "webrtc-consume-stream", reuseProps);
+#endif
 
   uint8_t buffer[1024] = {};
   const spa_pod* params[1];
   spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)};
+
+#if PW_CHECK_VERSION(0, 3, 0)
+  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));
+#else
   params[0] = reinterpret_cast<spa_pod*>(spa_pod_builder_object(
       &builder,
       // id to enumerate formats
@@ -507,18 +665,26 @@
       // max values and the format is undecided (u) to allow negotiation
       ":", pw_type_->format_video.size, "Rru", &pwMinScreenBounds,
       SPA_POD_PROP_MIN_MAX(&pwMinScreenBounds, &pwMaxScreenBounds)));
+#endif
 
-  pw_stream_add_listener(pw_stream_, &spa_stream_listener_, &pw_stream_events_,
+  pw_stream_add_listener(stream, &spa_stream_listener_, &pw_stream_events_,
                          this);
+#if PW_CHECK_VERSION(0, 3, 0)
+  if (pw_stream_connect(stream, PW_DIRECTION_INPUT, pw_stream_node_id_,
+                        PW_STREAM_FLAG_AUTOCONNECT, params, 1) != 0) {
+#else
   pw_stream_flags flags = static_cast<pw_stream_flags>(
       PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE);
-  if (pw_stream_connect(pw_stream_, PW_DIRECTION_INPUT, /*port_path=*/nullptr,
+  if (pw_stream_connect(stream, PW_DIRECTION_INPUT, /*port_path=*/nullptr,
                         flags, params,
                         /*n_params=*/1) != 0) {
+#endif
     RTC_LOG(LS_ERROR) << "Could not connect receiving stream.";
     portal_init_failed_ = true;
-    return;
+    return nullptr;
   }
+
+  return stream;
 }
 
 void BaseCapturerPipeWire::HandleBuffer(pw_buffer* buffer) {
@@ -531,15 +697,24 @@
     return;
   }
 
+#if PW_CHECK_VERSION(0, 3, 0)
+  if (spaBuffer->datas[0].type == SPA_DATA_MemFd ||
+      spaBuffer->datas[0].type == SPA_DATA_DmaBuf) {
+#else
   if (spaBuffer->datas[0].type == pw_core_type_->data.MemFd ||
       spaBuffer->datas[0].type == pw_core_type_->data.DmaBuf) {
+#endif
     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,
+#if PW_CHECK_VERSION(0, 3, 0)
+        spaBuffer->datas[0].type == SPA_DATA_DmaBuf,
+#else
         spaBuffer->datas[0].type == pw_core_type_->data.DmaBuf,
+#endif
         spaBuffer->datas[0].fd);
 
     if (!map) {
@@ -548,12 +723,20 @@
       return;
     }
 
+#if PW_CHECK_VERSION(0, 3, 0)
+    if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf) {
+#else
     if (spaBuffer->datas[0].type == pw_core_type_->data.DmaBuf) {
+#endif
       SyncDmaBuf(spaBuffer->datas[0].fd, DMA_BUF_SYNC_START);
     }
 
     src = SPA_MEMBER(map.get(), spaBuffer->datas[0].mapoffset, uint8_t);
+#if PW_CHECK_VERSION(0, 3, 0)
+  } else if (spaBuffer->datas[0].type == SPA_DATA_MemPtr) {
+#else
   } else if (spaBuffer->datas[0].type == pw_core_type_->data.MemPtr) {
+#endif
     src = static_cast<uint8_t*>(spaBuffer->datas[0].data);
   }
 
@@ -561,15 +744,28 @@
     return;
   }
 
+#if PW_CHECK_VERSION(0, 3, 0)
+  struct spa_meta_region* video_metadata =
+      static_cast<struct spa_meta_region*>(spa_buffer_find_meta_data(
+          spaBuffer, SPA_META_VideoCrop, sizeof(*video_metadata)));
+#else
   struct spa_meta_video_crop* video_metadata =
       static_cast<struct spa_meta_video_crop*>(
           spa_buffer_find_meta(spaBuffer, pw_core_type_->meta.VideoCrop));
+#endif
 
   // 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
   // just quit now.
+#if PW_CHECK_VERSION(0, 3, 0)
+  if (video_metadata && (video_metadata->region.size.width >
+                             static_cast<uint32_t>(desktop_size_.width()) ||
+                         video_metadata->region.size.height >
+                             static_cast<uint32_t>(desktop_size_.height()))) {
+#else
   if (video_metadata && (video_metadata->width > desktop_size_.width() ||
                          video_metadata->height > desktop_size_.height())) {
+#endif
     RTC_LOG(LS_ERROR) << "Stream metadata sizes are wrong!";
     return;
   }
@@ -578,18 +774,35 @@
   // video stream size, so we need to adjust it.
   bool video_is_full_width = true;
   bool video_is_full_height = true;
+#if PW_CHECK_VERSION(0, 3, 0)
+  if (video_metadata && video_metadata->region.size.width != 0 &&
+      video_metadata->region.size.height != 0) {
+    if (video_metadata->region.size.width <
+        static_cast<uint32_t>(desktop_size_.width())) {
+      video_is_full_width = false;
+    } else if (video_metadata->region.size.height <
+               static_cast<uint32_t>(desktop_size_.height())) {
+      video_is_full_height = false;
+    }
+  }
+#else
   if (video_metadata && video_metadata->width != 0 &&
       video_metadata->height != 0) {
     if (video_metadata->width < desktop_size_.width()) {
-      video_is_full_width = false;
     } else if (video_metadata->height < desktop_size_.height()) {
       video_is_full_height = false;
     }
   }
+#endif
 
   DesktopSize video_size_prev = video_size_;
   if (!video_is_full_height || !video_is_full_width) {
+#if PW_CHECK_VERSION(0, 3, 0)
+    video_size_ = DesktopSize(video_metadata->region.size.width,
+                              video_metadata->region.size.height);
+#else
     video_size_ = DesktopSize(video_metadata->width, video_metadata->height);
+#endif
   } else {
     video_size_ = desktop_size_;
   }
@@ -613,6 +826,19 @@
   }
 
   // Adjust source content based on metadata video position
+#if PW_CHECK_VERSION(0, 3, 0)
+  if (!video_is_full_height &&
+      (video_metadata->region.position.y + video_size_.height() <=
+       desktop_size_.height())) {
+    src += src_stride * video_metadata->region.position.y;
+  }
+  const int x_offset =
+      !video_is_full_width &&
+              (video_metadata->region.position.x + video_size_.width() <=
+               desktop_size_.width())
+          ? video_metadata->region.position.x * kBytesPerPixel
+          : 0;
+#else
   if (!video_is_full_height &&
       (video_metadata->y + video_size_.height() <= desktop_size_.height())) {
     src += src_stride * video_metadata->y;
@@ -623,6 +849,7 @@
               (video_metadata->x + video_size_.width() <= desktop_size_.width())
           ? video_metadata->x * kBytesPerPixel
           : 0;
+#endif
 
   uint8_t* dst = current_frame_.get();
   for (int i = 0; i < video_size_.height(); ++i) {
@@ -631,8 +858,13 @@
     std::memcpy(dst, src, dst_stride);
     // If both sides decided to go with the RGBx format we need to convert it to
     // BGRx to match color format expected by WebRTC.
+#if PW_CHECK_VERSION(0, 3, 0)
+    if (spa_video_format_.format == SPA_VIDEO_FORMAT_RGBx ||
+        spa_video_format_.format == SPA_VIDEO_FORMAT_RGBA) {
+#else
     if (spa_video_format_->format == pw_type_->video_format.RGBx ||
         spa_video_format_->format == pw_type_->video_format.RGBA) {
+#endif
       ConvertRGBxToBGRx(dst, dst_stride);
     }
     src += src_stride - x_offset;
diff --git a/modules/desktop_capture/linux/base_capturer_pipewire.h b/modules/desktop_capture/linux/base_capturer_pipewire.h
index 88b567a..75d20db 100644
--- a/modules/desktop_capture/linux/base_capturer_pipewire.h
+++ b/modules/desktop_capture/linux/base_capturer_pipewire.h
@@ -14,6 +14,9 @@
 #define typeof __typeof__
 #include <pipewire/pipewire.h>
 #include <spa/param/video/format-utils.h>
+#if PW_CHECK_VERSION(0, 3, 0)
+#include <spa/utils/result.h>
+#endif
 
 #include "absl/types/optional.h"
 #include "modules/desktop_capture/desktop_capture_options.h"
@@ -23,6 +26,7 @@
 
 namespace webrtc {
 
+#if !PW_CHECK_VERSION(0, 3, 0)
 class PipeWireType {
  public:
   spa_type_media_type media_type;
@@ -30,6 +34,7 @@
   spa_type_format_video format_video;
   spa_type_video_format video_format;
 };
+#endif
 
 class BaseCapturerPipeWire : public DesktopCapturer {
  public:
@@ -56,6 +61,21 @@
 
  private:
   // PipeWire types -->
+#if PW_CHECK_VERSION(0, 3, 0)
+  struct pw_context* pw_context_ = nullptr;
+  struct pw_core* pw_core_ = nullptr;
+  struct pw_stream* pw_stream_ = nullptr;
+  struct pw_thread_loop* pw_main_loop_ = nullptr;
+
+  spa_hook spa_core_listener_;
+  spa_hook spa_stream_listener_;
+
+  // event handlers
+  pw_core_events pw_core_events_ = {};
+  pw_stream_events pw_stream_events_ = {};
+
+  struct spa_video_info_raw spa_video_format_;
+#else
   pw_core* pw_core_ = nullptr;
   pw_type* pw_core_type_ = nullptr;
   pw_stream* pw_stream_ = nullptr;
@@ -71,6 +91,7 @@
   pw_remote_events pw_remote_events_ = {};
 
   spa_video_info_raw* spa_video_format_ = nullptr;
+#endif
 
   guint32 pw_stream_node_id_ = 0;
   gint32 pw_fd_ = -1;
@@ -105,21 +126,32 @@
   void InitPipeWire();
   void InitPipeWireTypes();
 
-  void CreateReceivingStream();
+  pw_stream* CreateReceivingStream();
   void HandleBuffer(pw_buffer* buffer);
 
   void ConvertRGBxToBGRx(uint8_t* frame, uint32_t size);
 
+#if PW_CHECK_VERSION(0, 3, 0)
+  static void OnCoreError(void* data,
+                          uint32_t id,
+                          int seq,
+                          int res,
+                          const char* message);
+  static void OnStreamParamChanged(void* data,
+                                   uint32_t id,
+                                   const struct spa_pod* format);
+#else
   static void OnStateChanged(void* data,
                              pw_remote_state old_state,
                              pw_remote_state state,
                              const char* error);
+  static void OnStreamFormatChanged(void* data, const struct spa_pod* format);
+#endif
   static void OnStreamStateChanged(void* data,
                                    pw_stream_state old_state,
                                    pw_stream_state state,
                                    const char* error_message);
 
-  static void OnStreamFormatChanged(void* data, const struct spa_pod* format);
   static void OnStreamProcess(void* data);
   static void OnNewBuffer(void* data, uint32_t id);
 
diff --git a/modules/desktop_capture/linux/pipewire.sigs b/modules/desktop_capture/linux/pipewire02.sigs
similarity index 95%
rename from modules/desktop_capture/linux/pipewire.sigs
rename to modules/desktop_capture/linux/pipewire02.sigs
index e915bc9..5ac3d1d 100644
--- a/modules/desktop_capture/linux/pipewire.sigs
+++ b/modules/desktop_capture/linux/pipewire02.sigs
@@ -26,6 +26,7 @@
 int pw_remote_connect_fd(pw_remote *remote, int fd);
 void pw_remote_destroy(pw_remote *remote);
 pw_remote * pw_remote_new(pw_core *core, pw_properties *properties, size_t user_data_size);
+enum pw_remote_state pw_remote_get_state(pw_remote *remote, const char **error);
 
 // stream.h
 void pw_stream_add_listener(pw_stream *stream, spa_hook *listener, const pw_stream_events *events, void *data);
diff --git a/modules/desktop_capture/linux/pipewire03.sigs b/modules/desktop_capture/linux/pipewire03.sigs
new file mode 100644
index 0000000..78d241f
--- /dev/null
+++ b/modules/desktop_capture/linux/pipewire03.sigs
@@ -0,0 +1,46 @@
+// Copyright 2018 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 PipeWire used in capturer code.
+//------------------------------------------------
+
+// core.h
+int pw_core_disconnect(pw_core *core);
+
+// loop.h
+void pw_loop_destroy(pw_loop *loop);
+pw_loop * pw_loop_new(const spa_dict *props);
+
+
+// pipewire.h
+void pw_init(int *argc, char **argv[]);
+
+// properties.h
+pw_properties * pw_properties_new_string(const char *args);
+
+// stream.h
+void pw_stream_add_listener(pw_stream *stream, spa_hook *listener, const pw_stream_events *events, void *data);
+int pw_stream_connect(pw_stream *stream, enum pw_direction direction, uint32_t target_id, enum pw_stream_flags flags, const spa_pod **params, uint32_t n_params);
+pw_buffer *pw_stream_dequeue_buffer(pw_stream *stream);
+void pw_stream_destroy(pw_stream *stream);
+pw_stream * pw_stream_new(pw_core *core, const char *name, pw_properties *props);
+int pw_stream_queue_buffer(pw_stream *stream, pw_buffer *buffer);
+int pw_stream_set_active(pw_stream *stream, bool active);
+int pw_stream_update_params(pw_stream *stream, const spa_pod **params, uint32_t n_params);
+
+// thread-loop.h
+void pw_thread_loop_destroy(pw_thread_loop *loop);
+pw_thread_loop * pw_thread_loop_new(const char *name, const spa_dict *props);
+int pw_thread_loop_start(pw_thread_loop *loop);
+void pw_thread_loop_stop(pw_thread_loop *loop);
+void pw_thread_loop_lock(pw_thread_loop *loop);
+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);
+pw_core * pw_context_connect(pw_context *context, pw_properties *properties, size_t user_data_size);
diff --git a/webrtc.gni b/webrtc.gni
index c4530dd..05a230c 100644
--- a/webrtc.gni
+++ b/webrtc.gni
@@ -117,6 +117,10 @@
   # Set this to link PipeWire directly instead of using the dlopen.
   rtc_link_pipewire = false
 
+  # Set this to use certain PipeWire version
+  # Currently we support PipeWire 0.2 (default) and PipeWire 0.3
+  rtc_pipewire_version = "0.2"
+
   # Enable to use the Mozilla internal settings.
   build_with_mozilla = false