PipeWire: call pw_deinit() only when running against PipeWire 3.49+

pw_deinit() was not reetrant in old PipeWire versions and it only got
fixed with PipeWire 3.49. This is causing a crash for old Ubuntu 22.04
with PipeWire 3.48 when sharing screen multiple times.

This also moves all the PipeWire version parsing logic to pipewire_utils
from screencast_utils as it makes more sense and is used from there now.

Bug: chromium:490340738
Change-Id: I62ec7adfcba040b4ab99bf535b3f16b5e4b27c1a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/455180
Commit-Queue: Jan Grulich <grulja@gmail.com>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Andreas Pehrson <apehrson@mozilla.com>
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Reviewed-by: Mark Foltz <mfoltz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#47100}
diff --git a/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc b/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc
index 7e4ad53..a590cac 100644
--- a/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc
+++ b/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc
@@ -26,10 +26,7 @@
 #include <tuple>
 #include <vector>
 
-#include "absl/strings/string_view.h"
 #include "modules/desktop_capture/linux/wayland/egl_dmabuf.h"
-#include "rtc_base/string_encode.h"
-#include "rtc_base/string_to_number.h"
 
 #if !PW_CHECK_VERSION(0, 3, 29)
 #define SPA_POD_PROP_FLAG_MANDATORY (1u << 3)
@@ -44,50 +41,6 @@
     SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA, SPA_VIDEO_FORMAT_BGRx,
     SPA_VIDEO_FORMAT_RGBx};
 
-PipeWireVersion PipeWireVersion::Parse(const absl::string_view& version) {
-  std::vector<absl::string_view> parsed_version = split(version, '.');
-
-  if (parsed_version.size() != 3) {
-    return {};
-  }
-
-  std::optional<int> major = StringToNumber<int>(parsed_version.at(0));
-  std::optional<int> minor = StringToNumber<int>(parsed_version.at(1));
-  std::optional<int> micro = StringToNumber<int>(parsed_version.at(2));
-
-  // Return invalid version if we failed to parse it
-  if (!major || !minor || !micro) {
-    return {};
-  }
-
-  return {.major = major.value(),
-          .minor = minor.value(),
-          .micro = micro.value(),
-          .full_version = std::string(version)};
-}
-
-bool PipeWireVersion::operator>=(const PipeWireVersion& other) {
-  if (!major && !minor && !micro) {
-    return false;
-  }
-
-  return std::tie(major, minor, micro) >=
-         std::tie(other.major, other.minor, other.micro);
-}
-
-bool PipeWireVersion::operator<=(const PipeWireVersion& other) {
-  if (!major && !minor && !micro) {
-    return false;
-  }
-
-  return std::tie(major, minor, micro) <=
-         std::tie(other.major, other.minor, other.micro);
-}
-
-absl::string_view PipeWireVersion::ToStringView() const {
-  return full_version;
-}
-
 void BuildBaseFormatParams(spa_pod_builder* builder,
                            uint32_t format,
                            const struct spa_rectangle* resolution,
diff --git a/modules/desktop_capture/linux/wayland/screencast_stream_utils.h b/modules/desktop_capture/linux/wayland/screencast_stream_utils.h
index 27d58d2..4e3492e 100644
--- a/modules/desktop_capture/linux/wayland/screencast_stream_utils.h
+++ b/modules/desktop_capture/linux/wayland/screencast_stream_utils.h
@@ -15,8 +15,6 @@
 
 #include <vector>
 
-#include "absl/strings/string_view.h"
-
 struct spa_pod;
 struct spa_pod_builder;
 struct spa_rectangle;
@@ -26,22 +24,6 @@
 
 class EglDrmDevice;
 
-struct PipeWireVersion {
-  static PipeWireVersion Parse(const absl::string_view& version);
-
-  // Returns whether current version is newer or same as required version
-  bool operator>=(const PipeWireVersion& other);
-  // Returns whether current version is older or same as required version
-  bool operator<=(const PipeWireVersion& other);
-
-  absl::string_view ToStringView() const;
-
-  int major = 0;
-  int minor = 0;
-  int micro = 0;
-  std::string full_version;
-};
-
 // Builds base video format parameters. The format parameter consists of:
 // - SPA_FORMAT_mediaType with SPA_MEDIA_TYPE_video
 // - SPA_FORMAT_mediaSubtype with SPA_MEDIA_SUBTYPE_raw
diff --git a/modules/portal/pipewire_utils.cc b/modules/portal/pipewire_utils.cc
index 91a2ae6..850726c 100644
--- a/modules/portal/pipewire_utils.cc
+++ b/modules/portal/pipewire_utils.cc
@@ -12,7 +12,15 @@
 
 #include <pipewire/pipewire.h>
 
+#include <optional>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <vector>
+
 #include "rtc_base/sanitizer.h"
+#include "rtc_base/string_encode.h"
+#include "rtc_base/string_to_number.h"
 
 #if defined(WEBRTC_DLOPEN_PIPEWIRE)
 #include "modules/portal/pipewire_stubs.h"
@@ -20,6 +28,45 @@
 
 namespace webrtc {
 
+constexpr PipeWireVersion kReentrantDeinitMinVersion = {.major = 0,
+                                                        .minor = 3,
+                                                        .micro = 49};
+
+PipeWireVersion PipeWireVersion::Parse(const std::string_view& version) {
+  std::vector<std::string_view> parsed_version = split(version, '.');
+
+  if (parsed_version.size() != 3) {
+    return {};
+  }
+
+  std::optional<int> major = StringToNumber<int>(parsed_version.at(0));
+  std::optional<int> minor = StringToNumber<int>(parsed_version.at(1));
+  std::optional<int> micro = StringToNumber<int>(parsed_version.at(2));
+
+  // Return invalid version if we failed to parse it
+  if (!major || !minor || !micro) {
+    return {};
+  }
+
+  return {.major = major.value(),
+          .minor = minor.value(),
+          .micro = micro.value(),
+          .full_version = std::string(version)};
+}
+
+bool PipeWireVersion::operator>=(const PipeWireVersion& other) {
+  if (!major && !minor && !micro) {
+    return false;
+  }
+
+  return std::tie(major, minor, micro) >=
+         std::tie(other.major, other.minor, other.micro);
+}
+
+std::string_view PipeWireVersion::ToStringView() const {
+  return full_version;
+}
+
 RTC_NO_SANITIZE("cfi-icall")
 bool InitializePipeWire() {
 #if defined(WEBRTC_DLOPEN_PIPEWIRE)
@@ -57,7 +104,11 @@
 
 RTC_NO_SANITIZE("cfi-icall")
 PipeWireInitializer::~PipeWireInitializer() {
-  pw_deinit();
+  PipeWireVersion pw_client_version =
+      PipeWireVersion::Parse(pw_get_library_version());
+  if (pw_client_version >= kReentrantDeinitMinVersion) {
+    pw_deinit();
+  }
 }
 
 }  // namespace webrtc
diff --git a/modules/portal/pipewire_utils.h b/modules/portal/pipewire_utils.h
index c22f296..d0734c1 100644
--- a/modules/portal/pipewire_utils.h
+++ b/modules/portal/pipewire_utils.h
@@ -17,6 +17,8 @@
 
 #include <cerrno>
 #include <cstdint>
+#include <string>
+#include <string_view>
 
 // static
 struct dma_buf_sync {
@@ -34,6 +36,20 @@
 
 constexpr int kInvalidPipeWireFd = -1;
 
+struct PipeWireVersion {
+  static PipeWireVersion Parse(const std::string_view& version);
+
+  // Returns whether current version is newer or same as required version
+  bool operator>=(const PipeWireVersion& other);
+
+  std::string_view ToStringView() const;
+
+  int major = 0;
+  int minor = 0;
+  int micro = 0;
+  std::string full_version;
+};
+
 // Prepare PipeWire so that it is ready to be used. If it needs to be dlopen'd
 // this will do so. Note that this does not guarantee a PipeWire server is
 // running nor does it establish a connection to one.