summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Stransky <stransky@redhat.com>2020-11-12 14:33:57 +0100
committerMartin Stransky <stransky@redhat.com>2020-11-12 14:33:57 +0100
commitd6756537dd8cf4d9816dc63ada66ea026e0fd128 (patch)
tree73822a6950c9dc87de309cccda7234e40a4f9fa4
parentchangelog date fix (diff)
downloadlibrewolf-fedora-ff-d6756537dd8cf4d9816dc63ada66ea026e0fd128.tar.gz
librewolf-fedora-ff-d6756537dd8cf4d9816dc63ada66ea026e0fd128.tar.bz2
librewolf-fedora-ff-d6756537dd8cf4d9816dc63ada66ea026e0fd128.zip
Updated to 83.0, Updated PipeWire patches from mozbz#1672944
-rw-r--r--.gitignore2
-rw-r--r--firefox-pipewire-0-2.patch526
-rw-r--r--firefox.spec48
-rw-r--r--mozilla-1634404.patch20
-rw-r--r--mozilla-1640567.patch18
-rw-r--r--mozilla-1656727.patch213
-rw-r--r--mozilla-1661192.patch25
-rw-r--r--mozilla-1665329.patch16
-rw-r--r--mozilla-1669442.patch13
-rw-r--r--mozilla-1669495.patch130
-rw-r--r--pw1.patch78
-rw-r--r--pw2.patch (renamed from firefox-pipewire-0-3.patch)613
-rw-r--r--pw3.patch183
-rw-r--r--pw4.patch18928
-rw-r--r--pw5.patch53
-rw-r--r--pw6.patch75
-rw-r--r--pw7.patch95
-rw-r--r--sources4
18 files changed, 19778 insertions, 1262 deletions
diff --git a/.gitignore b/.gitignore
index cef3fb8..4b0d985 100644
--- a/.gitignore
+++ b/.gitignore
@@ -430,3 +430,5 @@ firefox-3.6.4.source.tar.bz2
/firefox-langpacks-82.0.2-20201029.tar.xz
/firefox-82.0.3.source.tar.xz
/firefox-langpacks-82.0.3-20201109.tar.xz
+/firefox-83.0.source.tar.xz
+/firefox-langpacks-83.0-20201112.tar.xz
diff --git a/firefox-pipewire-0-2.patch b/firefox-pipewire-0-2.patch
deleted file mode 100644
index b1c6950..0000000
--- a/firefox-pipewire-0-2.patch
+++ /dev/null
@@ -1,526 +0,0 @@
-diff -up firefox-79.0/config/system-headers.mozbuild.firefox-pipewire-0-2 firefox-79.0/config/system-headers.mozbuild
---- firefox-79.0/config/system-headers.mozbuild.firefox-pipewire-0-2 2020-07-21 00:49:36.000000000 +0200
-+++ firefox-79.0/config/system-headers.mozbuild 2020-07-29 11:03:51.455284187 +0200
-@@ -314,6 +314,7 @@ system_headers = [
- 'Gestalt.h',
- 'getopt.h',
- 'gio/gio.h',
-+ 'gio/gunixfdlist.h',
- 'glibconfig.h',
- 'glib.h',
- 'glib-object.h',
-@@ -607,6 +608,7 @@ system_headers = [
- 'Pgenerr.h',
- 'PGenErr.h',
- 'Ph.h',
-+ 'pipewire/pipewire.h',
- 'pixman.h',
- 'pk11func.h',
- 'pk11pqg.h',
-diff -up firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build.firefox-pipewire-0-2 firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build
---- firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build.firefox-pipewire-0-2 2020-07-29 11:03:51.455284187 +0200
-+++ firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build 2020-07-29 11:04:40.898017241 +0200
-@@ -231,6 +231,27 @@ if CONFIG["OS_TARGET"] == "OpenBSD":
- "/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc"
- ]
-
-+# PipeWire specific files
-+if CONFIG["OS_TARGET"] == "Linux":
-+
-+ DEFINES["WEBRTC_USE_PIPEWIRE"] = "1"
-+
-+ OS_LIBS += [
-+ "rt",
-+ "pipewire-0.2",
-+ "glib-2.0",
-+ "gio-2.0",
-+ "gobject-2.0"
-+ ]
-+
-+ CXXFLAGS += CONFIG['TK_CFLAGS']
-+
-+ UNIFIED_SOURCES += [
-+ "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc",
-+ "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc",
-+ "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc"
-+ ]
-+
- if CONFIG["OS_TARGET"] == "WINNT":
-
- DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
-diff -up firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h.firefox-pipewire-0-2 firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h
---- firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h.firefox-pipewire-0-2 2020-07-20 22:54:16.000000000 +0200
-+++ firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h 2020-07-29 11:03:51.456284181 +0200
-@@ -141,7 +141,7 @@ class DesktopCaptureOptions {
- bool disable_effects_ = true;
- bool detect_updated_region_ = false;
- #if defined(WEBRTC_USE_PIPEWIRE)
-- bool allow_pipewire_ = false;
-+ bool allow_pipewire_ = true;
- #endif
- };
-
-diff -up firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc.firefox-pipewire-0-2 firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc
---- firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc.firefox-pipewire-0-2 2020-07-20 22:54:27.000000000 +0200
-+++ firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc 2020-07-29 11:03:51.618283306 +0200
-@@ -18,6 +18,11 @@
- #include <spa/param/video/raw-utils.h>
- #include <spa/support/type-map.h>
-
-+#include <linux/dma-buf.h>
-+#include <sys/mman.h>
-+#include <sys/ioctl.h>
-+#include <sys/syscall.h>
-+
- #include <memory>
- #include <utility>
-
-@@ -36,6 +41,27 @@ const char kSessionInterfaceName[] = "or
- const char kRequestInterfaceName[] = "org.freedesktop.portal.Request";
- const char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast";
-
-+
-+// static
-+void BaseCapturerPipeWire::SyncDmaBuf(int fd, uint64_t start_or_end) {
-+ struct dma_buf_sync sync = { 0 };
-+
-+ 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;
-+ }
-+ }
-+}
-+
- // static
- void BaseCapturerPipeWire::OnStateChanged(void* data,
- pw_remote_state old_state,
-@@ -108,11 +134,13 @@ void BaseCapturerPipeWire::OnStreamForma
- auto stride = SPA_ROUND_UP_N(width * kBytesPerPixel, 4);
- auto size = height * stride;
-
-+ that->desktop_size_ = DesktopSize(width, height);
-+
- 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[2];
-+ const struct spa_pod* params[3];
- params[0] = reinterpret_cast<spa_pod*>(spa_pod_builder_object(
- &builder,
- // id to enumerate buffer requirements
-@@ -141,8 +169,14 @@ void BaseCapturerPipeWire::OnStreamForma
- // Size: size of the metadata, specified as integer (i)
- ":", that->pw_core_type_->param_meta.size, "i",
- sizeof(struct spa_meta_header)));
--
-- pw_stream_finish_format(that->pw_stream_, /*res=*/0, params, /*n_params=*/2);
-+ params[2] = reinterpret_cast<spa_pod*>(
-+ spa_pod_builder_object(&builder, that->pw_core_type_->param.idMeta,
-+ that->pw_core_type_->param_meta.Meta, ":",
-+ that->pw_core_type_->param_meta.type, "I",
-+ that->pw_core_type_->meta.VideoCrop, ":",
-+ 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);
- }
-
- // static
-@@ -150,15 +184,25 @@ void BaseCapturerPipeWire::OnStreamProce
- BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(data);
- RTC_DCHECK(that);
-
-- pw_buffer* buf = nullptr;
-+ struct pw_buffer *next_buffer;
-+ struct pw_buffer *buffer = nullptr;
-
-- if (!(buf = pw_stream_dequeue_buffer(that->pw_stream_))) {
-+ next_buffer = pw_stream_dequeue_buffer(that->pw_stream_);
-+ while (next_buffer) {
-+ buffer = next_buffer;
-+ next_buffer = pw_stream_dequeue_buffer(that->pw_stream_);
-+
-+ if (next_buffer)
-+ pw_stream_queue_buffer (that->pw_stream_, buffer);
-+ }
-+
-+ if (!buffer) {
- return;
- }
-
-- that->HandleBuffer(buf);
-+ that->HandleBuffer(buffer);
-
-- pw_stream_queue_buffer(that->pw_stream_, buf);
-+ pw_stream_queue_buffer(that->pw_stream_, buffer);
- }
-
- BaseCapturerPipeWire::BaseCapturerPipeWire(CaptureSourceType source_type)
-@@ -197,10 +241,6 @@ BaseCapturerPipeWire::~BaseCapturerPipeW
- pw_loop_destroy(pw_loop_);
- }
-
-- if (current_frame_) {
-- free(current_frame_);
-- }
--
- if (start_request_signal_id_) {
- g_dbus_connection_signal_unsubscribe(connection_, start_request_signal_id_);
- }
-@@ -290,12 +330,7 @@ void BaseCapturerPipeWire::InitPipeWireT
-
- void BaseCapturerPipeWire::CreateReceivingStream() {
- spa_rectangle pwMinScreenBounds = spa_rectangle{1, 1};
-- spa_rectangle pwScreenBounds =
-- spa_rectangle{static_cast<uint32_t>(desktop_size_.width()),
-- static_cast<uint32_t>(desktop_size_.height())};
--
-- spa_fraction pwFrameRateMin = spa_fraction{0, 1};
-- spa_fraction pwFrameRateMax = spa_fraction{60, 1};
-+ spa_rectangle pwMaxScreenBounds = spa_rectangle{INT32_MAX, INT32_MAX};
-
- pw_properties* reuseProps = pw_properties_new("pipewire.client.reuse", "1",
- /*end of varargs*/ nullptr);
-@@ -313,27 +348,19 @@ void BaseCapturerPipeWire::CreateReceivi
- // then allowed formats are enumerated (e) and the format is undecided (u)
- // to allow negotiation
- ":", pw_type_->format_video.format, "Ieu", pw_type_->video_format.BGRx,
-- SPA_POD_PROP_ENUM(2, pw_type_->video_format.RGBx,
-- pw_type_->video_format.BGRx),
-+ SPA_POD_PROP_ENUM(
-+ 4, pw_type_->video_format.RGBx, pw_type_->video_format.BGRx,
-+ pw_type_->video_format.RGBA, pw_type_->video_format.BGRA),
- // Video size: specified as rectangle (R), preferred size is specified as
- // first parameter, then allowed size is defined as range (r) from min and
- // max values and the format is undecided (u) to allow negotiation
-- ":", pw_type_->format_video.size, "Rru", &pwScreenBounds, 2,
-- &pwMinScreenBounds, &pwScreenBounds,
-- // Frame rate: specified as fraction (F) and set to minimum frame rate
-- // value
-- ":", pw_type_->format_video.framerate, "F", &pwFrameRateMin,
-- // Max frame rate: specified as fraction (F), preferred frame rate is set
-- // to maximum value, then allowed frame rate is defined as range (r) from
-- // min and max values and it is undecided (u) to allow negotiation
-- ":", pw_type_->format_video.max_framerate, "Fru", &pwFrameRateMax, 2,
-- &pwFrameRateMin, &pwFrameRateMax));
-+ ":", pw_type_->format_video.size, "Rru", &pwMinScreenBounds,
-+ SPA_POD_PROP_MIN_MAX(&pwMinScreenBounds, &pwMaxScreenBounds)));
-
- pw_stream_add_listener(pw_stream_, &spa_stream_listener_, &pw_stream_events_,
- this);
- pw_stream_flags flags = static_cast<pw_stream_flags>(
-- PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE |
-- PW_STREAM_FLAG_MAP_BUFFERS);
-+ PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE);
- if (pw_stream_connect(pw_stream_, PW_DIRECTION_INPUT, /*port_path=*/nullptr,
- flags, params,
- /*n_params=*/1) != 0) {
-@@ -344,15 +371,81 @@ void BaseCapturerPipeWire::CreateReceivi
- }
-
- void BaseCapturerPipeWire::HandleBuffer(pw_buffer* buffer) {
-+ struct spa_meta_video_crop* video_crop;
- spa_buffer* spaBuffer = buffer->buffer;
-- void* src = nullptr;
-+ uint8_t *map = nullptr;
-+ uint8_t* src = nullptr;
-+ uint8_t* dst = nullptr;
-+
-+ if (spaBuffer->datas[0].chunk->size == 0) {
-+ map = nullptr;
-+ src = nullptr;
-+ } else if (spaBuffer->datas[0].type == pw_core_type_->data.MemFd) {
-+ map = static_cast<uint8_t*>(mmap(
-+ nullptr, spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
-+ PROT_READ, MAP_PRIVATE, spaBuffer->datas[0].fd, 0));
-+ src = SPA_MEMBER(map, spaBuffer->datas[0].mapoffset, uint8_t);
-+ } else if (spaBuffer->datas[0].type == pw_core_type_->data.DmaBuf) {
-+ int fd;
-+ fd = spaBuffer->datas[0].fd;
-+
-+ map = static_cast<uint8_t*>(mmap(
-+ nullptr, spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
-+ PROT_READ, MAP_PRIVATE, fd, 0));
-+ SyncDmaBuf(fd, DMA_BUF_SYNC_START);
-+
-+ src = SPA_MEMBER(map, spaBuffer->datas[0].mapoffset, uint8_t);
-+ } else if (spaBuffer->datas[0].type == pw_core_type_->data.MemPtr) {
-+ map = nullptr;
-+ src = static_cast<uint8_t*>(spaBuffer->datas[0].data);
-+ } else {
-+ return;
-+ }
-
-- if (!(src = spaBuffer->datas[0].data)) {
-+ if (!src) {
- return;
- }
-
-- uint32_t maxSize = spaBuffer->datas[0].maxsize;
-- int32_t srcStride = spaBuffer->datas[0].chunk->stride;
-+ DesktopSize prev_crop_size = DesktopSize(0, 0);
-+ if (video_crop_size_initialized_) {
-+ prev_crop_size = video_crop_size_;
-+ }
-+
-+ if ((video_crop = static_cast<struct spa_meta_video_crop*>(
-+ spa_buffer_find_meta(spaBuffer, pw_core_type_->meta.VideoCrop)))) {
-+ RTC_DCHECK(video_crop->width <= desktop_size_.width() &&
-+ video_crop->height <= desktop_size_.height());
-+ if ((video_crop->width != desktop_size_.width() ||
-+ video_crop->height != desktop_size_.height()) && video_crop->width && video_crop->height) {
-+ video_crop_size_ = DesktopSize(video_crop->width, video_crop->height);
-+ video_crop_size_initialized_ = true;
-+ } else {
-+ video_crop_size_initialized_ = false;
-+ }
-+ } else {
-+ video_crop_size_initialized_ = false;
-+ }
-+
-+ size_t frame_size;
-+ if (video_crop_size_initialized_) {
-+ frame_size =
-+ video_crop_size_.width() * video_crop_size_.height() * kBytesPerPixel;
-+ } else {
-+ frame_size =
-+ desktop_size_.width() * desktop_size_.height() * kBytesPerPixel;
-+ }
-+
-+ if (!current_frame_ ||
-+ (video_crop_size_initialized_ && !video_crop_size_.equals(prev_crop_size))) {
-+ current_frame_ = std::make_unique<uint8_t[]>(frame_size);
-+ }
-+ RTC_DCHECK(current_frame_ != nullptr);
-+
-+ const int32_t dstStride = video_crop_size_initialized_
-+ ? video_crop_size_.width() * kBytesPerPixel
-+ : desktop_size_.width() * kBytesPerPixel;
-+ const int32_t srcStride = spaBuffer->datas[0].chunk->stride;
-+
- if (srcStride != (desktop_size_.width() * kBytesPerPixel)) {
- RTC_LOG(LS_ERROR) << "Got buffer with stride different from screen stride: "
- << srcStride
-@@ -361,21 +454,39 @@ void BaseCapturerPipeWire::HandleBuffer(
- return;
- }
-
-- if (!current_frame_) {
-- current_frame_ = static_cast<uint8_t*>(malloc(maxSize));
-+ dst = current_frame_.get();
-+
-+ // Adjust source content based on crop video position
-+ if (video_crop_size_initialized_ &&
-+ (video_crop->y + video_crop_size_.height() <= desktop_size_.height())) {
-+ for (int i = 0; i < video_crop->y; ++i) {
-+ src += srcStride;
-+ }
-+ }
-+ const int xOffset =
-+ video_crop_size_initialized_ && (video_crop->x + video_crop_size_.width() <=
-+ desktop_size_.width())
-+ ? video_crop->x * kBytesPerPixel
-+ : 0;
-+ const int height = video_crop_size_initialized_ ? video_crop_size_.height() : desktop_size_.height();
-+ for (int i = 0; i < height; ++i) {
-+ // Adjust source content based on crop video position if needed
-+ src += xOffset;
-+ std::memcpy(dst, src, dstStride);
-+ // 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 (spa_video_format_->format == pw_type_->video_format.RGBx) {
-+ ConvertRGBxToBGRx(dst, dstStride);
-+ }
-+ src += srcStride - xOffset;
-+ dst += dstStride;
- }
-- RTC_DCHECK(current_frame_ != nullptr);
-
-- // 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 (spa_video_format_->format == pw_type_->video_format.RGBx) {
-- uint8_t* tempFrame = static_cast<uint8_t*>(malloc(maxSize));
-- std::memcpy(tempFrame, src, maxSize);
-- ConvertRGBxToBGRx(tempFrame, maxSize);
-- std::memcpy(current_frame_, tempFrame, maxSize);
-- free(tempFrame);
-- } else {
-- std::memcpy(current_frame_, src, maxSize);
-+ if (map) {
-+ if (spaBuffer->datas[0].type == pw_core_type_->data.DmaBuf) {
-+ SyncDmaBuf(spaBuffer->datas[0].fd, DMA_BUF_SYNC_END);
-+ }
-+ munmap(map, spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset);
- }
- }
-
-@@ -725,10 +836,6 @@ void BaseCapturerPipeWire::OnStartReques
- g_variant_get(variant, "(u@a{sv})", &stream_id, &options);
- RTC_DCHECK(options != nullptr);
-
-- g_variant_lookup(options, "size", "(ii)", &width, &height);
--
-- that->desktop_size_.set(width, height);
--
- g_variant_unref(options);
- g_variant_unref(variant);
- }
-@@ -813,10 +920,15 @@ void BaseCapturerPipeWire::CaptureFrame(
- return;
- }
-
-- std::unique_ptr<DesktopFrame> result(new BasicDesktopFrame(desktop_size_));
-+ DesktopSize frame_size = desktop_size_;
-+ if (video_crop_size_initialized_) {
-+ frame_size = video_crop_size_;
-+ }
-+
-+ std::unique_ptr<DesktopFrame> result(new BasicDesktopFrame(frame_size));
- result->CopyPixelsFrom(
-- current_frame_, (desktop_size_.width() * kBytesPerPixel),
-- DesktopRect::MakeWH(desktop_size_.width(), desktop_size_.height()));
-+ current_frame_.get(), (frame_size.width() * kBytesPerPixel),
-+ DesktopRect::MakeWH(frame_size.width(), frame_size.height()));
- if (!result) {
- callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
- return;
-@@ -837,4 +949,22 @@ bool BaseCapturerPipeWire::SelectSource(
- return true;
- }
-
-+// static
-+std::unique_ptr<DesktopCapturer>
-+BaseCapturerPipeWire::CreateRawScreenCapturer(
-+ const DesktopCaptureOptions& options) {
-+ std::unique_ptr<BaseCapturerPipeWire> capturer =
-+ std::make_unique<BaseCapturerPipeWire>(BaseCapturerPipeWire::CaptureSourceType::kAny);
-+ return std::move(capturer);}
-+
-+// static
-+std::unique_ptr<DesktopCapturer>
-+BaseCapturerPipeWire::CreateRawWindowCapturer(
-+ const DesktopCaptureOptions& options) {
-+
-+ std::unique_ptr<BaseCapturerPipeWire> capturer =
-+ std::make_unique<BaseCapturerPipeWire>(BaseCapturerPipeWire::CaptureSourceType::kAny);
-+ return std::move(capturer);
-+}
-+
- } // namespace webrtc
-diff -up firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.h.firefox-pipewire-0-2 firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.h
---- firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.h.firefox-pipewire-0-2 2020-07-20 22:54:40.000000000 +0200
-+++ firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.h 2020-07-29 11:03:51.619283301 +0200
-@@ -32,7 +32,11 @@ class PipeWireType {
-
- class BaseCapturerPipeWire : public DesktopCapturer {
- public:
-- enum CaptureSourceType { Screen = 1, Window };
-+ enum CaptureSourceType : uint32_t {
-+ kScreen = 0b01,
-+ kWindow = 0b10,
-+ kAny = 0b11
-+ };
-
- explicit BaseCapturerPipeWire(CaptureSourceType source_type);
- ~BaseCapturerPipeWire() override;
-@@ -43,6 +47,12 @@ class BaseCapturerPipeWire : public Desk
- bool GetSourceList(SourceList* sources) override;
- bool SelectSource(SourceId id) override;
-
-+ static std::unique_ptr<DesktopCapturer> CreateRawScreenCapturer(
-+ const DesktopCaptureOptions& options);
-+
-+ static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
-+ const DesktopCaptureOptions& options);
-+
- private:
- // PipeWire types -->
- pw_core* pw_core_ = nullptr;
-@@ -64,7 +74,7 @@ class BaseCapturerPipeWire : public Desk
- gint32 pw_fd_ = -1;
-
- CaptureSourceType capture_source_type_ =
-- BaseCapturerPipeWire::CaptureSourceType::Screen;
-+ BaseCapturerPipeWire::CaptureSourceType::kAny;
-
- // <-- end of PipeWire types
-
-@@ -78,10 +88,12 @@ class BaseCapturerPipeWire : public Desk
- guint sources_request_signal_id_ = 0;
- guint start_request_signal_id_ = 0;
-
-+ bool video_crop_size_initialized_ = false;
-+ DesktopSize video_crop_size_;;
- DesktopSize desktop_size_ = {};
- DesktopCaptureOptions options_ = {};
-
-- uint8_t* current_frame_ = nullptr;
-+ std::unique_ptr<uint8_t[]> current_frame_;
- Callback* callback_ = nullptr;
-
- bool portal_init_failed_ = false;
-@@ -95,6 +107,7 @@ class BaseCapturerPipeWire : public Desk
-
- void ConvertRGBxToBGRx(uint8_t* frame, uint32_t size);
-
-+ static void SyncDmaBuf(int fd, uint64_t start_or_end);
- static void OnStateChanged(void* data,
- pw_remote_state old_state,
- pw_remote_state state,
-diff -up firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc.firefox-pipewire-0-2 firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc
---- firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc.firefox-pipewire-0-2 2020-07-20 22:53:57.000000000 +0200
-+++ firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc 2020-07-29 11:03:51.619283301 +0200
-@@ -15,7 +15,7 @@
- namespace webrtc {
-
- ScreenCapturerPipeWire::ScreenCapturerPipeWire()
-- : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::Screen) {}
-+ : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::kScreen) {}
- ScreenCapturerPipeWire::~ScreenCapturerPipeWire() {}
-
- // static
-diff -up firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc.firefox-pipewire-0-2 firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc
---- firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc.firefox-pipewire-0-2 2020-07-20 22:54:18.000000000 +0200
-+++ firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc 2020-07-29 11:03:51.619283301 +0200
-@@ -15,7 +15,7 @@
- namespace webrtc {
-
- WindowCapturerPipeWire::WindowCapturerPipeWire()
-- : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::Window) {}
-+ : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::kWindow) {}
- WindowCapturerPipeWire::~WindowCapturerPipeWire() {}
-
- // static
-diff -up firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc.firefox-pipewire-0-2 firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc
---- firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc.firefox-pipewire-0-2 2020-07-20 22:54:40.000000000 +0200
-+++ firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc 2020-07-29 11:03:51.620283296 +0200
-@@ -26,7 +26,7 @@ std::unique_ptr<DesktopCapturer> Desktop
- const DesktopCaptureOptions& options) {
- #if defined(WEBRTC_USE_PIPEWIRE)
- if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) {
-- return ScreenCapturerPipeWire::CreateRawScreenCapturer(options);
-+ return BaseCapturerPipeWire::CreateRawScreenCapturer(options);
- }
- #endif // defined(WEBRTC_USE_PIPEWIRE)
-
-diff -up firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc.firefox-pipewire-0-2 firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc
---- firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc.firefox-pipewire-0-2 2020-07-20 22:53:32.000000000 +0200
-+++ firefox-79.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc 2020-07-29 11:03:51.620283296 +0200
-@@ -26,7 +26,7 @@ std::unique_ptr<DesktopCapturer> Desktop
- const DesktopCaptureOptions& options) {
- #if defined(WEBRTC_USE_PIPEWIRE)
- if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) {
-- return WindowCapturerPipeWire::CreateRawWindowCapturer(options);
-+ return BaseCapturerPipeWire::CreateRawWindowCapturer(options);
- }
- #endif // defined(WEBRTC_USE_PIPEWIRE)
-
diff --git a/firefox.spec b/firefox.spec
index 687300d..3f6fd90 100644
--- a/firefox.spec
+++ b/firefox.spec
@@ -121,13 +121,13 @@ ExcludeArch: s390x
Summary: Mozilla Firefox Web browser
Name: firefox
-Version: 82.0.3
-Release: 2%{?pre_tag}%{?dist}
+Version: 83.0
+Release: 1%{?pre_tag}%{?dist}
URL: https://www.mozilla.org/firefox/
License: MPLv1.1 or GPLv2+ or LGPLv2+
Source0: https://archive.mozilla.org/pub/firefox/releases/%{version}%{?pre_version}/source/firefox-%{version}%{?pre_version}.source.tar.xz
%if %{with langpacks}
-Source1: firefox-langpacks-%{version}%{?pre_version}-20201109.tar.xz
+Source1: firefox-langpacks-%{version}%{?pre_version}-20201112.tar.xz
%endif
Source2: cbindgen-vendor.tar.xz
Source10: firefox-mozconfig
@@ -166,7 +166,6 @@ Patch49: build-arm-libaom.patch
Patch53: firefox-gcc-build.patch
# This should be fixed in Firefox 83
Patch54: mozilla-1669639.patch
-Patch55: mozilla-1669442.patch
# Fedora specific patches
Patch215: firefox-enable-addons.patch
@@ -180,23 +179,22 @@ Patch228: disable-openh264-download.patch
# Upstream patches
Patch402: mozilla-1196777.patch
-Patch406: mozilla-1665329.patch
Patch407: mozilla-1667096.patch
Patch408: mozilla-1663844.patch
-Patch409: mozilla-1640567.patch
-Patch410: mozilla-1661192.patch
-Patch412: mozilla-1634404.patch
-Patch413: mozilla-1669495.patch
-Patch414: mozilla-1656727.patch
Patch415: mozilla-1670333.patch
Patch416: mozilla-1673202.patch
Patch417: mozilla-1673313.patch
Patch418: mozilla-1556931-s390x-hidden-syms.patch
Patch419: mozilla-1885133.patch
-# Wayland specific upstream patches
-Patch574: firefox-pipewire-0-2.patch
-Patch575: firefox-pipewire-0-3.patch
+# Upstream patches from mozbz#1672944
+Patch450: pw1.patch
+Patch451: pw2.patch
+Patch452: pw3.patch
+Patch453: pw4.patch
+Patch454: pw5.patch
+Patch455: pw6.patch
+Patch456: pw7.patch
#VA-API patches
Patch584: firefox-disable-ffvpx-with-vapi.patch
@@ -380,7 +378,6 @@ This package contains results of tests executed during build.
%patch49 -p1 -b .build-arm-libaom
%patch53 -p1 -b .firefox-gcc-build
%patch54 -p1 -b .1669639
-%patch55 -p1 -b .1669442
# Fedora patches
%patch215 -p1 -b .addons
@@ -395,14 +392,8 @@ This package contains results of tests executed during build.
%patch228 -p1 -b .disable-openh264-download
%patch402 -p1 -b .1196777
-%patch406 -p1 -b .1665329
%patch407 -p1 -b .1667096
%patch408 -p1 -b .1663844
-%patch409 -p1 -b .1640567
-%patch410 -p1 -b .1661192
-%patch412 -p1 -b .1634404
-%patch413 -p1 -b .1669495
-%patch414 -p1 -b .1656727
%patch415 -p1 -b .1670333
%if 0%{?fedora} > 33 || 0%{?eln}
%patch416 -p1 -b .1673202
@@ -411,12 +402,13 @@ This package contains results of tests executed during build.
%patch418 -p1 -b .1556931-s390x-hidden-syms
%patch419 -p1 -b .1885133
-# Wayland specific upstream patches
-%if 0%{?fedora} > 31 || 0%{?eln}
-%patch575 -p1 -b .firefox-pipewire-0-3
-%else
-%patch574 -p1 -b .firefox-pipewire-0-2
-%endif
+%patch450 -p1 -b .pw1
+%patch451 -p1 -b .pw2
+%patch452 -p1 -b .pw3
+%patch453 -p1 -b .pw4
+%patch454 -p1 -b .pw5
+%patch455 -p1 -b .pw6
+%patch456 -p1 -b .pw7
# VA-API fixes
%patch584 -p1 -b .firefox-disable-ffvpx-with-vapi
@@ -986,6 +978,10 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
#---------------------------------------------------------------------
%changelog
+* Thu Nov 12 2020 Martin Stransky <stransky@redhat.com> - 83.0-1
+- Updated to 83.0
+- Updated PipeWire patches from mozbz#1672944
+
* Tue Nov 10 2020 Martin Stransky <stransky@redhat.com> - 82.0.3-2
- Added fix for mozbz#1885133
diff --git a/mozilla-1634404.patch b/mozilla-1634404.patch
deleted file mode 100644
index 75ea8ce..0000000
--- a/mozilla-1634404.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
---- a/widget/gtk/nsWindow.cpp
-+++ b/widget/gtk/nsWindow.cpp
-@@ -1593,7 +1593,14 @@
- // Get anchor rectangle
- LayoutDeviceIntRect anchorRect(0, 0, 0, 0);
- nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
-- int32_t p2a = AppUnitsPerCSSPixel() / gfxPlatformGtk::GetFontScaleFactor();
-+
-+ int32_t p2a;
-+ double devPixelsPerCSSPixel = StaticPrefs::layout_css_devPixelsPerPx();
-+ if (devPixelsPerCSSPixel > 0.0) {
-+ p2a = AppUnitsPerCSSPixel() / devPixelsPerCSSPixel * GdkScaleFactor();
-+ } else {
-+ p2a = AppUnitsPerCSSPixel() / gfxPlatformGtk::GetFontScaleFactor();
-+ }
- if (popupFrame) {
- #ifdef MOZ_WAYLAND
- anchorRect = LayoutDeviceIntRect::FromAppUnitsToOutside(
-
diff --git a/mozilla-1640567.patch b/mozilla-1640567.patch
deleted file mode 100644
index c1f3f44..0000000
--- a/mozilla-1640567.patch
+++ /dev/null
@@ -1,18 +0,0 @@
-diff --git a/layout/xul/nsMenuPopupFrame.cpp b/layout/xul/nsMenuPopupFrame.cpp
---- a/layout/xul/nsMenuPopupFrame.cpp
-+++ b/layout/xul/nsMenuPopupFrame.cpp
-@@ -1422,11 +1422,9 @@
- !GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
- screenPoint = nsPoint(anchorRect.x, anchorRect.y);
- mAnchorRect = anchorRect;
-- } else
-+ }
- #endif
-- {
-- screenPoint = AdjustPositionForAnchorAlign(anchorRect, hFlip, vFlip);
-- }
-+ screenPoint = AdjustPositionForAnchorAlign(anchorRect, hFlip, vFlip);
- } else {
- // with no anchor, the popup is positioned relative to the root frame
- anchorRect = rootScreenRect;
-
diff --git a/mozilla-1656727.patch b/mozilla-1656727.patch
deleted file mode 100644
index 91547b0..0000000
--- a/mozilla-1656727.patch
+++ /dev/null
@@ -1,213 +0,0 @@
-diff -up firefox-82.0/widget/gtk/WindowSurfaceWayland.cpp.1656727 firefox-82.0/widget/gtk/WindowSurfaceWayland.cpp
---- firefox-82.0/widget/gtk/WindowSurfaceWayland.cpp.1656727 2020-10-15 16:16:53.522050159 +0200
-+++ firefox-82.0/widget/gtk/WindowSurfaceWayland.cpp 2020-10-15 16:18:24.956289348 +0200
-@@ -158,7 +158,6 @@ We allocate shared memory (shm) by mmap(
- between us and wayland compositor. We draw our graphics data to the shm and
- handle to wayland compositor by WindowBackBuffer/WindowSurfaceWayland
- (wl_buffer/wl_surface).
--
- */
-
- #define EVENT_LOOP_DELAY (1000 / 240)
-@@ -166,6 +165,44 @@ handle to wayland compositor by WindowBa
- #define BUFFER_BPP 4
- gfx::SurfaceFormat WindowBackBuffer::mFormat = gfx::SurfaceFormat::B8G8R8A8;
-
-+static mozilla::Mutex* gDelayedCommitLock = nullptr;
-+static GList* gDelayedCommits = nullptr;
-+
-+static void DelayedCommitsEnsureMutext() {
-+ if (!gDelayedCommitLock) {
-+ gDelayedCommitLock = new mozilla::Mutex("DelayedCommit lock");
-+ }
-+}
-+
-+static bool DelayedCommitsCheckAndRemoveSurface(
-+ WindowSurfaceWayland* aSurface) {
-+ MutexAutoLock lock(*gDelayedCommitLock);
-+ GList* foundCommit = g_list_find(gDelayedCommits, aSurface);
-+ if (foundCommit) {
-+ gDelayedCommits = g_list_delete_link(gDelayedCommits, foundCommit);
-+ }
-+ return foundCommit != nullptr;
-+}
-+
-+static bool DelayedCommitsCheckAndAddSurface(WindowSurfaceWayland* aSurface) {
-+ MutexAutoLock lock(*gDelayedCommitLock);
-+ GList* foundCommit = g_list_find(gDelayedCommits, aSurface);
-+ if (!foundCommit) {
-+ gDelayedCommits = g_list_prepend(gDelayedCommits, aSurface);
-+ }
-+ return foundCommit == nullptr;
-+}
-+
-+// When a new window is created we may not have a valid wl_surface
-+// for drawing (Gtk haven't created it yet). All commits are queued
-+// and CommitWaylandBuffer() is called by timer when wl_surface is ready
-+// for drawing.
-+static void WaylandBufferDelayCommitHandler(WindowSurfaceWayland* aSurface) {
-+ if (DelayedCommitsCheckAndRemoveSurface(aSurface)) {
-+ aSurface->CommitWaylandBuffer();
-+ }
-+}
-+
- RefPtr<nsWaylandDisplay> WindowBackBuffer::GetWaylandDisplay() {
- return mWindowSurfaceWayland->GetWaylandDisplay();
- }
-@@ -399,7 +436,6 @@ WindowSurfaceWayland::WindowSurfaceWayla
- mWaylandFullscreenDamage(false),
- mFrameCallback(nullptr),
- mLastCommittedSurface(nullptr),
-- mDelayedCommitHandle(nullptr),
- mLastCommitTime(0),
- mDrawToWaylandBufferDirectly(true),
- mCanSwitchWaylandBuffer(true),
-@@ -411,6 +447,7 @@ WindowSurfaceWayland::WindowSurfaceWayla
- for (int i = 0; i < BACK_BUFFER_NUM; i++) {
- mShmBackupBuffer[i] = nullptr;
- }
-+ DelayedCommitsEnsureMutext();
- }
-
- WindowSurfaceWayland::~WindowSurfaceWayland() {
-@@ -418,12 +455,9 @@ WindowSurfaceWayland::~WindowSurfaceWayl
- NS_WARNING("Deleted WindowSurfaceWayland with a pending commit!");
- }
-
-- if (mDelayedCommitHandle) {
-- // Delete reference to this to prevent WaylandBufferDelayCommitHandler()
-- // operate on released this. mDelayedCommitHandle itself will
-- // be released at WaylandBufferDelayCommitHandler().
-- *mDelayedCommitHandle = nullptr;
-- }
-+ // Delete reference to this to prevent WaylandBufferDelayCommitHandler()
-+ // operate on released this.
-+ DelayedCommitsCheckAndRemoveSurface(this);
-
- if (mFrameCallback) {
- wl_callback_destroy(mFrameCallback);
-@@ -864,23 +898,11 @@ bool WindowSurfaceWayland::CommitImageCa
- return true;
- }
-
--static void WaylandBufferDelayCommitHandler(WindowSurfaceWayland** aSurface) {
-- if (*aSurface) {
-- (*aSurface)->DelayedCommitHandler();
-- } else {
-- // Referenced WindowSurfaceWayland is already deleted.
-- // Do nothing but just release the mDelayedCommitHandle allocated at
-- // WindowSurfaceWayland::CommitWaylandBuffer().
-- free(aSurface);
-- }
--}
--
- void WindowSurfaceWayland::CommitWaylandBuffer() {
- LOGWAYLAND(("WindowSurfaceWayland::CommitWaylandBuffer [%p]\n", (void*)this));
- LOGWAYLAND(
- (" mDrawToWaylandBufferDirectly = %d\n", mDrawToWaylandBufferDirectly));
- LOGWAYLAND((" mCanSwitchWaylandBuffer = %d\n", mCanSwitchWaylandBuffer));
-- LOGWAYLAND((" mDelayedCommitHandle = %p\n", mDelayedCommitHandle));
- LOGWAYLAND((" mFrameCallback = %p\n", mFrameCallback));
- LOGWAYLAND((" mLastCommittedSurface = %p\n", mLastCommittedSurface));
- LOGWAYLAND((" mBufferPendingCommit = %d\n", mBufferPendingCommit));
-@@ -916,16 +938,10 @@ void WindowSurfaceWayland::CommitWayland
- MOZ_ASSERT(!mFrameCallback || waylandSurface != mLastCommittedSurface,
- "Missing wayland surface at frame callback!");
-
-- // Do nothing if there's already mDelayedCommitHandle pending.
-- if (!mDelayedCommitHandle) {
-- mDelayedCommitHandle = static_cast<WindowSurfaceWayland**>(
-- moz_xmalloc(sizeof(*mDelayedCommitHandle)));
-- *mDelayedCommitHandle = this;
--
-+ if (DelayedCommitsCheckAndAddSurface(this)) {
- MessageLoop::current()->PostDelayedTask(
- NewRunnableFunction("WaylandBackBufferCommit",
-- &WaylandBufferDelayCommitHandler,
-- mDelayedCommitHandle),
-+ &WaylandBufferDelayCommitHandler, this),
- EVENT_LOOP_DELAY);
- }
- return;
-@@ -1037,25 +1053,6 @@ void WindowSurfaceWayland::FrameCallback
-
- CommitWaylandBuffer();
- }
--
--void WindowSurfaceWayland::DelayedCommitHandler() {
-- MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
-- MOZ_ASSERT(mDelayedCommitHandle != nullptr, "Missing mDelayedCommitHandle!");
--
-- LOGWAYLAND(
-- ("WindowSurfaceWayland::DelayedCommitHandler [%p]\n", (void*)this));
--
-- if (!mDelayedCommitHandle) {
-- LOGWAYLAND((" We're missing mDelayedCommitHandle!\n"));
-- return;
-- }
--
-- *mDelayedCommitHandle = nullptr;
-- free(mDelayedCommitHandle);
-- mDelayedCommitHandle = nullptr;
--
-- CommitWaylandBuffer();
--}
-
- } // namespace widget
- } // namespace mozilla
-diff -up firefox-82.0/widget/gtk/WindowSurfaceWayland.h.1656727 firefox-82.0/widget/gtk/WindowSurfaceWayland.h
---- firefox-82.0/widget/gtk/WindowSurfaceWayland.h.1656727 2020-10-14 19:20:27.000000000 +0200
-+++ firefox-82.0/widget/gtk/WindowSurfaceWayland.h 2020-10-15 16:16:53.528050175 +0200
-@@ -161,7 +161,7 @@ class WindowSurfaceWayland : public Wind
- // If we fail (wayland compositor is busy,
- // wl_surface is not created yet) we queue the painting
- // and we send it to wayland compositor in FrameCallbackHandler()/
-- // DelayedCommitHandler/CommitWaylandBuffer().
-+ // CommitWaylandBuffer().
- already_AddRefed<gfx::DrawTarget> Lock(
- const LayoutDeviceIntRegion& aRegion) override;
- void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final;
-@@ -171,12 +171,6 @@ class WindowSurfaceWayland : public Wind
- // queued commits.
- void FrameCallbackHandler();
-
-- // When a new window is created we may not have a valid wl_surface
-- // for drawing (Gtk haven't created it yet). All commits are queued
-- // and DelayedCommitHandler() is called by timer when wl_surface is ready
-- // for drawing.
-- void DelayedCommitHandler();
--
- // Try to commit all queued drawings to Wayland compositor. This is usually
- // called from other routines but can be used to explicitly flush
- // all drawings as we do when wl_buffer is released
-@@ -249,17 +243,14 @@ class WindowSurfaceWayland : public Wind
- wl_callback* mFrameCallback;
- wl_surface* mLastCommittedSurface;
-
-- // Registered reference to pending DelayedCommitHandler() call.
-- WindowSurfaceWayland** mDelayedCommitHandle;
--
- // Cached drawings. If we can't get WaylandBuffer (wl_buffer) at
- // WindowSurfaceWayland::Lock() we direct gecko rendering to
- // mImageSurface.
- // If we can't get WaylandBuffer at WindowSurfaceWayland::Commit()
- // time, mImageSurface is moved to mDelayedImageCommits which
- // holds all cached drawings.
-- // mDelayedImageCommits can be drawn by FrameCallbackHandler(),
-- // DelayedCommitHandler() or when WaylandBuffer is detached.
-+ // mDelayedImageCommits can be drawn by FrameCallbackHandler()
-+ // or when WaylandBuffer is detached.
- RefPtr<gfxImageSurface> mImageSurface;
- AutoTArray<WindowImageSurface, 30> mDelayedImageCommits;
-
-@@ -282,8 +273,8 @@ class WindowSurfaceWayland : public Wind
- // We can't send WaylandBuffer (wl_buffer) to compositor when gecko
- // is rendering into it (i.e. between WindowSurfaceWayland::Lock() /
- // WindowSurfaceWayland::Commit()).
-- // Thus we use mBufferCommitAllowed to disable commit by callbacks
-- // (FrameCallbackHandler(), DelayedCommitHandler())
-+ // Thus we use mBufferCommitAllowed to disable commit by
-+ // CommitWaylandBuffer().
- bool mBufferCommitAllowed;
-
- // We need to clear WaylandBuffer when entire transparent window is repainted.
diff --git a/mozilla-1661192.patch b/mozilla-1661192.patch
deleted file mode 100644
index fa3cd42..0000000
--- a/mozilla-1661192.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
---- a/widget/gtk/nsWindow.cpp
-+++ b/widget/gtk/nsWindow.cpp
-@@ -1600,9 +1600,11 @@
- #endif
- }
-
-+ bool hasAnchorRect = true;
- if (anchorRect.width == 0) {
- LOG((" No anchor rect given, use aPosition for anchor"));
- anchorRect.SetRect(aPosition->x, aPosition->y, 1, 1);
-+ hasAnchorRect = false;
- }
- LOG((" anchor x %d y %d width %d height %d (absolute coords)\n",
- anchorRect.x, anchorRect.y, anchorRect.width, anchorRect.height));
-@@ -1704,7 +1706,7 @@
- nsPoint cursorOffset(0, 0);
- #ifdef MOZ_WAYLAND
- // Offset is already computed to the tooltips
-- if (popupFrame && mPopupType != ePopupTypeTooltip) {
-+ if (hasAnchorRect && popupFrame && mPopupType != ePopupTypeTooltip) {
- nsMargin margin(0, 0, 0, 0);
- popupFrame->StyleMargin()->GetMargin(margin);
- switch (popupFrame->GetPopupAlignment()) {
-
diff --git a/mozilla-1665329.patch b/mozilla-1665329.patch
deleted file mode 100644
index 62ce5ae..0000000
--- a/mozilla-1665329.patch
+++ /dev/null
@@ -1,16 +0,0 @@
-diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
---- a/modules/libpref/init/StaticPrefList.yaml
-+++ b/modules/libpref/init/StaticPrefList.yaml
-@@ -7565,11 +7565,7 @@
- # acceleration for decoding.
- - name: media.navigator.mediadatadecoder_vpx_enabled
- type: RelaxedAtomicBool
-- #if defined(NIGHTLY_BUILD)
- value: true
-- #else
-- value: false
-- #endif
- mirror: always
-
- # Use MediaDataDecoder API for H264 in WebRTC. This includes hardware
-
diff --git a/mozilla-1669442.patch b/mozilla-1669442.patch
deleted file mode 100644
index 6373816..0000000
--- a/mozilla-1669442.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/config/recurse.mk b/config/recurse.mk
---- a/config/recurse.mk
-+++ b/config/recurse.mk
-@@ -206,7 +206,7 @@
- # Interdependencies that moz.build world don't know about yet for compilation.
- # Note some others are hardcoded or "guessed" in recursivemake.py and emitter.py
- ifeq ($(MOZ_WIDGET_TOOLKIT),gtk)
--toolkit/library/target: widget/gtk/mozgtk/gtk3/target
-+toolkit/library/build/target: widget/gtk/mozgtk/gtk3/target
- endif
-
- ifndef MOZ_FOLD_LIBS
-
diff --git a/mozilla-1669495.patch b/mozilla-1669495.patch
deleted file mode 100644
index e30bb89..0000000
--- a/mozilla-1669495.patch
+++ /dev/null
@@ -1,130 +0,0 @@
-diff -up firefox-82.0/layout/xul/nsMenuPopupFrame.cpp.1669495 firefox-82.0/layout/xul/nsMenuPopupFrame.cpp
---- firefox-82.0/layout/xul/nsMenuPopupFrame.cpp.1669495 2020-10-15 16:13:12.304471453 +0200
-+++ firefox-82.0/layout/xul/nsMenuPopupFrame.cpp 2020-10-15 16:13:12.308471463 +0200
-@@ -533,6 +533,26 @@ void nsMenuPopupFrame::LayoutPopup(nsBox
- }
- prefSize = XULBoundsCheck(minSize, prefSize, maxSize);
-
-+#ifdef MOZ_WAYLAND
-+ static bool inWayland = gdk_display_get_default() &&
-+ !GDK_IS_X11_DISPLAY(gdk_display_get_default());
-+#else
-+ static bool inWayland = false;
-+#endif
-+ if (inWayland) {
-+ // If prefSize it is not a whole number in css pixels we need round it up
-+ // to avoid reflow of the tooltips/popups and putting the text on two lines
-+ // (usually happens with 200% scale factor and font scale factor <> 1)
-+ // because GTK thrown away the decimals.
-+ int32_t appPerCSS = AppUnitsPerCSSPixel();
-+ if (prefSize.width % appPerCSS > 0) {
-+ prefSize.width += appPerCSS;
-+ }
-+ if (prefSize.height % appPerCSS > 0) {
-+ prefSize.height += appPerCSS;
-+ }
-+ }
-+
- bool sizeChanged = (mPrefSize != prefSize);
- // if the size changed then set the bounds to be the preferred size
- if (sizeChanged) {
-diff -up firefox-82.0/widget/gtk/nsWindow.cpp.1669495 firefox-82.0/widget/gtk/nsWindow.cpp
---- firefox-82.0/widget/gtk/nsWindow.cpp.1669495 2020-10-15 16:13:12.307471461 +0200
-+++ firefox-82.0/widget/gtk/nsWindow.cpp 2020-10-15 16:15:49.243882006 +0200
-@@ -1092,11 +1092,13 @@ void nsWindow::Show(bool aState) {
-
- void nsWindow::ResizeInt(int aX, int aY, int aWidth, int aHeight, bool aMove,
- bool aRepaint) {
-- LOG(("nsWindow::ResizeInt [%p] %d %d -> %d %d repaint %d\n", (void*)this, aX,
-- aY, aWidth, aHeight, aRepaint));
-+ LOG(("nsWindow::ResizeInt [%p] x:%d y:%d -> w:%d h:%d repaint %d aMove %d\n",
-+ (void*)this, aX, aY, aWidth, aHeight, aRepaint, aMove));
-
- ConstrainSize(&aWidth, &aHeight);
-
-+ LOG((" ConstrainSize: w:%d h;%d\n", aWidth, aHeight));
-+
- // If we used to have insane bounds, we may have skipped actually positioning
- // the widget in NativeMoveResizeWaylandPopup, in which case we need to
- // actually position it now as well.
-@@ -1141,8 +1143,7 @@ void nsWindow::ResizeInt(int aX, int aY,
- }
-
- void nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) {
-- LOG(("nsWindow::Resize [%p] %d %d\n", (void*)this, (int)aWidth,
-- (int)aHeight));
-+ LOG(("nsWindow::Resize [%p] %f %f\n", (void*)this, aWidth, aHeight));
-
- double scale =
- BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
-@@ -1154,8 +1155,8 @@ void nsWindow::Resize(double aWidth, dou
-
- void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
- bool aRepaint) {
-- LOG(("nsWindow::Resize [%p] %d %d repaint %d\n", (void*)this, (int)aWidth,
-- (int)aHeight, aRepaint));
-+ LOG(("nsWindow::Resize [%p] %f %f repaint %d\n", (void*)this, aWidth, aHeight,
-+ aRepaint));
-
- double scale =
- BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
-@@ -1478,14 +1479,15 @@ void nsWindow::NativeMoveResizeWaylandPo
-
- newBounds.x = GdkCoordToDevicePixels(newBounds.x);
- newBounds.y = GdkCoordToDevicePixels(newBounds.y);
-- LOG((" new mBounds x=%d y=%d width=%d height=%d\n", newBounds.x,
-- newBounds.y, newBounds.width, newBounds.height));
-
- double scale =
- BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
- int32_t newWidth = NSToIntRound(scale * newBounds.width);
- int32_t newHeight = NSToIntRound(scale * newBounds.height);
-
-+ LOG((" new mBounds x=%d y=%d width=%d height=%d\n", newBounds.x,
-+ newBounds.y, newWidth, newHeight));
-+
- bool needsPositionUpdate =
- (newBounds.x != mBounds.x || newBounds.y != mBounds.y);
- bool needsSizeUpdate =
-@@ -1493,6 +1495,7 @@ void nsWindow::NativeMoveResizeWaylandPo
- // Update view
-
- if (needsSizeUpdate) {
-+ LOG((" needSizeUpdate\n"));
- int32_t p2a = AppUnitsPerCSSPixel() / gfxPlatformGtk::GetFontScaleFactor();
- mPreferredPopupRect = nsRect(NSIntPixelsToAppUnits(newBounds.x, p2a),
- NSIntPixelsToAppUnits(newBounds.y, p2a),
-@@ -1511,6 +1514,7 @@ void nsWindow::NativeMoveResizeWaylandPo
- }
-
- if (needsPositionUpdate) {
-+ LOG((" needPositionUpdate\n"));
- // The newBounds are in coordinates relative to the parent window/popup.
- // The NotifyWindowMoved requires the coordinates relative to the toplevel.
- // We use the gdk_window_get_origin to get correct coordinates.
-@@ -4245,6 +4249,8 @@ nsresult nsWindow::Create(nsIWidget* aPa
-
- // save our bounds
- mBounds = aRect;
-+ LOG((" mBounds: x:%d y:%d w:%d h:%d\n", mBounds.x, mBounds.y, mBounds.width,
-+ mBounds.height));
-
- mPreferredPopupRectFlushed = false;
-
-@@ -5083,13 +5089,16 @@ void nsWindow::NativeShow(bool aAction)
- }
- }
-
-+ LOG((" calling gtk_widget_show(mShell)\n"));
- gtk_widget_show(mShell);
- if (!mIsX11Display) {
- WaylandStartVsync();
- }
- } else if (mContainer) {
-+ LOG((" calling gtk_widget_show(mContainer)\n"));
- gtk_widget_show(GTK_WIDGET(mContainer));
- } else if (mGdkWindow) {
-+ LOG((" calling gdk_window_show_unraised\n"));
- gdk_window_show_unraised(mGdkWindow);
- }
- } else {
diff --git a/pw1.patch b/pw1.patch
new file mode 100644
index 0000000..1f88545
--- /dev/null
+++ b/pw1.patch
@@ -0,0 +1,78 @@
+
+# HG changeset patch
+# User stransky <stransky@redhat.com>
+# Date 1604562416 0
+# Node ID 1c126b520042591194e88618ae11a6adc1da9a08
+# Parent 6e2e4f0e4a95b0cae777dda9369a9e9bf49a51b1
+Bug 1672987 Use PipeWire when Wayland display is actually used, r=dminor
+
+Right now PipeWire is enabled when Wayland session is used regardless of an active Gtk backend (X11/Wayland).
+Let's use PipeWire only when Wayland Gtk backend is used and disable it for X11 one to avoid possible regressions.
+
+Differential Revision: https://phabricator.services.mozilla.com/D94588
+
+diff --git a/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capturer.cc b/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capturer.cc
+--- a/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capturer.cc
++++ b/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capturer.cc
+@@ -8,16 +8,21 @@
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+ #include "modules/desktop_capture/desktop_capturer.h"
+
+ #include "modules/desktop_capture/desktop_capture_options.h"
+ #include "modules/desktop_capture/desktop_capturer_differ_wrapper.h"
+
++#if defined(WEBRTC_USE_PIPEWIRE) || defined(USE_X11)
++#include <gtk/gtk.h>
++#include <gtk/gtkx.h>
++#endif
++
+ namespace webrtc {
+
+ DesktopCapturer::~DesktopCapturer() = default;
+
+ void DesktopCapturer::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {}
+
+ void DesktopCapturer::SetExcludedWindow(WindowId window) {}
+@@ -67,21 +72,37 @@ std::unique_ptr<DesktopCapturer> Desktop
+ if (capturer && options.detect_updated_region()) {
+ capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
+ }
+
+ return capturer;
+ }
+
+ #if defined(WEBRTC_USE_PIPEWIRE) || defined(USE_X11)
+-bool DesktopCapturer::IsRunningUnderWayland() {
++// Return true if Firefox is actually running with Wayland backend.
++static bool IsWaylandDisplayUsed() {
++ const auto display = gdk_display_get_default();
++ if (display == nullptr) {
++ // We're running in headless mode.
++ return false;
++ }
++ return !GDK_IS_X11_DISPLAY(display);
++}
++
++// Return true if Firefox is actually running on Wayland enabled session.
++// It means some screensharing capabilities may be limited.
++static bool IsWaylandSessionUsed() {
+ const char* xdg_session_type = getenv("XDG_SESSION_TYPE");
+ if (!xdg_session_type || strncmp(xdg_session_type, "wayland", 7) != 0)
+ return false;
+
+ if (!(getenv("WAYLAND_DISPLAY")))
+ return false;
+
+ return true;
+ }
++
++bool DesktopCapturer::IsRunningUnderWayland() {
++ return IsWaylandSessionUsed() ? IsWaylandDisplayUsed() : false;
++}
+ #endif // defined(WEBRTC_USE_PIPEWIRE) || defined(USE_X11)
+
+ } // namespace webrtc
+
diff --git a/firefox-pipewire-0-3.patch b/pw2.patch
index 8dc7eed..9390460 100644
--- a/firefox-pipewire-0-3.patch
+++ b/pw2.patch
@@ -1,100 +1,47 @@
-diff -up firefox-81.0/config/system-headers.mozbuild.firefox-pipewire-0-3 firefox-81.0/config/system-headers.mozbuild
---- firefox-81.0/config/system-headers.mozbuild.firefox-pipewire-0-3 2020-09-15 03:48:26.000000000 +0200
-+++ firefox-81.0/config/system-headers.mozbuild 2020-09-15 14:40:00.721481417 +0200
-@@ -314,6 +314,7 @@ system_headers = [
- 'Gestalt.h',
- 'getopt.h',
- 'gio/gio.h',
-+ 'gio/gunixfdlist.h',
- 'glibconfig.h',
- 'glib.h',
- 'glib-object.h',
-@@ -607,6 +608,7 @@ system_headers = [
- 'Pgenerr.h',
- 'PGenErr.h',
- 'Ph.h',
-+ 'pipewire/pipewire.h',
- 'pixman.h',
- 'pk11func.h',
- 'pk11pqg.h',
-diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn.firefox-pipewire-0-3 firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn
---- firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn.firefox-pipewire-0-3 2020-09-15 03:48:32.000000000 +0200
-+++ firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn 2020-09-15 14:40:00.721481417 +0200
-@@ -158,7 +158,7 @@ if (rtc_include_tests) {
- if (is_linux) {
- if (rtc_use_pipewire) {
- pkg_config("pipewire") {
-- packages = [ "libpipewire-0.2" ]
-+ packages = [ "libpipewire-0.3" ]
-
- defines = [ "WEBRTC_USE_PIPEWIRE" ]
- }
-diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build.firefox-pipewire-0-3 firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build
---- firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build.firefox-pipewire-0-3 2020-09-15 14:40:00.722481420 +0200
-+++ firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build 2020-09-15 14:48:47.454733146 +0200
-@@ -193,6 +193,28 @@ if CONFIG["OS_TARGET"] == "Linux":
- "/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc"
- ]
-
-+# PipeWire specific files
-+if CONFIG["OS_TARGET"] == "Linux":
-+ DEFINES["WEBRTC_USE_PIPEWIRE"] = "1"
-+
-+ OS_LIBS += [
-+ "rt",
-+ "pipewire-0.3",
-+ "glib-2.0",
-+ "gio-2.0",
-+ "gobject-2.0"
-+ ]
-+
-+ CXXFLAGS += CONFIG['TK_CFLAGS']
-+ CXXFLAGS += [ "-I/usr/include/pipewire-0.3" ]
-+ CXXFLAGS += [ "-I/usr/include/spa-0.2" ]
-+
-+ UNIFIED_SOURCES += [
-+ "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc",
-+ "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc",
-+ "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc"
-+ ]
-+
- if CONFIG["OS_TARGET"] == "NetBSD":
-
- DEFINES["USE_X11"] = "1"
-diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h.firefox-pipewire-0-3 firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h
---- firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h.firefox-pipewire-0-3 2020-09-15 03:48:32.000000000 +0200
-+++ firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_options.h 2020-09-15 14:40:00.722481420 +0200
-@@ -141,7 +141,7 @@ class DesktopCaptureOptions {
- bool disable_effects_ = true;
- bool detect_updated_region_ = false;
- #if defined(WEBRTC_USE_PIPEWIRE)
-- bool allow_pipewire_ = false;
-+ bool allow_pipewire_ = true;
- #endif
- };
-
-diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc.firefox-pipewire-0-3 firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc
---- firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc.firefox-pipewire-0-3 2020-09-15 03:48:32.000000000 +0200
-+++ firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc 2020-09-15 14:40:00.722481420 +0200
-@@ -15,8 +15,11 @@
+
+# HG changeset patch
+# User stransky <stransky@redhat.com>
+# Date 1604560111 0
+# Node ID 998e6d0b24e4a560e5664aaef87307e9c069ad87
+# Parent 1c126b520042591194e88618ae11a6adc1da9a08
+Bug 1672947 Update PipeWire WebRTC code to PipeWire 0.3, r=ng
+
+Differential Revision: https://phabricator.services.mozilla.com/D94589
+
+diff --git a/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc b/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc
+--- a/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc
++++ b/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc
+@@ -10,18 +10,20 @@
+
+ #include "modules/desktop_capture/linux/base_capturer_pipewire.h"
+
+ #include <gio/gunixfdlist.h>
+ #include <glib-object.h>
#include <spa/param/format-utils.h>
#include <spa/param/props.h>
-#include <spa/param/video/raw-utils.h>
-#include <spa/support/type-map.h>
+
-+#include <linux/dma-buf.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
#include <memory>
#include <utility>
-@@ -36,32 +39,37 @@ const char kSessionInterfaceName[] = "or
+
+ #include "modules/desktop_capture/desktop_capture_options.h"
+ #include "modules/desktop_capture/desktop_capturer.h"
+ #include "rtc_base/checks.h"
+ #include "rtc_base/logging.h"
+@@ -32,180 +34,166 @@ const char kDesktopBusName[] = "org.free
+ const char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop";
+ const char kDesktopRequestObjectPath[] =
+ "/org/freedesktop/portal/desktop/request";
+ const char kSessionInterfaceName[] = "org.freedesktop.portal.Session";
const char kRequestInterfaceName[] = "org.freedesktop.portal.Request";
const char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast";
-+
// static
-void BaseCapturerPipeWire::OnStateChanged(void* data,
- pw_remote_state old_state,
@@ -102,19 +49,21 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
- const char* error_message) {
- BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(data);
- RTC_DCHECK(that);
-+void BaseCapturerPipeWire::SyncDmaBuf(int fd, uint64_t start_or_end) {
-+ struct dma_buf_sync sync = { 0 };
++struct dma_buf_sync {
++ uint64_t flags;
++};
++#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)
- switch (state) {
- case PW_REMOTE_STATE_ERROR:
- RTC_LOG(LS_ERROR) << "PipeWire remote state error: " << error_message;
-- break;
-- case PW_REMOTE_STATE_CONNECTED:
-- RTC_LOG(LS_INFO) << "PipeWire remote state: connected.";
-- that->CreateReceivingStream();
-- break;
-- case PW_REMOTE_STATE_CONNECTING:
-- RTC_LOG(LS_INFO) << "PipeWire remote state: connecting.";
++static void SyncDmaBuf(int fd, uint64_t start_or_end) {
++ struct dma_buf_sync sync = { 0 };
++
+ sync.flags = start_or_end | DMA_BUF_SYNC_READ;
+
+ while(true) {
@@ -125,10 +74,17 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
+ } else if (ret == -1) {
+ RTC_LOG(LS_ERROR) << "Failed to synchronize DMA buffer: " << g_strerror(errno);
break;
-- case PW_REMOTE_STATE_UNCONNECTED:
-- RTC_LOG(LS_INFO) << "PipeWire remote state: unconnected.";
+- case PW_REMOTE_STATE_CONNECTED:
+- RTC_LOG(LS_INFO) << "PipeWire remote state: connected.";
+- that->CreateReceivingStream();
+ } else {
break;
+- case PW_REMOTE_STATE_CONNECTING:
+- RTC_LOG(LS_INFO) << "PipeWire remote state: connecting.";
+- break;
+- case PW_REMOTE_STATE_UNCONNECTED:
+- RTC_LOG(LS_INFO) << "PipeWire remote state: unconnected.";
+- break;
+ }
}
}
@@ -146,20 +102,24 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
void BaseCapturerPipeWire::OnStreamStateChanged(void* data,
pw_stream_state old_state,
pw_stream_state state,
-@@ -73,76 +81,54 @@ void BaseCapturerPipeWire::OnStreamState
+ const char* error_message) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(data);
+ RTC_DCHECK(that);
+
+ switch (state) {
case PW_STREAM_STATE_ERROR:
RTC_LOG(LS_ERROR) << "PipeWire stream state error: " << error_message;
break;
- case PW_STREAM_STATE_CONFIGURE:
- pw_stream_set_active(that->pw_stream_, true);
- break;
-- case PW_STREAM_STATE_UNCONNECTED:
-- case PW_STREAM_STATE_CONNECTING:
++ case PW_STREAM_STATE_PAUSED:
++ case PW_STREAM_STATE_STREAMING:
+ case PW_STREAM_STATE_UNCONNECTED:
+ case PW_STREAM_STATE_CONNECTING:
- case PW_STREAM_STATE_READY:
- case PW_STREAM_STATE_PAUSED:
- case PW_STREAM_STATE_STREAMING:
-+ case PW_STREAM_STATE_UNCONNECTED:
-+ case PW_STREAM_STATE_CONNECTING:
+- case PW_STREAM_STATE_PAUSED:
+- case PW_STREAM_STATE_STREAMING:
break;
}
}
@@ -249,24 +209,25 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
}
// static
-@@ -150,15 +136,25 @@ void BaseCapturerPipeWire::OnStreamProce
+ void BaseCapturerPipeWire::OnStreamProcess(void* data) {
BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(data);
RTC_DCHECK(that);
- pw_buffer* buf = nullptr;
+ struct pw_buffer *next_buffer;
+ struct pw_buffer *buffer = nullptr;
-+
+
+- if (!(buf = pw_stream_dequeue_buffer(that->pw_stream_))) {
+ next_buffer = pw_stream_dequeue_buffer(that->pw_stream_);
+ while (next_buffer) {
+ buffer = next_buffer;
+ next_buffer = pw_stream_dequeue_buffer(that->pw_stream_);
+
-+ if (next_buffer)
++ if (next_buffer) {
+ pw_stream_queue_buffer (that->pw_stream_, buffer);
++ }
+ }
-
-- if (!(buf = pw_stream_dequeue_buffer(that->pw_stream_))) {
++
+ if (!buffer) {
return;
}
@@ -279,7 +240,10 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
}
BaseCapturerPipeWire::BaseCapturerPipeWire(CaptureSourceType source_type)
-@@ -169,38 +165,22 @@ BaseCapturerPipeWire::~BaseCapturerPipeW
+ : capture_source_type_(source_type) {}
+
+ BaseCapturerPipeWire::~BaseCapturerPipeWire() {
+ if (pw_main_loop_) {
pw_thread_loop_stop(pw_main_loop_);
}
@@ -322,16 +286,22 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
if (start_request_signal_id_) {
g_dbus_connection_signal_unsubscribe(connection_, start_request_signal_id_);
}
-@@ -250,27 +230,35 @@ void BaseCapturerPipeWire::InitPortal()
+ if (sources_request_signal_id_) {
+ g_dbus_connection_signal_unsubscribe(connection_,
+ sources_request_signal_id_);
+ }
+ if (session_request_signal_id_) {
+@@ -245,143 +233,210 @@ void BaseCapturerPipeWire::InitPortal()
+ kDesktopBusName, kDesktopObjectPath, kScreenCastInterfaceName,
+ /*cancellable=*/nullptr,
+ reinterpret_cast<GAsyncReadyCallback>(OnProxyRequested), this);
+ }
+
void BaseCapturerPipeWire::InitPipeWire() {
pw_init(/*argc=*/nullptr, /*argc=*/nullptr);
- pw_loop_ = pw_loop_new(/*properties=*/nullptr);
- pw_main_loop_ = pw_thread_loop_new(pw_loop_, "pipewire-main-loop");
--
-- pw_core_ = pw_core_new(pw_loop_, /*properties=*/nullptr);
-- pw_core_type_ = pw_core_get_type(pw_core_);
-- pw_remote_ = pw_remote_new(pw_core_, nullptr, /*user_data_size=*/0);
+ pw_main_loop_ = pw_thread_loop_new("pipewire-main-loop", nullptr);
+ pw_context_ = pw_context_new(pw_thread_loop_get_loop(pw_main_loop_), nullptr, 0);
+ if (!pw_context_) {
@@ -339,6 +309,10 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
+ return;
+ }
+- pw_core_ = pw_core_new(pw_loop_, /*properties=*/nullptr);
+- pw_core_type_ = pw_core_get_type(pw_core_);
+- pw_remote_ = pw_remote_new(pw_core_, nullptr, /*user_data_size=*/0);
+-
- InitPipeWireTypes();
+ pw_core_ = pw_context_connect(pw_context_, nullptr, 0);
+ if (!pw_core_) {
@@ -371,30 +345,30 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
if (pw_thread_loop_start(pw_main_loop_) < 0) {
RTC_LOG(LS_ERROR) << "Failed to start main PipeWire loop";
-@@ -278,81 +266,132 @@ void BaseCapturerPipeWire::InitPipeWire(
+ portal_init_failed_ = true;
}
}
-void BaseCapturerPipeWire::InitPipeWireTypes() {
- spa_type_map* map = pw_core_type_->map;
- pw_type_ = new PipeWireType();
-+pw_stream* BaseCapturerPipeWire::CreateReceivingStream() {
-+ spa_rectangle pwMinScreenBounds = spa_rectangle{1, 1};
-+ spa_rectangle pwMaxScreenBounds = spa_rectangle{INT32_MAX, INT32_MAX};
-
+-
- spa_type_media_type_map(map, &pw_type_->media_type);
- spa_type_media_subtype_map(map, &pw_type_->media_subtype);
- spa_type_format_video_map(map, &pw_type_->format_video);
- spa_type_video_format_map(map, &pw_type_->video_format);
-}
-+ auto stream = pw_stream_new(pw_core_, "webrtc-pipewire-stream", nullptr);
++pw_stream* BaseCapturerPipeWire::CreateReceivingStream() {
++ spa_rectangle pwMinScreenBounds = spa_rectangle{1, 1};
++ spa_rectangle pwMaxScreenBounds = spa_rectangle{UINT32_MAX, UINT32_MAX};
-void BaseCapturerPipeWire::CreateReceivingStream() {
- spa_rectangle pwMinScreenBounds = spa_rectangle{1, 1};
- spa_rectangle pwScreenBounds =
- spa_rectangle{static_cast<uint32_t>(desktop_size_.width()),
- static_cast<uint32_t>(desktop_size_.height())};
--
++ auto stream = pw_stream_new(pw_core_, "webrtc-pipewire-stream", nullptr);
+
- spa_fraction pwFrameRateMin = spa_fraction{0, 1};
- spa_fraction pwFrameRateMax = spa_fraction{60, 1};
-
@@ -435,7 +409,15 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
- &pwFrameRateMin, &pwFrameRateMax));
+ const spa_pod* params[2];
+ spa_pod_builder builder = SPA_POD_BUILDER_INIT(buffer, sizeof (buffer));
-+
+
+- pw_stream_add_listener(pw_stream_, &spa_stream_listener_, &pw_stream_events_,
+- this);
+- pw_stream_flags flags = static_cast<pw_stream_flags>(
+- PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE |
+- PW_STREAM_FLAG_MAP_BUFFERS);
+- if (pw_stream_connect(pw_stream_, PW_DIRECTION_INPUT, /*port_path=*/nullptr,
+- flags, params,
+- /*n_params=*/1) != 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),
@@ -447,143 +429,145 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
+ &pwMaxScreenBounds),
+ 0));
+ pw_stream_add_listener(stream, &spa_stream_listener_, &pw_stream_events_, this);
-
-- pw_stream_add_listener(pw_stream_, &spa_stream_listener_, &pw_stream_events_,
-- this);
- pw_stream_flags flags = static_cast<pw_stream_flags>(
-- PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE |
-- PW_STREAM_FLAG_MAP_BUFFERS);
-- if (pw_stream_connect(pw_stream_, PW_DIRECTION_INPUT, /*port_path=*/nullptr,
-- flags, params,
-- /*n_params=*/1) != 0) {
-+ PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE);
+
-+ if (pw_stream_connect(stream, PW_DIRECTION_INPUT, pw_stream_node_id_, PW_STREAM_FLAG_AUTOCONNECT, params, 1) != 0) {
++ if (pw_stream_connect(stream, PW_DIRECTION_INPUT, pw_stream_node_id_,
++ PW_STREAM_FLAG_AUTOCONNECT, params, 1) != 0) {
RTC_LOG(LS_ERROR) << "Could not connect receiving stream.";
portal_init_failed_ = true;
- return;
- }
++ }
+
+ return stream;
++}
++
++static void SpaBufferUnmap(unsigned char *map, int map_size, bool IsDMABuf, int fd) {
++ if (map) {
++ if (IsDMABuf) {
++ SyncDmaBuf(fd, DMA_BUF_SYNC_END);
++ }
++ munmap(map, map_size);
+ }
}
void BaseCapturerPipeWire::HandleBuffer(pw_buffer* buffer) {
-+ struct spa_meta_region* video_crop;
spa_buffer* spaBuffer = buffer->buffer;
- void* src = nullptr;
+ uint8_t *map = nullptr;
+ uint8_t* src = nullptr;
-+ uint8_t* dst = nullptr;
-+
-+ if (spaBuffer->datas[0].chunk->size == 0) {
-+ map = nullptr;
-+ src = nullptr;
-+ } else if (spaBuffer->datas[0].type == SPA_DATA_MemFd) {
-+ map = static_cast<uint8_t*>(mmap(
-+ nullptr, spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
-+ PROT_READ, MAP_PRIVATE, spaBuffer->datas[0].fd, 0));
-+
-+ if (map == MAP_FAILED) {
-+ RTC_LOG(LS_ERROR) << "Failed to mmap the memory: " << std::strerror(errno);
-+ return;
-+ }
-+
-+ src = SPA_MEMBER(map, spaBuffer->datas[0].mapoffset, uint8_t);
-+ } else if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf) {
-+ int fd;
-+ fd = spaBuffer->datas[0].fd;
-+
-+ map = static_cast<uint8_t*>(mmap(
-+ nullptr, spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
-+ PROT_READ, MAP_PRIVATE, fd, 0));
-+
-+ if (map == MAP_FAILED) {
-+ RTC_LOG(LS_ERROR) << "Failed to mmap the memory: " << std::strerror(errno);
-+ return;
-+ }
- if (!(src = spaBuffer->datas[0].data)) {
-+ SyncDmaBuf(fd, DMA_BUF_SYNC_START);
-+
-+ src = SPA_MEMBER(map, spaBuffer->datas[0].mapoffset, uint8_t);
-+ } else if (spaBuffer->datas[0].type == SPA_DATA_MemPtr) {
-+ map = nullptr;
-+ src = static_cast<uint8_t*>(spaBuffer->datas[0].data);
-+ } else {
++ if (spaBuffer->datas[0].chunk->size == 0) {
++ RTC_LOG(LS_ERROR) << "Failed to get video stream: Zero size.";
return;
}
- uint32_t maxSize = spaBuffer->datas[0].maxsize;
- int32_t srcStride = spaBuffer->datas[0].chunk->stride;
-+ if (!src) {
-+ return;
-+ }
-+
-+ DesktopSize prev_crop_size = DesktopSize(0, 0);
-+ if (video_crop_size_initialized_) {
-+ prev_crop_size = video_crop_size_;
-+ }
-+
-+ if ((video_crop = static_cast<struct spa_meta_region*>(
-+ spa_buffer_find_meta_data(spaBuffer, SPA_META_VideoCrop, sizeof(*video_crop))))) {
-+ RTC_DCHECK(video_crop->region.size.width <= desktop_size_.width() &&
-+ video_crop->region.size.height <= desktop_size_.height());
-+ if ((video_crop->region.size.width != desktop_size_.width() ||
-+ video_crop->region.size.height != desktop_size_.height()) && video_crop->region.size.width && video_crop->region.size.height) {
-+ video_crop_size_ = DesktopSize(video_crop->region.size.width, video_crop->region.size.height);
-+ video_crop_size_initialized_ = true;
-+ } else {
-+ video_crop_size_initialized_ = false;
-+ }
-+ } else {
-+ video_crop_size_initialized_ = false;
-+ }
-+
-+ size_t frame_size;
-+ if (video_crop_size_initialized_) {
-+ frame_size =
-+ video_crop_size_.width() * video_crop_size_.height() * kBytesPerPixel;
-+ } else {
-+ frame_size =
-+ desktop_size_.width() * desktop_size_.height() * kBytesPerPixel;
+- if (srcStride != (desktop_size_.width() * kBytesPerPixel)) {
+- RTC_LOG(LS_ERROR) << "Got buffer with stride different from screen stride: "
+- << srcStride
+- << " != " << (desktop_size_.width() * kBytesPerPixel);
+- portal_init_failed_ = true;
++ switch (spaBuffer->datas[0].type) {
++ case SPA_DATA_MemFd:
++ case SPA_DATA_DmaBuf:
++ map = static_cast<uint8_t*>(mmap(
++ nullptr, spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
++ PROT_READ, MAP_PRIVATE, spaBuffer->datas[0].fd, 0));
++ if (map == MAP_FAILED) {
++ RTC_LOG(LS_ERROR) << "Failed to mmap memory: " << std::strerror(errno);
++ return;
++ }
++ if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf) {
++ SyncDmaBuf(spaBuffer->datas[0].fd, DMA_BUF_SYNC_START);
++ }
++ src = SPA_MEMBER(map, spaBuffer->datas[0].mapoffset, uint8_t);
++ break;
++ case SPA_DATA_MemPtr:
++ map = nullptr;
++ src = static_cast<uint8_t*>(spaBuffer->datas[0].data);
++ break;
++ default:
++ return;
+ }
+
-+ if (!current_frame_ ||
-+ (video_crop_size_initialized_ && !video_crop_size_.equals(prev_crop_size))) {
-+ current_frame_ = std::make_unique<uint8_t[]>(frame_size);
++ if (!src) {
++ RTC_LOG(LS_ERROR) << "Failed to get video stream: Wrong data after mmap()";
++ SpaBufferUnmap(map,
++ spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
++ spaBuffer->datas[0].type == SPA_DATA_DmaBuf, spaBuffer->datas[0].fd);
++ return;
+ }
-+ RTC_DCHECK(current_frame_ != nullptr);
-+
-+ const int32_t dstStride = video_crop_size_initialized_
-+ ? video_crop_size_.width() * kBytesPerPixel
-+ : desktop_size_.width() * kBytesPerPixel;
-+ const int32_t srcStride = spaBuffer->datas[0].chunk->stride;
+
- if (srcStride != (desktop_size_.width() * kBytesPerPixel)) {
- RTC_LOG(LS_ERROR) << "Got buffer with stride different from screen stride: "
- << srcStride
-@@ -361,21 +400,40 @@ void BaseCapturerPipeWire::HandleBuffer(
++ struct spa_meta_region* video_metadata =
++ static_cast<struct spa_meta_region*>(
++ spa_buffer_find_meta_data(spaBuffer, SPA_META_VideoCrop, sizeof(*video_metadata)));
++
++ // Video size from metada is bigger than an actual video stream size.
++ // The metadata are wrong or we should up-scale te video...in both cases
++ // just quit now.
++ if (video_metadata &&
++ (video_metadata->region.size.width > (uint32_t)desktop_size_.width() ||
++ video_metadata->region.size.height > (uint32_t)desktop_size_.height())) {
++ RTC_LOG(LS_ERROR) << "Stream metadata sizes are wrong!";
++ SpaBufferUnmap(map,
++ spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
++ spaBuffer->datas[0].type == SPA_DATA_DmaBuf, spaBuffer->datas[0].fd);
return;
}
- if (!current_frame_) {
- current_frame_ = static_cast<uint8_t*>(malloc(maxSize));
-+ dst = current_frame_.get();
++ // Use video metada when video size from metadata is set and smaller than
++ // video stream size, so we need to adjust it.
++ video_metadata_use_ = (video_metadata &&
++ video_metadata->region.size.width != 0 &&
++ video_metadata->region.size.height != 0 &&
++ (video_metadata->region.size.width < (uint32_t)desktop_size_.width() ||
++ video_metadata->region.size.height < (uint32_t)desktop_size_.height()));
++
++ DesktopSize video_size_prev = video_size_;
++ if (video_metadata_use_) {
++ video_size_ = DesktopSize(video_metadata->region.size.width,
++ video_metadata->region.size.height);
++ } else {
++ video_size_ = desktop_size_;
+ }
+- RTC_DCHECK(current_frame_ != nullptr);
+
-+ // Adjust source content based on crop video position
-+ if (video_crop_size_initialized_ &&
-+ (video_crop->region.position.y + video_crop_size_.height() <= desktop_size_.height())) {
-+ for (int i = 0; i < video_crop->region.position.y; ++i) {
-+ src += srcStride;
-+ }
++ if (!current_frame_ ||
++ (video_metadata_use_ && !video_size_.equals(video_size_prev))) {
++ current_frame_ =
++ std::make_unique<uint8_t[]>
++ (video_size_.width() * video_size_.height() * kBytesPerPixel);
+ }
++
++ const int32_t dstStride = video_size_.width() * kBytesPerPixel;
++ const int32_t srcStride = spaBuffer->datas[0].chunk->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 (spa_video_format_->format == pw_type_->video_format.RGBx) {
+- uint8_t* tempFrame = static_cast<uint8_t*>(malloc(maxSize));
+- std::memcpy(tempFrame, src, maxSize);
+- ConvertRGBxToBGRx(tempFrame, maxSize);
+- std::memcpy(current_frame_, tempFrame, maxSize);
+- free(tempFrame);
+- } else {
+- std::memcpy(current_frame_, src, maxSize);
++ // Adjust source content based on metadata video position
++ if (video_metadata_use_ &&
++ (video_metadata->region.position.y + video_size_.height() <= desktop_size_.height())) {
++ src += srcStride * video_metadata->region.position.y;
+ }
+ const int xOffset =
-+ video_crop_size_initialized_ && (video_crop->region.position.x + video_crop_size_.width() <=
-+ desktop_size_.width())
-+ ? video_crop->region.position.x * kBytesPerPixel
++ video_metadata_use_ &&
++ (video_metadata->region.position.x + video_size_.width() <= desktop_size_.width())
++ ? video_metadata->region.position.x * kBytesPerPixel
+ : 0;
-+ const int height = video_crop_size_initialized_ ? video_crop_size_.height() : desktop_size_.height();
-+ for (int i = 0; i < height; ++i) {
++
++ uint8_t* dst = current_frame_.get();
++ for (int i = 0; i < video_size_.height(); ++i) {
+ // Adjust source content based on crop video position if needed
+ src += xOffset;
+ std::memcpy(dst, src, dstStride);
@@ -595,28 +579,32 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
+ }
+ src += srcStride - xOffset;
+ dst += dstStride;
- }
-- RTC_DCHECK(current_frame_ != nullptr);
-
-- // 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 (spa_video_format_->format == pw_type_->video_format.RGBx) {
-- uint8_t* tempFrame = static_cast<uint8_t*>(malloc(maxSize));
-- std::memcpy(tempFrame, src, maxSize);
-- ConvertRGBxToBGRx(tempFrame, maxSize);
-- std::memcpy(current_frame_, tempFrame, maxSize);
-- free(tempFrame);
-- } else {
-- std::memcpy(current_frame_, src, maxSize);
-+ if (map) {
-+ if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf) {
-+ SyncDmaBuf(spaBuffer->datas[0].fd, DMA_BUF_SYNC_END);
-+ }
-+ munmap(map, spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset);
- }
++ }
++
++ SpaBufferUnmap(map,
++ spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
++ spaBuffer->datas[0].type == SPA_DATA_DmaBuf, spaBuffer->datas[0].fd);
}
-@@ -725,10 +783,7 @@ void BaseCapturerPipeWire::OnStartReques
+ void BaseCapturerPipeWire::ConvertRGBxToBGRx(uint8_t* frame, uint32_t size) {
+ // Change color format for KDE KWin which uses RGBx and not BGRx
+ for (uint32_t i = 0; i < size; i += 4) {
+ uint8_t tempR = frame[i];
+ uint8_t tempB = frame[i + 2];
+ frame[i] = tempB;
+@@ -713,27 +768,22 @@ void BaseCapturerPipeWire::OnStartReques
+ // Array of PipeWire streams. See
+ // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml
+ // documentation for <method name="Start">.
+ if (g_variant_lookup(response_data, "streams", "a(ua{sv})", &iter)) {
+ GVariant* variant;
+
+ while (g_variant_iter_next(iter, "@(ua{sv})", &variant)) {
+ guint32 stream_id;
+- gint32 width;
+- gint32 height;
+ GVariant* options;
+
g_variant_get(variant, "(u@a{sv})", &stream_id, &options);
RTC_DCHECK(options != nullptr);
@@ -628,16 +616,26 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
g_variant_unref(options);
g_variant_unref(variant);
}
-@@ -813,10 +868,15 @@ void BaseCapturerPipeWire::CaptureFrame(
+ }
+ g_variant_iter_free(iter);
+ g_variant_unref(response_data);
+
+ that->OpenPipeWireRemote();
+@@ -808,20 +858,25 @@ void BaseCapturerPipeWire::CaptureFrame(
+ return;
+ }
+
+ if (!current_frame_) {
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
return;
}
- std::unique_ptr<DesktopFrame> result(new BasicDesktopFrame(desktop_size_));
+ DesktopSize frame_size = desktop_size_;
-+ if (video_crop_size_initialized_) {
-+ frame_size = video_crop_size_;
++ if (video_metadata_use_) {
++ frame_size = video_size_;
+ }
-+
++
+ std::unique_ptr<DesktopFrame> result(new BasicDesktopFrame(frame_size));
result->CopyPixelsFrom(
- current_frame_, (desktop_size_.width() * kBytesPerPixel),
@@ -647,7 +645,17 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
if (!result) {
callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
return;
-@@ -837,4 +897,22 @@ bool BaseCapturerPipeWire::SelectSource(
+ }
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(result));
+ }
+
+ bool BaseCapturerPipeWire::GetSourceList(SourceList* sources) {
+@@ -832,9 +887,27 @@ bool BaseCapturerPipeWire::GetSourceList
+ return true;
+ }
+
+ bool BaseCapturerPipeWire::SelectSource(SourceId id) {
+ // Screen selection is handled by the xdg-desktop-portal.
return true;
}
@@ -670,10 +678,15 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
+}
+
} // namespace webrtc
-diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.h.firefox-pipewire-0-3 firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.h
---- firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.h.firefox-pipewire-0-3 2020-09-15 03:48:32.000000000 +0200
-+++ firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.h 2020-09-15 14:40:00.722481420 +0200
-@@ -22,17 +22,13 @@
+diff --git a/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.h b/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.h
+--- a/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.h
++++ b/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.h
+@@ -17,99 +17,102 @@
+ #include <spa/param/video/format-utils.h>
+
+ #include "modules/desktop_capture/desktop_capture_options.h"
+ #include "modules/desktop_capture/desktop_capturer.h"
+ #include "rtc_base/constructormagic.h"
namespace webrtc {
@@ -696,7 +709,10 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
explicit BaseCapturerPipeWire(CaptureSourceType source_type);
~BaseCapturerPipeWire() override;
-@@ -43,28 +39,32 @@ class BaseCapturerPipeWire : public Desk
+
+ // DesktopCapturer interface.
+ void Start(Callback* delegate) override;
+ void CaptureFrame() override;
bool GetSourceList(SourceList* sources) override;
bool SelectSource(SourceId id) override;
@@ -737,12 +753,18 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
// <-- end of PipeWire types
-@@ -78,33 +78,37 @@ class BaseCapturerPipeWire : public Desk
+ GDBusConnection* connection_ = nullptr;
+ GDBusProxy* proxy_ = nullptr;
+ gchar* portal_handle_ = nullptr;
+ gchar* session_handle_ = nullptr;
+ gchar* sources_handle_ = nullptr;
+ gchar* start_handle_ = nullptr;
+ guint session_request_signal_id_ = 0;
guint sources_request_signal_id_ = 0;
guint start_request_signal_id_ = 0;
-+ bool video_crop_size_initialized_ = false;
-+ DesktopSize video_crop_size_;;
++ bool video_metadata_use_ = false;
++ DesktopSize video_size_;
DesktopSize desktop_size_ = {};
DesktopCaptureOptions options_ = {};
@@ -766,7 +788,6 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
- pw_remote_state old_state,
- pw_remote_state state,
- const char* error);
-+ static void SyncDmaBuf(int fd, uint64_t start_or_end);
+ static void OnCoreError(void *data,
+ uint32_t id,
+ int seq,
@@ -784,10 +805,20 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/ba
static void OnStreamProcess(void* data);
static void OnNewBuffer(void* data, uint32_t id);
-diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc.firefox-pipewire-0-3 firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc
---- firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc.firefox-pipewire-0-3 2020-09-15 03:48:32.000000000 +0200
-+++ firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc 2020-09-15 14:40:00.722481420 +0200
-@@ -15,7 +15,7 @@
+ guint SetupRequestResponseSignal(const gchar* object_path,
+ GDBusSignalCallback callback);
+
+ static void OnProxyRequested(GObject* object,
+ GAsyncResult* result,
+diff --git a/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc b/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc
+--- a/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc
++++ b/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc
+@@ -10,17 +10,17 @@
+
+ #include "modules/desktop_capture/linux/screen_capturer_pipewire.h"
+
+ #include <memory>
+
namespace webrtc {
ScreenCapturerPipeWire::ScreenCapturerPipeWire()
@@ -796,10 +827,20 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/sc
ScreenCapturerPipeWire::~ScreenCapturerPipeWire() {}
// static
-diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc.firefox-pipewire-0-3 firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc
---- firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc.firefox-pipewire-0-3 2020-09-15 03:48:32.000000000 +0200
-+++ firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc 2020-09-15 14:40:00.722481420 +0200
-@@ -15,7 +15,7 @@
+ std::unique_ptr<DesktopCapturer>
+ ScreenCapturerPipeWire::CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options) {
+ return std::make_unique<ScreenCapturerPipeWire>();
+ }
+diff --git a/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc b/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc
+--- a/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc
++++ b/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc
+@@ -10,17 +10,17 @@
+
+ #include "modules/desktop_capture/linux/window_capturer_pipewire.h"
+
+ #include <memory>
+
namespace webrtc {
WindowCapturerPipeWire::WindowCapturerPipeWire()
@@ -808,10 +849,20 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/wi
WindowCapturerPipeWire::~WindowCapturerPipeWire() {}
// static
-diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc.firefox-pipewire-0-3 firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc
---- firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc.firefox-pipewire-0-3 2020-09-15 03:48:32.000000000 +0200
-+++ firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc 2020-09-15 14:40:00.722481420 +0200
-@@ -26,7 +26,7 @@ std::unique_ptr<DesktopCapturer> Desktop
+ std::unique_ptr<DesktopCapturer>
+ WindowCapturerPipeWire::CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options) {
+ return std::make_unique<WindowCapturerPipeWire>();
+ }
+diff --git a/third_party/libwebrtc/webrtc/modules/desktop_capture/screen_capturer_linux.cc b/third_party/libwebrtc/webrtc/modules/desktop_capture/screen_capturer_linux.cc
+--- a/third_party/libwebrtc/webrtc/modules/desktop_capture/screen_capturer_linux.cc
++++ b/third_party/libwebrtc/webrtc/modules/desktop_capture/screen_capturer_linux.cc
+@@ -21,17 +21,17 @@
+
+ namespace webrtc {
+
+ // static
+ std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
const DesktopCaptureOptions& options) {
#if defined(WEBRTC_USE_PIPEWIRE)
if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) {
@@ -820,10 +871,20 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_c
}
#endif // defined(WEBRTC_USE_PIPEWIRE)
-diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc.firefox-pipewire-0-3 firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc
---- firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc.firefox-pipewire-0-3 2020-09-15 03:48:32.000000000 +0200
-+++ firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc 2020-09-15 14:40:00.722481420 +0200
-@@ -26,7 +26,7 @@ std::unique_ptr<DesktopCapturer> Desktop
+ #if defined(USE_X11)
+ return ScreenCapturerX11::CreateRawScreenCapturer(options);
+ #endif // defined(USE_X11)
+
+ return nullptr;
+diff --git a/third_party/libwebrtc/webrtc/modules/desktop_capture/window_capturer_linux.cc b/third_party/libwebrtc/webrtc/modules/desktop_capture/window_capturer_linux.cc
+--- a/third_party/libwebrtc/webrtc/modules/desktop_capture/window_capturer_linux.cc
++++ b/third_party/libwebrtc/webrtc/modules/desktop_capture/window_capturer_linux.cc
+@@ -21,17 +21,17 @@
+
+ namespace webrtc {
+
+ // static
+ std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
const DesktopCaptureOptions& options) {
#if defined(WEBRTC_USE_PIPEWIRE)
if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) {
@@ -832,3 +893,9 @@ diff -up firefox-81.0/media/webrtc/trunk/webrtc/modules/desktop_capture/window_c
}
#endif // defined(WEBRTC_USE_PIPEWIRE)
+ #if defined(USE_X11)
+ return WindowCapturerX11::CreateRawWindowCapturer(options);
+ #endif // defined(USE_X11)
+
+ return nullptr;
+
diff --git a/pw3.patch b/pw3.patch
new file mode 100644
index 0000000..82d451e
--- /dev/null
+++ b/pw3.patch
@@ -0,0 +1,183 @@
+
+# HG changeset patch
+# User stransky <stransky@redhat.com>
+# Date 1604562423 0
+# Node ID d1a244822d7811575f5bb3cd024f8f324275aec2
+# Parent 998e6d0b24e4a560e5664aaef87307e9c069ad87
+Bug 1672989 Build PipeWire as a part of default builds, r=dminor
+
+Differential Revision: https://phabricator.services.mozilla.com/D94590
+
+diff --git a/third_party/libwebrtc/webrtc/modules/desktop_capture/BUILD.gn b/third_party/libwebrtc/webrtc/modules/desktop_capture/BUILD.gn
+--- a/third_party/libwebrtc/webrtc/modules/desktop_capture/BUILD.gn
++++ b/third_party/libwebrtc/webrtc/modules/desktop_capture/BUILD.gn
+@@ -152,22 +152,17 @@ if (rtc_include_tests) {
+ "../../rtc_base:rtc_base_approved",
+ "../../test:test_support",
+ ]
+ }
+ }
+
+ if (is_linux) {
+ if (rtc_use_pipewire) {
+- pkg_config("pipewire") {
+- packages = [ "libpipewire-0.2" ]
+-
+- defines = [ "WEBRTC_USE_PIPEWIRE" ]
+- }
+-
++ defines = [ "WEBRTC_USE_PIPEWIRE" ]
+ pkg_config("gio") {
+ packages = [
+ "gio-2.0",
+ "gio-unix-2.0",
+ ]
+ }
+ }
+ }
+@@ -326,16 +321,17 @@ rtc_static_library("desktop_capture_gene
+ } else {
+ sources += [
+ "fake_desktop_capturer.cc",
+ "fake_desktop_capturer.h",
+ ]
+ }
+
+ if (use_x11 || rtc_use_pipewire) {
++ include_dirs = [ "/third_party/libwebrtc/third_party/pipewire" ]
+ sources += [
+ "mouse_cursor_monitor_linux.cc",
+ "screen_capturer_linux.cc",
+ "window_capturer_linux.cc",
+ ]
+ }
+
+ if (use_x11) {
+@@ -367,16 +363,17 @@ rtc_static_library("desktop_capture_gene
+ "linux/desktop_device_info_x11.h",
+ "linux/shared_x_util.cc",
+ "linux/shared_x_util.h",
+ ]
+ }
+ }
+
+ if (rtc_use_pipewire) {
++ include_dirs = [ "/third_party/libwebrtc/third_party/pipewire" ]
+ sources += [
+ "linux/base_capturer_pipewire.cc",
+ "linux/base_capturer_pipewire.h",
+ "linux/screen_capturer_pipewire.cc",
+ "linux/screen_capturer_pipewire.h",
+ "linux/window_capturer_pipewire.cc",
+ "linux/window_capturer_pipewire.h",
+ ]
+diff --git a/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build b/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build
+--- a/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build
++++ b/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build
+@@ -20,17 +20,18 @@ DEFINES["WEBRTC_RESTRICT_LOGGING"] = Tru
+ FINAL_LIBRARY = "webrtc"
+
+
+ LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "/ipc/chromium/src",
+ "/ipc/glue",
+ "/media/libyuv/libyuv/include/",
+- "/third_party/libwebrtc/webrtc/"
++ "/third_party/libwebrtc/webrtc/",
++ "/third_party/pipewire"
+ ]
+
+ UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/capture_result_desktop_capturer_wrapper.cc",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/cropped_desktop_frame.cc",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/cropping_window_capturer.cc",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_and_cursor_composer.cc",
+@@ -156,16 +157,17 @@ if CONFIG["OS_TARGET"] == "FreeBSD":
+
+ if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
++ DEFINES["WEBRTC_USE_PIPEWIRE"] = "1"
+
+ OS_LIBS += [
+ "rt",
+ "X11",
+ "X11-xcb",
+ "xcb",
+ "Xcomposite",
+ "Xcursor",
+@@ -188,16 +190,24 @@ if CONFIG["OS_TARGET"] == "Linux":
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/x_atom_cache.cc",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/x_error_trap.cc",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/x_server_pixel_buffer.cc",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/screen_capturer_linux.cc",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/window_capturer_linux.cc"
+ ]
+
++ CXXFLAGS += CONFIG['TK_CFLAGS']
++
++ UNIFIED_SOURCES += [
++ "/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc",
++ "/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/screen_capturer_pipewire.cc",
++ "/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/window_capturer_pipewire.cc"
++ ]
++
+ if CONFIG["OS_TARGET"] == "NetBSD":
+
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+
+ OS_LIBS += [
+diff --git a/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capture_options.h b/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capture_options.h
+--- a/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capture_options.h
++++ b/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capture_options.h
+@@ -136,15 +136,15 @@ class DesktopCaptureOptions {
+ #if defined(USE_X11)
+ bool use_update_notifications_ = false;
+ #else
+ bool use_update_notifications_ = true;
+ #endif
+ bool disable_effects_ = true;
+ bool detect_updated_region_ = false;
+ #if defined(WEBRTC_USE_PIPEWIRE)
+- bool allow_pipewire_ = false;
++ bool allow_pipewire_ = true;
+ #endif
+ };
+
+ } // namespace webrtc
+
+ #endif // MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_OPTIONS_H_
+diff --git a/third_party/libwebrtc/webrtc/moz.build b/third_party/libwebrtc/webrtc/moz.build
+--- a/third_party/libwebrtc/webrtc/moz.build
++++ b/third_party/libwebrtc/webrtc/moz.build
+@@ -181,16 +181,19 @@ if CONFIG["OS_TARGET"] == "Linux":
+
+ DIRS += [
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capture_generic_gn",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capture_gn",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/primitives_gn",
+ "/third_party/libwebrtc/webrtc/modules/video_capture/video_capture_internal_impl_gn",
+ "/third_party/libwebrtc/webrtc/system_wrappers/cpu_features_linux_gn"
+ ]
++ DIRS += [
++ "/third_party/pipewire/libpipewire"
++ ]
+
+ if CONFIG["OS_TARGET"] == "NetBSD":
+
+ DIRS += [
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capture_generic_gn",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capture_gn",
+ "/third_party/libwebrtc/webrtc/modules/desktop_capture/primitives_gn",
+ "/third_party/libwebrtc/webrtc/modules/video_capture/video_capture_internal_impl_gn"
+
diff --git a/pw4.patch b/pw4.patch
new file mode 100644
index 0000000..03bda37
--- /dev/null
+++ b/pw4.patch
@@ -0,0 +1,18928 @@
+
+# HG changeset patch
+# User stransky <stransky@redhat.com>
+# Date 1604009732 0
+# Node ID e5e706b532041baa3ef46420c9bf10629b0cb3ba
+# Parent 7dad14e6de1a80a5c6a194df8a7b16504fb3d617
+Bug 1672945 Ship PipeWire 0.3 headers and library wrapper to build PW support out of the box, r=dminor
+
+- Ship PipeWire 0.3 headers at third_party/pipewire/pipewire
+- Ship SPA 0.2 headers at third_party/pipewire/spa
+- Ship PipeWire library wrapper at third_party/pipewire/libpipewire
+
+Differential Revision: https://phabricator.services.mozilla.com/D94580
+
+diff --git a/third_party/pipewire/README b/third_party/pipewire/README
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/README
+@@ -0,0 +1,4 @@
++Libpipewire is a pipewire library wrapper needed to build and run Firefox with
++Pipewire support on Linux (https://pipewire.org/).
++
++Pipewire directory stores headers of pipewire-0.2 and spa-0.2 needed for build only.
+diff --git a/third_party/pipewire/libpipewire/moz.build b/third_party/pipewire/libpipewire/moz.build
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/libpipewire/moz.build
+@@ -0,0 +1,15 @@
++# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
++# vim: set filetype=python:
++# This Source Code Form is subject to the terms of the Mozilla Public
++# License, v. 2.0. If a copy of the MPL was not distributed with this
++# file, You can obtain one at http://mozilla.org/MPL/2.0/.
++
++SOURCES += [
++ 'mozpipewire.cpp',
++]
++
++CXXFLAGS += CONFIG['TK_CFLAGS']
++
++LOCAL_INCLUDES += ['/third_party/pipewire']
++
++FINAL_LIBRARY = 'xul'
+diff --git a/third_party/pipewire/libpipewire/mozpipewire.cpp b/third_party/pipewire/libpipewire/mozpipewire.cpp
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/libpipewire/mozpipewire.cpp
+@@ -0,0 +1,312 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
++/* vim:expandtab:shiftwidth=4:tabstop=4:
++ */
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++#include <glib.h>
++#include "mozilla/Types.h"
++#include "prlink.h"
++
++#include <pipewire/pipewire.h>
++
++#define GET_FUNC(func, lib) \
++ func##_fn = \
++ (decltype(func##_fn))PR_FindFunctionSymbol(lib, #func) \
++
++#define IS_FUNC_LOADED(func) \
++ (func != nullptr) \
++
++struct GUnixFDList;
++
++extern "C" gint
++g_unix_fd_list_get(struct GUnixFDList *list,
++ gint index_,
++ GError **error)
++{
++ static PRLibrary* gioLib = nullptr;
++ static bool gioInitialized = false;
++ static gint (*g_unix_fd_list_get_fn)(struct GUnixFDList *list,
++ gint index_, GError **error) = nullptr;
++
++ if (!gioInitialized) {
++ gioInitialized = true;
++ gioLib = PR_LoadLibrary("libgio-2.0.so.0");
++ if (!gioLib) {
++ return -1;
++ }
++ GET_FUNC(g_unix_fd_list_get, gioLib);
++ }
++
++ if (!g_unix_fd_list_get_fn) {
++ return -1;
++ }
++
++ return g_unix_fd_list_get_fn(list, index_, error);
++}
++
++static struct pw_core * (*pw_context_connect_fn)(struct pw_context *context,
++ struct pw_properties *properties,
++ size_t user_data_size);
++static void (*pw_context_destroy_fn)(struct pw_context *context);
++struct pw_context * (*pw_context_new_fn)(struct pw_loop *main_loop,
++ struct pw_properties *props,
++ size_t user_data_size);
++static int (*pw_core_disconnect_fn)(struct pw_core *core);
++static void (*pw_init_fn)(int *argc, char **argv[]);
++static void (*pw_stream_add_listener_fn)(struct pw_stream *stream,
++ struct spa_hook *listener,
++ const struct pw_stream_events *events,
++ void *data);
++static int (*pw_stream_connect_fn)(struct pw_stream *stream,
++ enum pw_direction direction,
++ uint32_t target_id,
++ enum pw_stream_flags flags,
++ const struct spa_pod **params,
++ uint32_t n_params);
++static struct pw_buffer* (*pw_stream_dequeue_buffer_fn)(struct pw_stream *stream);
++static void (*pw_stream_destroy_fn)(struct pw_stream *stream);
++static struct pw_stream* (*pw_stream_new_fn)(struct pw_core *core,
++ const char *name,
++ struct pw_properties *props);
++static int (*pw_stream_queue_buffer_fn)(struct pw_stream *stream,
++ struct pw_buffer *buffer);
++static int (*pw_stream_update_params_fn)(struct pw_stream *stream,
++ const struct spa_pod **params,
++ uint32_t n_params);
++static void (*pw_thread_loop_destroy_fn)(struct pw_thread_loop *loop);
++static struct pw_loop* (*pw_thread_loop_get_loop_fn)(struct pw_thread_loop *loop);
++static struct pw_thread_loop* (*pw_thread_loop_new_fn)(const char *name,
++ const struct spa_dict *props);
++static int (*pw_thread_loop_start_fn)(struct pw_thread_loop *loop);
++static void (*pw_thread_loop_stop_fn)(struct pw_thread_loop *loop);
++
++bool IsPwLibraryLoaded() {
++ static bool isLoaded =
++ (IS_FUNC_LOADED(pw_context_connect_fn) &&
++ IS_FUNC_LOADED(pw_context_destroy_fn) &&
++ IS_FUNC_LOADED(pw_context_new_fn) &&
++ IS_FUNC_LOADED(pw_core_disconnect_fn) &&
++ IS_FUNC_LOADED(pw_init_fn) &&
++ IS_FUNC_LOADED(pw_stream_add_listener_fn) &&
++ IS_FUNC_LOADED(pw_stream_connect_fn) &&
++ IS_FUNC_LOADED(pw_stream_dequeue_buffer_fn) &&
++ IS_FUNC_LOADED(pw_stream_destroy_fn) &&
++ IS_FUNC_LOADED(pw_stream_new_fn) &&
++ IS_FUNC_LOADED(pw_stream_queue_buffer_fn) &&
++ IS_FUNC_LOADED(pw_stream_update_params_fn) &&
++ IS_FUNC_LOADED(pw_thread_loop_destroy_fn) &&
++ IS_FUNC_LOADED(pw_thread_loop_get_loop_fn) &&
++ IS_FUNC_LOADED(pw_thread_loop_new_fn) &&
++ IS_FUNC_LOADED(pw_thread_loop_start_fn) &&
++ IS_FUNC_LOADED(pw_thread_loop_stop_fn));
++
++ return isLoaded;
++}
++
++bool LoadPWLibrary() {
++ static PRLibrary* pwLib = nullptr;
++ static bool pwInitialized = false;
++
++ //TODO Thread safe
++ if (!pwInitialized) {
++ pwInitialized = true;
++ pwLib = PR_LoadLibrary("libpipewire-0.3.so.0");
++ if (!pwLib) {
++ return false;
++ }
++
++ GET_FUNC(pw_context_connect, pwLib);
++ GET_FUNC(pw_context_destroy, pwLib);
++ GET_FUNC(pw_context_new, pwLib);
++ GET_FUNC(pw_core_disconnect, pwLib);
++ GET_FUNC(pw_init, pwLib);
++ GET_FUNC(pw_stream_add_listener, pwLib);
++ GET_FUNC(pw_stream_connect, pwLib);
++ GET_FUNC(pw_stream_dequeue_buffer, pwLib);
++ GET_FUNC(pw_stream_destroy, pwLib);
++ GET_FUNC(pw_stream_new, pwLib);
++ GET_FUNC(pw_stream_queue_buffer, pwLib);
++ GET_FUNC(pw_stream_update_params, pwLib);
++ GET_FUNC(pw_thread_loop_destroy, pwLib);
++ GET_FUNC(pw_thread_loop_get_loop, pwLib);
++ GET_FUNC(pw_thread_loop_new, pwLib);
++ GET_FUNC(pw_thread_loop_start, pwLib);
++ GET_FUNC(pw_thread_loop_stop, pwLib);
++ }
++
++ return IsPwLibraryLoaded();
++}
++
++struct pw_core *
++pw_context_connect(struct pw_context *context,
++ struct pw_properties *properties,
++ size_t user_data_size)
++{
++ if (!LoadPWLibrary()) {
++ return nullptr;
++ }
++ return pw_context_connect_fn(context, properties, user_data_size);
++}
++
++void
++pw_context_destroy(struct pw_context *context)
++{
++ if (!LoadPWLibrary()) {
++ return;
++ }
++ pw_context_destroy_fn(context);
++}
++
++struct pw_context *
++pw_context_new(struct pw_loop *main_loop,
++ struct pw_properties *props,
++ size_t user_data_size)
++{
++ if (!LoadPWLibrary()) {
++ return nullptr;
++ }
++ return pw_context_new_fn(main_loop, props, user_data_size);
++}
++
++int
++pw_core_disconnect(struct pw_core *core)
++{
++ if (!LoadPWLibrary()) {
++ return 0;
++ }
++ return pw_core_disconnect_fn(core);
++}
++
++void
++pw_init(int *argc, char **argv[])
++{
++ if (!LoadPWLibrary()) {
++ return;
++ }
++ return pw_init_fn(argc, argv);
++}
++
++void
++pw_stream_add_listener(struct pw_stream *stream,
++ struct spa_hook *listener,
++ const struct pw_stream_events *events,
++ void *data)
++{
++ if (!LoadPWLibrary()) {
++ return;
++ }
++ return pw_stream_add_listener_fn(stream, listener, events, data);
++}
++
++int
++pw_stream_connect(struct pw_stream *stream,
++ enum pw_direction direction,
++ uint32_t target_id,
++ enum pw_stream_flags flags,
++ const struct spa_pod **params,
++ uint32_t n_params)
++{
++ if (!LoadPWLibrary()) {
++ return 0;
++ }
++ return pw_stream_connect_fn(stream, direction, target_id, flags,
++ params, n_params);
++}
++
++struct pw_buffer *
++pw_stream_dequeue_buffer(struct pw_stream *stream)
++{
++ if (!LoadPWLibrary()) {
++ return nullptr;
++ }
++ return pw_stream_dequeue_buffer_fn(stream);
++}
++
++void
++pw_stream_destroy(struct pw_stream *stream)
++{
++ if (!LoadPWLibrary()) {
++ return;
++ }
++ return pw_stream_destroy_fn(stream);
++}
++
++struct pw_stream *
++pw_stream_new(struct pw_core *core,
++ const char *name,
++ struct pw_properties *props)
++{
++ if (!LoadPWLibrary()) {
++ return nullptr;
++ }
++ return pw_stream_new_fn(core, name, props);
++}
++
++int
++pw_stream_queue_buffer(struct pw_stream *stream,
++ struct pw_buffer *buffer)
++{
++ if (!LoadPWLibrary()) {
++ return 0;
++ }
++ return pw_stream_queue_buffer_fn(stream, buffer);
++}
++
++int
++pw_stream_update_params(struct pw_stream *stream,
++ const struct spa_pod **params,
++ uint32_t n_params)
++{
++ if (!LoadPWLibrary()) {
++ return 0;
++ }
++ return pw_stream_update_params_fn(stream, params, n_params);
++}
++
++void
++pw_thread_loop_destroy(struct pw_thread_loop *loop)
++{
++ if (!LoadPWLibrary()) {
++ return;
++ }
++ return pw_thread_loop_destroy_fn(loop);
++}
++
++struct pw_loop *
++pw_thread_loop_get_loop(struct pw_thread_loop *loop)
++{
++ if (!LoadPWLibrary()) {
++ return nullptr;
++ }
++ return pw_thread_loop_get_loop_fn(loop);
++}
++
++struct pw_thread_loop *
++pw_thread_loop_new(const char *name,
++ const struct spa_dict *props)
++{
++ if (!LoadPWLibrary()) {
++ return nullptr;
++ }
++ return pw_thread_loop_new_fn(name, props);
++}
++
++int
++pw_thread_loop_start(struct pw_thread_loop *loop)
++{
++ if (!LoadPWLibrary()) {
++ return 0;
++ }
++ return pw_thread_loop_start_fn(loop);
++}
++
++void
++pw_thread_loop_stop(struct pw_thread_loop *loop)
++{
++ if (!LoadPWLibrary()) {
++ return;
++ }
++ return pw_thread_loop_stop_fn(loop);
++}
+diff --git a/third_party/pipewire/pipewire/array.h b/third_party/pipewire/pipewire/array.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/array.h
+@@ -0,0 +1,165 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_ARRAY_H
++#define PIPEWIRE_ARRAY_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <errno.h>
++
++#include <spa/utils/defs.h>
++
++/** \class pw_array
++ *
++ * \brief An array object
++ *
++ * The array is a dynamically resizable data structure that can
++ * hold items of the same size.
++ */
++struct pw_array {
++ void *data; /**< pointer to array data */
++ size_t size; /**< length of array in bytes */
++ size_t alloc; /**< number of allocated memory in \a data */
++ size_t extend; /**< number of bytes to extend with */
++};
++
++#define PW_ARRAY_INIT(extend) (struct pw_array) { NULL, 0, 0, extend }
++
++#define pw_array_get_len_s(a,s) ((a)->size / (s))
++#define pw_array_get_unchecked_s(a,idx,s,t) SPA_MEMBER((a)->data,(idx)*(s),t)
++#define pw_array_check_index_s(a,idx,s) ((idx) < pw_array_get_len_s(a,s))
++
++/** Get the number of items of type \a t in array \memberof pw_array */
++#define pw_array_get_len(a,t) pw_array_get_len_s(a,sizeof(t))
++/** Get the item with index \a idx and type \a t from array \memberof pw_array */
++#define pw_array_get_unchecked(a,idx,t) pw_array_get_unchecked_s(a,idx,sizeof(t),t)
++/** Check if an item with index \a idx and type \a t exist in array \memberof pw_array */
++#define pw_array_check_index(a,idx,t) pw_array_check_index_s(a,idx,sizeof(t))
++
++#define pw_array_first(a) ((a)->data)
++#define pw_array_end(a) SPA_MEMBER((a)->data, (a)->size, void)
++#define pw_array_check(a,p) (SPA_MEMBER(p,sizeof(*p),void) <= pw_array_end(a))
++
++#define pw_array_for_each(pos, array) \
++ for (pos = (__typeof__(pos)) pw_array_first(array); \
++ pw_array_check(array, pos); \
++ (pos)++)
++
++#define pw_array_consume(pos, array) \
++ for (pos = (__typeof__(pos)) pw_array_first(array); \
++ pw_array_check(array, pos); \
++ pos = (__typeof__(pos)) pw_array_first(array))
++
++#define pw_array_remove(a,p) \
++({ \
++ (a)->size -= sizeof(*(p)); \
++ memmove(p, SPA_MEMBER((p), sizeof(*(p)), void), \
++ SPA_PTRDIFF(pw_array_end(a),(p))); \
++})
++
++/** Initialize the array with given extend \memberof pw_array */
++static inline void pw_array_init(struct pw_array *arr, size_t extend)
++{
++ arr->data = NULL;
++ arr->size = arr->alloc = 0;
++ arr->extend = extend;
++}
++
++/** Clear the array */
++static inline void pw_array_clear(struct pw_array *arr)
++{
++ free(arr->data);
++}
++
++/** Reset the array */
++static inline void pw_array_reset(struct pw_array *arr)
++{
++ arr->size = 0;
++}
++
++/** Make sure \a size bytes can be added to the array \memberof pw_array */
++static inline int pw_array_ensure_size(struct pw_array *arr, size_t size)
++{
++ size_t alloc, need;
++
++ alloc = arr->alloc;
++ need = arr->size + size;
++
++ if (SPA_UNLIKELY(alloc < need)) {
++ void *data;
++ alloc = SPA_MAX(alloc, arr->extend);
++ while (alloc < need)
++ alloc *= 2;
++ if (SPA_UNLIKELY((data = realloc(arr->data, alloc)) == NULL))
++ return -errno;
++ arr->data = data;
++ arr->alloc = alloc;
++ }
++ return 0;
++}
++
++/** Add \a ref size bytes to \a arr. A pointer to memory that can
++ * hold at least \a size bytes is returned \memberof pw_array */
++static inline void *pw_array_add(struct pw_array *arr, size_t size)
++{
++ void *p;
++
++ if (pw_array_ensure_size(arr, size) < 0)
++ return NULL;
++
++ p = SPA_MEMBER(arr->data, arr->size, void);
++ arr->size += size;
++
++ return p;
++}
++
++/** Add \a ref size bytes to \a arr. When there is not enough memory to
++ * hold \a size bytes, NULL is returned \memberof pw_array */
++static inline void *pw_array_add_fixed(struct pw_array *arr, size_t size)
++{
++ void *p;
++
++ if (SPA_UNLIKELY(arr->alloc < arr->size + size)) {
++ errno = ENOSPC;
++ return NULL;
++ }
++
++ p = SPA_MEMBER(arr->data, arr->size, void);
++ arr->size += size;
++
++ return p;
++}
++
++/** Add a pointer to array \memberof pw_array */
++#define pw_array_add_ptr(a,p) \
++ *((void**) pw_array_add(a, sizeof(void*))) = (p)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_ARRAY_H */
+diff --git a/third_party/pipewire/pipewire/buffers.h b/third_party/pipewire/pipewire/buffers.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/buffers.h
+@@ -0,0 +1,60 @@
++/* PipeWire
++ *
++ * Copyright © 2019 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_BUFFERS_H
++#define PIPEWIRE_BUFFERS_H
++
++#include <spa/node/node.h>
++
++#include <pipewire/context.h>
++#include <pipewire/mem.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define PW_BUFFERS_FLAG_NONE 0
++#define PW_BUFFERS_FLAG_NO_MEM (1<<0) /**< don't allocate buffer memory */
++#define PW_BUFFERS_FLAG_SHARED (1<<1) /**< buffers can be shared */
++#define PW_BUFFERS_FLAG_DYNAMIC (1<<2) /**< buffers have dynamic data */
++
++struct pw_buffers {
++ struct pw_memblock *mem; /**< allocated buffer memory */
++ struct spa_buffer **buffers; /**< port buffers */
++ uint32_t n_buffers; /**< number of port buffers */
++ uint32_t flags; /**< flags */
++};
++
++int pw_buffers_negotiate(struct pw_context *context, uint32_t flags,
++ struct spa_node *outnode, uint32_t out_port_id,
++ struct spa_node *innode, uint32_t in_port_id,
++ struct pw_buffers *result);
++
++void pw_buffers_clear(struct pw_buffers *buffers);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_BUFFERS_H */
+diff --git a/third_party/pipewire/pipewire/client.h b/third_party/pipewire/pipewire/client.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/client.h
+@@ -0,0 +1,171 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_CLIENT_H
++#define PIPEWIRE_CLIENT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/param/param.h>
++
++#include <pipewire/proxy.h>
++#include <pipewire/permission.h>
++
++#define PW_TYPE_INTERFACE_Client PW_TYPE_INFO_INTERFACE_BASE "Client"
++
++#define PW_VERSION_CLIENT 3
++struct pw_client;
++
++/* default ID of the current client after connect */
++#define PW_ID_CLIENT 1
++
++/** The client information. Extra information can be added in later versions \memberof pw_introspect */
++struct pw_client_info {
++ uint32_t id; /**< id of the global */
++#define PW_CLIENT_CHANGE_MASK_PROPS (1 << 0)
++#define PW_CLIENT_CHANGE_MASK_ALL ((1 << 1)-1)
++ uint64_t change_mask; /**< bitfield of changed fields since last call */
++ struct spa_dict *props; /**< extra properties */
++};
++
++/** Update and existing \ref pw_client_info with \a update \memberof pw_introspect */
++struct pw_client_info *
++pw_client_info_update(struct pw_client_info *info,
++ const struct pw_client_info *update);
++
++/** Free a \ref pw_client_info \memberof pw_introspect */
++void pw_client_info_free(struct pw_client_info *info);
++
++
++#define PW_CLIENT_EVENT_INFO 0
++#define PW_CLIENT_EVENT_PERMISSIONS 1
++#define PW_CLIENT_EVENT_NUM 2
++
++/** Client events */
++struct pw_client_events {
++#define PW_VERSION_CLIENT_EVENTS 0
++ uint32_t version;
++ /**
++ * Notify client info
++ *
++ * \param info info about the client
++ */
++ void (*info) (void *object, const struct pw_client_info *info);
++ /**
++ * Notify a client permission
++ *
++ * Event emitted as a result of the get_permissions method.
++ *
++ * \param default_permissions the default permissions
++ * \param index the index of the first permission entry
++ * \param n_permissions the number of permissions
++ * \param permissions the permissions
++ */
++ void (*permissions) (void *object,
++ uint32_t index,
++ uint32_t n_permissions,
++ const struct pw_permission *permissions);
++};
++
++
++#define PW_CLIENT_METHOD_ADD_LISTENER 0
++#define PW_CLIENT_METHOD_ERROR 1
++#define PW_CLIENT_METHOD_UPDATE_PROPERTIES 2
++#define PW_CLIENT_METHOD_GET_PERMISSIONS 3
++#define PW_CLIENT_METHOD_UPDATE_PERMISSIONS 4
++#define PW_CLIENT_METHOD_NUM 5
++
++/** Client methods */
++struct pw_client_methods {
++#define PW_VERSION_CLIENT_METHODS 0
++ uint32_t version;
++
++ int (*add_listener) (void *object,
++ struct spa_hook *listener,
++ const struct pw_client_events *events,
++ void *data);
++ /**
++ * Send an error to a client
++ *
++ * \param id the global id to report the error on
++ * \param res an errno style error code
++ * \param message an error string
++ */
++ int (*error) (void *object, uint32_t id, int res, const char *message);
++ /**
++ * Update client properties
++ *
++ * \param props new properties
++ */
++ int (*update_properties) (void *object, const struct spa_dict *props);
++
++ /**
++ * Get client permissions
++ *
++ * A permissions event will be emitted with the permissions.
++ *
++ * \param index the first index to query, 0 for first
++ * \param num the maximum number of items to get
++ */
++ int (*get_permissions) (void *object, uint32_t index, uint32_t num);
++ /**
++ * Manage the permissions of the global objects for this
++ * client
++ *
++ * Update the permissions of the global objects using the
++ * provided array with permissions
++ *
++ * Globals can use the default permissions or can have specific
++ * permissions assigned to them.
++ *
++ * \param n_permissions number of permissions
++ * \param permissions array of permissions
++ */
++ int (*update_permissions) (void *object, uint32_t n_permissions,
++ const struct pw_permission *permissions);
++};
++
++#define pw_client_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ spa_interface_call_res((struct spa_interface*)o, \
++ struct pw_client_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#define pw_client_add_listener(c,...) pw_client_method(c,add_listener,0,__VA_ARGS__)
++#define pw_client_error(c,...) pw_client_method(c,error,0,__VA_ARGS__)
++#define pw_client_update_properties(c,...) pw_client_method(c,update_properties,0,__VA_ARGS__)
++#define pw_client_get_permissions(c,...) pw_client_method(c,get_permissions,0,__VA_ARGS__)
++#define pw_client_update_permissions(c,...) pw_client_method(c,update_permissions,0,__VA_ARGS__)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_CLIENT_H */
+diff --git a/third_party/pipewire/pipewire/context.h b/third_party/pipewire/pipewire/context.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/context.h
+@@ -0,0 +1,188 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_CONTEXT_H
++#define PIPEWIRE_CONTEXT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/hook.h>
++
++/** \class pw_context
++ *
++ * \brief the PipeWire context
++ *
++ * The context object manages all locally available resources. It
++ * is used by both clients and servers.
++ *
++ * The context is used to:
++ *
++ * - Load modules and extend the functionality. This includes
++ * extending the protocol with new object types or creating
++ * any of the available objects.
++ *
++ * - Create implementations of various objects like nodes,
++ * devices, factories, modules, etc.. This will usually also
++ * create pw_global objects that can then be shared with
++ * clients.
++ *
++ * - Connect to another PipeWire instance (the main daemon, for
++ * example) and interact with it (See \subpage page_core_api).
++ *
++ * - Export a local implementation of an object to another
++ * instance.
++ */
++struct pw_context;
++
++struct pw_global;
++struct pw_impl_client;
++
++#include <pipewire/core.h>
++#include <pipewire/loop.h>
++#include <pipewire/properties.h>
++
++/** \page page_context_api Core API
++ *
++ * \section page_context_overview Overview
++ *
++ * \subpage page_context
++ *
++ * \subpage page_global
++ *
++ * \subpage page_client
++ *
++ * \subpage page_resource
++ *
++ * \subpage page_node
++ *
++ * \subpage page_port
++ *
++ * \subpage page_link
++ */
++
++/** \page page_context Context
++ *
++ * \section page_context_overview Overview
++ *
++ * The context object is an object that manages the state and
++ * resources of a PipeWire instance.
++ */
++
++/** context events emitted by the context object added with \ref pw_context_add_listener */
++struct pw_context_events {
++#define PW_VERSION_CONTEXT_EVENTS 0
++ uint32_t version;
++
++ /** The context is being destroyed */
++ void (*destroy) (void *data);
++ /** The context is being freed */
++ void (*free) (void *data);
++ /** a new client object is added */
++ void (*check_access) (void *data, struct pw_impl_client *client);
++ /** a new global object was added */
++ void (*global_added) (void *data, struct pw_global *global);
++ /** a global object was removed */
++ void (*global_removed) (void *data, struct pw_global *global);
++};
++
++/** Make a new context object for a given main_loop. Ownership of the properties is taken */
++struct pw_context * pw_context_new(struct pw_loop *main_loop, /**< a main loop to run in */
++ struct pw_properties *props, /**< extra properties */
++ size_t user_data_size /**< extra user data size */);
++
++/** destroy a context object, all resources except the main_loop will be destroyed */
++void pw_context_destroy(struct pw_context *context);
++
++/** Get the context user data */
++void *pw_context_get_user_data(struct pw_context *context);
++
++/** Add a new event listener to a context */
++void pw_context_add_listener(struct pw_context *context,
++ struct spa_hook *listener,
++ const struct pw_context_events *events,
++ void *data);
++
++/** Get the context properties */
++const struct pw_properties *pw_context_get_properties(struct pw_context *context);
++
++/** Update the context properties */
++int pw_context_update_properties(struct pw_context *context, const struct spa_dict *dict);
++
++/** Get the context support objects */
++const struct spa_support *pw_context_get_support(struct pw_context *context, uint32_t *n_support);
++
++/** get the context main loop */
++struct pw_loop *pw_context_get_main_loop(struct pw_context *context);
++
++/** Iterate the globals of the context. The callback should return
++ * 0 to fetch the next item, any other value stops the iteration and returns
++ * the value. When all callbacks return 0, this function returns 0 when all
++ * globals are iterated. */
++int pw_context_for_each_global(struct pw_context *context, /**< the context */
++ int (*callback) (void *data, struct pw_global *global),
++ void *data);
++
++/** Find a context global by id */
++struct pw_global *pw_context_find_global(struct pw_context *context, /**< the context */
++ uint32_t id /**< the global id */);
++
++/** add a spa library for the given factory_name regex */
++int pw_context_add_spa_lib(struct pw_context *context, const char *factory_regex, const char *lib);
++
++/** find the library name for a spa factory */
++const char * pw_context_find_spa_lib(struct pw_context *context, const char *factory_name);
++
++struct spa_handle *pw_context_load_spa_handle(struct pw_context *context,
++ const char *factory_name,
++ const struct spa_dict *info);
++
++
++/** data for registering export functions */
++struct pw_export_type {
++ struct spa_list link;
++ const char *type;
++ struct pw_proxy * (*func) (struct pw_core *core,
++ const char *type, const struct spa_dict *props, void *object,
++ size_t user_data_size);
++};
++
++/** register a type that can be exported on a context_proxy. This is usually used by
++ * extension modules */
++int pw_context_register_export_type(struct pw_context *context, struct pw_export_type *type);
++/** find information about registered export type */
++const struct pw_export_type *pw_context_find_export_type(struct pw_context *context, const char *type);
++
++/** add an object to the context */
++int pw_context_set_object(struct pw_context *context, const char *type, void *value);
++/** get an object from the context */
++void *pw_context_get_object(struct pw_context *context, const char *type);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_CONTEXT_H */
+diff --git a/third_party/pipewire/pipewire/control.h b/third_party/pipewire/pipewire/control.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/control.h
+@@ -0,0 +1,79 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_CONTROL_H
++#define PIPEWIRE_CONTROL_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/hook.h>
++
++/** \page page_control Control
++ *
++ * \section page_control_overview Overview
++ *
++ * A control can be used to control a port property.
++ */
++/** \class pw_control
++ *
++ * The control object
++ */
++struct pw_control;
++
++#include <pipewire/impl.h>
++
++/** Port events, use \ref pw_control_add_listener */
++struct pw_control_events {
++#define PW_VERSION_CONTROL_EVENTS 0
++ uint32_t version;
++
++ /** The control is destroyed */
++ void (*destroy) (void *data);
++
++ /** The control is freed */
++ void (*free) (void *data);
++
++ /** control is linked to another control */
++ void (*linked) (void *data, struct pw_control *other);
++ /** control is unlinked from another control */
++ void (*unlinked) (void *data, struct pw_control *other);
++
++};
++
++/** Get the control parent port or NULL when not set */
++struct pw_impl_port *pw_control_get_port(struct pw_control *control);
++
++/** Add an event listener on the control */
++void pw_control_add_listener(struct pw_control *control,
++ struct spa_hook *listener,
++ const struct pw_control_events *events,
++ void *data);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_CONTROL_H */
+diff --git a/third_party/pipewire/pipewire/core.h b/third_party/pipewire/pipewire/core.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/core.h
+@@ -0,0 +1,584 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_CORE_H
++#define PIPEWIRE_CORE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdarg.h>
++#include <errno.h>
++
++#include <spa/utils/hook.h>
++
++#define PW_TYPE_INTERFACE_Core PW_TYPE_INFO_INTERFACE_BASE "Core"
++#define PW_TYPE_INTERFACE_Registry PW_TYPE_INFO_INTERFACE_BASE "Registry"
++
++#define PW_VERSION_CORE 3
++struct pw_core;
++#define PW_VERSION_REGISTRY 3
++struct pw_registry;
++
++/* the default remote name to connect to */
++#define PW_DEFAULT_REMOTE "pipewire-0"
++
++/* default ID for the core object after connect */
++#define PW_ID_CORE 0
++
++/* invalid ID that matches any object when used for permissions */
++#define PW_ID_ANY (uint32_t)(0xffffffff)
++
++/** The core information. Extra information can be added in later versions \memberof pw_introspect */
++struct pw_core_info {
++ uint32_t id; /**< id of the global */
++ uint32_t cookie; /**< a random cookie for identifying this instance of PipeWire */
++ const char *user_name; /**< name of the user that started the core */
++ const char *host_name; /**< name of the machine the core is running on */
++ const char *version; /**< version of the core */
++ const char *name; /**< name of the core */
++#define PW_CORE_CHANGE_MASK_PROPS (1 << 0)
++#define PW_CORE_CHANGE_MASK_ALL ((1 << 1)-1)
++ uint64_t change_mask; /**< bitfield of changed fields since last call */
++ struct spa_dict *props; /**< extra properties */
++};
++
++#include <pipewire/context.h>
++#include <pipewire/properties.h>
++#include <pipewire/proxy.h>
++
++/** Update and existing \ref pw_core_info with \a update \memberof pw_introspect */
++struct pw_core_info *
++pw_core_info_update(struct pw_core_info *info,
++ const struct pw_core_info *update);
++
++/** Free a \ref pw_core_info \memberof pw_introspect */
++void pw_core_info_free(struct pw_core_info *info);
++
++/**
++ * \page page_iface_pw_core pw_core
++ * \section page_iface_pw_core_desc Description
++ *
++ * The core global object. This is a special singleton object. It
++ * is used for internal PipeWire protocol features.
++ * \section page_iface_pw_core API
++ */
++
++/** Core */
++
++#define PW_CORE_EVENT_INFO 0
++#define PW_CORE_EVENT_DONE 1
++#define PW_CORE_EVENT_PING 2
++#define PW_CORE_EVENT_ERROR 3
++#define PW_CORE_EVENT_REMOVE_ID 4
++#define PW_CORE_EVENT_BOUND_ID 5
++#define PW_CORE_EVENT_ADD_MEM 6
++#define PW_CORE_EVENT_REMOVE_MEM 7
++#define PW_CORE_EVENT_NUM 8
++
++/** \struct pw_core_events
++ * \brief Core events
++ * \ingroup pw_core_interface The pw_core interface
++ */
++struct pw_core_events {
++#define PW_VERSION_CORE_EVENTS 0
++ uint32_t version;
++
++ /**
++ * Notify new core info
++ *
++ * This event is emitted when first bound to the core or when the
++ * hello method is called.
++ *
++ * \param info new core info
++ */
++ void (*info) (void *object, const struct pw_core_info *info);
++ /**
++ * Emit a done event
++ *
++ * The done event is emitted as a result of a sync method with the
++ * same seq number.
++ *
++ * \param seq the seq number passed to the sync method call
++ */
++ void (*done) (void *object, uint32_t id, int seq);
++
++ /** Emit a ping event
++ *
++ * The client should reply with a pong reply with the same seq
++ * number.
++ */
++ void (*ping) (void *object, uint32_t id, int seq);
++
++ /**
++ * Fatal error event
++ *
++ * The error event is sent out when a fatal (non-recoverable)
++ * error has occurred. The id argument is the proxy object where
++ * the error occurred, most often in response to a request to that
++ * object. The message is a brief description of the error,
++ * for (debugging) convenience.
++ *
++ * This event is usually also emitted on the proxy object with
++ * \a id.
++ *
++ * \param id object where the error occurred
++ * \param seq the sequence number that generated the error
++ * \param res error code
++ * \param message error description
++ */
++ void (*error) (void *object, uint32_t id, int seq, int res, const char *message);
++ /**
++ * Remove an object ID
++ *
++ * This event is used internally by the object ID management
++ * logic. When a client deletes an object, the server will send
++ * this event to acknowledge that it has seen the delete request.
++ * When the client receives this event, it will know that it can
++ * safely reuse the object ID.
++ *
++ * \param id deleted object ID
++ */
++ void (*remove_id) (void *object, uint32_t id);
++
++ /**
++ * Notify an object binding
++ *
++ * This event is emitted when a local object ID is bound to a
++ * global ID. It is emitted before the global becomes visible in the
++ * registry.
++ *
++ * \param id bound object ID
++ * \param global_id the global id bound to
++ */
++ void (*bound_id) (void *object, uint32_t id, uint32_t global_id);
++
++ /**
++ * Add memory for a client
++ *
++ * Memory is given to a client as \a fd of a certain
++ * memory \a type.
++ *
++ * Further references to this fd will be made with the per memory
++ * unique identifier \a id.
++ *
++ * \param id the unique id of the memory
++ * \param type the memory type, one of enum spa_data_type
++ * \param fd the file descriptor
++ * \param flags extra flags
++ */
++ void (*add_mem) (void *object, uint32_t id, uint32_t type, int fd, uint32_t flags);
++
++ /**
++ * Remove memory for a client
++ *
++ * \param id the memory id to remove
++ */
++ void (*remove_mem) (void *object, uint32_t id);
++};
++
++#define PW_CORE_METHOD_ADD_LISTENER 0
++#define PW_CORE_METHOD_HELLO 1
++#define PW_CORE_METHOD_SYNC 2
++#define PW_CORE_METHOD_PONG 3
++#define PW_CORE_METHOD_ERROR 4
++#define PW_CORE_METHOD_GET_REGISTRY 5
++#define PW_CORE_METHOD_CREATE_OBJECT 6
++#define PW_CORE_METHOD_DESTROY 7
++#define PW_CORE_METHOD_NUM 8
++
++/**
++ * \struct pw_core_methods
++ * \brief Core methods
++ *
++ * The core global object. This is a singleton object used for
++ * creating new objects in the remote PipeWire instance. It is
++ * also used for internal features.
++ */
++struct pw_core_methods {
++#define PW_VERSION_CORE_METHODS 0
++ uint32_t version;
++
++ int (*add_listener) (void *object,
++ struct spa_hook *listener,
++ const struct pw_core_events *events,
++ void *data);
++ /**
++ * Start a conversation with the server. This will send
++ * the core info and will destroy all resources for the client
++ * (except the core and client resource).
++ */
++ int (*hello) (void *object, uint32_t version);
++ /**
++ * Do server roundtrip
++ *
++ * Ask the server to emit the 'done' event with \a seq.
++ *
++ * Since methods are handled in-order and events are delivered
++ * in-order, this can be used as a barrier to ensure all previous
++ * methods and the resulting events have been handled.
++ *
++ * \param seq the seq number passed to the done event
++ */
++ int (*sync) (void *object, uint32_t id, int seq);
++ /**
++ * Reply to a server ping event.
++ *
++ * Reply to the server ping event with the same seq.
++ *
++ * \param seq the seq number received in the ping event
++ */
++ int (*pong) (void *object, uint32_t id, int seq);
++ /**
++ * Fatal error event
++ *
++ * The error method is sent out when a fatal (non-recoverable)
++ * error has occurred. The id argument is the proxy object where
++ * the error occurred, most often in response to an event on that
++ * object. The message is a brief description of the error,
++ * for (debugging) convenience.
++ *
++ * This method is usually also emitted on the resource object with
++ * \a id.
++ *
++ * \param id object where the error occurred
++ * \param res error code
++ * \param message error description
++ */
++ int (*error) (void *object, uint32_t id, int seq, int res, const char *message);
++ /**
++ * Get the registry object
++ *
++ * Create a registry object that allows the client to list and bind
++ * the global objects available from the PipeWire server
++ * \param version the client version
++ * \param user_data_size extra size
++ */
++ struct pw_registry * (*get_registry) (void *object, uint32_t version,
++ size_t user_data_size);
++
++ /**
++ * Create a new object on the PipeWire server from a factory.
++ *
++ * \param factory_name the factory name to use
++ * \param type the interface to bind to
++ * \param version the version of the interface
++ * \param props extra properties
++ * \param user_data_size extra size
++ */
++ void * (*create_object) (void *object,
++ const char *factory_name,
++ const char *type,
++ uint32_t version,
++ const struct spa_dict *props,
++ size_t user_data_size);
++ /**
++ * Destroy an resource
++ *
++ * Destroy the server resource for the given proxy.
++ *
++ * \param obj the proxy to destroy
++ */
++ int (*destroy) (void *object, void *proxy);
++};
++
++#define pw_core_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ spa_interface_call_res((struct spa_interface*)o, \
++ struct pw_core_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#define pw_core_add_listener(c,...) pw_core_method(c,add_listener,0,__VA_ARGS__)
++#define pw_core_hello(c,...) pw_core_method(c,hello,0,__VA_ARGS__)
++#define pw_core_sync(c,...) pw_core_method(c,sync,0,__VA_ARGS__)
++#define pw_core_pong(c,...) pw_core_method(c,pong,0,__VA_ARGS__)
++#define pw_core_error(c,...) pw_core_method(c,error,0,__VA_ARGS__)
++
++
++static inline
++SPA_PRINTF_FUNC(5, 0) int
++pw_core_errorv(struct pw_core *core, uint32_t id, int seq,
++ int res, const char *message, va_list args)
++{
++ char buffer[1024];
++ vsnprintf(buffer, sizeof(buffer), message, args);
++ buffer[1023] = '\0';
++ return pw_core_error(core, id, seq, res, buffer);
++}
++
++static inline
++SPA_PRINTF_FUNC(5, 6) int
++pw_core_errorf(struct pw_core *core, uint32_t id, int seq,
++ int res, const char *message, ...)
++{
++ va_list args;
++ int r;
++ va_start(args, message);
++ r = pw_core_errorv(core, id, seq, res, message, args);
++ va_end(args);
++ return r;
++}
++
++static inline struct pw_registry *
++pw_core_get_registry(struct pw_core *core, uint32_t version, size_t user_data_size)
++{
++ struct pw_registry *res = NULL;
++ spa_interface_call_res((struct spa_interface*)core,
++ struct pw_core_methods, res,
++ get_registry, 0, version, user_data_size);
++ return res;
++}
++
++static inline void *
++pw_core_create_object(struct pw_core *core,
++ const char *factory_name,
++ const char *type,
++ uint32_t version,
++ const struct spa_dict *props,
++ size_t user_data_size)
++{
++ void *res = NULL;
++ spa_interface_call_res((struct spa_interface*)core,
++ struct pw_core_methods, res,
++ create_object, 0, factory_name,
++ type, version, props, user_data_size);
++ return res;
++}
++
++#define pw_core_destroy(c,...) pw_core_method(c,destroy,0,__VA_ARGS__)
++
++/** \page page_registry Registry
++ *
++ * \section page_registry_overview Overview
++ *
++ * The registry object is a singleton object that keeps track of
++ * global objects on the PipeWire instance. See also \ref page_global.
++ *
++ * Global objects typically represent an actual object in PipeWire
++ * (for example, a module or node) or they are singleton
++ * objects such as the core.
++ *
++ * When a client creates a registry object, the registry object
++ * will emit a global event for each global currently in the
++ * registry. Globals come and go as a result of device hotplugs or
++ * reconfiguration or other events, and the registry will send out
++ * global and global_remove events to keep the client up to date
++ * with the changes. To mark the end of the initial burst of
++ * events, the client can use the pw_core.sync methosd immediately
++ * after calling pw_core.get_registry.
++ *
++ * A client can bind to a global object by using the bind
++ * request. This creates a client-side proxy that lets the object
++ * emit events to the client and lets the client invoke methods on
++ * the object. See \ref page_proxy
++ *
++ * Clients can also change the permissions of the global objects that
++ * it can see. This is interesting when you want to configure a
++ * pipewire session before handing it to another application. You
++ * can, for example, hide certain existing or new objects or limit
++ * the access permissions on an object.
++ */
++
++#define PW_REGISTRY_EVENT_GLOBAL 0
++#define PW_REGISTRY_EVENT_GLOBAL_REMOVE 1
++#define PW_REGISTRY_EVENT_NUM 2
++
++/** Registry events */
++struct pw_registry_events {
++#define PW_VERSION_REGISTRY_EVENTS 0
++ uint32_t version;
++ /**
++ * Notify of a new global object
++ *
++ * The registry emits this event when a new global object is
++ * available.
++ *
++ * \param id the global object id
++ * \param permissions the permissions of the object
++ * \param type the type of the interface
++ * \param version the version of the interface
++ * \param props extra properties of the global
++ */
++ void (*global) (void *object, uint32_t id,
++ uint32_t permissions, const char *type, uint32_t version,
++ const struct spa_dict *props);
++ /**
++ * Notify of a global object removal
++ *
++ * Emitted when a global object was removed from the registry.
++ * If the client has any bindings to the global, it should destroy
++ * those.
++ *
++ * \param id the id of the global that was removed
++ */
++ void (*global_remove) (void *object, uint32_t id);
++};
++
++#define PW_REGISTRY_METHOD_ADD_LISTENER 0
++#define PW_REGISTRY_METHOD_BIND 1
++#define PW_REGISTRY_METHOD_DESTROY 2
++#define PW_REGISTRY_METHOD_NUM 3
++
++/** Registry methods */
++struct pw_registry_methods {
++#define PW_VERSION_REGISTRY_METHODS 0
++ uint32_t version;
++
++ int (*add_listener) (void *object,
++ struct spa_hook *listener,
++ const struct pw_registry_events *events,
++ void *data);
++ /**
++ * Bind to a global object
++ *
++ * Bind to the global object with \a id and use the client proxy
++ * with new_id as the proxy. After this call, methods can be
++ * send to the remote global object and events can be received
++ *
++ * \param id the global id to bind to
++ * \param type the interface type to bind to
++ * \param version the interface version to use
++ * \returns the new object
++ */
++ void * (*bind) (void *object, uint32_t id, const char *type, uint32_t version,
++ size_t use_data_size);
++
++ /**
++ * Attempt to destroy a global object
++ *
++ * Try to destroy the global object.
++ *
++ * \param id the global id to destroy
++ */
++ int (*destroy) (void *object, uint32_t id);
++};
++
++#define pw_registry_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ spa_interface_call_res((struct spa_interface*)o, \
++ struct pw_registry_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++/** Registry */
++#define pw_registry_add_listener(p,...) pw_registry_method(p,add_listener,0,__VA_ARGS__)
++
++static inline void *
++pw_registry_bind(struct pw_registry *registry,
++ uint32_t id, const char *type, uint32_t version,
++ size_t user_data_size)
++{
++ void *res = NULL;
++ spa_interface_call_res((struct spa_interface*)registry,
++ struct pw_registry_methods, res,
++ bind, 0, id, type, version, user_data_size);
++ return res;
++}
++
++#define pw_registry_destroy(p,...) pw_registry_method(p,destroy,0,__VA_ARGS__)
++
++
++/** Connect to a PipeWire instance \memberof pw_core
++ * \return a pw_core on success or NULL with errno set on error. The core
++ * will have an id of PW_ID_CORE (0) */
++struct pw_core *
++pw_context_connect(struct pw_context *context, /**< a \ref pw_context */
++ struct pw_properties *properties, /**< optional properties, ownership of
++ * the properties is taken.*/
++ size_t user_data_size /**< extra user data size */);
++
++/** Connect to a PipeWire instance on the given socket \memberof pw_core
++ * \param fd the connected socket to use, the socket will be closed
++ * automatically on disconnect or error.
++ * \return a pw_core on success or NULL with errno set on error */
++struct pw_core *
++pw_context_connect_fd(struct pw_context *context, /**< a \ref pw_context */
++ int fd, /**< an fd */
++ struct pw_properties *properties, /**< optional properties, ownership of
++ * the properties is taken.*/
++ size_t user_data_size /**< extra user data size */);
++
++/** Connect to a given PipeWire instance \memberof pw_core
++ * \return a pw_core on success or NULL with errno set on error */
++struct pw_core *
++pw_context_connect_self(struct pw_context *context, /**< a \ref pw_context to connect to */
++ struct pw_properties *properties, /**< optional properties, ownership of
++ * the properties is taken.*/
++ size_t user_data_size /**< extra user data size */);
++
++/** Steal the fd of the core connection or < 0 on error. The core
++ * will be disconnected after this call. */
++int pw_core_steal_fd(struct pw_core *core);
++
++/** Pause or resume the core. When the core is paused, no new events
++ * will be dispatched until the core is resumed again. */
++int pw_core_set_paused(struct pw_core *core, bool paused);
++
++/** disconnect and destroy a core */
++int pw_core_disconnect(struct pw_core *core);
++
++/** Get the user_data. It is of the size specified when this object was
++ * constructed */
++void *pw_core_get_user_data(struct pw_core *core);
++
++/** Get the client proxy of the connected core. This will have the id
++ * of PW_ID_CLIENT (1) */
++struct pw_client * pw_core_get_client(struct pw_core *core);
++
++/** Get the context object used to created this core */
++struct pw_context * pw_core_get_context(struct pw_core *core);
++
++/** Get properties from the core */
++const struct pw_properties *pw_core_get_properties(struct pw_core *core);
++
++/** Update the core properties. This updates the properties
++ * of the associated client.
++ * \return the number of properties that were updated */
++int pw_core_update_properties(struct pw_core *core, const struct spa_dict *dict);
++
++/** Get the core mempool object */
++struct pw_mempool * pw_core_get_mempool(struct pw_core *core);
++
++/** Get the proxy with the given id */
++struct pw_proxy *pw_core_find_proxy(struct pw_core *core, uint32_t id);
++
++/** Export an object into the PipeWire instance associated with core */
++struct pw_proxy *pw_core_export(struct pw_core *core, /**< the core */
++ const char *type, /**< the type of object */
++ const struct spa_dict *props, /**< extra properties */
++ void *object, /**< object to export */
++ size_t user_data_size /**< extra user data */);
++
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_CORE_H */
+diff --git a/third_party/pipewire/pipewire/data-loop.h b/third_party/pipewire/pipewire/data-loop.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/data-loop.h
+@@ -0,0 +1,95 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_DATA_LOOP_H
++#define PIPEWIRE_DATA_LOOP_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/hook.h>
++
++/** \class pw_data_loop
++ *
++ * PipeWire rt-loop object. This loop starts a new real-time thread that
++ * is designed to run the processing graph.
++ */
++struct pw_data_loop;
++
++#include <pipewire/loop.h>
++#include <pipewire/properties.h>
++
++/** Loop events, use \ref pw_data_loop_add_listener to add a listener */
++struct pw_data_loop_events {
++#define PW_VERSION_DATA_LOOP_EVENTS 0
++ uint32_t version;
++ /** The loop is destroyed */
++ void (*destroy) (void *data);
++};
++
++/** Make a new loop. */
++struct pw_data_loop *
++pw_data_loop_new(const struct spa_dict *props);
++
++/** Add an event listener to loop */
++void pw_data_loop_add_listener(struct pw_data_loop *loop,
++ struct spa_hook *listener,
++ const struct pw_data_loop_events *events,
++ void *data);
++
++/** wait for activity on the loop up to \a timeout milliseconds.
++ * Should be called from the loop function */
++int pw_data_loop_wait(struct pw_data_loop *loop, int timeout);
++
++/** make sure the thread will exit. Can be called from a loop callback */
++void pw_data_loop_exit(struct pw_data_loop *loop);
++
++/** Get the loop implementation of this data loop */
++struct pw_loop *
++pw_data_loop_get_loop(struct pw_data_loop *loop);
++
++/** Destroy the loop */
++void pw_data_loop_destroy(struct pw_data_loop *loop);
++
++/** Start the processing thread */
++int pw_data_loop_start(struct pw_data_loop *loop);
++
++/** Stop the processing thread */
++int pw_data_loop_stop(struct pw_data_loop *loop);
++
++/** Check if the current thread is the processing thread */
++bool pw_data_loop_in_thread(struct pw_data_loop *loop);
++
++/** invoke func in the context of the thread or in the caller thread when
++ * the loop is not running. Since 0.3.3 */
++int pw_data_loop_invoke(struct pw_data_loop *loop,
++ spa_invoke_func_t func, uint32_t seq, const void *data, size_t size,
++ bool block, void *user_data);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_DATA_LOOP_H */
+diff --git a/third_party/pipewire/pipewire/device.h b/third_party/pipewire/pipewire/device.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/device.h
+@@ -0,0 +1,162 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_DEVICE_H
++#define PIPEWIRE_DEVICE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/hook.h>
++
++#include <pipewire/proxy.h>
++
++#define PW_TYPE_INTERFACE_Device PW_TYPE_INFO_INTERFACE_BASE "Device"
++
++#define PW_VERSION_DEVICE 3
++struct pw_device;
++
++/** The device information. Extra information can be added in later versions \memberof pw_introspect */
++struct pw_device_info {
++ uint32_t id; /**< id of the global */
++#define PW_DEVICE_CHANGE_MASK_PROPS (1 << 0)
++#define PW_DEVICE_CHANGE_MASK_PARAMS (1 << 1)
++#define PW_DEVICE_CHANGE_MASK_ALL ((1 << 2)-1)
++ uint64_t change_mask; /**< bitfield of changed fields since last call */
++ struct spa_dict *props; /**< extra properties */
++ struct spa_param_info *params; /**< parameters */
++ uint32_t n_params; /**< number of items in \a params */
++};
++
++/** Update and existing \ref pw_device_info with \a update \memberof pw_introspect */
++struct pw_device_info *
++pw_device_info_update(struct pw_device_info *info,
++ const struct pw_device_info *update);
++
++/** Free a \ref pw_device_info \memberof pw_introspect */
++void pw_device_info_free(struct pw_device_info *info);
++
++#define PW_DEVICE_EVENT_INFO 0
++#define PW_DEVICE_EVENT_PARAM 1
++#define PW_DEVICE_EVENT_NUM 2
++
++/** Device events */
++struct pw_device_events {
++#define PW_VERSION_DEVICE_EVENTS 0
++ uint32_t version;
++ /**
++ * Notify device info
++ *
++ * \param info info about the device
++ */
++ void (*info) (void *object, const struct pw_device_info *info);
++ /**
++ * Notify a device param
++ *
++ * Event emitted as a result of the enum_params method.
++ *
++ * \param seq the sequence number of the request
++ * \param id the param id
++ * \param index the param index
++ * \param next the param index of the next param
++ * \param param the parameter
++ */
++ void (*param) (void *object, int seq,
++ uint32_t id, uint32_t index, uint32_t next,
++ const struct spa_pod *param);
++};
++
++
++#define PW_DEVICE_METHOD_ADD_LISTENER 0
++#define PW_DEVICE_METHOD_SUBSCRIBE_PARAMS 1
++#define PW_DEVICE_METHOD_ENUM_PARAMS 2
++#define PW_DEVICE_METHOD_SET_PARAM 3
++#define PW_DEVICE_METHOD_NUM 4
++
++/** Device methods */
++struct pw_device_methods {
++#define PW_VERSION_DEVICE_METHODS 0
++ uint32_t version;
++
++ int (*add_listener) (void *object,
++ struct spa_hook *listener,
++ const struct pw_device_events *events,
++ void *data);
++ /**
++ * Subscribe to parameter changes
++ *
++ * Automatically emit param events for the given ids when
++ * they are changed.
++ *
++ * \param ids an array of param ids
++ * \param n_ids the number of ids in \a ids
++ */
++ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids);
++
++ /**
++ * Enumerate device parameters
++ *
++ * Start enumeration of device parameters. For each param, a
++ * param event will be emitted.
++ *
++ * \param seq a sequence number to place in the reply
++ * \param id the parameter id to enum or PW_ID_ANY for all
++ * \param start the start index or 0 for the first param
++ * \param num the maximum number of params to retrieve
++ * \param filter a param filter or NULL
++ */
++ int (*enum_params) (void *object, int seq, uint32_t id, uint32_t start, uint32_t num,
++ const struct spa_pod *filter);
++ /**
++ * Set a parameter on the device
++ *
++ * \param id the parameter id to set
++ * \param flags extra parameter flags
++ * \param param the parameter to set
++ */
++ int (*set_param) (void *object, uint32_t id, uint32_t flags,
++ const struct spa_pod *param);
++};
++
++#define pw_device_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ spa_interface_call_res((struct spa_interface*)o, \
++ struct pw_device_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#define pw_device_add_listener(c,...) pw_device_method(c,add_listener,0,__VA_ARGS__)
++#define pw_device_subscribe_params(c,...) pw_device_method(c,subscribe_params,0,__VA_ARGS__)
++#define pw_device_enum_params(c,...) pw_device_method(c,enum_params,0,__VA_ARGS__)
++#define pw_device_set_param(c,...) pw_device_method(c,set_param,0,__VA_ARGS__)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_DEVICE_H */
+diff --git a/third_party/pipewire/pipewire/factory.h b/third_party/pipewire/pipewire/factory.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/factory.h
+@@ -0,0 +1,109 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_FACTORY_H
++#define PIPEWIRE_FACTORY_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdarg.h>
++#include <errno.h>
++
++#include <spa/utils/defs.h>
++#include <spa/utils/hook.h>
++
++#include <pipewire/proxy.h>
++
++#define PW_TYPE_INTERFACE_Factory PW_TYPE_INFO_INTERFACE_BASE "Factory"
++
++#define PW_VERSION_FACTORY 3
++struct pw_factory;
++
++/** The factory information. Extra information can be added in later versions \memberof pw_introspect */
++struct pw_factory_info {
++ uint32_t id; /**< id of the global */
++ const char *name; /**< name the factory */
++ const char *type; /**< type of the objects created by this factory */
++ uint32_t version; /**< version of the objects */
++#define PW_FACTORY_CHANGE_MASK_PROPS (1 << 0)
++#define PW_FACTORY_CHANGE_MASK_ALL ((1 << 1)-1)
++ uint64_t change_mask; /**< bitfield of changed fields since last call */
++ struct spa_dict *props; /**< the properties of the factory */
++};
++
++struct pw_factory_info *
++pw_factory_info_update(struct pw_factory_info *info,
++ const struct pw_factory_info *update);
++
++void
++pw_factory_info_free(struct pw_factory_info *info);
++
++
++#define PW_FACTORY_EVENT_INFO 0
++#define PW_FACTORY_EVENT_NUM 1
++
++/** Factory events */
++struct pw_factory_events {
++#define PW_VERSION_FACTORY_EVENTS 0
++ uint32_t version;
++ /**
++ * Notify factory info
++ *
++ * \param info info about the factory
++ */
++ void (*info) (void *object, const struct pw_factory_info *info);
++};
++
++#define PW_FACTORY_METHOD_ADD_LISTENER 0
++#define PW_FACTORY_METHOD_NUM 1
++
++/** Factory methods */
++struct pw_factory_methods {
++#define PW_VERSION_FACTORY_METHODS 0
++ uint32_t version;
++
++ int (*add_listener) (void *object,
++ struct spa_hook *listener,
++ const struct pw_factory_events *events,
++ void *data);
++};
++
++#define pw_factory_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ spa_interface_call_res((struct spa_interface*)o, \
++ struct pw_factory_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#define pw_factory_add_listener(c,...) pw_factory_method(c,add_listener,0,__VA_ARGS__)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_FACTORY_H */
+diff --git a/third_party/pipewire/pipewire/filter.h b/third_party/pipewire/pipewire/filter.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/filter.h
+@@ -0,0 +1,240 @@
++/* PipeWire
++ *
++ * Copyright © 2019 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_FILTER_H
++#define PIPEWIRE_FILTER_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** \class pw_filter
++ *
++ * \brief PipeWire filter object class
++ *
++ * The filter object provides a convenient way to implement
++ * processing filters.
++ *
++ * See also \ref page_filters and \ref page_core_api
++ */
++struct pw_filter;
++
++#include <spa/buffer/buffer.h>
++#include <spa/node/io.h>
++#include <spa/param/param.h>
++
++#include <pipewire/core.h>
++#include <pipewire/stream.h>
++
++/** \enum pw_filter_state The state of a filter \memberof pw_filter */
++enum pw_filter_state {
++ PW_FILTER_STATE_ERROR = -1, /**< the strean is in error */
++ PW_FILTER_STATE_UNCONNECTED = 0, /**< unconnected */
++ PW_FILTER_STATE_CONNECTING = 1, /**< connection is in progress */
++ PW_FILTER_STATE_PAUSED = 2, /**< filter is connected and paused */
++ PW_FILTER_STATE_STREAMING = 3 /**< filter is streaming */
++};
++
++#if 0
++struct pw_buffer {
++ struct spa_buffer *buffer; /**< the spa buffer */
++ void *user_data; /**< user data attached to the buffer */
++ uint64_t size; /**< For input ports, this field is set by pw_filter
++ * with the duration of the buffer in ticks.
++ * For output ports, this field is set by the user.
++ * This field is added for all queued buffers and
++ * returned in the time info. */
++};
++#endif
++
++/** Events for a filter. These events are always called from the mainloop
++ * unless explicitly documented otherwise. */
++struct pw_filter_events {
++#define PW_VERSION_FILTER_EVENTS 0
++ uint32_t version;
++
++ void (*destroy) (void *data);
++ /** when the filter state changes */
++ void (*state_changed) (void *data, enum pw_filter_state old,
++ enum pw_filter_state state, const char *error);
++
++ /** when io changed on a port of the filter (when port_data is NULL). */
++ void (*io_changed) (void *data, void *port_data,
++ uint32_t id, void *area, uint32_t size);
++ /** when a parameter changed on a port of the filter (when port_data is NULL). */
++ void (*param_changed) (void *data, void *port_data,
++ uint32_t id, const struct spa_pod *param);
++
++ /** when a new buffer was created for a port */
++ void (*add_buffer) (void *data, void *port_data, struct pw_buffer *buffer);
++ /** when a buffer was destroyed for a port */
++ void (*remove_buffer) (void *data, void *port_data, struct pw_buffer *buffer);
++
++ /** do processing. This is normally called from the
++ * mainloop but can also be called directly from the realtime data
++ * thread if the user is prepared to deal with this. */
++ void (*process) (void *data, struct spa_io_position *position);
++
++ /** The filter is drained */
++ void (*drained) (void *data);
++};
++
++/** Convert a filter state to a readable string \memberof pw_filter */
++const char * pw_filter_state_as_string(enum pw_filter_state state);
++
++/** \enum pw_filter_flags Extra flags that can be used in \ref pw_filter_connect() \memberof pw_filter */
++enum pw_filter_flags {
++ PW_FILTER_FLAG_NONE = 0, /**< no flags */
++ PW_FILTER_FLAG_INACTIVE = (1 << 0), /**< start the filter inactive,
++ * pw_filter_set_active() needs to be
++ * called explicitly */
++ PW_FILTER_FLAG_DRIVER = (1 << 1), /**< be a driver */
++ PW_FILTER_FLAG_RT_PROCESS = (1 << 2), /**< call process from the realtime
++ * thread */
++};
++
++enum pw_filter_port_flags {
++ PW_FILTER_PORT_FLAG_NONE = 0, /**< no flags */
++ PW_FILTER_PORT_FLAG_MAP_BUFFERS = (1 << 0), /**< mmap the buffers */
++ PW_FILTER_PORT_FLAG_ALLOC_BUFFERS = (1 << 1), /**< the application will allocate buffer
++ * memory. In the add_buffer event, the
++ * data of the buffer should be set */
++};
++
++/** Create a new unconneced \ref pw_filter \memberof pw_filter
++ * \return a newly allocated \ref pw_filter */
++struct pw_filter *
++pw_filter_new(struct pw_core *core, /**< a \ref pw_core */
++ const char *name, /**< a filter media name */
++ struct pw_properties *props /**< filter properties, ownership is taken */);
++
++struct pw_filter *
++pw_filter_new_simple(struct pw_loop *loop, /**< a \ref pw_loop to use */
++ const char *name, /**< a filter media name */
++ struct pw_properties *props, /**< filter properties, ownership is taken */
++ const struct pw_filter_events *events, /**< filter events */
++ void *data /**< data passed to events */);
++
++/** Destroy a filter \memberof pw_filter */
++void pw_filter_destroy(struct pw_filter *filter);
++
++void pw_filter_add_listener(struct pw_filter *filter,
++ struct spa_hook *listener,
++ const struct pw_filter_events *events,
++ void *data);
++
++enum pw_filter_state pw_filter_get_state(struct pw_filter *filter, const char **error);
++
++const char *pw_filter_get_name(struct pw_filter *filter);
++
++struct pw_core *pw_filter_get_core(struct pw_filter *filter);
++
++/** Connect a filter for processing. \memberof pw_filter
++ * \return 0 on success < 0 on error.
++ *
++ * You should connect to the process event and use pw_filter_dequeue_buffer()
++ * to get the latest metadata and data. */
++int
++pw_filter_connect(struct pw_filter *filter, /**< a \ref pw_filter */
++ enum pw_filter_flags flags, /**< filter flags */
++ const struct spa_pod **params, /**< an array with params. */
++ uint32_t n_params /**< number of items in \a params */);
++
++/** Get the node ID of the filter. \memberof pw_filter
++ * \return node ID. */
++uint32_t
++pw_filter_get_node_id(struct pw_filter *filter);
++
++/** Disconnect \a filter \memberof pw_filter */
++int pw_filter_disconnect(struct pw_filter *filter);
++
++/** add a port to the filter, returns user data of port_data_size. */
++void *pw_filter_add_port(struct pw_filter *filter,
++ enum pw_direction direction, /**< port direction */
++ enum pw_filter_port_flags flags, /**< port flags */
++ size_t port_data_size, /**< allocated and given to the user as port_data */
++ struct pw_properties *props, /**< port properties, ownership is taken */
++ const struct spa_pod **params, /**< an array of params. The params should
++ * ideally contain the supported formats */
++ uint32_t n_params /**< number of elements in \a params */);
++
++/** remove a port from the filter */
++int pw_filter_remove_port(void *port_data /**< data associated with port */);
++
++/** get properties, port_data of NULL will give global properties */
++const struct pw_properties *pw_filter_get_properties(struct pw_filter *filter,
++ void *port_data);
++
++/** Update properties, use NULL port_data for global filter properties */
++int pw_filter_update_properties(struct pw_filter *filter,
++ void *port_data, const struct spa_dict *dict);
++
++/** Set the filter in error state */
++int pw_filter_set_error(struct pw_filter *filter, /**< a \ref pw_filter */
++ int res, /**< a result code */
++ const char *error, ... /**< an error message */) SPA_PRINTF_FUNC(3, 4);
++
++/** Update params, use NULL port_data for global filter params */
++int
++pw_filter_update_params(struct pw_filter *filter, /**< a \ref pw_filter */
++ void *port_data, /**< data associated with port */
++ const struct spa_pod **params, /**< an array of params. */
++ uint32_t n_params /**< number of elements in \a params */);
++
++
++#if 0
++/** A time structure \memberof pw_filter */
++struct pw_time {
++ int64_t now; /**< the monotonic time */
++ struct spa_fraction rate; /**< the rate of \a ticks and delay */
++ uint64_t ticks; /**< the ticks at \a now. This is the current time that
++ * the remote end is reading/writing. */
++};
++#endif
++
++/** Query the time on the filter \memberof pw_filter */
++int pw_filter_get_time(struct pw_filter *filter, struct pw_time *time);
++
++/** Get a buffer that can be filled for output ports or consumed
++ * for input ports. */
++struct pw_buffer *pw_filter_dequeue_buffer(void *port_data);
++
++/** Submit a buffer for playback or recycle a buffer for capture. */
++int pw_filter_queue_buffer(void *port_data, struct pw_buffer *buffer);
++
++/** Get a data pointer to the buffer data */
++void *pw_filter_get_dsp_buffer(void *port_data, uint32_t n_samples);
++
++/** Activate or deactivate the filter \memberof pw_filter */
++int pw_filter_set_active(struct pw_filter *filter, bool active);
++
++/** Flush a filter. When \a drain is true, the drained callback will
++ * be called when all data is played or recorded */
++int pw_filter_flush(struct pw_filter *filter, bool drain);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_FILTER_H */
+diff --git a/third_party/pipewire/pipewire/global.h b/third_party/pipewire/pipewire/global.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/global.h
+@@ -0,0 +1,155 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_GLOBAL_H
++#define PIPEWIRE_GLOBAL_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** \page page_global Global
++ *
++ * Global objects represent resources that are available on the PipeWire
++ * context and are accessible to remote clients.
++ * Globals come and go when devices or other resources become available for
++ * clients.
++ *
++ * Remote clients receives a list of globals when it binds to the registry
++ * object. See \ref page_registry.
++ *
++ * A client can bind to a global to send methods or receive events from
++ * the global.
++ */
++/** \class pw_global
++ *
++ * \brief A global object visible to remote clients
++ *
++ * A global object is visible to remote clients and represents a resource
++ * that can be used or inspected.
++ *
++ * See \ref page_remote_api
++ */
++struct pw_global;
++
++#include <pipewire/impl.h>
++
++typedef int (*pw_global_bind_func_t) (void *object,
++ struct pw_impl_client *client, /**< client that binds */
++ uint32_t permissions, /**< permissions for the bind */
++ uint32_t version, /**< client interface version */
++ uint32_t id /**< client proxy id */);
++
++/** Global events, use \ref pw_global_add_listener */
++struct pw_global_events {
++#define PW_VERSION_GLOBAL_EVENTS 0
++ uint32_t version;
++
++ /** The global is destroyed */
++ void (*destroy) (void *data);
++ /** The global is freed */
++ void (*free) (void *data);
++ /** The permissions changed for a client */
++ void (*permissions_changed) (void *data,
++ struct pw_impl_client *client,
++ uint32_t old_permissions,
++ uint32_t new_permissions);
++};
++
++/** Create a new global object */
++struct pw_global *
++pw_global_new(struct pw_context *context, /**< the context */
++ const char *type, /**< the interface type of the global */
++ uint32_t version, /**< the interface version of the global */
++ struct pw_properties *properties, /**< extra properties */
++ pw_global_bind_func_t func, /**< function to bind */
++ void *object /**< global object */);
++
++/** Register a global object to the context registry */
++int pw_global_register(struct pw_global *global);
++
++/** Add an event listener on the global */
++void pw_global_add_listener(struct pw_global *global,
++ struct spa_hook *listener,
++ const struct pw_global_events *events,
++ void *data);
++
++/** Get the permissions of the global for a given client */
++uint32_t pw_global_get_permissions(struct pw_global *global, struct pw_impl_client *client);
++
++/** Get the context object of this global */
++struct pw_context *pw_global_get_context(struct pw_global *global);
++
++/** Get the global type */
++const char *pw_global_get_type(struct pw_global *global);
++
++/** Check a global type */
++bool pw_global_is_type(struct pw_global *global, const char *type);
++
++/** Get the global version */
++uint32_t pw_global_get_version(struct pw_global *global);
++
++/** Get the global properties */
++const struct pw_properties *pw_global_get_properties(struct pw_global *global);
++
++/** Update the global properties, must be done when unregistered */
++int pw_global_update_keys(struct pw_global *global,
++ const struct spa_dict *dict, const char *keys[]);
++
++/** Get the object associated with the global. This depends on the type of the
++ * global */
++void *pw_global_get_object(struct pw_global *global);
++
++/** Get the unique id of the global */
++uint32_t pw_global_get_id(struct pw_global *global);
++
++/** Add a resource to a global */
++int pw_global_add_resource(struct pw_global *global, struct pw_resource *resource);
++
++/** Iterate all resources added to the global The callback should return
++ * 0 to fetch the next item, any other value stops the iteration and returns
++ * the value. When all callbacks return 0, this function returns 0 when all
++ * items are iterated. */
++int pw_global_for_each_resource(struct pw_global *global,
++ int (*callback) (void *data, struct pw_resource *resource),
++ void *data);
++
++/** Let a client bind to a global */
++int pw_global_bind(struct pw_global *global,
++ struct pw_impl_client *client,
++ uint32_t permissions,
++ uint32_t version,
++ uint32_t id);
++
++int pw_global_update_permissions(struct pw_global *global, struct pw_impl_client *client,
++ uint32_t old_permissions, uint32_t new_permissions);
++
++/** Destroy a global */
++void pw_global_destroy(struct pw_global *global);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_GLOBAL_H */
+diff --git a/third_party/pipewire/pipewire/impl-client.h b/third_party/pipewire/pipewire/impl-client.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/impl-client.h
+@@ -0,0 +1,174 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_IMPL_CLIENT_H
++#define PIPEWIRE_IMPL_CLIENT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/hook.h>
++
++/** \class pw_impl_client
++ *
++ * \brief PipeWire client object class.
++ *
++ * The client object represents a client connection with the PipeWire
++ * server.
++ *
++ * Each client has its own list of resources it is bound to along with
++ * a mapping between the client types and server types.
++ */
++struct pw_impl_client;
++
++#include <pipewire/context.h>
++#include <pipewire/global.h>
++#include <pipewire/properties.h>
++#include <pipewire/resource.h>
++#include <pipewire/permission.h>
++
++/** \page page_client Client
++ *
++ * \section sec_page_client_overview Overview
++ *
++ * The \ref pw_impl_client object is created by a protocol implementation when
++ * a new client connects.
++ *
++ * The client is used to keep track of all resources belonging to one
++ * connection with the PipeWire server.
++ *
++ * \section sec_page_client_credentials Credentials
++ *
++ * The client object will have its credentials filled in by the protocol.
++ * This information is used to check if a resource or action is available
++ * for this client. See also \ref page_access
++ *
++ * \section sec_page_client_types Types
++ *
++ * The client and server maintain a mapping between the client and server
++ * types. All type ids that are in messages exchanged between the client
++ * and server will automatically be remapped. See also \ref page_types.
++ *
++ * \section sec_page_client_resources Resources
++ *
++ * When a client binds to context global object, a resource is made for this
++ * binding and a unique id is assigned to the resources. The client and
++ * server will use this id as the destination when exchanging messages.
++ * See also \ref page_resource
++ */
++
++/** The events that a client can emit */
++struct pw_impl_client_events {
++#define PW_VERSION_IMPL_CLIENT_EVENTS 0
++ uint32_t version;
++
++ /** emitted when the client is destroyed */
++ void (*destroy) (void *data);
++
++ /** emitted right before the client is freed */
++ void (*free) (void *data);
++
++ /** the client is initialized */
++ void (*initialized) (void *data);
++
++ /** emitted when the client info changed */
++ void (*info_changed) (void *data, const struct pw_client_info *info);
++
++ /** emitted when a new resource is added for client */
++ void (*resource_added) (void *data, struct pw_resource *resource);
++
++ /** emitted when a resource is removed */
++ void (*resource_removed) (void *data, struct pw_resource *resource);
++
++ /** emitted when the client becomes busy processing an asynchronous
++ * message. In the busy state no messages should be processed.
++ * Processing should resume when the client becomes not busy */
++ void (*busy_changed) (void *data, bool busy);
++};
++
++/** Create a new client. This is mainly used by protocols. */
++struct pw_impl_client *
++pw_context_create_client(struct pw_impl_core *core, /**< the core object */
++ struct pw_protocol *prototol, /**< the client protocol */
++ struct pw_properties *properties, /**< client properties */
++ size_t user_data_size /**< extra user data size */);
++
++/** Destroy a previously created client */
++void pw_impl_client_destroy(struct pw_impl_client *client);
++
++/** Finish configuration and register a client */
++int pw_impl_client_register(struct pw_impl_client *client, /**< the client to register */
++ struct pw_properties *properties/**< extra properties */);
++
++/** Get the client user data */
++void *pw_impl_client_get_user_data(struct pw_impl_client *client);
++
++/** Get the client information */
++const struct pw_client_info *pw_impl_client_get_info(struct pw_impl_client *client);
++
++/** Update the client properties */
++int pw_impl_client_update_properties(struct pw_impl_client *client, const struct spa_dict *dict);
++
++/** Update the client permissions */
++int pw_impl_client_update_permissions(struct pw_impl_client *client, uint32_t n_permissions,
++ const struct pw_permission *permissions);
++
++/** check if a client has permissions for global_id, Since 0.3.9 */
++int pw_impl_client_check_permissions(struct pw_impl_client *client,
++ uint32_t global_id, uint32_t permissions);
++
++/** Get the client properties */
++const struct pw_properties *pw_impl_client_get_properties(struct pw_impl_client *client);
++
++/** Get the context used to create this client */
++struct pw_context *pw_impl_client_get_context(struct pw_impl_client *client);
++/** Get the protocol used to create this client */
++struct pw_protocol *pw_impl_client_get_protocol(struct pw_impl_client *client);
++
++/** Get the client core resource */
++struct pw_resource *pw_impl_client_get_core_resource(struct pw_impl_client *client);
++
++/** Get a resource with the given id */
++struct pw_resource *pw_impl_client_find_resource(struct pw_impl_client *client, uint32_t id);
++
++/** Get the global associated with this client */
++struct pw_global *pw_impl_client_get_global(struct pw_impl_client *client);
++
++/** listen to events from this client */
++void pw_impl_client_add_listener(struct pw_impl_client *client,
++ struct spa_hook *listener,
++ const struct pw_impl_client_events *events,
++ void *data);
++
++
++/** Mark the client busy. This can be used when an asynchronous operation is
++ * started and no further processing is allowed to happen for the client */
++void pw_impl_client_set_busy(struct pw_impl_client *client, bool busy);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_IMPL_CLIENT_H */
+diff --git a/third_party/pipewire/pipewire/impl-core.h b/third_party/pipewire/pipewire/impl-core.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/impl-core.h
+@@ -0,0 +1,94 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_IMPL_CORE_H
++#define PIPEWIRE_IMPL_CORE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** \class pw_impl_core
++ *
++ * \brief PipeWire core interface.
++ *
++ * The core is used to make objects on demand.
++ */
++struct pw_impl_core;
++
++#include <pipewire/context.h>
++#include <pipewire/global.h>
++#include <pipewire/properties.h>
++#include <pipewire/resource.h>
++
++/** Factory events, listen to them with \ref pw_impl_core_add_listener */
++struct pw_impl_core_events {
++#define PW_VERSION_IMPL_CORE_EVENTS 0
++ uint32_t version;
++
++ /** the core is destroyed */
++ void (*destroy) (void *data);
++ /** the core is freed */
++ void (*free) (void *data);
++ /** the core is initialized */
++ void (*initialized) (void *data);
++};
++
++struct pw_impl_core *pw_context_create_core(struct pw_context *context,
++ struct pw_properties *properties,
++ size_t user_data_size);
++
++/* get the default core in a context */
++struct pw_impl_core *pw_context_get_default_core(struct pw_context *context);
++
++/** Get the core properties */
++const struct pw_properties *pw_impl_core_get_properties(struct pw_impl_core *core);
++
++/** Get the core information */
++const struct pw_core_info *pw_impl_core_get_info(struct pw_impl_core *core);
++
++/** Update the core properties */
++int pw_impl_core_update_properties(struct pw_impl_core *core, const struct spa_dict *dict);
++
++int pw_impl_core_register(struct pw_impl_core *core,
++ struct pw_properties *properties);
++
++void pw_impl_core_destroy(struct pw_impl_core *core);
++
++void *pw_impl_core_get_user_data(struct pw_impl_core *core);
++
++/** Get the global of this core */
++struct pw_global *pw_impl_core_get_global(struct pw_impl_core *core);
++
++/** Add an event listener */
++void pw_impl_core_add_listener(struct pw_impl_core *core,
++ struct spa_hook *listener,
++ const struct pw_impl_core_events *events,
++ void *data);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_IMPL_CORE_H */
+diff --git a/third_party/pipewire/pipewire/impl-device.h b/third_party/pipewire/pipewire/impl-device.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/impl-device.h
+@@ -0,0 +1,109 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_IMPL_DEVICE_H
++#define PIPEWIRE_IMPL_DEVICE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** \class pw_impl_device
++ *
++ * \brief PipeWire device interface.
++ *
++ * The device is an object that manages nodes. It typically
++ * corresponds to a physical hardware device but it does not
++ * have to be.
++ *
++ * The purpose of the device is to provide an interface to
++ * dynamically create/remove/configure the nodes it manages.
++ */
++struct pw_impl_device;
++
++#include <spa/monitor/device.h>
++
++#include <pipewire/context.h>
++#include <pipewire/global.h>
++#include <pipewire/properties.h>
++#include <pipewire/resource.h>
++
++/** Device events, listen to them with \ref pw_impl_device_add_listener */
++struct pw_impl_device_events {
++#define PW_VERSION_IMPL_DEVICE_EVENTS 0
++ uint32_t version;
++
++ /** the device is destroyed */
++ void (*destroy) (void *data);
++ /** the device is freed */
++ void (*free) (void *data);
++ /** the device is initialized */
++ void (*initialized) (void *data);
++
++ /** the device info changed */
++ void (*info_changed) (void *data, const struct pw_device_info *info);
++};
++
++struct pw_impl_device *pw_context_create_device(struct pw_context *context,
++ struct pw_properties *properties,
++ size_t user_data_size);
++
++int pw_impl_device_register(struct pw_impl_device *device,
++ struct pw_properties *properties);
++
++void pw_impl_device_destroy(struct pw_impl_device *device);
++
++void *pw_impl_device_get_user_data(struct pw_impl_device *device);
++
++/** Set the device implementation */
++int pw_impl_device_set_implementation(struct pw_impl_device *device, struct spa_device *spa_device);
++/** Get the device implementation */
++struct spa_device *pw_impl_device_get_implementation(struct pw_impl_device *device);
++
++/** Get the global of this device */
++struct pw_global *pw_impl_device_get_global(struct pw_impl_device *device);
++
++/** Add an event listener */
++void pw_impl_device_add_listener(struct pw_impl_device *device,
++ struct spa_hook *listener,
++ const struct pw_impl_device_events *events,
++ void *data);
++
++int pw_impl_device_update_properties(struct pw_impl_device *device, const struct spa_dict *dict);
++
++const struct pw_properties *pw_impl_device_get_properties(struct pw_impl_device *device);
++
++int pw_impl_device_for_each_param(struct pw_impl_device *device,
++ int seq, uint32_t param_id,
++ uint32_t index, uint32_t max,
++ const struct spa_pod *filter,
++ int (*callback) (void *data, int seq,
++ uint32_t id, uint32_t index, uint32_t next,
++ struct spa_pod *param),
++ void *data);
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_IMPL_DEVICE_H */
+diff --git a/third_party/pipewire/pipewire/impl-factory.h b/third_party/pipewire/pipewire/impl-factory.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/impl-factory.h
+@@ -0,0 +1,124 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_IMPL_FACTORY_H
++#define PIPEWIRE_IMPL_FACTORY_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** \class pw_impl_factory
++ *
++ * \brief PipeWire factory interface.
++ *
++ * The factory is used to make objects on demand.
++ */
++struct pw_impl_factory;
++
++#include <pipewire/context.h>
++#include <pipewire/impl-client.h>
++#include <pipewire/global.h>
++#include <pipewire/properties.h>
++#include <pipewire/resource.h>
++
++/** Factory events, listen to them with \ref pw_impl_factory_add_listener */
++struct pw_impl_factory_events {
++#define PW_VERSION_IMPL_FACTORY_EVENTS 0
++ uint32_t version;
++
++ /** the factory is destroyed */
++ void (*destroy) (void *data);
++ /** the factory is freed */
++ void (*free) (void *data);
++ /** the factory is initialized */
++ void (*initialized) (void *data);
++};
++
++struct pw_impl_factory_implementation {
++#define PW_VERSION_IMPL_FACTORY_IMPLEMENTATION 0
++ uint32_t version;
++
++ /** The function to create an object from this factory */
++ void *(*create_object) (void *data,
++ struct pw_resource *resource,
++ const char *type,
++ uint32_t version,
++ struct pw_properties *properties,
++ uint32_t new_id);
++};
++
++struct pw_impl_factory *pw_context_create_factory(struct pw_context *context,
++ const char *name,
++ const char *type,
++ uint32_t version,
++ struct pw_properties *properties,
++ size_t user_data_size);
++
++/** Get the factory properties */
++const struct pw_properties *pw_impl_factory_get_properties(struct pw_impl_factory *factory);
++
++/** Get the factory info */
++const struct pw_factory_info *pw_impl_factory_get_info(struct pw_impl_factory *factory);
++
++/** Update the factory properties */
++int pw_impl_factory_update_properties(struct pw_impl_factory *factory, const struct spa_dict *dict);
++
++int pw_impl_factory_register(struct pw_impl_factory *factory,
++ struct pw_properties *properties);
++
++void pw_impl_factory_destroy(struct pw_impl_factory *factory);
++
++void *pw_impl_factory_get_user_data(struct pw_impl_factory *factory);
++
++/** Get the global of this factory */
++struct pw_global *pw_impl_factory_get_global(struct pw_impl_factory *factory);
++
++/** Add an event listener */
++void pw_impl_factory_add_listener(struct pw_impl_factory *factory,
++ struct spa_hook *listener,
++ const struct pw_impl_factory_events *events,
++ void *data);
++
++void pw_impl_factory_set_implementation(struct pw_impl_factory *factory,
++ const struct pw_impl_factory_implementation *implementation,
++ void *data);
++
++void *pw_impl_factory_create_object(struct pw_impl_factory *factory,
++ struct pw_resource *resource,
++ const char *type,
++ uint32_t version,
++ struct pw_properties *properties,
++ uint32_t new_id);
++
++/** Find a factory by name */
++struct pw_impl_factory *
++pw_context_find_factory(struct pw_context *context /**< the context */,
++ const char *name /**< the factory name */);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_IMPL_FACTORY_H */
+diff --git a/third_party/pipewire/pipewire/impl-link.h b/third_party/pipewire/pipewire/impl-link.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/impl-link.h
+@@ -0,0 +1,128 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_IMPL_LINK_H
++#define PIPEWIRE_IMPL_LINK_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** \class pw_impl_link
++ *
++ * PipeWire link object.
++ */
++struct pw_impl_link;
++struct pw_impl_port;
++
++#include <pipewire/impl.h>
++
++/** \page page_link Link
++ *
++ * \section page_link_overview Overview
++ *
++ * A link is the connection between 2 nodes (\ref page_node). Nodes are
++ * linked together on ports.
++ *
++ * The link is responsible for negotiating the format and buffers for
++ * the nodes.
++ */
++
++/** link events added with \ref pw_impl_link_add_listener */
++struct pw_impl_link_events {
++#define PW_VERSION_IMPL_LINK_EVENTS 0
++ uint32_t version;
++
++ /** A link is destroyed */
++ void (*destroy) (void *data);
++
++ /** A link is freed */
++ void (*free) (void *data);
++
++ /** a Link is initialized */
++ void (*initialized) (void *data);
++
++ /** The info changed on a link */
++ void (*info_changed) (void *data, const struct pw_link_info *info);
++
++ /** The link state changed, \a error is only valid when the state is
++ * in error. */
++ void (*state_changed) (void *data, enum pw_link_state old,
++ enum pw_link_state state, const char *error);
++
++ /** A port is unlinked */
++ void (*port_unlinked) (void *data, struct pw_impl_port *port);
++};
++
++
++/** Make a new link between two ports \memberof pw_impl_link
++ * \return a newly allocated link */
++struct pw_impl_link *
++pw_context_create_link(struct pw_context *context, /**< the context object */
++ struct pw_impl_port *output, /**< an output port */
++ struct pw_impl_port *input, /**< an input port */
++ struct spa_pod *format_filter, /**< an optional format filter */
++ struct pw_properties *properties /**< extra properties */,
++ size_t user_data_size /**< extra user data size */);
++
++/** Destroy a link \memberof pw_impl_link */
++void pw_impl_link_destroy(struct pw_impl_link *link);
++
++/** Add an event listener to \a link */
++void pw_impl_link_add_listener(struct pw_impl_link *link,
++ struct spa_hook *listener,
++ const struct pw_impl_link_events *events,
++ void *data);
++
++/** Finish link configuration and register */
++int pw_impl_link_register(struct pw_impl_link *link, /**< the link to register */
++ struct pw_properties *properties /**< extra properties */);
++
++/** Get the context of a link */
++struct pw_context *pw_impl_link_get_context(struct pw_impl_link *link);
++
++/** Get the user_data of a link, the size of the memory is given when
++ * constructing the link */
++void *pw_impl_link_get_user_data(struct pw_impl_link *link);
++
++/** Get the link info */
++const struct pw_link_info *pw_impl_link_get_info(struct pw_impl_link *link);
++
++/** Get the global of the link */
++struct pw_global *pw_impl_link_get_global(struct pw_impl_link *link);
++
++/** Get the output port of the link */
++struct pw_impl_port *pw_impl_link_get_output(struct pw_impl_link *link);
++
++/** Get the input port of the link */
++struct pw_impl_port *pw_impl_link_get_input(struct pw_impl_link *link);
++
++/** Find the link between 2 ports \memberof pw_impl_link */
++struct pw_impl_link *pw_impl_link_find(struct pw_impl_port *output, struct pw_impl_port *input);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_IMPL_LINK_H */
+diff --git a/third_party/pipewire/pipewire/impl-module.h b/third_party/pipewire/pipewire/impl-module.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/impl-module.h
+@@ -0,0 +1,110 @@
++/* PipeWire
++ * Copyright © 2016 Axis Communications <dev-gstreamer@axis.com>
++ * @author Linus Svensson <linus.svensson@axis.com>
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_IMPL_MODULE_H
++#define PIPEWIRE_IMPL_MODULE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/hook.h>
++
++#include <pipewire/context.h>
++
++#define PIPEWIRE_SYMBOL_MODULE_INIT "pipewire__module_init"
++#define PIPEWIRE_MODULE_PREFIX "libpipewire-"
++
++/** \class pw_impl_module
++ *
++ * A dynamically loadable module
++ */
++struct pw_impl_module;
++
++/** Module init function signature
++ *
++ * \param module A \ref pw_impl_module
++ * \param args Arguments to the module
++ * \return 0 on success, < 0 otherwise with an errno style error
++ *
++ * A module should provide an init function with this signature. This function
++ * will be called when a module is loaded.
++ *
++ * \memberof pw_impl_module
++ */
++typedef int (*pw_impl_module_init_func_t) (struct pw_impl_module *module, const char *args);
++
++/** Module events added with \ref pw_impl_module_add_listener */
++struct pw_impl_module_events {
++#define PW_VERSION_IMPL_MODULE_EVENTS 0
++ uint32_t version;
++
++ /** The module is destroyed */
++ void (*destroy) (void *data);
++ /** The module is freed */
++ void (*free) (void *data);
++ /** The module is initialized */
++ void (*initialized) (void *data);
++
++ /** The module is registered. This is a good time to register
++ * objectes created from the module. */
++ void (*registered) (void *data);
++};
++
++struct pw_impl_module *
++pw_context_load_module(struct pw_context *context,
++ const char *name, /**< name of the module */
++ const char *args /**< arguments of the module */,
++ struct pw_properties *properties /**< extra global properties */);
++
++/** Get the context of a module */
++struct pw_context * pw_impl_module_get_context(struct pw_impl_module *module);
++
++/** Get the global of a module */
++struct pw_global * pw_impl_module_get_global(struct pw_impl_module *module);
++
++/** Get the node properties */
++const struct pw_properties *pw_impl_module_get_properties(struct pw_impl_module *module);
++
++/** Update the module properties */
++int pw_impl_module_update_properties(struct pw_impl_module *module, const struct spa_dict *dict);
++
++/** Get the module info */
++const struct pw_module_info *pw_impl_module_get_info(struct pw_impl_module *module);
++
++/** Add an event listener to a module */
++void pw_impl_module_add_listener(struct pw_impl_module *module,
++ struct spa_hook *listener,
++ const struct pw_impl_module_events *events,
++ void *data);
++
++/** Destroy a module */
++void pw_impl_module_destroy(struct pw_impl_module *module);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_IMPL_MODULE_H */
+diff --git a/third_party/pipewire/pipewire/impl-node.h b/third_party/pipewire/pipewire/impl-node.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/impl-node.h
+@@ -0,0 +1,182 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_IMPL_NODE_H
++#define PIPEWIRE_IMPL_NODE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** \page page_node Node
++ *
++ * \section page_node_overview Overview
++ *
++ * The node object processes data. The node has a list of
++ * input and output ports (\ref page_port) on which it
++ * will receive and send out buffers respectively.
++ */
++/** \class pw_impl_node
++ *
++ * PipeWire node class.
++ */
++struct pw_impl_node;
++struct pw_impl_port;
++
++#include <spa/node/node.h>
++#include <spa/node/event.h>
++
++#include <pipewire/impl.h>
++
++/** Node events, listen to them with \ref pw_impl_node_add_listener */
++struct pw_impl_node_events {
++#define PW_VERSION_IMPL_NODE_EVENTS 0
++ uint32_t version;
++
++ /** the node is destroyed */
++ void (*destroy) (void *data);
++ /** the node is about to be freed */
++ void (*free) (void *data);
++ /** the node is initialized */
++ void (*initialized) (void *data);
++
++ /** a port is being initialized on the node */
++ void (*port_init) (void *data, struct pw_impl_port *port);
++ /** a port was added */
++ void (*port_added) (void *data, struct pw_impl_port *port);
++ /** a port was removed */
++ void (*port_removed) (void *data, struct pw_impl_port *port);
++
++ /** the node info changed */
++ void (*info_changed) (void *data, const struct pw_node_info *info);
++ /** a port on the node changed info */
++ void (*port_info_changed) (void *data, struct pw_impl_port *port,
++ const struct pw_port_info *info);
++ /** the node active state changed */
++ void (*active_changed) (void *data, bool active);
++
++ /** a new state is requested on the node */
++ void (*state_request) (void *data, enum pw_node_state state);
++ /** the state of the node changed */
++ void (*state_changed) (void *data, enum pw_node_state old,
++ enum pw_node_state state, const char *error);
++
++ /** a result was received */
++ void (*result) (void *data, int seq, int res, uint32_t type, const void *result);
++
++ /** an event is emitted */
++ void (*event) (void *data, const struct spa_event *event);
++
++ /** the driver of the node changed */
++ void (*driver_changed) (void *data, struct pw_impl_node *old, struct pw_impl_node *driver);
++
++ /** a peer was added */
++ void (*peer_added) (void *data, struct pw_impl_node *peer);
++ /** a peer was removed */
++ void (*peer_removed) (void *data, struct pw_impl_node *peer);
++};
++
++/** Create a new node \memberof pw_impl_node */
++struct pw_impl_node *
++pw_context_create_node(struct pw_context *context, /**< the context */
++ struct pw_properties *properties, /**< extra properties */
++ size_t user_data_size /**< user data size */);
++
++/** Complete initialization of the node and register */
++int pw_impl_node_register(struct pw_impl_node *node, /**< node to register */
++ struct pw_properties *properties /**< extra properties */);
++
++/** Destroy a node */
++void pw_impl_node_destroy(struct pw_impl_node *node);
++
++/** Get the node info */
++const struct pw_node_info *pw_impl_node_get_info(struct pw_impl_node *node);
++
++/** Get node user_data. The size of the memory was given in \ref pw_context_create_node */
++void * pw_impl_node_get_user_data(struct pw_impl_node *node);
++
++/** Get the context of this node */
++struct pw_context *pw_impl_node_get_context(struct pw_impl_node *node);
++
++/** Get the global of this node */
++struct pw_global *pw_impl_node_get_global(struct pw_impl_node *node);
++
++/** Get the node properties */
++const struct pw_properties *pw_impl_node_get_properties(struct pw_impl_node *node);
++
++/** Update the node properties */
++int pw_impl_node_update_properties(struct pw_impl_node *node, const struct spa_dict *dict);
++
++/** Set the node implementation */
++int pw_impl_node_set_implementation(struct pw_impl_node *node, struct spa_node *spa_node);
++
++/** Get the node implementation */
++struct spa_node *pw_impl_node_get_implementation(struct pw_impl_node *node);
++
++/** Add an event listener */
++void pw_impl_node_add_listener(struct pw_impl_node *node,
++ struct spa_hook *listener,
++ const struct pw_impl_node_events *events,
++ void *data);
++
++/** Iterate the ports in the given direction. The callback should return
++ * 0 to fetch the next item, any other value stops the iteration and returns
++ * the value. When all callbacks return 0, this function returns 0 when all
++ * items are iterated. */
++int pw_impl_node_for_each_port(struct pw_impl_node *node,
++ enum pw_direction direction,
++ int (*callback) (void *data, struct pw_impl_port *port),
++ void *data);
++
++int pw_impl_node_for_each_param(struct pw_impl_node *node,
++ int seq, uint32_t param_id,
++ uint32_t index, uint32_t max,
++ const struct spa_pod *filter,
++ int (*callback) (void *data, int seq,
++ uint32_t id, uint32_t index, uint32_t next,
++ struct spa_pod *param),
++ void *data);
++
++/** Find the port with direction and port_id or NULL when not found. Passing
++ * PW_ID_ANY for port_id will return any port, preferably an unlinked one. */
++struct pw_impl_port *
++pw_impl_node_find_port(struct pw_impl_node *node, enum pw_direction direction, uint32_t port_id);
++
++/** Get a free unused port_id from the node */
++uint32_t pw_impl_node_get_free_port_id(struct pw_impl_node *node, enum pw_direction direction);
++
++int pw_impl_node_initialized(struct pw_impl_node *node);
++
++/** Set a node active. This will start negotiation with all linked active
++ * nodes and start data transport */
++int pw_impl_node_set_active(struct pw_impl_node *node, bool active);
++
++/** Check if a node is active */
++bool pw_impl_node_is_active(struct pw_impl_node *node);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_IMPL_NODE_H */
+diff --git a/third_party/pipewire/pipewire/impl-port.h b/third_party/pipewire/pipewire/impl-port.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/impl-port.h
+@@ -0,0 +1,138 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_IMPL_PORT_H
++#define PIPEWIRE_IMPL_PORT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/hook.h>
++
++/** \page page_port Port
++ *
++ * \section page_node_overview Overview
++ *
++ * A port can be used to link two nodes.
++ */
++/** \class pw_impl_port
++ *
++ * The port object
++ */
++struct pw_impl_port;
++struct pw_impl_link;
++struct pw_control;
++
++#include <pipewire/impl.h>
++
++enum pw_impl_port_state {
++ PW_IMPL_PORT_STATE_ERROR = -1, /**< the port is in error */
++ PW_IMPL_PORT_STATE_INIT = 0, /**< the port is being created */
++ PW_IMPL_PORT_STATE_CONFIGURE = 1, /**< the port is ready for format negotiation */
++ PW_IMPL_PORT_STATE_READY = 2, /**< the port is ready for buffer allocation */
++ PW_IMPL_PORT_STATE_PAUSED = 3, /**< the port is paused */
++};
++
++/** Port events, use \ref pw_impl_port_add_listener */
++struct pw_impl_port_events {
++#define PW_VERSION_IMPL_PORT_EVENTS 1
++ uint32_t version;
++
++ /** The port is destroyed */
++ void (*destroy) (void *data);
++
++ /** The port is freed */
++ void (*free) (void *data);
++
++ /** The port is initialized */
++ void (*initialized) (void *data);
++
++ /** the port info changed */
++ void (*info_changed) (void *data, const struct pw_port_info *info);
++
++ /** a new link is added on this port */
++ void (*link_added) (void *data, struct pw_impl_link *link);
++
++ /** a link is removed from this port */
++ void (*link_removed) (void *data, struct pw_impl_link *link);
++
++ /** the state of the port changed */
++ void (*state_changed) (void *data, enum pw_impl_port_state old,
++ enum pw_impl_port_state state, const char *error);
++
++ /** a control was added to the port */
++ void (*control_added) (void *data, struct pw_control *control);
++
++ /** a control was removed from the port */
++ void (*control_removed) (void *data, struct pw_control *control);
++
++ /** a parameter changed, since version 1 */
++ void (*param_changed) (void *data, uint32_t id);
++};
++
++/** Create a new port \memberof pw_impl_port
++ * \return a newly allocated port */
++struct pw_impl_port *
++pw_context_create_port(struct pw_context *context,
++ enum pw_direction direction,
++ uint32_t port_id,
++ const struct spa_port_info *info,
++ size_t user_data_size);
++
++/** Get the port direction */
++enum pw_direction pw_impl_port_get_direction(struct pw_impl_port *port);
++
++/** Get the port properties */
++const struct pw_properties *pw_impl_port_get_properties(struct pw_impl_port *port);
++
++/** Update the port properties */
++int pw_impl_port_update_properties(struct pw_impl_port *port, const struct spa_dict *dict);
++
++/** Get the port info */
++const struct pw_port_info *pw_impl_port_get_info(struct pw_impl_port *port);
++
++/** Get the port id */
++uint32_t pw_impl_port_get_id(struct pw_impl_port *port);
++
++/** Get the port parent node or NULL when not yet set */
++struct pw_impl_node *pw_impl_port_get_node(struct pw_impl_port *port);
++
++/** check is a port has links, return 0 if not, 1 if it is linked */
++int pw_impl_port_is_linked(struct pw_impl_port *port);
++
++/** Add a port to a node \memberof pw_impl_port */
++int pw_impl_port_add(struct pw_impl_port *port, struct pw_impl_node *node);
++
++/** Add an event listener on the port */
++void pw_impl_port_add_listener(struct pw_impl_port *port,
++ struct spa_hook *listener,
++ const struct pw_impl_port_events *events,
++ void *data);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_IMPL_PORT_H */
+diff --git a/third_party/pipewire/pipewire/impl.h b/third_party/pipewire/pipewire/impl.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/impl.h
+@@ -0,0 +1,57 @@
++/* PipeWire
++ *
++ * Copyright © 2019 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_IMPL_H
++#define PIPEWIRE_IMPL_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++struct pw_impl_client;
++struct pw_impl_module;
++struct pw_global;
++struct pw_node;
++struct pw_impl_port;
++struct pw_resource;
++
++#include <pipewire/pipewire.h>
++#include <pipewire/control.h>
++#include <pipewire/impl-core.h>
++#include <pipewire/impl-client.h>
++#include <pipewire/impl-device.h>
++#include <pipewire/impl-factory.h>
++#include <pipewire/global.h>
++#include <pipewire/impl-link.h>
++#include <pipewire/impl-module.h>
++#include <pipewire/impl-node.h>
++#include <pipewire/impl-port.h>
++#include <pipewire/resource.h>
++#include <pipewire/work-queue.h>
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_IMPL_H */
+diff --git a/third_party/pipewire/pipewire/keys.h b/third_party/pipewire/pipewire/keys.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/keys.h
+@@ -0,0 +1,278 @@
++/* PipeWire
++ *
++ * Copyright © 2019 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_KEYS_H
++#define PIPEWIRE_KEYS_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/**
++ * A collection of keys that are used to add extra information on objects.
++ *
++ * Keys that start with "pipewire." are in general set-once and then
++ * read-only. They are usually used for security sensitive information that
++ * needs to be fixed.
++ *
++ * Properties from other objects can also appear. This usually suggests some
++ * sort of parent/child or owner/owned relationship.
++ */
++#define PW_KEY_PROTOCOL "pipewire.protocol" /**< protocol used for connection */
++#define PW_KEY_ACCESS "pipewire.access" /**< how the client access is controlled */
++
++/** Various keys related to the identity of a client process and its security.
++ * Must be obtained from trusted sources by the protocol and placed as
++ * read-only properties. */
++#define PW_KEY_SEC_PID "pipewire.sec.pid" /**< Client pid, set by protocol */
++#define PW_KEY_SEC_UID "pipewire.sec.uid" /**< Client uid, set by protocol*/
++#define PW_KEY_SEC_GID "pipewire.sec.gid" /**< client gid, set by protocol*/
++#define PW_KEY_SEC_LABEL "pipewire.sec.label" /**< client security label, set by protocol*/
++
++#define PW_KEY_LIBRARY_NAME_SYSTEM "library.name.system" /**< name of the system library to use */
++#define PW_KEY_LIBRARY_NAME_LOOP "library.name.loop" /**< name of the loop library to use */
++#define PW_KEY_LIBRARY_NAME_DBUS "library.name.dbus" /**< name of the dbus library to use */
++
++#define PW_KEY_OBJECT_PATH "object.path" /**< unique path to construct the object */
++#define PW_KEY_OBJECT_ID "object.id" /**< a global object id */
++
++/* context */
++#define PW_KEY_CONTEXT_PROFILE_MODULES "context.profile.modules" /**< a context profile for modules */
++#define PW_KEY_USER_NAME "context.user-name" /**< The user name that runs pipewire */
++#define PW_KEY_HOST_NAME "context.host-name" /**< The host name of the machine */
++
++/* core */
++#define PW_KEY_CORE_NAME "core.name" /**< The name of the core. Default is
++ * pipewire-<user-name>-<pid>, overwritten
++ * by env(PIPEWIRE_CORE) */
++#define PW_KEY_CORE_VERSION "core.version" /**< The version of the core. */
++#define PW_KEY_CORE_DAEMON "core.daemon" /**< If the core is listening for connections. */
++
++#define PW_KEY_CORE_ID "core.id" /**< the core id */
++#define PW_KEY_CORE_MONITORS "core.monitors" /**< the apis monitored by core. */
++
++/* cpu */
++#define PW_KEY_CPU_MAX_ALIGN "cpu.max-align" /**< maximum alignment needed to support
++ * all CPU optimizations */
++#define PW_KEY_CPU_CORES "cpu.cores" /**< number of cores */
++
++/* priorities */
++#define PW_KEY_PRIORITY_SESSION "priority.session" /**< priority in session manager */
++#define PW_KEY_PRIORITY_DRIVER "priority.driver" /**< priority to be a driver */
++
++/* remote keys */
++#define PW_KEY_REMOTE_NAME "remote.name" /**< The name of the remote to connect to,
++ * default pipewire-0, overwritten by
++ * env(PIPEWIRE_REMOTE) */
++#define PW_KEY_REMOTE_INTENTION "remote.intention" /**< The intention of the remote connection,
++ * "generic", "screencast" */
++
++/** application keys */
++#define PW_KEY_APP_NAME "application.name" /**< application name. Ex: "Totem Music Player" */
++#define PW_KEY_APP_ID "application.id" /**< a textual id for identifying an
++ * application logically. Ex: "org.gnome.Totem" */
++#define PW_KEY_APP_VERSION "application.version"
++#define PW_KEY_APP_ICON "application.icon" /**< aa base64 blob with PNG image data */
++#define PW_KEY_APP_ICON_NAME "application.icon-name" /**< an XDG icon name for the application.
++ * Ex: "totem" */
++#define PW_KEY_APP_LANGUAGE "application.language" /**< application language if applicable, in
++ * standard POSIX format. Ex: "en_GB" */
++
++#define PW_KEY_APP_PROCESS_ID "application.process.id" /**< process id (pid)*/
++#define PW_KEY_APP_PROCESS_BINARY "application.process.binary" /**< binary name */
++#define PW_KEY_APP_PROCESS_USER "application.process.user" /**< user name */
++#define PW_KEY_APP_PROCESS_HOST "application.process.host" /**< host name */
++#define PW_KEY_APP_PROCESS_MACHINE_ID "application.process.machine-id" /**< the D-Bus host id the
++ * application runs on */
++#define PW_KEY_APP_PROCESS_SESSION_ID "application.process.session-id" /**< login session of the
++ * application, on Unix the
++ * value of $XDG_SESSION_ID. */
++/** window system */
++#define PW_KEY_WINDOW_X11_DISPLAY "window.x11.display" /**< the X11 display string. Ex. ":0.0" */
++
++/** Client properties */
++#define PW_KEY_CLIENT_ID "client.id" /**< a client id */
++#define PW_KEY_CLIENT_NAME "client.name" /**< the client name */
++#define PW_KEY_CLIENT_API "client.api" /**< the client api used to access
++ * PipeWire */
++
++/** Node keys */
++#define PW_KEY_NODE_ID "node.id" /**< node id */
++#define PW_KEY_NODE_NAME "node.name" /**< node name */
++#define PW_KEY_NODE_NICK "node.nick" /**< short node name */
++#define PW_KEY_NODE_DESCRIPTION "node.description" /**< localized human readable node one-line
++ * description. Ex. "Foobar USB Headset" */
++#define PW_KEY_NODE_PLUGGED "node.plugged" /**< when the node was created. As a uint64 in
++ * nanoseconds. */
++
++#define PW_KEY_NODE_SESSION "node.session" /**< the session id this node is part of */
++#define PW_KEY_NODE_GROUP "node.group" /**< the group id this node is part of. Nodes
++ * in the same group are always scheduled
++ * with the same driver. */
++#define PW_KEY_NODE_EXCLUSIVE "node.exclusive" /**< node wants exclusive access to resources */
++#define PW_KEY_NODE_AUTOCONNECT "node.autoconnect" /**< node wants to be automatically connected
++ * to a compatible node */
++#define PW_KEY_NODE_TARGET "node.target" /**< node wants to be connected to the target
++ * node/session */
++#define PW_KEY_NODE_LATENCY "node.latency" /**< the requested latency of the node as
++ * a fraction. Ex: 128/48000 */
++#define PW_KEY_NODE_DONT_RECONNECT "node.dont-reconnect" /**< don't reconnect this node */
++#define PW_KEY_NODE_ALWAYS_PROCESS "node.always-process" /**< process even when unlinked */
++#define PW_KEY_NODE_PAUSE_ON_IDLE "node.pause-on-idle" /**< pause the node when idle */
++#define PW_KEY_NODE_DRIVER "node.driver" /**< node can drive the graph */
++#define PW_KEY_NODE_STREAM "node.stream" /**< node is a stream, the server side should
++ * add a converter */
++/** Port keys */
++#define PW_KEY_PORT_ID "port.id" /**< port id */
++#define PW_KEY_PORT_NAME "port.name" /**< port name */
++#define PW_KEY_PORT_DIRECTION "port.direction" /**< the port direction, one of "in" or "out"
++ * or "control" and "notify" for control ports */
++#define PW_KEY_PORT_ALIAS "port.alias" /**< port alias */
++#define PW_KEY_PORT_PHYSICAL "port.physical" /**< if this is a physical port */
++#define PW_KEY_PORT_TERMINAL "port.terminal" /**< if this port consumes the data */
++#define PW_KEY_PORT_CONTROL "port.control" /**< if this port is a control port */
++#define PW_KEY_PORT_MONITOR "port.monitor" /**< if this port is a monitor port */
++
++/** link properties */
++#define PW_KEY_LINK_ID "link.id" /**< a link id */
++#define PW_KEY_LINK_INPUT_NODE "link.input.node" /**< input node id of a link */
++#define PW_KEY_LINK_INPUT_PORT "link.input.port" /**< input port id of a link */
++#define PW_KEY_LINK_OUTPUT_NODE "link.output.node" /**< output node id of a link */
++#define PW_KEY_LINK_OUTPUT_PORT "link.output.port" /**< output port id of a link */
++#define PW_KEY_LINK_PASSIVE "link.passive" /**< indicate that a link is passive and
++ * does not cause the graph to be
++ * runnable. */
++/** device properties */
++#define PW_KEY_DEVICE_ID "device.id" /**< device id */
++#define PW_KEY_DEVICE_NAME "device.name" /**< device name */
++#define PW_KEY_DEVICE_PLUGGED "device.plugged" /**< when the device was created. As a uint64 in
++ * nanoseconds. */
++#define PW_KEY_DEVICE_NICK "device.nick" /**< a short device nickname */
++#define PW_KEY_DEVICE_STRING "device.string" /**< device string in the underlying layer's
++ * format. Ex. "surround51:0" */
++#define PW_KEY_DEVICE_API "device.api" /**< API this device is accessed with.
++ * Ex. "alsa", "v4l2" */
++#define PW_KEY_DEVICE_DESCRIPTION "device.description" /**< localized human readable device one-line
++ * description. Ex. "Foobar USB Headset" */
++#define PW_KEY_DEVICE_BUS_PATH "device.bus-path" /**< bus path to the device in the OS'
++ * format. Ex. "pci-0000:00:14.0-usb-0:3.2:1.0" */
++#define PW_KEY_DEVICE_SERIAL "device.serial" /**< Serial number if applicable */
++#define PW_KEY_DEVICE_VENDOR_ID "device.vendor.id" /**< vendor ID if applicable */
++#define PW_KEY_DEVICE_VENDOR_NAME "device.vendor.name" /**< vendor name if applicable */
++#define PW_KEY_DEVICE_PRODUCT_ID "device.product.id" /**< product ID if applicable */
++#define PW_KEY_DEVICE_PRODUCT_NAME "device.product.name" /**< product name if applicable */
++#define PW_KEY_DEVICE_CLASS "device.class" /**< device class */
++#define PW_KEY_DEVICE_FORM_FACTOR "device.form-factor" /**< form factor if applicable. One of
++ * "internal", "speaker", "handset", "tv",
++ * "webcam", "microphone", "headset",
++ * "headphone", "hands-free", "car", "hifi",
++ * "computer", "portable" */
++#define PW_KEY_DEVICE_BUS "device.bus" /**< bus of the device if applicable. One of
++ * "isa", "pci", "usb", "firewire",
++ * "bluetooth" */
++#define PW_KEY_DEVICE_SUBSYSTEM "device.subsystem" /**< device subsystem */
++#define PW_KEY_DEVICE_ICON "device.icon" /**< icon for the device. A base64 blob
++ * containing PNG image data */
++#define PW_KEY_DEVICE_ICON_NAME "device.icon-name" /**< an XDG icon name for the device.
++ * Ex. "sound-card-speakers-usb" */
++#define PW_KEY_DEVICE_INTENDED_ROLES "device.intended-roles" /**< intended use. A space separated list of
++ * roles (see PW_KEY_MEDIA_ROLE) this device
++ * is particularly well suited for, due to
++ * latency, quality or form factor. */
++
++/** module properties */
++#define PW_KEY_MODULE_ID "module.id" /**< the module id */
++#define PW_KEY_MODULE_NAME "module.name" /**< the name of the module */
++#define PW_KEY_MODULE_AUTHOR "module.author" /**< the author's name */
++#define PW_KEY_MODULE_DESCRIPTION "module.description" /**< a human readable one-line description
++ * of the module's purpose.*/
++#define PW_KEY_MODULE_USAGE "module.usage" /**< a human readable usage description of
++ * the module's arguments. */
++#define PW_KEY_MODULE_VERSION "module.version" /**< a version string for the module. */
++
++/** Factory properties */
++#define PW_KEY_FACTORY_ID "factory.id" /**< the factory id */
++#define PW_KEY_FACTORY_NAME "factory.name" /**< the name of the factory */
++#define PW_KEY_FACTORY_USAGE "factory.usage" /**< the usage of the factory */
++#define PW_KEY_FACTORY_TYPE_NAME "factory.type.name" /**< the name of the type created by a factory */
++#define PW_KEY_FACTORY_TYPE_VERSION "factory.type.version" /**< the version of the type created by a factory */
++
++/** Stream properties */
++#define PW_KEY_STREAM_IS_LIVE "stream.is-live" /**< Indicates that the stream is live. */
++#define PW_KEY_STREAM_LATENCY_MIN "stream.latency.min" /**< The minimum latency of the stream. */
++#define PW_KEY_STREAM_LATENCY_MAX "stream.latency.max" /**< The maximum latency of the stream */
++#define PW_KEY_STREAM_MONITOR "stream.monitor" /**< Indicates that the stream is monitoring
++ * and might select a less accurate but faster
++ * conversion algorithm. */
++#define PW_KEY_STREAM_DONT_REMIX "stream.dont-remix" /**< don't remix channels */
++
++/** object properties */
++#define PW_KEY_OBJECT_LINGER "object.linger" /**< the object lives on even after the client
++ * that created it has been destroyed */
++
++/** Media */
++#define PW_KEY_MEDIA_TYPE "media.type" /**< Media type, one of
++ * Audio, Video, Midi */
++#define PW_KEY_MEDIA_CATEGORY "media.category" /**< Media Category:
++ * Playback, Capture, Duplex, Monitor */
++#define PW_KEY_MEDIA_ROLE "media.role" /**< Role: Movie, Music, Camera,
++ * Screen, Communication, Game,
++ * Notification, DSP, Production,
++ * Accessibility, Test */
++#define PW_KEY_MEDIA_CLASS "media.class" /**< class Ex: "Video/Source" */
++#define PW_KEY_MEDIA_NAME "media.name" /**< media name. Ex: "Pink Floyd: Time" */
++#define PW_KEY_MEDIA_TITLE "media.title" /**< title. Ex: "Time" */
++#define PW_KEY_MEDIA_ARTIST "media.artist" /**< artist. Ex: "Pink Floyd" */
++#define PW_KEY_MEDIA_COPYRIGHT "media.copyright" /**< copyright string */
++#define PW_KEY_MEDIA_SOFTWARE "media.software" /**< generator software */
++#define PW_KEY_MEDIA_LANGUAGE "media.language" /**< language in POSIX format. Ex: en_GB */
++#define PW_KEY_MEDIA_FILENAME "media.filename" /**< filename */
++#define PW_KEY_MEDIA_ICON "media.icon" /**< icon for the media, a base64 blob with
++ * PNG image data */
++#define PW_KEY_MEDIA_ICON_NAME "media.icon-name" /**< an XDG icon name for the media.
++ * Ex: "audio-x-mp3" */
++
++/** format related properties */
++#define PW_KEY_FORMAT_DSP "format.dsp" /**< a dsp format.
++ * Ex: "32 bit float mono audio" */
++/** audio related properties */
++#define PW_KEY_AUDIO_CHANNEL "audio.channel" /**< an audio channel. Ex: "FL" */
++#define PW_KEY_AUDIO_RATE "audio.samplerate" /**< an audio samplerate */
++#define PW_KEY_AUDIO_CHANNELS "audio.channels" /**< number of audio channels */
++#define PW_KEY_AUDIO_FORMAT "audio.format" /**< an audio format. Ex: "S16LE" */
++
++/** video related properties */
++#define PW_KEY_VIDEO_RATE "video.framerate" /**< a video framerate */
++#define PW_KEY_VIDEO_FORMAT "video.format" /**< a video format */
++#define PW_KEY_VIDEO_SIZE "video.size" /**< a video size as "<width>x<height" */
++
++#ifdef PW_ENABLE_DEPRECATED
++#define PW_KEY_PRIORITY_MASTER "priority.master" /**< deprecated */
++#endif /* PW_ENABLE_DEPRECATED */
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_KEYS_H */
+diff --git a/third_party/pipewire/pipewire/link.h b/third_party/pipewire/pipewire/link.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/link.h
+@@ -0,0 +1,124 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_LINK_H
++#define PIPEWIRE_LINK_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/hook.h>
++
++#include <pipewire/proxy.h>
++
++#define PW_TYPE_INTERFACE_Link PW_TYPE_INFO_INTERFACE_BASE "Link"
++
++#define PW_VERSION_LINK 3
++struct pw_link;
++
++/** \enum pw_link_state The different link states \memberof pw_link */
++enum pw_link_state {
++ PW_LINK_STATE_ERROR = -2, /**< the link is in error */
++ PW_LINK_STATE_UNLINKED = -1, /**< the link is unlinked */
++ PW_LINK_STATE_INIT = 0, /**< the link is initialized */
++ PW_LINK_STATE_NEGOTIATING = 1, /**< the link is negotiating formats */
++ PW_LINK_STATE_ALLOCATING = 2, /**< the link is allocating buffers */
++ PW_LINK_STATE_PAUSED = 3, /**< the link is paused */
++};
++
++/** Convert a \ref pw_link_state to a readable string \memberof pw_link */
++const char * pw_link_state_as_string(enum pw_link_state state);
++/** The link information. Extra information can be added in later versions \memberof pw_introspect */
++struct pw_link_info {
++ uint32_t id; /**< id of the global */
++ uint32_t output_node_id; /**< server side output node id */
++ uint32_t output_port_id; /**< output port id */
++ uint32_t input_node_id; /**< server side input node id */
++ uint32_t input_port_id; /**< input port id */
++#define PW_LINK_CHANGE_MASK_STATE (1 << 0)
++#define PW_LINK_CHANGE_MASK_FORMAT (1 << 1)
++#define PW_LINK_CHANGE_MASK_PROPS (1 << 2)
++#define PW_LINK_CHANGE_MASK_ALL ((1 << 3)-1)
++ uint64_t change_mask; /**< bitfield of changed fields since last call */
++ enum pw_link_state state; /**< the current state of the link */
++ const char *error; /**< an error reason if \a state is error */
++ struct spa_pod *format; /**< format over link */
++ struct spa_dict *props; /**< the properties of the link */
++};
++
++struct pw_link_info *
++pw_link_info_update(struct pw_link_info *info,
++ const struct pw_link_info *update);
++
++void
++pw_link_info_free(struct pw_link_info *info);
++
++
++#define PW_LINK_EVENT_INFO 0
++#define PW_LINK_EVENT_NUM 1
++
++/** Link events */
++struct pw_link_events {
++#define PW_VERSION_LINK_EVENTS 0
++ uint32_t version;
++ /**
++ * Notify link info
++ *
++ * \param info info about the link
++ */
++ void (*info) (void *object, const struct pw_link_info *info);
++};
++
++#define PW_LINK_METHOD_ADD_LISTENER 0
++#define PW_LINK_METHOD_NUM 1
++
++/** Link methods */
++struct pw_link_methods {
++#define PW_VERSION_LINK_METHODS 0
++ uint32_t version;
++
++ int (*add_listener) (void *object,
++ struct spa_hook *listener,
++ const struct pw_link_events *events,
++ void *data);
++};
++
++#define pw_link_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ spa_interface_call_res((struct spa_interface*)o, \
++ struct pw_link_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#define pw_link_add_listener(c,...) pw_link_method(c,add_listener,0,__VA_ARGS__)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_LINK_H */
+diff --git a/third_party/pipewire/pipewire/log.h b/third_party/pipewire/pipewire/log.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/log.h
+@@ -0,0 +1,97 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_LOG_H
++#define PIPEWIRE_LOG_H
++
++#include <spa/support/log.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** \class pw_log
++ *
++ * Logging functions of PipeWire
++ *
++ * Logging is performed to stdout and stderr. Trace logging is performed
++ * in a lockfree ringbuffer and written out from the main thread as to not
++ * block the realtime threads.
++ */
++
++/** The global log level */
++extern enum spa_log_level pw_log_level;
++
++/** Configure a logging module. This is usually done automatically
++ * in pw_init() but you can install a custom logger before calling
++ * pw_init(). */
++void pw_log_set(struct spa_log *log);
++
++/** Get the log interface */
++struct spa_log *pw_log_get(void);
++
++/** Configure the logging level */
++void pw_log_set_level(enum spa_log_level level);
++
++
++/** Log a message */
++void
++pw_log_log(enum spa_log_level level,
++ const char *file,
++ int line, const char *func,
++ const char *fmt, ...) SPA_PRINTF_FUNC(5, 6);
++
++/** Log a message */
++void
++pw_log_logv(enum spa_log_level level,
++ const char *file,
++ int line, const char *func,
++ const char *fmt, va_list args) SPA_PRINTF_FUNC(5, 0);
++
++
++/** Check if a loglevel is enabled \memberof pw_log */
++#define pw_log_level_enabled(lev) (pw_log_level >= (lev))
++
++#define pw_log(lev,...) \
++({ \
++ if (SPA_UNLIKELY(pw_log_level_enabled (lev))) \
++ pw_log_log(lev,__FILE__,__LINE__,__func__,__VA_ARGS__); \
++})
++
++#define pw_log_error(...) pw_log(SPA_LOG_LEVEL_ERROR,__VA_ARGS__)
++#define pw_log_warn(...) pw_log(SPA_LOG_LEVEL_WARN,__VA_ARGS__)
++#define pw_log_info(...) pw_log(SPA_LOG_LEVEL_INFO,__VA_ARGS__)
++#define pw_log_debug(...) pw_log(SPA_LOG_LEVEL_DEBUG,__VA_ARGS__)
++#define pw_log_trace(...) pw_log(SPA_LOG_LEVEL_TRACE,__VA_ARGS__)
++
++#ifndef FASTPATH
++#define pw_log_trace_fp(...) pw_log(SPA_LOG_LEVEL_TRACE,__VA_ARGS__)
++#else
++#define pw_log_trace_fp(...)
++#endif
++
++#ifdef __cplusplus
++}
++#endif
++#endif /* PIPEWIRE_LOG_H */
+diff --git a/third_party/pipewire/pipewire/loop.h b/third_party/pipewire/pipewire/loop.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/loop.h
+@@ -0,0 +1,80 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_LOOP_H
++#define PIPEWIRE_LOOP_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/support/loop.h>
++#include <spa/utils/dict.h>
++
++/** \class pw_loop
++ *
++ * PipeWire loop object provides an implementation of
++ * the spa loop interfaces. It can be used to implement various
++ * event loops.
++ */
++struct pw_loop {
++ struct spa_system *system; /**< system utils */
++ struct spa_loop *loop; /**< wrapped loop */
++ struct spa_loop_control *control; /**< loop control */
++ struct spa_loop_utils *utils; /**< loop utils */
++};
++
++struct pw_loop *
++pw_loop_new(const struct spa_dict *props);
++
++void
++pw_loop_destroy(struct pw_loop *loop);
++
++#define pw_loop_add_source(l,...) spa_loop_add_source((l)->loop,__VA_ARGS__)
++#define pw_loop_update_source(l,...) spa_loop_update_source(__VA_ARGS__)
++#define pw_loop_remove_source(l,...) spa_loop_remove_source(__VA_ARGS__)
++#define pw_loop_invoke(l,...) spa_loop_invoke((l)->loop,__VA_ARGS__)
++
++#define pw_loop_get_fd(l) spa_loop_control_get_fd((l)->control)
++#define pw_loop_add_hook(l,...) spa_loop_control_add_hook((l)->control,__VA_ARGS__)
++#define pw_loop_enter(l) spa_loop_control_enter((l)->control)
++#define pw_loop_iterate(l,...) spa_loop_control_iterate((l)->control,__VA_ARGS__)
++#define pw_loop_leave(l) spa_loop_control_leave((l)->control)
++
++#define pw_loop_add_io(l,...) spa_loop_utils_add_io((l)->utils,__VA_ARGS__)
++#define pw_loop_update_io(l,...) spa_loop_utils_update_io((l)->utils,__VA_ARGS__)
++#define pw_loop_add_idle(l,...) spa_loop_utils_add_idle((l)->utils,__VA_ARGS__)
++#define pw_loop_enable_idle(l,...) spa_loop_utils_enable_idle((l)->utils,__VA_ARGS__)
++#define pw_loop_add_event(l,...) spa_loop_utils_add_event((l)->utils,__VA_ARGS__)
++#define pw_loop_signal_event(l,...) spa_loop_utils_signal_event((l)->utils,__VA_ARGS__)
++#define pw_loop_add_timer(l,...) spa_loop_utils_add_timer((l)->utils,__VA_ARGS__)
++#define pw_loop_update_timer(l,...) spa_loop_utils_update_timer((l)->utils,__VA_ARGS__)
++#define pw_loop_add_signal(l,...) spa_loop_utils_add_signal((l)->utils,__VA_ARGS__)
++#define pw_loop_destroy_source(l,...) spa_loop_utils_destroy_source((l)->utils,__VA_ARGS__)
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_LOOP_H */
+diff --git a/third_party/pipewire/pipewire/main-loop.h b/third_party/pipewire/pipewire/main-loop.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/main-loop.h
+@@ -0,0 +1,78 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_MAIN_LOOP_H
++#define PIPEWIRE_MAIN_LOOP_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** \class pw_main_loop
++ *
++ * \brief PipeWire main-loop interface.
++ *
++ * A main loop object
++ */
++/** A main loop object \memberof pw_main_loop */
++struct pw_main_loop;
++
++#include <pipewire/loop.h>
++
++/** Events of the main loop */
++struct pw_main_loop_events {
++#define PW_VERSION_MAIN_LOOP_EVENTS 0
++ uint32_t version;
++
++ /** Emitted when the main loop is destroyed */
++ void (*destroy) (void *data);
++};
++
++/** Create a new main loop. */
++struct pw_main_loop *
++pw_main_loop_new(const struct spa_dict *props);
++
++/** Add an event listener */
++void pw_main_loop_add_listener(struct pw_main_loop *loop,
++ struct spa_hook *listener,
++ const struct pw_main_loop_events *events,
++ void *data);
++
++/** Get the loop implementation */
++struct pw_loop * pw_main_loop_get_loop(struct pw_main_loop *loop);
++
++/** Destroy a loop */
++void pw_main_loop_destroy(struct pw_main_loop *loop);
++
++/** Run a main loop. This blocks until \ref pw_main_loop_quit is called */
++int pw_main_loop_run(struct pw_main_loop *loop);
++
++/** Quit a main loop */
++int pw_main_loop_quit(struct pw_main_loop *loop);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_MAIN_LOOP_H */
+diff --git a/third_party/pipewire/pipewire/map.h b/third_party/pipewire/pipewire/map.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/map.h
+@@ -0,0 +1,206 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_MAP_H
++#define PIPEWIRE_MAP_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <string.h>
++#include <errno.h>
++
++#include <spa/utils/defs.h>
++#include <pipewire/array.h>
++
++/** \class pw_map
++ *
++ * A map that holds objects indexed by id
++ */
++
++/** An entry in the map \memberof pw_map */
++union pw_map_item {
++ uint32_t next; /**< next free index */
++ void *data; /**< data of this item, must be an even address */
++};
++
++/** A map \memberof pw_map */
++struct pw_map {
++ struct pw_array items; /**< an array with the map items */
++ uint32_t free_list; /**< the free items */
++};
++
++#define PW_MAP_INIT(extend) (struct pw_map) { PW_ARRAY_INIT(extend), 0 }
++
++#define pw_map_get_size(m) pw_array_get_len(&(m)->items, union pw_map_item)
++#define pw_map_get_item(m,id) pw_array_get_unchecked(&(m)->items,id,union pw_map_item)
++#define pw_map_item_is_free(item) ((item)->next & 0x1)
++#define pw_map_id_is_free(m,id) (pw_map_item_is_free(pw_map_get_item(m,id)))
++#define pw_map_check_id(m,id) ((id) < pw_map_get_size(m))
++#define pw_map_has_item(m,id) (pw_map_check_id(m,id) && !pw_map_id_is_free(m, id))
++#define pw_map_lookup_unchecked(m,id) pw_map_get_item(m,id)->data
++
++/** Convert an id to a pointer that can be inserted into the map \memberof pw_map */
++#define PW_MAP_ID_TO_PTR(id) (SPA_UINT32_TO_PTR((id)<<1))
++/** Convert a pointer to an id that can be retrieved from the map \memberof pw_map */
++#define PW_MAP_PTR_TO_ID(p) (SPA_PTR_TO_UINT32(p)>>1)
++
++/** Initialize a map
++ * \param map the map to initialize
++ * \param size the initial size of the map
++ * \param extend the amount to bytes to grow the map with when needed
++ * \memberof pw_map
++ */
++static inline void pw_map_init(struct pw_map *map, size_t size, size_t extend)
++{
++ pw_array_init(&map->items, extend);
++ pw_array_ensure_size(&map->items, size * sizeof(union pw_map_item));
++ map->free_list = SPA_ID_INVALID;
++}
++
++/** Clear a map
++ * \param map the map to clear
++ * \memberof pw_map
++ */
++static inline void pw_map_clear(struct pw_map *map)
++{
++ pw_array_clear(&map->items);
++}
++
++static inline void pw_map_reset(struct pw_map *map)
++{
++ pw_array_reset(&map->items);
++ map->free_list = SPA_ID_INVALID;
++}
++
++/** Insert data in the map
++ * \param map the map to insert into
++ * \param data the item to add
++ * \return the id where the item was inserted or SPA_ID_INVALID when the
++ * item can not be inserted.
++ * \memberof pw_map
++ */
++static inline uint32_t pw_map_insert_new(struct pw_map *map, void *data)
++{
++ union pw_map_item *start, *item;
++ uint32_t id;
++
++ if (map->free_list != SPA_ID_INVALID) {
++ start = (union pw_map_item *) map->items.data;
++ item = &start[map->free_list >> 1];
++ map->free_list = item->next;
++ } else {
++ item = (union pw_map_item *) pw_array_add(&map->items, sizeof(union pw_map_item));
++ if (item == NULL)
++ return SPA_ID_INVALID;
++ start = (union pw_map_item *) map->items.data;
++ }
++ item->data = data;
++ id = (item - start);
++ return id;
++}
++
++/** Insert data in the map at an index
++ * \param map the map to inser into
++ * \param id the index to insert at
++ * \param data the data to insert
++ * \return 0 on success, -ENOSPC value when the index is invalid or a < 0
++ * errno value.
++ * \memberof pw_map
++ */
++static inline int pw_map_insert_at(struct pw_map *map, uint32_t id, void *data)
++{
++ size_t size = pw_map_get_size(map);
++ union pw_map_item *item;
++
++ if (id > size)
++ return -ENOSPC;
++ else if (id == size) {
++ item = (union pw_map_item *) pw_array_add(&map->items, sizeof(union pw_map_item));
++ if (item == NULL)
++ return -errno;
++ }
++ else {
++ item = pw_map_get_item(map, id);
++ }
++ item->data = data;
++ return 0;
++}
++
++/** Remove an item at index
++ * \param map the map to remove from
++ * \param id the index to remove
++ * \memberof pw_map
++ */
++static inline void pw_map_remove(struct pw_map *map, uint32_t id)
++{
++ pw_map_get_item(map, id)->next = map->free_list;
++ map->free_list = (id << 1) | 1;
++}
++
++/** Find an item in the map
++ * \param map the map to use
++ * \param id the index to look at
++ * \return the item at \a id or NULL when no such item exists
++ * \memberof pw_map
++ */
++static inline void *pw_map_lookup(struct pw_map *map, uint32_t id)
++{
++ if (SPA_LIKELY(pw_map_check_id(map, id))) {
++ union pw_map_item *item = pw_map_get_item(map, id);
++ if (!pw_map_item_is_free(item))
++ return item->data;
++ }
++ return NULL;
++}
++
++/** Iterate all map items
++ * \param map the map to iterate
++ * \param func the function to call for each item, the item data and \a data is
++ * passed to the function. When \a func returns a non-zero result,
++ * iteration ends and the result is returned.
++ * \param data data to pass to \a func
++ * \return the result of the last call to \a func or 0 when all callbacks returned 0.
++ * \memberof pw_map
++ */
++static inline int pw_map_for_each(struct pw_map *map,
++ int (*func) (void *item_data, void *data), void *data)
++{
++ union pw_map_item *item;
++ int res = 0;
++
++ pw_array_for_each(item, &map->items) {
++ if (!pw_map_item_is_free(item))
++ if ((res = func(item->data, data)) != 0)
++ break;
++ }
++ return res;
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_MAP_H */
+diff --git a/third_party/pipewire/pipewire/mem.h b/third_party/pipewire/pipewire/mem.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/mem.h
+@@ -0,0 +1,199 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_MEM_H
++#define PIPEWIRE_MEM_H
++
++#include <pipewire/properties.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** Flags passed to \ref pw_mempool_alloc() \memberof pw_memblock */
++enum pw_memblock_flags {
++ PW_MEMBLOCK_FLAG_NONE = 0,
++ PW_MEMBLOCK_FLAG_READABLE = (1 << 0), /**< memory is readable */
++ PW_MEMBLOCK_FLAG_WRITABLE = (1 << 1), /**< memory is writable */
++ PW_MEMBLOCK_FLAG_SEAL = (1 << 2), /**< seal the fd */
++ PW_MEMBLOCK_FLAG_MAP = (1 << 3), /**< mmap the fd */
++ PW_MEMBLOCK_FLAG_DONT_CLOSE = (1 << 4), /**< don't close fd */
++ PW_MEMBLOCK_FLAG_DONT_NOTIFY = (1 << 5), /**< don't notify events */
++
++ PW_MEMBLOCK_FLAG_READWRITE = PW_MEMBLOCK_FLAG_READABLE | PW_MEMBLOCK_FLAG_WRITABLE,
++};
++
++enum pw_memmap_flags {
++ PW_MEMMAP_FLAG_NONE = 0,
++ PW_MEMMAP_FLAG_READ = (1 << 0), /**< map in read mode */
++ PW_MEMMAP_FLAG_WRITE = (1 << 1), /**< map in write mode */
++ PW_MEMMAP_FLAG_TWICE = (1 << 2), /**< map the same area twice after each other,
++ * creating a circular ringbuffer */
++ PW_MEMMAP_FLAG_PRIVATE = (1 << 3), /**< writes will be private */
++ PW_MEMMAP_FLAG_READWRITE = PW_MEMMAP_FLAG_READ | PW_MEMMAP_FLAG_WRITE,
++};
++
++struct pw_memchunk;
++
++/** \class pw_memblock
++ *
++ * A memory pool is a collection of pw_memblocks */
++struct pw_mempool {
++ struct pw_properties *props;
++};
++
++/** \class pw_memblock
++ * Memory block structure */
++struct pw_memblock {
++ struct pw_mempool *pool; /**< owner pool */
++ uint32_t id; /**< unique id */
++ int ref; /**< refcount */
++ uint32_t flags; /**< flags for the memory block on of enum pw_memblock_flags */
++ uint32_t type; /**< type of the fd, one of enum spa_data_type */
++ int fd; /**< fd */
++ uint32_t size; /**< size of memory */
++ struct pw_memmap *map; /**< optional map when PW_MEMBLOCK_FLAG_MAP was given */
++};
++
++/** a mapped region of a pw_memblock */
++struct pw_memmap {
++ struct pw_memblock *block; /**< owner memblock */
++ void *ptr; /**< mapped pointer */
++ uint32_t flags; /**< flags for the mapping on of enum pw_memmap_flags */
++ uint32_t offset; /**< offset in memblock */
++ uint32_t size; /**< size in memblock */
++ uint32_t tag[5]; /**< user tag */
++};
++
++struct pw_mempool_events {
++#define PW_VERSION_MEMPOOL_EVENTS 0
++ uint32_t version;
++
++ /** the pool is destroyed */
++ void (*destroy) (void *data);
++
++ /** a new memory block is added to the pool */
++ void (*added) (void *data, struct pw_memblock *block);
++
++ /** a memory block is removed from the pool */
++ void (*removed) (void *data, struct pw_memblock *block);
++};
++
++/** Create a new memory pool */
++struct pw_mempool *pw_mempool_new(struct pw_properties *props);
++
++/** Listen for events */
++void pw_mempool_add_listener(struct pw_mempool *pool,
++ struct spa_hook *listener,
++ const struct pw_mempool_events *events,
++ void *data);
++
++/** Clear a pool */
++void pw_mempool_clear(struct pw_mempool *pool);
++
++/** Clear and destroy a pool */
++void pw_mempool_destroy(struct pw_mempool *pool);
++
++
++/** Allocate a memory block from the pool */
++struct pw_memblock * pw_mempool_alloc(struct pw_mempool *pool,
++ enum pw_memblock_flags flags, uint32_t type, size_t size);
++
++/** Import a block from another pool */
++struct pw_memblock * pw_mempool_import_block(struct pw_mempool *pool,
++ struct pw_memblock *mem);
++
++/** Import an fd into the pool */
++struct pw_memblock * pw_mempool_import(struct pw_mempool *pool,
++ enum pw_memblock_flags flags, uint32_t type, int fd);
++
++/** Free a memblock regardless of the refcount and destroy all mappings */
++void pw_memblock_free(struct pw_memblock *mem);
++
++/** Unref a memblock */
++static inline void pw_memblock_unref(struct pw_memblock *mem)
++{
++ if (--mem->ref == 0)
++ pw_memblock_free(mem);
++}
++
++/** Remove a memblock for given \a id */
++int pw_mempool_remove_id(struct pw_mempool *pool, uint32_t id);
++
++/** Find memblock for given \a ptr */
++struct pw_memblock * pw_mempool_find_ptr(struct pw_mempool *pool, const void *ptr);
++
++/** Find memblock for given \a id */
++struct pw_memblock * pw_mempool_find_id(struct pw_mempool *pool, uint32_t id);
++
++/** Find memblock for given \a fd */
++struct pw_memblock * pw_mempool_find_fd(struct pw_mempool *pool, int fd);
++
++
++/** Map a region of a memory block */
++struct pw_memmap * pw_memblock_map(struct pw_memblock *block,
++ enum pw_memmap_flags flags, uint32_t offset, uint32_t size,
++ uint32_t tag[5]);
++
++/** Map a region of a memory block with \a id */
++struct pw_memmap * pw_mempool_map_id(struct pw_mempool *pool, uint32_t id,
++ enum pw_memmap_flags flags, uint32_t offset, uint32_t size,
++ uint32_t tag[5]);
++
++struct pw_memmap * pw_mempool_import_map(struct pw_mempool *pool,
++ struct pw_mempool *other, void *data, uint32_t size, uint32_t tag[5]);
++
++/** find a map with the given tag */
++struct pw_memmap * pw_mempool_find_tag(struct pw_mempool *pool, uint32_t tag[5], size_t size);
++
++/** Unmap a region */
++int pw_memmap_free(struct pw_memmap *map);
++
++
++/** parameters to map a memory range */
++struct pw_map_range {
++ uint32_t start; /** offset in first page with start of data */
++ uint32_t offset; /** page aligned offset to map */
++ uint32_t size; /** size to map */
++};
++
++#define PW_MAP_RANGE_INIT (struct pw_map_range){ 0, }
++
++/** Calculate parameters to mmap() memory into \a range so that
++ * \a size bytes at \a offset can be mapped with mmap(). */
++static inline void pw_map_range_init(struct pw_map_range *range,
++ uint32_t offset, uint32_t size,
++ uint32_t page_size)
++{
++ range->offset = SPA_ROUND_DOWN_N(offset, page_size);
++ range->start = offset - range->offset;
++ range->size = SPA_ROUND_UP_N(range->start + size, page_size);
++}
++
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_MEM_H */
+diff --git a/third_party/pipewire/pipewire/module.h b/third_party/pipewire/pipewire/module.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/module.h
+@@ -0,0 +1,106 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_MODULE_H
++#define PIPEWIRE_MODULE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/hook.h>
++
++#include <pipewire/proxy.h>
++
++#define PW_TYPE_INTERFACE_Module PW_TYPE_INFO_INTERFACE_BASE "Module"
++
++#define PW_VERSION_MODULE 3
++struct pw_module;
++
++/** The module information. Extra information can be added in later versions \memberof pw_introspect */
++struct pw_module_info {
++ uint32_t id; /**< id of the global */
++ const char *name; /**< name of the module */
++ const char *filename; /**< filename of the module */
++ const char *args; /**< arguments passed to the module */
++#define PW_MODULE_CHANGE_MASK_PROPS (1 << 0)
++#define PW_MODULE_CHANGE_MASK_ALL ((1 << 1)-1)
++ uint64_t change_mask; /**< bitfield of changed fields since last call */
++ struct spa_dict *props; /**< extra properties */
++};
++
++/** Update and existing \ref pw_module_info with \a update \memberof pw_introspect */
++struct pw_module_info *
++pw_module_info_update(struct pw_module_info *info,
++ const struct pw_module_info *update);
++
++/** Free a \ref pw_module_info \memberof pw_introspect */
++void pw_module_info_free(struct pw_module_info *info);
++
++#define PW_MODULE_EVENT_INFO 0
++#define PW_MODULE_EVENT_NUM 1
++
++/** Module events */
++struct pw_module_events {
++#define PW_VERSION_MODULE_EVENTS 0
++ uint32_t version;
++ /**
++ * Notify module info
++ *
++ * \param info info about the module
++ */
++ void (*info) (void *object, const struct pw_module_info *info);
++};
++
++#define PW_MODULE_METHOD_ADD_LISTENER 0
++#define PW_MODULE_METHOD_NUM 1
++
++/** Module methods */
++struct pw_module_methods {
++#define PW_VERSION_MODULE_METHODS 0
++ uint32_t version;
++
++ int (*add_listener) (void *object,
++ struct spa_hook *listener,
++ const struct pw_module_events *events,
++ void *data);
++};
++
++#define pw_module_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ spa_interface_call_res((struct spa_interface*)o, \
++ struct pw_module_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#define pw_module_add_listener(c,...) pw_module_method(c,add_listener,0,__VA_ARGS__)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_MODULE_H */
+diff --git a/third_party/pipewire/pipewire/node.h b/third_party/pipewire/pipewire/node.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/node.h
+@@ -0,0 +1,200 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_NODE_H
++#define PIPEWIRE_NODE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdarg.h>
++#include <errno.h>
++
++#include <spa/utils/defs.h>
++#include <spa/utils/hook.h>
++#include <spa/node/command.h>
++#include <spa/param/param.h>
++
++#include <pipewire/proxy.h>
++
++#define PW_TYPE_INTERFACE_Node PW_TYPE_INFO_INTERFACE_BASE "Node"
++
++#define PW_VERSION_NODE 3
++struct pw_node;
++
++/** \enum pw_node_state The different node states \memberof pw_node */
++enum pw_node_state {
++ PW_NODE_STATE_ERROR = -1, /**< error state */
++ PW_NODE_STATE_CREATING = 0, /**< the node is being created */
++ PW_NODE_STATE_SUSPENDED = 1, /**< the node is suspended, the device might
++ * be closed */
++ PW_NODE_STATE_IDLE = 2, /**< the node is running but there is no active
++ * port */
++ PW_NODE_STATE_RUNNING = 3, /**< the node is running */
++};
++
++/** Convert a \ref pw_node_state to a readable string \memberof pw_node */
++const char * pw_node_state_as_string(enum pw_node_state state);
++
++/** The node information. Extra information can be added in later versions \memberof pw_introspect */
++struct pw_node_info {
++ uint32_t id; /**< id of the global */
++ uint32_t max_input_ports; /**< maximum number of inputs */
++ uint32_t max_output_ports; /**< maximum number of outputs */
++#define PW_NODE_CHANGE_MASK_INPUT_PORTS (1 << 0)
++#define PW_NODE_CHANGE_MASK_OUTPUT_PORTS (1 << 1)
++#define PW_NODE_CHANGE_MASK_STATE (1 << 2)
++#define PW_NODE_CHANGE_MASK_PROPS (1 << 3)
++#define PW_NODE_CHANGE_MASK_PARAMS (1 << 4)
++#define PW_NODE_CHANGE_MASK_ALL ((1 << 5)-1)
++ uint64_t change_mask; /**< bitfield of changed fields since last call */
++ uint32_t n_input_ports; /**< number of inputs */
++ uint32_t n_output_ports; /**< number of outputs */
++ enum pw_node_state state; /**< the current state of the node */
++ const char *error; /**< an error reason if \a state is error */
++ struct spa_dict *props; /**< the properties of the node */
++ struct spa_param_info *params; /**< parameters */
++ uint32_t n_params; /**< number of items in \a params */
++};
++
++struct pw_node_info *
++pw_node_info_update(struct pw_node_info *info,
++ const struct pw_node_info *update);
++
++void
++pw_node_info_free(struct pw_node_info *info);
++
++#define PW_NODE_EVENT_INFO 0
++#define PW_NODE_EVENT_PARAM 1
++#define PW_NODE_EVENT_NUM 2
++
++/** Node events */
++struct pw_node_events {
++#define PW_VERSION_NODE_EVENTS 0
++ uint32_t version;
++ /**
++ * Notify node info
++ *
++ * \param info info about the node
++ */
++ void (*info) (void *object, const struct pw_node_info *info);
++ /**
++ * Notify a node param
++ *
++ * Event emitted as a result of the enum_params method.
++ *
++ * \param seq the sequence number of the request
++ * \param id the param id
++ * \param index the param index
++ * \param next the param index of the next param
++ * \param param the parameter
++ */
++ void (*param) (void *object, int seq,
++ uint32_t id, uint32_t index, uint32_t next,
++ const struct spa_pod *param);
++};
++
++#define PW_NODE_METHOD_ADD_LISTENER 0
++#define PW_NODE_METHOD_SUBSCRIBE_PARAMS 1
++#define PW_NODE_METHOD_ENUM_PARAMS 2
++#define PW_NODE_METHOD_SET_PARAM 3
++#define PW_NODE_METHOD_SEND_COMMAND 4
++#define PW_NODE_METHOD_NUM 5
++
++/** Node methods */
++struct pw_node_methods {
++#define PW_VERSION_NODE_METHODS 0
++ uint32_t version;
++
++ int (*add_listener) (void *object,
++ struct spa_hook *listener,
++ const struct pw_node_events *events,
++ void *data);
++ /**
++ * Subscribe to parameter changes
++ *
++ * Automatically emit param events for the given ids when
++ * they are changed.
++ *
++ * \param ids an array of param ids
++ * \param n_ids the number of ids in \a ids
++ */
++ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids);
++
++ /**
++ * Enumerate node parameters
++ *
++ * Start enumeration of node parameters. For each param, a
++ * param event will be emitted.
++ *
++ * \param seq a sequence number to place in the reply
++ * \param id the parameter id to enum or PW_ID_ANY for all
++ * \param start the start index or 0 for the first param
++ * \param num the maximum number of params to retrieve
++ * \param filter a param filter or NULL
++ */
++ int (*enum_params) (void *object, int seq, uint32_t id,
++ uint32_t start, uint32_t num,
++ const struct spa_pod *filter);
++
++ /**
++ * Set a parameter on the node
++ *
++ * \param id the parameter id to set
++ * \param flags extra parameter flags
++ * \param param the parameter to set
++ */
++ int (*set_param) (void *object, uint32_t id, uint32_t flags,
++ const struct spa_pod *param);
++
++ /**
++ * Send a command to the node
++ *
++ * \param command the command to send
++ */
++ int (*send_command) (void *object, const struct spa_command *command);
++};
++
++#define pw_node_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ spa_interface_call_res((struct spa_interface*)o, \
++ struct pw_node_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++/** Node */
++#define pw_node_add_listener(c,...) pw_node_method(c,add_listener,0,__VA_ARGS__)
++#define pw_node_subscribe_params(c,...) pw_node_method(c,subscribe_params,0,__VA_ARGS__)
++#define pw_node_enum_params(c,...) pw_node_method(c,enum_params,0,__VA_ARGS__)
++#define pw_node_set_param(c,...) pw_node_method(c,set_param,0,__VA_ARGS__)
++#define pw_node_send_command(c,...) pw_node_method(c,send_command,0,__VA_ARGS__)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_NODE_H */
+diff --git a/third_party/pipewire/pipewire/permission.h b/third_party/pipewire/pipewire/permission.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/permission.h
+@@ -0,0 +1,79 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_PERMISSION_H
++#define PIPEWIRE_PERMISSION_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++
++/** \class pw_permission
++ *
++ * \brief a PipeWire permission
++ *
++ * Permissions are kept for a client and describe what the client is
++ * allowed to do with an object.
++ *
++ * See \ref page_core_api
++ */
++
++#define PW_PERM_R 0400 /**< object can be seen and events can be received */
++#define PW_PERM_W 0200 /**< methods can be called that modify the object */
++#define PW_PERM_X 0100 /**< methods can be called on the object. The W flag must be
++ * present in order to call methods that modify the object. */
++#define PW_PERM_M 0010 /**< metadata can be set on object, Since 0.3.9 */
++
++#define PW_PERM_RWX (PW_PERM_R|PW_PERM_W|PW_PERM_X)
++#define PW_PERM_RWXM (PW_PERM_RWX|PW_PERM_M)
++
++#define PW_PERM_IS_R(p) (((p)&PW_PERM_R) == PW_PERM_R)
++#define PW_PERM_IS_W(p) (((p)&PW_PERM_W) == PW_PERM_W)
++#define PW_PERM_IS_X(p) (((p)&PW_PERM_X) == PW_PERM_X)
++#define PW_PERM_IS_M(p) (((p)&PW_PERM_M) == PW_PERM_M)
++
++#define PW_PERM_ALL PW_PERM_RWXM
++#define PW_PERM_INVALID (uint32_t)(0xffffffff)
++
++struct pw_permission {
++ uint32_t id; /**< id of object, PW_ID_ANY for default permission */
++ uint32_t permissions; /**< bitmask of above permissions */
++};
++
++#define PW_PERMISSION_INIT(id,p) (struct pw_permission){ (id), (p) }
++
++#define PW_PERMISSION_FORMAT "%c%c%c%c"
++#define PW_PERMISSION_ARGS(permission) \
++ (permission) & PW_PERM_R ? 'r' : '-', \
++ (permission) & PW_PERM_W ? 'w' : '-', \
++ (permission) & PW_PERM_X ? 'x' : '-', \
++ (permission) & PW_PERM_M ? 'm' : '-'
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_PERMISSION_H */
+diff --git a/third_party/pipewire/pipewire/pipewire.h b/third_party/pipewire/pipewire/pipewire.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/pipewire.h
+@@ -0,0 +1,158 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_H
++#define PIPEWIRE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/support/plugin.h>
++
++#include <pipewire/array.h>
++#include <pipewire/client.h>
++#include <pipewire/context.h>
++#include <pipewire/device.h>
++#include <pipewire/buffers.h>
++#include <pipewire/core.h>
++#include <pipewire/factory.h>
++#include <pipewire/keys.h>
++#include <pipewire/log.h>
++#include <pipewire/loop.h>
++#include <pipewire/link.h>
++#include <pipewire/main-loop.h>
++#include <pipewire/map.h>
++#include <pipewire/mem.h>
++#include <pipewire/module.h>
++#include <pipewire/node.h>
++#include <pipewire/properties.h>
++#include <pipewire/proxy.h>
++#include <pipewire/permission.h>
++#include <pipewire/protocol.h>
++#include <pipewire/port.h>
++#include <pipewire/stream.h>
++#include <pipewire/filter.h>
++#include <pipewire/thread-loop.h>
++#include <pipewire/data-loop.h>
++#include <pipewire/type.h>
++#include <pipewire/utils.h>
++#include <pipewire/version.h>
++
++/** \mainpage
++ *
++ * \section sec_intro Introduction
++ *
++ * This document describes the API for the PipeWire multimedia framework.
++ * The API consists of two parts:
++ *
++ * \li The core API to access a PipeWire instance.
++ * (See \subpage page_core_api)
++ * \li The implementation API and tools to build new objects and
++ * modules (See \subpage page_implementation_api)
++ *
++ * \section sec_errors Error reporting
++ *
++ * Functions return either NULL with errno set or a negative int error
++ * code when an error occurs. Error codes are used from the SPA plugin
++ * library on which PipeWire is built.
++ *
++ * Some functions might return asynchronously. The error code for such
++ * functions is positive and SPA_RESULT_IS_ASYNC() will return true.
++ * SPA_RESULT_ASYNC_SEQ() can be used to get the unique sequence number
++ * associated with the async operation.
++ *
++ * The object returning the async result code will have some way to
++ * signal the completion of the async operation (with, for example, a
++ * callback). The sequence number can be used to see which operation
++ * completed.
++ *
++ * \section sec_logging Logging
++ *
++ * The 'PIPEWIRE_DEBUG' environment variable can be used to enable
++ * more debugging. The format is:
++ *
++ * &lt;level&gt;[:&lt;category&gt;,...]
++ *
++ * - &lt;level&gt;: specifies the log level:
++ * + `0`: no logging is enabled
++ * + `1`: Error logging is enabled
++ * + `2`: Warnings are enabled
++ * + `3`: Informational messages are enabled
++ * + `4`: Debug messages are enabled
++ * + `5`: Trace messages are enabled. These messages can be logged
++ * from the realtime threads.
++ *
++ * - &lt;category&gt;: Specifies a string category to enable. Many categories
++ * can be separated by commas. Current categories are:
++ * + `connection`: to log connection messages
++ */
++
++/** \class pw_pipewire
++ *
++ * \brief PipeWire initialization and infrastructure functions
++ */
++void
++pw_init(int *argc, char **argv[]);
++
++void pw_deinit(void);
++
++bool
++pw_debug_is_category_enabled(const char *name);
++
++const char *
++pw_get_application_name(void);
++
++const char *
++pw_get_prgname(void);
++
++const char *
++pw_get_user_name(void);
++
++const char *
++pw_get_host_name(void);
++
++const char *
++pw_get_client_name(void);
++
++bool pw_in_valgrind(void);
++
++enum pw_direction
++pw_direction_reverse(enum pw_direction direction);
++
++uint32_t pw_get_support(struct spa_support *support, uint32_t max_support);
++
++struct spa_handle *pw_load_spa_handle(const char *lib,
++ const char *factory_name,
++ const struct spa_dict *info,
++ uint32_t n_support,
++ const struct spa_support support[]);
++
++int pw_unload_spa_handle(struct spa_handle *handle);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_H */
+diff --git a/third_party/pipewire/pipewire/port.h b/third_party/pipewire/pipewire/port.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/port.h
+@@ -0,0 +1,169 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_PORT_H
++#define PIPEWIRE_PORT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdarg.h>
++#include <errno.h>
++
++#include <spa/utils/defs.h>
++#include <spa/utils/hook.h>
++#include <spa/param/param.h>
++
++#include <pipewire/proxy.h>
++
++#define PW_TYPE_INTERFACE_Port PW_TYPE_INFO_INTERFACE_BASE "Port"
++
++#define PW_VERSION_PORT 3
++struct pw_port;
++
++/** \enum pw_direction The direction of a port \memberof pw_introspect */
++#define pw_direction spa_direction
++#define PW_DIRECTION_INPUT SPA_DIRECTION_INPUT
++#define PW_DIRECTION_OUTPUT SPA_DIRECTION_OUTPUT
++
++/** Convert a \ref pw_direction to a readable string \memberof pw_introspect */
++const char * pw_direction_as_string(enum pw_direction direction);
++
++
++/** \class pw_introspect
++ *
++ * The introspection methods and structures are used to get information
++ * about the object in the PipeWire server
++ */
++
++struct pw_port_info {
++ uint32_t id; /**< id of the global */
++ enum pw_direction direction; /**< port direction */
++#define PW_PORT_CHANGE_MASK_PROPS (1 << 0)
++#define PW_PORT_CHANGE_MASK_PARAMS (1 << 1)
++#define PW_PORT_CHANGE_MASK_ALL ((1 << 2)-1)
++ uint64_t change_mask; /**< bitfield of changed fields since last call */
++ struct spa_dict *props; /**< the properties of the port */
++ struct spa_param_info *params; /**< parameters */
++ uint32_t n_params; /**< number of items in \a params */
++};
++
++struct pw_port_info *
++pw_port_info_update(struct pw_port_info *info,
++ const struct pw_port_info *update);
++
++void
++pw_port_info_free(struct pw_port_info *info);
++
++#define PW_PORT_EVENT_INFO 0
++#define PW_PORT_EVENT_PARAM 1
++#define PW_PORT_EVENT_NUM 2
++
++/** Port events */
++struct pw_port_events {
++#define PW_VERSION_PORT_EVENTS 0
++ uint32_t version;
++ /**
++ * Notify port info
++ *
++ * \param info info about the port
++ */
++ void (*info) (void *object, const struct pw_port_info *info);
++ /**
++ * Notify a port param
++ *
++ * Event emitted as a result of the enum_params method.
++ *
++ * \param seq the sequence number of the request
++ * \param id the param id
++ * \param index the param index
++ * \param next the param index of the next param
++ * \param param the parameter
++ */
++ void (*param) (void *object, int seq,
++ uint32_t id, uint32_t index, uint32_t next,
++ const struct spa_pod *param);
++};
++
++#define PW_PORT_METHOD_ADD_LISTENER 0
++#define PW_PORT_METHOD_SUBSCRIBE_PARAMS 1
++#define PW_PORT_METHOD_ENUM_PARAMS 2
++#define PW_PORT_METHOD_NUM 3
++
++/** Port methods */
++struct pw_port_methods {
++#define PW_VERSION_PORT_METHODS 0
++ uint32_t version;
++
++ int (*add_listener) (void *object,
++ struct spa_hook *listener,
++ const struct pw_port_events *events,
++ void *data);
++ /**
++ * Subscribe to parameter changes
++ *
++ * Automatically emit param events for the given ids when
++ * they are changed.
++ *
++ * \param ids an array of param ids
++ * \param n_ids the number of ids in \a ids
++ */
++ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids);
++
++ /**
++ * Enumerate port parameters
++ *
++ * Start enumeration of port parameters. For each param, a
++ * param event will be emitted.
++ *
++ * \param seq a sequence number returned in the reply
++ * \param id the parameter id to enumerate
++ * \param start the start index or 0 for the first param
++ * \param num the maximum number of params to retrieve
++ * \param filter a param filter or NULL
++ */
++ int (*enum_params) (void *object, int seq,
++ uint32_t id, uint32_t start, uint32_t num,
++ const struct spa_pod *filter);
++};
++
++#define pw_port_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ spa_interface_call_res((struct spa_interface*)o, \
++ struct pw_port_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#define pw_port_add_listener(c,...) pw_port_method(c,add_listener,0,__VA_ARGS__)
++#define pw_port_subscribe_params(c,...) pw_port_method(c,subscribe_params,0,__VA_ARGS__)
++#define pw_port_enum_params(c,...) pw_port_method(c,enum_params,0,__VA_ARGS__)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_PORT_H */
+diff --git a/third_party/pipewire/pipewire/properties.h b/third_party/pipewire/pipewire/properties.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/properties.h
+@@ -0,0 +1,121 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_PROPERTIES_H
++#define PIPEWIRE_PROPERTIES_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdarg.h>
++
++#include <spa/utils/dict.h>
++
++/** \class pw_properties
++ *
++ * \brief A collection of key/value pairs
++ *
++ * Properties are used to pass around arbitrary key/value pairs.
++ * Both keys and values are strings which keeps things simple.
++ * Encoding of arbitrary values should be done by using a string
++ * serialization such as base64 for binary blobs.
++ */
++struct pw_properties {
++ struct spa_dict dict; /**< dictionary of key/values */
++ uint32_t flags; /**< extra flags */
++};
++
++struct pw_properties *
++pw_properties_new(const char *key, ...) SPA_SENTINEL;
++
++struct pw_properties *
++pw_properties_new_dict(const struct spa_dict *dict);
++
++struct pw_properties *
++pw_properties_new_string(const char *args);
++
++struct pw_properties *
++pw_properties_copy(const struct pw_properties *properties);
++
++int pw_properties_update_keys(struct pw_properties *props,
++ const struct spa_dict *dict, const char *keys[]);
++
++int pw_properties_update(struct pw_properties *oldprops,
++ const struct spa_dict *dict);
++
++int pw_properties_add(struct pw_properties *oldprops,
++ const struct spa_dict *dict);
++int pw_properties_add_keys(struct pw_properties *oldprops,
++ const struct spa_dict *dict, const char *keys[]);
++
++void pw_properties_clear(struct pw_properties *properties);
++
++void
++pw_properties_free(struct pw_properties *properties);
++
++int
++pw_properties_set(struct pw_properties *properties, const char *key, const char *value);
++
++int
++pw_properties_setf(struct pw_properties *properties,
++ const char *key, const char *format, ...) SPA_PRINTF_FUNC(3, 4);
++int
++pw_properties_setva(struct pw_properties *properties,
++ const char *key, const char *format, va_list args) SPA_PRINTF_FUNC(3,0);
++const char *
++pw_properties_get(const struct pw_properties *properties, const char *key);
++
++const char *
++pw_properties_iterate(const struct pw_properties *properties, void **state);
++
++static inline bool pw_properties_parse_bool(const char *value) {
++ return (strcmp(value, "true") == 0 || atoi(value) == 1);
++}
++
++static inline int pw_properties_parse_int(const char *value) {
++ return strtol(value, NULL, 0);
++}
++
++static inline int64_t pw_properties_parse_int64(const char *value) {
++ return strtoll(value, NULL, 0);
++}
++
++static inline uint64_t pw_properties_parse_uint64(const char *value) {
++ return strtoull(value, NULL, 0);
++}
++
++static inline float pw_properties_parse_float(const char *value) {
++ return strtof(value, NULL);
++}
++
++static inline double pw_properties_parse_double(const char *value) {
++ return strtod(value, NULL);
++}
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_PROPERTIES_H */
+diff --git a/third_party/pipewire/pipewire/protocol.h b/third_party/pipewire/pipewire/protocol.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/protocol.h
+@@ -0,0 +1,152 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_PROTOCOL_H
++#define PIPEWIRE_PROTOCOL_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/list.h>
++
++struct pw_protocol;
++
++#include <pipewire/context.h>
++#include <pipewire/properties.h>
++#include <pipewire/utils.h>
++
++#define PW_TYPE_INFO_Protocol "PipeWire:Protocol"
++#define PW_TYPE_INFO_PROTOCOL_BASE PW_TYPE_INFO_Protocol ":"
++
++struct pw_protocol_client {
++ struct spa_list link; /**< link in protocol client_list */
++ struct pw_protocol *protocol; /**< the owner protocol */
++
++ struct pw_core *core;
++
++ int (*connect) (struct pw_protocol_client *client,
++ const struct spa_dict *props,
++ void (*done_callback) (void *data, int result),
++ void *data);
++ int (*connect_fd) (struct pw_protocol_client *client, int fd, bool close);
++ int (*steal_fd) (struct pw_protocol_client *client);
++ void (*disconnect) (struct pw_protocol_client *client);
++ void (*destroy) (struct pw_protocol_client *client);
++ int (*set_paused) (struct pw_protocol_client *client, bool paused);
++};
++
++#define pw_protocol_client_connect(c,p,cb,d) ((c)->connect(c,p,cb,d))
++#define pw_protocol_client_connect_fd(c,fd,cl) ((c)->connect_fd(c,fd,cl))
++#define pw_protocol_client_steal_fd(c) ((c)->steal_fd(c))
++#define pw_protocol_client_disconnect(c) ((c)->disconnect(c))
++#define pw_protocol_client_destroy(c) ((c)->destroy(c))
++#define pw_protocol_client_set_paused(c,p) ((c)->set_paused(c,p))
++
++struct pw_protocol_server {
++ struct spa_list link; /**< link in protocol server_list */
++ struct pw_protocol *protocol; /**< the owner protocol */
++
++ struct pw_impl_core *core;
++
++ struct spa_list client_list; /**< list of clients of this protocol */
++
++ void (*destroy) (struct pw_protocol_server *listen);
++};
++
++#define pw_protocol_server_destroy(l) ((l)->destroy(l))
++
++struct pw_protocol_marshal {
++ const char *type; /**< interface type */
++ uint32_t version; /**< version */
++#define PW_PROTOCOL_MARSHAL_FLAG_IMPL (1 << 0) /**< marshal for implementations */
++ uint32_t flags; /**< version */
++ uint32_t n_client_methods; /**< number of client methods */
++ uint32_t n_server_methods; /**< number of server methods */
++ const void *client_marshal;
++ const void *server_demarshal;
++ const void *server_marshal;
++ const void *client_demarshal;
++};
++
++struct pw_protocol_implementaton {
++#define PW_VERSION_PROTOCOL_IMPLEMENTATION 0
++ uint32_t version;
++
++ struct pw_protocol_client * (*new_client) (struct pw_protocol *protocol,
++ struct pw_core *core,
++ const struct spa_dict *props);
++ struct pw_protocol_server * (*add_server) (struct pw_protocol *protocol,
++ struct pw_impl_core *core,
++ const struct spa_dict *props);
++};
++
++struct pw_protocol_events {
++#define PW_VERSION_PROTOCOL_EVENTS 0
++ uint32_t version;
++
++ void (*destroy) (void *data);
++};
++
++#define pw_protocol_new_client(p,...) (pw_protocol_get_implementation(p)->new_client(p,__VA_ARGS__))
++#define pw_protocol_add_server(p,...) (pw_protocol_get_implementation(p)->add_server(p,__VA_ARGS__))
++#define pw_protocol_ext(p,type,method,...) (((type*)pw_protocol_get_extension(p))->method( __VA_ARGS__))
++
++struct pw_protocol *pw_protocol_new(struct pw_context *context, const char *name, size_t user_data_size);
++
++void pw_protocol_destroy(struct pw_protocol *protocol);
++
++struct pw_context *pw_protocol_get_context(struct pw_protocol *protocol);
++
++void *pw_protocol_get_user_data(struct pw_protocol *protocol);
++
++const struct pw_protocol_implementaton *
++pw_protocol_get_implementation(struct pw_protocol *protocol);
++
++const void *
++pw_protocol_get_extension(struct pw_protocol *protocol);
++
++
++void pw_protocol_add_listener(struct pw_protocol *protocol,
++ struct spa_hook *listener,
++ const struct pw_protocol_events *events,
++ void *data);
++
++/** \class pw_protocol
++ *
++ * \brief Manages protocols and their implementation
++ */
++int pw_protocol_add_marshal(struct pw_protocol *protocol,
++ const struct pw_protocol_marshal *marshal);
++
++const struct pw_protocol_marshal *
++pw_protocol_get_marshal(struct pw_protocol *protocol, const char *type, uint32_t version, uint32_t flags);
++
++struct pw_protocol * pw_context_find_protocol(struct pw_context *context, const char *name);
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_PROTOCOL_H */
+diff --git a/third_party/pipewire/pipewire/proxy.h b/third_party/pipewire/pipewire/proxy.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/proxy.h
+@@ -0,0 +1,207 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_PROXY_H
++#define PIPEWIRE_PROXY_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/hook.h>
++
++/** \page page_proxy Proxy
++ *
++ * \section sec_page_proxy_overview Overview
++ *
++ * The proxy object is a client side representation of a resource
++ * that lives on a remote PipeWire instance.
++ *
++ * It is used to communicate with the remote object.
++ *
++ * \section sec_page_proxy_core Core proxy
++ *
++ * A proxy for a remote core object can be obtained by making
++ * a remote connection with \ref pw_core_connect.
++ * See \ref pw_page_remote_api
++ *
++ * Some methods on proxy object allow creation of more proxy objects or
++ * create a binding between a local proxy and global resource.
++ *
++ * \section sec_page_proxy_create Create
++ *
++ * A client first creates a new proxy object with pw_proxy_new(). A
++ * type must be provided for this object.
++ *
++ * The protocol of the context will usually install an interface to
++ * translate method calls and events to the wire format.
++ *
++ * The creator of the proxy will usually also install an event
++ * implementation of the particular object type.
++ *
++ * \section sec_page_proxy_bind Bind
++ *
++ * To actually use the proxy object, one needs to create a server
++ * side resource for it. This can be done by, for example, binding
++ * to a global object or by calling a method that creates and binds
++ * to a new remote object. In all cases, the local id is passed to
++ * the server and is used to create a resource with the same id.
++ *
++ * \section sec_page_proxy_methods Methods
++ *
++ * To call a method on the proxy use the interface methods. Calling
++ * any interface method will result in a request to the server to
++ * perform the requested action on the corresponding resource.
++ *
++ * \section sec_page_proxy_events Events
++ *
++ * Events send from the server to the proxy will be demarshalled by
++ * the protocol and will then result in a call to the installed
++ * implementation of the proxy.
++ *
++ * \section sec_page_proxy_destroy Destroy
++ *
++ * Use pw_proxy_destroy() to destroy the client side object. This
++ * is usually done automatically when the server removes the resource
++ * associated to the proxy.
++ */
++
++/** \class pw_proxy
++ *
++ * \brief Represents an object on the client side.
++ *
++ * A pw_proxy acts as a client side proxy to an object existing in a remote
++ * pipewire instance. The proxy is responsible for converting interface functions
++ * invoked by the client to PipeWire messages. Events will call the handlers
++ * set in listener.
++ *
++ * See \ref page_proxy
++ */
++struct pw_proxy;
++
++#include <pipewire/protocol.h>
++
++/** Proxy events, use \ref pw_proxy_add_listener */
++struct pw_proxy_events {
++#define PW_VERSION_PROXY_EVENTS 0
++ uint32_t version;
++
++ /** The proxy is destroyed */
++ void (*destroy) (void *data);
++
++ /** a proxy is bound to a global id */
++ void (*bound) (void *data, uint32_t global_id);
++
++ /** a proxy is removed from the server. Use pw_proxy_destroy to
++ * free the proxy. */
++ void (*removed) (void *data);
++
++ /** a reply to a sync method completed */
++ void (*done) (void *data, int seq);
++
++ /** an error occurred on the proxy */
++ void (*error) (void *data, int seq, int res, const char *message);
++};
++
++/** Make a new proxy object. The id can be used to bind to a remote object and
++ * can be retrieved with \ref pw_proxy_get_id . */
++struct pw_proxy *
++pw_proxy_new(struct pw_proxy *factory, /**< factory */
++ const char *type, /**< interface type */
++ uint32_t version, /**< interface version */
++ size_t user_data_size /**< size of user data */);
++
++/** Add an event listener to proxy */
++void pw_proxy_add_listener(struct pw_proxy *proxy,
++ struct spa_hook *listener,
++ const struct pw_proxy_events *events,
++ void *data);
++
++/** Add a listener for the events received from the remote object. The
++ * events depend on the type of the remote object type. */
++void pw_proxy_add_object_listener(struct pw_proxy *proxy, /**< the proxy */
++ struct spa_hook *listener, /**< listener */
++ const void *funcs, /**< proxied functions */
++ void *data /**< data passed to events */);
++
++/** destroy a proxy */
++void pw_proxy_destroy(struct pw_proxy *proxy);
++
++/** Get the user_data. The size was given in \ref pw_proxy_new */
++void *pw_proxy_get_user_data(struct pw_proxy *proxy);
++
++/** Get the local id of the proxy */
++uint32_t pw_proxy_get_id(struct pw_proxy *proxy);
++
++/** Get the type and version of the proxy */
++const char *pw_proxy_get_type(struct pw_proxy *proxy, uint32_t *version);
++
++/** Get the protocol used for the proxy */
++struct pw_protocol *pw_proxy_get_protocol(struct pw_proxy *proxy);
++
++/** Generate an sync method for a proxy. This will generate a done event
++ * with the same seq number of the reply. */
++int pw_proxy_sync(struct pw_proxy *proxy, int seq);
++
++/** Set the global id this proxy is bound to. This is usually used internally
++ * and will also emit the bound event */
++int pw_proxy_set_bound_id(struct pw_proxy *proxy, uint32_t global_id);
++/** Get the global id bound to this proxy of SPA_ID_INVALID when not bound
++ * to a global */
++uint32_t pw_proxy_get_bound_id(struct pw_proxy *proxy);
++
++/** Generate an error for a proxy */
++int pw_proxy_error(struct pw_proxy *proxy, int res, const char *error);
++int pw_proxy_errorf(struct pw_proxy *proxy, int res, const char *error, ...) SPA_PRINTF_FUNC(3, 4);
++
++/** Get the listener of proxy */
++struct spa_hook_list *pw_proxy_get_object_listeners(struct pw_proxy *proxy);
++
++/** Get the marshal functions for the proxy */
++const struct pw_protocol_marshal *pw_proxy_get_marshal(struct pw_proxy *proxy);
++
++/** Install a marshal function on a proxy */
++int pw_proxy_install_marshal(struct pw_proxy *proxy, bool implementor);
++
++#define pw_proxy_notify(p,type,event,version,...) \
++ spa_hook_list_call(pw_proxy_get_object_listeners(p), \
++ type, event, version, ## __VA_ARGS__)
++
++#define pw_proxy_call(p,type,method,version,...) \
++ spa_interface_call((struct spa_interface*)p, \
++ type, method, version, ##__VA_ARGS__)
++
++#define pw_proxy_call_res(p,type,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ spa_interface_call_res((struct spa_interface*)p, \
++ type, _res, method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_PROXY_H */
+diff --git a/third_party/pipewire/pipewire/resource.h b/third_party/pipewire/pipewire/resource.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/resource.h
+@@ -0,0 +1,168 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_RESOURCE_H
++#define PIPEWIRE_RESOURCE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/hook.h>
++
++/** \page page_resource Resource
++ *
++ * \section sec_page_resource Overview
++ *
++ * Resources represent objects owned by a \ref pw_impl_client. They are
++ * the result of binding to a global resource or by calling API that
++ * creates client owned objects.
++ *
++ * The client usually has a proxy object associated with the resource
++ * that it can use to communicate with the resource. See \ref page_proxy.
++ *
++ * Resources are destroyed when the client or the bound object is
++ * destroyed.
++ *
++ */
++
++/** \class pw_resource
++ *
++ * \brief Client owned objects
++ *
++ * Resources are objects owned by a client and are destroyed when the
++ * client disappears.
++ *
++ * See also \ref page_resource
++ */
++struct pw_resource;
++
++#include <pipewire/impl-client.h>
++
++/** Resource events */
++struct pw_resource_events {
++#define PW_VERSION_RESOURCE_EVENTS 0
++ uint32_t version;
++
++ /** The resource is destroyed */
++ void (*destroy) (void *data);
++
++ /** a reply to a ping event completed */
++ void (*pong) (void *data, int seq);
++
++ /** an error occurred on the resource */
++ void (*error) (void *data, int seq, int res, const char *message);
++};
++
++/** Make a new resource for client */
++struct pw_resource *
++pw_resource_new(struct pw_impl_client *client, /**< the client owning the resource */
++ uint32_t id, /**< the remote per client id */
++ uint32_t permissions, /**< permissions on this resource */
++ const char *type, /**< interface of the resource */
++ uint32_t version, /**< requested interface version */
++ size_t user_data_size /**< extra user data size */);
++
++/** Destroy a resource */
++void pw_resource_destroy(struct pw_resource *resource);
++
++/** Remove a resource, like pw_resource_destroy but without sending a
++ * remove_id message to the client */
++void pw_resource_remove(struct pw_resource *resource);
++
++/** Get the client owning this resource */
++struct pw_impl_client *pw_resource_get_client(struct pw_resource *resource);
++
++/** Get the unique id of this resource */
++uint32_t pw_resource_get_id(struct pw_resource *resource);
++
++/** Get the permissions of this resource */
++uint32_t pw_resource_get_permissions(struct pw_resource *resource);
++
++/** Get the type and optionally the version of this resource */
++const char *pw_resource_get_type(struct pw_resource *resource, uint32_t *version);
++
++/** Get the protocol used for this resource */
++struct pw_protocol *pw_resource_get_protocol(struct pw_resource *resource);
++
++/** Get the user data for the resource, the size was given in \ref pw_resource_new */
++void *pw_resource_get_user_data(struct pw_resource *resource);
++
++/** Add an event listener */
++void pw_resource_add_listener(struct pw_resource *resource,
++ struct spa_hook *listener,
++ const struct pw_resource_events *events,
++ void *data);
++
++/** Set the resource implementation. */
++void pw_resource_add_object_listener(struct pw_resource *resource,
++ struct spa_hook *listener,
++ const void *funcs,
++ void *data);
++
++/** Generate an ping event for a resource. This will generate a pong event
++ * with the same \a sequence number in the return value. */
++int pw_resource_ping(struct pw_resource *resource, int seq);
++
++/** Notify global id this resource is bound to */
++int pw_resource_set_bound_id(struct pw_resource *resource, uint32_t global_id);
++
++/** Get the global id this resource is bound to or SPA_ID_INVALID when not bound */
++uint32_t pw_resource_get_bound_id(struct pw_resource *resource);
++
++/** Generate an error for a resource */
++void pw_resource_error(struct pw_resource *resource, int res, const char *error);
++void pw_resource_errorf(struct pw_resource *resource, int res, const char *error, ...) SPA_PRINTF_FUNC(3, 4);
++void pw_resource_errorf_id(struct pw_resource *resource, uint32_t id, int res, const char *error, ...) SPA_PRINTF_FUNC(4, 5);
++
++/** Get the list of object listeners from a resource */
++struct spa_hook_list *pw_resource_get_object_listeners(struct pw_resource *resource);
++
++/** Get the marshal functions for the resource */
++const struct pw_protocol_marshal *pw_resource_get_marshal(struct pw_resource *resource);
++
++/** install a marshal function on a resource */
++int pw_resource_install_marshal(struct pw_resource *resource, bool implementor);
++
++#define pw_resource_notify(r,type,event,version,...) \
++ spa_hook_list_call(pw_resource_get_object_listeners(r), \
++ type, event, version, ## __VA_ARGS__)
++
++#define pw_resource_call(r,type,method,version,...) \
++ spa_interface_call((struct spa_interface*)r, \
++ type, method, version, ##__VA_ARGS__)
++
++#define pw_resource_call_res(r,type,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ spa_interface_call_res((struct spa_interface*)r, \
++ type, _res, method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_RESOURCE_H */
+diff --git a/third_party/pipewire/pipewire/stream.h b/third_party/pipewire/pipewire/stream.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/stream.h
+@@ -0,0 +1,358 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_STREAM_H
++#define PIPEWIRE_STREAM_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** \page page_streams Media Streams
++ *
++ * \section sec_overview Overview
++ *
++ * Media streams are used to exchange data with the PipeWire server. A
++ * stream is a wrapper around a proxy for a \ref pw_client_node with
++ * an adapter. This means the stream will automatically do conversion
++ * to the type required by the server.
++ *
++ * Streams can be used to:
++ *
++ * \li Consume a stream from PipeWire. This is a PW_DIRECTION_INPUT stream.
++ * \li Produce a stream to PipeWire. This is a PW_DIRECTION_OUTPUT stream
++ *
++ * You can connect the stream port to a specific server port or let PipeWire
++ * choose a port for you.
++ *
++ * For more complicated nodes such as filters or ports with multiple
++ * inputs and/or outputs you will need to use the pw_filter or make
++ * a pw_node yourself and export it with \ref pw_core_export.
++ *
++ * \section sec_create Create
++ *
++ * Make a new stream with \ref pw_stream_new(). You will need to specify
++ * a name for the stream and extra properties. You can use \ref
++ * pw_fill_stream_properties() to get a basic set of properties for the
++ * stream.
++ *
++ * Once the stream is created, the state_changed event should be used to
++ * track the state of the stream.
++ *
++ * \section sec_connect Connect
++ *
++ * The stream is initially unconnected. To connect the stream, use
++ * \ref pw_stream_connect(). Pass the desired direction as an argument.
++ *
++ * \subsection ssec_stream_target Stream target
++ *
++ * To make the newly connected stream automatically connect to an existing
++ * PipeWire node, use the \ref PW_STREAM_FLAG_AUTOCONNECT and the port_path
++ * argument while connecting.
++ *
++ * \subsection ssec_stream_formats Stream formats
++ *
++ * An array of possible formats that this stream can consume or provide
++ * must be specified.
++ *
++ * \section sec_format Format negotiation
++ *
++ * After connecting the stream, the server will want to configure some
++ * parameters on the stream. You will be notified of these changes
++ * with the param_changed event.
++ *
++ * When a format param change is emitted, the client should now prepare
++ * itself to deal with the format and complete the negotiation procedure
++ * with a call to \ref pw_stream_update_params().
++ *
++ * As arguments to \ref pw_stream_update_params() an array of spa_param
++ * structures must be given. They contain parameters such as buffer size,
++ * number of buffers, required metadata and other parameters for the
++ * media buffers.
++ *
++ * \section sec_buffers Buffer negotiation
++ *
++ * After completing the format negotiation, PipeWire will allocate and
++ * notify the stream of the buffers that will be used to exchange data
++ * between client and server.
++ *
++ * With the add_buffer event, a stream will be notified of a new buffer
++ * that can be used for data transport. You can attach user_data to these
++ * buffers.
++ *
++ * After the buffers are negotiated, the stream will transition to the
++ * \ref PW_STREAM_STATE_PAUSED state.
++ *
++ * \section sec_streaming Streaming
++ *
++ * From the \ref PW_STREAM_STATE_PAUSED state, the stream can be set to
++ * the \ref PW_STREAM_STATE_STREAMING state by the PipeWire server when
++ * data transport is started.
++ *
++ * Depending on how the stream was connected it will need to Produce or
++ * Consume data for/from PipeWire as explained in the following
++ * subsections.
++ *
++ * \subsection ssec_consume Consume data
++ *
++ * The process event is emitted for each new buffer that can can be
++ * consumed.
++ *
++ * \ref pw_stream_dequeue_buffer() should be used to get the data and
++ * metadata of the buffer.
++ *
++ * When the buffer is no longer in use, call \ref pw_stream_queue_buffer()
++ * to let PipeWire reuse the buffer.
++ *
++ * \subsection ssec_produce Produce data
++ *
++ * \ref pw_stream_dequeue_buffer() gives an empty buffer that can be filled.
++ *
++ * Filled buffers should be queued with \ref pw_stream_queue_buffer().
++ *
++ * The process event is emitted when PipeWire has emptied a buffer that
++ * can now be refilled.
++ *
++ * \section sec_stream_disconnect Disconnect
++ *
++ * Use \ref pw_stream_disconnect() to disconnect a stream after use.
++ */
++/** \class pw_stream
++ *
++ * \brief PipeWire stream object class
++ *
++ * The stream object provides a convenient way to send and
++ * receive data streams from/to PipeWire.
++ *
++ * See also \ref page_streams and \ref page_context_api
++ */
++struct pw_stream;
++
++#include <spa/buffer/buffer.h>
++#include <spa/param/param.h>
++
++/** \enum pw_stream_state The state of a stream \memberof pw_stream */
++enum pw_stream_state {
++ PW_STREAM_STATE_ERROR = -1, /**< the stream is in error */
++ PW_STREAM_STATE_UNCONNECTED = 0, /**< unconnected */
++ PW_STREAM_STATE_CONNECTING = 1, /**< connection is in progress */
++ PW_STREAM_STATE_PAUSED = 2, /**< paused */
++ PW_STREAM_STATE_STREAMING = 3 /**< streaming */
++};
++
++struct pw_buffer {
++ struct spa_buffer *buffer; /**< the spa buffer */
++ void *user_data; /**< user data attached to the buffer */
++ uint64_t size; /**< This field is set by the user and the sum of
++ * all queued buffer is returned in the time info */
++};
++
++struct pw_stream_control {
++ const char *name; /**< name of the control */
++ uint32_t flags; /**< extra flags (unused) */
++ float def; /**< default value */
++ float min; /**< min value */
++ float max; /**< max value */
++ float *values; /**< array of values */
++ uint32_t n_values; /**< number of values in array */
++ uint32_t max_values; /**< max values that can be set on this control */
++};
++
++/** A time structure \memberof pw_stream */
++struct pw_time {
++ int64_t now; /**< the monotonic time */
++ struct spa_fraction rate; /**< the rate of \a ticks and delay */
++ uint64_t ticks; /**< the ticks at \a now. This is the current time that
++ * the remote end is reading/writing. */
++ int64_t delay; /**< delay to device, add to ticks to get the time of the
++ * device. Positive for INPUT streams and
++ * negative for OUTPUT streams. */
++ uint64_t queued; /**< data queued in the stream, this is the sum
++ * of the size fields in the pw_buffer that are
++ * currently queued */
++};
++
++#include <pipewire/pipewire.h>
++
++/** Events for a stream. These events are always called from the mainloop
++ * unless explicitly documented otherwise. */
++struct pw_stream_events {
++#define PW_VERSION_STREAM_EVENTS 0
++ uint32_t version;
++
++ void (*destroy) (void *data);
++ /** when the stream state changes */
++ void (*state_changed) (void *data, enum pw_stream_state old,
++ enum pw_stream_state state, const char *error);
++
++ /** Notify information about a control. */
++ void (*control_info) (void *data, uint32_t id, const struct pw_stream_control *control);
++
++ /** when io changed on the stream. */
++ void (*io_changed) (void *data, uint32_t id, void *area, uint32_t size);
++ /** when a parameter changed */
++ void (*param_changed) (void *data, uint32_t id, const struct spa_pod *param);
++
++ /** when a new buffer was created for this stream */
++ void (*add_buffer) (void *data, struct pw_buffer *buffer);
++ /** when a buffer was destroyed for this stream */
++ void (*remove_buffer) (void *data, struct pw_buffer *buffer);
++
++ /** when a buffer can be queued (for playback streams) or
++ * dequeued (for capture streams). This is normally called from the
++ * mainloop but can also be called directly from the realtime data
++ * thread if the user is prepared to deal with this. */
++ void (*process) (void *data);
++
++ /** The stream is drained */
++ void (*drained) (void *data);
++
++};
++
++/** Convert a stream state to a readable string \memberof pw_stream */
++const char * pw_stream_state_as_string(enum pw_stream_state state);
++
++/** \enum pw_stream_flags Extra flags that can be used in \ref pw_stream_connect() \memberof pw_stream */
++enum pw_stream_flags {
++ PW_STREAM_FLAG_NONE = 0, /**< no flags */
++ PW_STREAM_FLAG_AUTOCONNECT = (1 << 0), /**< try to automatically connect
++ * this stream */
++ PW_STREAM_FLAG_INACTIVE = (1 << 1), /**< start the stream inactive,
++ * pw_stream_set_active() needs to be
++ * called explicitly */
++ PW_STREAM_FLAG_MAP_BUFFERS = (1 << 2), /**< mmap the buffers */
++ PW_STREAM_FLAG_DRIVER = (1 << 3), /**< be a driver */
++ PW_STREAM_FLAG_RT_PROCESS = (1 << 4), /**< call process from the realtime
++ * thread. You MUST use RT safe functions
++ * in the process callback. */
++ PW_STREAM_FLAG_NO_CONVERT = (1 << 5), /**< don't convert format */
++ PW_STREAM_FLAG_EXCLUSIVE = (1 << 6), /**< require exclusive access to the
++ * device */
++ PW_STREAM_FLAG_DONT_RECONNECT = (1 << 7), /**< don't try to reconnect this stream
++ * when the sink/source is removed */
++ PW_STREAM_FLAG_ALLOC_BUFFERS = (1 << 8), /**< the application will allocate buffer
++ * memory. In the add_buffer event, the
++ * data of the buffer should be set */
++};
++
++/** Create a new unconneced \ref pw_stream \memberof pw_stream
++ * \return a newly allocated \ref pw_stream */
++struct pw_stream *
++pw_stream_new(struct pw_core *core, /**< a \ref pw_core */
++ const char *name, /**< a stream media name */
++ struct pw_properties *props /**< stream properties, ownership is taken */);
++
++struct pw_stream *
++pw_stream_new_simple(struct pw_loop *loop, /**< a \ref pw_loop to use */
++ const char *name, /**< a stream media name */
++ struct pw_properties *props,/**< stream properties, ownership is taken */
++ const struct pw_stream_events *events, /**< stream events */
++ void *data /**< data passed to events */);
++
++/** Destroy a stream \memberof pw_stream */
++void pw_stream_destroy(struct pw_stream *stream);
++
++void pw_stream_add_listener(struct pw_stream *stream,
++ struct spa_hook *listener,
++ const struct pw_stream_events *events,
++ void *data);
++
++enum pw_stream_state pw_stream_get_state(struct pw_stream *stream, const char **error);
++
++const char *pw_stream_get_name(struct pw_stream *stream);
++
++struct pw_core *pw_stream_get_core(struct pw_stream *stream);
++
++const struct pw_properties *pw_stream_get_properties(struct pw_stream *stream);
++
++int pw_stream_update_properties(struct pw_stream *stream, const struct spa_dict *dict);
++
++/** Connect a stream for input or output on \a port_path. \memberof pw_stream
++ * \return 0 on success < 0 on error.
++ *
++ * You should connect to the process event and use pw_stream_dequeue_buffer()
++ * to get the latest metadata and data. */
++int
++pw_stream_connect(struct pw_stream *stream, /**< a \ref pw_stream */
++ enum pw_direction direction, /**< the stream direction */
++ uint32_t target_id, /**< the target object id to connect to or
++ * PW_ID_ANY to let the manager
++ * select a target. */
++ enum pw_stream_flags flags, /**< stream flags */
++ const struct spa_pod **params, /**< an array with params. The params
++ * should ideally contain supported
++ * formats. */
++ uint32_t n_params /**< number of items in \a params */);
++
++/** Get the node ID of the stream. \memberof pw_stream
++ * \return node ID. */
++uint32_t
++pw_stream_get_node_id(struct pw_stream *stream);
++
++/** Disconnect \a stream \memberof pw_stream */
++int pw_stream_disconnect(struct pw_stream *stream);
++
++/** Set the stream in error state */
++int pw_stream_set_error(struct pw_stream *stream, /**< a \ref pw_stream */
++ int res, /**< a result code */
++ const char *error, ... /**< an error message */) SPA_PRINTF_FUNC(3, 4);
++
++/** Complete the negotiation process with result code \a res \memberof pw_stream
++ *
++ * This function should be called after notification of the format.
++
++ * When \a res indicates success, \a params contain the parameters for the
++ * allocation state. */
++int
++pw_stream_update_params(struct pw_stream *stream, /**< a \ref pw_stream */
++ const struct spa_pod **params, /**< an array of params. The params should
++ * ideally contain parameters for doing
++ * buffer allocation. */
++ uint32_t n_params /**< number of elements in \a params */);
++
++/** Set control values */
++int pw_stream_set_control(struct pw_stream *stream, uint32_t id, uint32_t n_values, float *values, ...);
++
++/** Query the time on the stream \memberof pw_stream */
++int pw_stream_get_time(struct pw_stream *stream, struct pw_time *time);
++
++/** Get a buffer that can be filled for playback streams or consumed
++ * for capture streams. */
++struct pw_buffer *pw_stream_dequeue_buffer(struct pw_stream *stream);
++
++/** Submit a buffer for playback or recycle a buffer for capture. */
++int pw_stream_queue_buffer(struct pw_stream *stream, struct pw_buffer *buffer);
++
++/** Activate or deactivate the stream \memberof pw_stream */
++int pw_stream_set_active(struct pw_stream *stream, bool active);
++
++/** Flush a stream. When \a drain is true, the drained callback will
++ * be called when all data is played or recorded */
++int pw_stream_flush(struct pw_stream *stream, bool drain);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_STREAM_H */
+diff --git a/third_party/pipewire/pipewire/thread-loop.h b/third_party/pipewire/pipewire/thread-loop.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/thread-loop.h
+@@ -0,0 +1,168 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_THREAD_LOOP_H
++#define PIPEWIRE_THREAD_LOOP_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <pipewire/loop.h>
++
++/** \page page_thread_loop Threaded Loop
++ *
++ * \section sec_thread_loop_overview Overview
++ *
++ * The threaded loop implementation is a special wrapper around the
++ * regular \ref pw_loop implementation.
++ *
++ * The added feature in the threaded loop is that it spawns a new thread
++ * that runs the wrapped loop. This allows a synchronous application to use
++ * the asynchronous API without risking to stall the PipeWire library.
++ *
++ * \section sec_thread_loop_create Creation
++ *
++ * A \ref pw_thread_loop object is created using pw_thread_loop_new().
++ * The \ref pw_loop to wrap must be given as an argument along with the name
++ * for the thread that will be spawned.
++ *
++ * After allocating the object, the thread must be started with
++ * pw_thread_loop_start()
++ *
++ * \section sec_thread_loop_destruction Destruction
++ *
++ * When the PipeWire connection has been terminated, the thread must be
++ * stopped and the resources freed. Stopping the thread is done using
++ * pw_thread_loop_stop(), which must be called without the lock (see
++ * below) held. When that function returns, the thread is stopped and the
++ * \ref pw_thread_loop object can be freed using pw_thread_loop_destroy().
++ *
++ * \section sec_thread_loop_locking Locking
++ *
++ * Since the PipeWire API doesn't allow concurrent accesses to objects,
++ * a locking scheme must be used to guarantee safe usage. The threaded
++ * loop API provides such a scheme through the functions
++ * pw_thread_loop_lock() and pw_thread_loop_unlock().
++ *
++ * The lock is recursive, so it's safe to use it multiple times from the same
++ * thread. Just make sure you call pw_thread_loop_unlock() the same
++ * number of times you called pw_thread_loop_lock().
++ *
++ * The lock needs to be held whenever you call any PipeWire function that
++ * uses an object associated with this loop. Make sure you do not hold
++ * on to the lock more than necessary though, as the threaded loop stops
++ * while the lock is held.
++ *
++ * \section sec_thread_loop_events Events and Callbacks
++ *
++ * All events and callbacks are called with the thread lock held.
++ *
++ */
++/** \class pw_thread_loop
++ *
++ * \brief PipeWire threaded loop object
++ *
++ * The threaded loop object runs a \ref pw_loop in a separate thread
++ * and ensures proper locking is done.
++ *
++ * All of the loop callbacks will be executed with the loop
++ * lock held.
++ *
++ * See also \ref page_thread_loop
++ */
++struct pw_thread_loop;
++
++/** Thread loop events */
++struct pw_thread_loop_events {
++#define PW_VERSION_THREAD_LOOP_EVENTS 0
++ uint32_t version;
++
++ /** the loop is destroyed */
++ void (*destroy) (void *data);
++};
++
++/** Make a new thread loop with the given name and optional properties. */
++struct pw_thread_loop *
++pw_thread_loop_new(const char *name, const struct spa_dict *props);
++
++/** Make a new thread loop with the given loop, name and optional properties.
++ * When \a loop is NULL, a new loop will be created. */
++struct pw_thread_loop *
++pw_thread_loop_new_full(struct pw_loop *loop, const char *name, const struct spa_dict *props);
++
++/** Destroy a thread loop */
++void pw_thread_loop_destroy(struct pw_thread_loop *loop);
++
++/** Add an event listener */
++void pw_thread_loop_add_listener(struct pw_thread_loop *loop,
++ struct spa_hook *listener,
++ const struct pw_thread_loop_events *events,
++ void *data);
++
++/** Get the loop implementation of the thread loop */
++struct pw_loop * pw_thread_loop_get_loop(struct pw_thread_loop *loop);
++
++/** Start the thread loop */
++int pw_thread_loop_start(struct pw_thread_loop *loop);
++
++/** Stop the thread loop */
++void pw_thread_loop_stop(struct pw_thread_loop *loop);
++
++/** Lock the loop. This ensures exclusive ownership of the loop */
++void pw_thread_loop_lock(struct pw_thread_loop *loop);
++
++/** Unlock the loop */
++void pw_thread_loop_unlock(struct pw_thread_loop *loop);
++
++/** Release the lock and wait until some thread calls \ref pw_thread_loop_signal */
++void pw_thread_loop_wait(struct pw_thread_loop *loop);
++
++/** Release the lock and wait a maximum of 'wait_max_sec' seconds
++ * until some thread calls \ref pw_thread_loop_signal or time out */
++int pw_thread_loop_timed_wait(struct pw_thread_loop *loop, int wait_max_sec);
++
++/** Get a struct timespec suitable for \ref pw_thread_loop_timed_wait_full.
++ * Since: 0.3.7 */
++int pw_thread_loop_get_time(struct pw_thread_loop *loop, struct timespec *abstime, int64_t timeout);
++
++/** Release the lock and wait up to \a abstime until some thread calls
++ * \ref pw_thread_loop_signal. Use \ref pw_thread_loop_get_time to make a timeout.
++ * Since: 0.3.7 */
++int pw_thread_loop_timed_wait_full(struct pw_thread_loop *loop, struct timespec *abstime);
++
++/** Signal all threads waiting with \ref pw_thread_loop_wait */
++void pw_thread_loop_signal(struct pw_thread_loop *loop, bool wait_for_accept);
++
++/** Signal all threads executing \ref pw_thread_loop_signal with wait_for_accept */
++void pw_thread_loop_accept(struct pw_thread_loop *loop);
++
++/** Check if inside the thread */
++bool pw_thread_loop_in_thread(struct pw_thread_loop *loop);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_THREAD_LOOP_H */
+diff --git a/third_party/pipewire/pipewire/type.h b/third_party/pipewire/pipewire/type.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/type.h
+@@ -0,0 +1,52 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_TYPE_H
++#define PIPEWIRE_TYPE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/type.h>
++
++enum {
++ PW_TYPE_FIRST = SPA_TYPE_VENDOR_PipeWire,
++};
++
++#define PW_TYPE_INFO_BASE "PipeWire:"
++
++#define PW_TYPE_INFO_Object PW_TYPE_INFO_BASE "Object"
++#define PW_TYPE_INFO_OBJECT_BASE PW_TYPE_INFO_Object ":"
++
++#define PW_TYPE_INFO_Interface PW_TYPE_INFO_BASE "Interface"
++#define PW_TYPE_INFO_INTERFACE_BASE PW_TYPE_INFO_Interface ":"
++
++const struct spa_type_info * pw_type_info(void);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_TYPE_H */
+diff --git a/third_party/pipewire/pipewire/utils.h b/third_party/pipewire/pipewire/utils.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/utils.h
+@@ -0,0 +1,59 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_UTILS_H
++#define PIPEWIRE_UTILS_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/pod/pod.h>
++
++/** \class pw_utils
++ *
++ * Various utility functions
++ */
++
++/** a function to destroy an item \memberof pw_utils */
++typedef void (*pw_destroy_t) (void *object);
++
++const char *
++pw_split_walk(const char *str, const char *delimiter, size_t *len, const char **state);
++
++char **
++pw_split_strv(const char *str, const char *delimiter, int max_tokens, int *n_tokens);
++
++void
++pw_free_strv(char **str);
++
++char *
++pw_strip(char *str, const char *whitespace);
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* PIPEWIRE_UTILS_H */
+diff --git a/third_party/pipewire/pipewire/version.h b/third_party/pipewire/pipewire/version.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/version.h
+@@ -0,0 +1,68 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_VERSION_H
++#define PIPEWIRE_VERSION_H
++
++/* WARNING: Make sure to edit the real source file version.h.in! */
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** Return the version of the header files. Keep in mind that this is
++a macro and not a function, so it is impossible to get the pointer of
++it. */
++#define pw_get_headers_version() ("0.3.13")
++
++/** Return the version of the library the current application is
++ * linked to. */
++const char* pw_get_library_version(void);
++
++/** The current API version. Versions prior to 0.2.0 have
++ * PW_API_VERSION undefined. Please note that this is only ever
++ * increased on incompatible API changes! */
++#define PW_API_VERSION "0.3"
++
++/** The major version of PipeWire. \since 0.2.0 */
++#define PW_MAJOR 0
++
++/** The minor version of PipeWire. \since 0.2.0 */
++#define PW_MINOR 3
++
++/** The micro version of PipeWire. \since 0.2.0 */
++#define PW_MICRO 13
++
++/** Evaluates to TRUE if the PipeWire library version is equal or
++ * newer than the specified. \since 0.2.0 */
++#define PW_CHECK_VERSION(major,minor,micro) \
++ ((PW_MAJOR > (major)) || \
++ (PW_MAJOR == (major) && PW_MINOR > (minor)) || \
++ (PW_MAJOR == (major) && PW_MINOR == (minor) && PW_MICRO >= (micro)))
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_VERION_H */
+diff --git a/third_party/pipewire/pipewire/work-queue.h b/third_party/pipewire/pipewire/work-queue.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/pipewire/work-queue.h
+@@ -0,0 +1,63 @@
++/* PipeWire
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef PIPEWIRE_WORK_QUEUE_H
++#define PIPEWIRE_WORK_QUEUE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** \class pw_work_queue
++ *
++ * PipeWire work queue object
++ */
++struct pw_work_queue;
++
++#include <pipewire/loop.h>
++
++typedef void (*pw_work_func_t) (void *obj, void *data, int res, uint32_t id);
++
++struct pw_work_queue *
++pw_work_queue_new(struct pw_loop *loop);
++
++void
++pw_work_queue_destroy(struct pw_work_queue *queue);
++
++uint32_t
++pw_work_queue_add(struct pw_work_queue *queue,
++ void *obj, int res,
++ pw_work_func_t func, void *data);
++
++int
++pw_work_queue_cancel(struct pw_work_queue *queue, void *obj, uint32_t id);
++
++int
++pw_work_queue_complete(struct pw_work_queue *queue, void *obj, uint32_t seq, int res);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* PIPEWIRE_WORK_QUEUE_H */
+diff --git a/third_party/pipewire/spa/buffer/alloc.h b/third_party/pipewire/spa/buffer/alloc.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/buffer/alloc.h
+@@ -0,0 +1,338 @@
++/* Simple Plugin API
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++#ifndef SPA_BUFFER_ALLOC_H
++#define SPA_BUFFER_ALLOC_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/buffer/buffer.h>
++
++/** information about the buffer layout */
++struct spa_buffer_alloc_info {
++#define SPA_BUFFER_ALLOC_FLAG_INLINE_META (1<<0) /**< add metadata data in the skeleton */
++#define SPA_BUFFER_ALLOC_FLAG_INLINE_CHUNK (1<<1) /**< add chunk data in the skeleton */
++#define SPA_BUFFER_ALLOC_FLAG_INLINE_DATA (1<<2) /**< add buffer data to the skeleton */
++#define SPA_BUFFER_ALLOC_FLAG_INLINE_ALL 0b111
++#define SPA_BUFFER_ALLOC_FLAG_NO_DATA (1<<3) /**< don't set data pointers */
++ uint32_t flags;
++ uint32_t max_align; /**< max of all alignments */
++ uint32_t n_metas;
++ uint32_t n_datas;
++ struct spa_meta *metas;
++ struct spa_data *datas;
++ uint32_t *data_aligns;
++ size_t skel_size; /**< size of the struct spa_buffer and inlined meta/chunk/data */
++ size_t meta_size; /**< size of the meta if not inlined */
++ size_t chunk_size; /**< size of the chunk if not inlined */
++ size_t data_size; /**< size of the data if not inlined */
++ size_t mem_size; /**< size of the total memory if not inlined */
++};
++
++/**
++ * Fill buffer allocation information
++ *
++ * Fill \a info with allocation information needed to allocate buffers
++ * with the given number of metadata and data members.
++ *
++ * The required size of the skeleton (the struct spa_buffer) information
++ * and the memory (for the metadata, chunk and buffer memory) will be
++ * calculated.
++ *
++ * The flags member in \a info should be configured before calling this
++ * functions.
++ *
++ * \param info the information to fill
++ * \param n_metas the number of metadatas for the buffer
++ * \param metas an array of metadata items
++ * \param n_datas the number of datas for the buffer
++ * \param datas an array of \a n_datas items
++ * \param data_aligns \a n_datas alignments
++ * \return 0 on success.
++ * */
++static inline int spa_buffer_alloc_fill_info(struct spa_buffer_alloc_info *info,
++ uint32_t n_metas, struct spa_meta metas[],
++ uint32_t n_datas, struct spa_data datas[],
++ uint32_t data_aligns[])
++{
++ size_t size, *target;
++ uint32_t i;
++
++ info->n_metas = n_metas;
++ info->metas = metas;
++ info->n_datas = n_datas;
++ info->datas = datas;
++ info->data_aligns = data_aligns;
++ info->max_align = 16;
++ info->mem_size = 0;
++ /*
++ * The buffer skeleton is placed in memory like below and can
++ * be accessed as a regular structure.
++ *
++ * +==============================+
++ * | struct spa_buffer |
++ * | uint32_t n_metas | number of metas
++ * | uint32_t n_datas | number of datas
++ * +-| struct spa_meta *metas | pointer to array of metas
++ * +|-| struct spa_data *datas | pointer to array of datas
++ * || +------------------------------+
++ * |+>| struct spa_meta |
++ * | | uint32_t type | metadata
++ * | | uint32_t size | size of metadata
++ * +|--| void *data | pointer to metadata
++ * || | ... <n_metas> | more spa_meta follow
++ * || +------------------------------+
++ * |+->| struct spa_data |
++ * | | uint32_t type | memory type
++ * | | uint32_t flags |
++ * | | int fd | fd of shared memory block
++ * | | uint32_t mapoffset | offset in shared memory of data
++ * | | uint32_t maxsize | size of data block
++ * | +-| void *data | pointer to data
++ * |+|-| struct spa_chunk *chunk | pointer to chunk
++ * ||| | ... <n_datas> | more spa_data follow
++ * ||| +==============================+
++ * VVV
++ *
++ * metadata, chunk and memory can either be placed right
++ * after the skeleton (inlined) or in a separate piece of memory.
++ *
++ * vvv
++ * ||| +==============================+
++ * +-->| meta data memory | metadata memory, 8 byte aligned
++ * || | ... <n_metas> |
++ * || +------------------------------+
++ * +->| struct spa_chunk | memory for n_datas chunks
++ * | | uint32_t offset |
++ * | | uint32_t size |
++ * | | int32_t stride |
++ * | | int32_t dummy |
++ * | | ... <n_datas> chunks |
++ * | +------------------------------+
++ * +>| data | memory for n_datas data, aligned
++ * | ... <n_datas> blocks | according to alignments
++ * +==============================+
++ */
++ info->skel_size = sizeof(struct spa_buffer);
++ info->skel_size += n_metas * sizeof(struct spa_meta);
++ info->skel_size += n_datas * sizeof(struct spa_data);
++
++ for (i = 0, size = 0; i < n_metas; i++)
++ size += SPA_ROUND_UP_N(metas[i].size, 8);
++ info->meta_size = size;
++
++ if (SPA_FLAG_IS_SET(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_META))
++ target = &info->skel_size;
++ else
++ target = &info->mem_size;
++ *target += info->meta_size;
++
++ info->chunk_size = n_datas * sizeof(struct spa_chunk);
++ if (SPA_FLAG_IS_SET(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_CHUNK))
++ target = &info->skel_size;
++ else
++ target = &info->mem_size;
++ *target += info->chunk_size;
++
++ for (i = 0, size = 0; i < n_datas; i++) {
++ info->max_align = SPA_MAX(info->max_align, data_aligns[i]);
++ size = SPA_ROUND_UP_N(size, data_aligns[i]);
++ size += datas[i].maxsize;
++ }
++ info->data_size = size;
++
++ if (!SPA_FLAG_IS_SET(info->flags, SPA_BUFFER_ALLOC_FLAG_NO_DATA) &&
++ SPA_FLAG_IS_SET(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_DATA))
++ target = &info->skel_size;
++ else
++ target = &info->mem_size;
++
++ *target = SPA_ROUND_UP_N(*target, n_datas ? data_aligns[0] : 1);
++ *target += info->data_size;
++ *target = SPA_ROUND_UP_N(*target, info->max_align);
++
++ return 0;
++}
++
++/**
++ * Fill skeleton and data according to the allocation info
++ *
++ * Use the allocation info to create a \ref struct spa_buffer into
++ * \a skel_mem and \a data_mem.
++ *
++ * Depending on the flags given when calling \ref
++ * spa_buffer_alloc_fill_info(), the buffer meta, chunk and memory
++ * will be referenced in either skel_mem or data_mem.
++ *
++ * \param info an allocation info
++ * \param skel_mem memory to hold the \ref struct spa_buffer and the
++ * pointers to meta, chunk and memory.
++ * \param data_mem memory to hold the meta, chunk and memory
++ * \return a \ref struct spa_buffer in \a skel_mem
++ */
++static inline struct spa_buffer *
++spa_buffer_alloc_layout(struct spa_buffer_alloc_info *info,
++ void *skel_mem, void *data_mem)
++{
++ struct spa_buffer *b = (struct spa_buffer*)skel_mem;
++ size_t size;
++ uint32_t i;
++ void **dp, *skel, *data;
++ struct spa_chunk *cp;
++
++ b->n_metas = info->n_metas;
++ b->metas = SPA_MEMBER(b, sizeof(struct spa_buffer), struct spa_meta);
++ b->n_datas = info->n_datas;
++ b->datas = SPA_MEMBER(b->metas, info->n_metas * sizeof(struct spa_meta), struct spa_data);
++
++ skel = SPA_MEMBER(b->datas, info->n_datas * sizeof(struct spa_data), void);
++ data = data_mem;
++
++ if (SPA_FLAG_IS_SET(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_META))
++ dp = &skel;
++ else
++ dp = &data;
++
++ for (i = 0; i < info->n_metas; i++) {
++ struct spa_meta *m = &b->metas[i];
++ *m = info->metas[i];
++ m->data = *dp;
++ *dp = SPA_MEMBER(*dp, SPA_ROUND_UP_N(m->size, 8), void);
++ }
++
++ size = info->n_datas * sizeof(struct spa_chunk);
++ if (SPA_FLAG_IS_SET(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_CHUNK)) {
++ cp = (struct spa_chunk*)skel;
++ skel = SPA_MEMBER(skel, size, void);
++ }
++ else {
++ cp = (struct spa_chunk*)data;
++ data = SPA_MEMBER(data, size, void);
++ }
++
++ if (SPA_FLAG_IS_SET(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_DATA))
++ dp = &skel;
++ else
++ dp = &data;
++
++ for (i = 0; i < info->n_datas; i++) {
++ struct spa_data *d = &b->datas[i];
++
++ *d = info->datas[i];
++ d->chunk = &cp[i];
++ if (!SPA_FLAG_IS_SET(info->flags, SPA_BUFFER_ALLOC_FLAG_NO_DATA)) {
++ *dp = SPA_PTR_ALIGN(*dp, info->data_aligns[i], void);
++ d->data = *dp;
++ *dp = SPA_MEMBER(*dp, d->maxsize, void);
++ }
++ }
++ return b;
++}
++
++/**
++ * Layout an array of buffers
++ *
++ * Use the allocation info to layout the memory of an array of buffers.
++ *
++ * \a skel_mem should point to at least info->skel_size * \a n_buffers bytes
++ * of memory.
++ * \a data_mem should point to at least info->mem_size * \a n_buffers bytes
++ * of memory.
++ *
++ * \param info the allocation info for one buffer
++ * \param n_buffers the number of buffers to create
++ * \param buffer a array with space to hold \a n_buffers pointers to buffers
++ * \param skel_mem memory for the \ref struct spa_buffer
++ * \param data_mem memory for the meta, chunk, memory of the buffer if not
++ * inlined in the skeleton.
++ * \return 0 on success.
++ *
++ */
++static inline int
++spa_buffer_alloc_layout_array(struct spa_buffer_alloc_info *info,
++ uint32_t n_buffers, struct spa_buffer *buffers[],
++ void *skel_mem, void *data_mem)
++{
++ uint32_t i;
++ for (i = 0; i < n_buffers; i++) {
++ buffers[i] = spa_buffer_alloc_layout(info, skel_mem, data_mem);
++ skel_mem = SPA_MEMBER(skel_mem, info->skel_size, void);
++ data_mem = SPA_MEMBER(data_mem, info->mem_size, void);
++ }
++ return 0;
++}
++
++/**
++ * Allocate an array of buffers
++ *
++ * Allocate \a n_buffers with the given metadata, memory and alignment
++ * information.
++ *
++ * The buffer array, structures, data and metadata will all be allocated
++ * in one block of memory with the proper requested alignment.
++ *
++ * \param n_buffers the number of buffers to create
++ * \param flags extra flags
++ * \param n_metas number of metadatas
++ * \param metas \a n_metas metadata specification
++ * \param n_datas number of datas
++ * \param datas \a n_datas memory specification
++ * \param data_aligns \a n_datas alignment specifications
++ * \returns an array of \a n_buffers pointers to \ref struct spa_buffer
++ * with the given metadata, data and alignment or NULL when
++ * allocation failed.
++ *
++ */
++static inline struct spa_buffer **
++spa_buffer_alloc_array(uint32_t n_buffers, uint32_t flags,
++ uint32_t n_metas, struct spa_meta metas[],
++ uint32_t n_datas, struct spa_data datas[],
++ uint32_t data_aligns[])
++{
++
++ struct spa_buffer **buffers;
++ struct spa_buffer_alloc_info info = { flags | SPA_BUFFER_ALLOC_FLAG_INLINE_ALL, };
++ void *skel;
++
++ spa_buffer_alloc_fill_info(&info, n_metas, metas, n_datas, datas, data_aligns);
++
++ buffers = (struct spa_buffer **)calloc(1, info.max_align +
++ n_buffers * (sizeof(struct spa_buffer *) + info.skel_size));
++ if (buffers == NULL)
++ return NULL;
++
++ skel = SPA_MEMBER(buffers, sizeof(struct spa_buffer *) * n_buffers, void);
++ skel = SPA_PTR_ALIGN(skel, info.max_align, void);
++
++ spa_buffer_alloc_layout_array(&info, n_buffers, buffers, skel, NULL);
++
++ return buffers;
++}
++
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_BUFFER_ALLOC_H */
+diff --git a/third_party/pipewire/spa/buffer/buffer.h b/third_party/pipewire/spa/buffer/buffer.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/buffer/buffer.h
+@@ -0,0 +1,114 @@
++/* Simple Plugin API
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_BUFFER_H
++#define SPA_BUFFER_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/buffer/meta.h>
++
++/** \page page_buffer Buffers
++ *
++ * Buffers describe the data and metadata that is exchanged between
++ * ports of a node.
++ */
++
++enum spa_data_type {
++ SPA_DATA_Invalid,
++ SPA_DATA_MemPtr, /**< pointer to memory, the data field in
++ * struct spa_data is set. */
++ SPA_DATA_MemFd, /**< generic fd, mmap to get to memory */
++ SPA_DATA_DmaBuf, /**< fd to dmabuf memory */
++ SPA_DATA_MemId, /**< memory is identified with an id */
++
++ SPA_DATA_LAST, /**< not part of ABI */
++};
++
++/** Chunk of memory, can change for each buffer */
++struct spa_chunk {
++ uint32_t offset; /**< offset of valid data. Should be taken
++ * modulo the data maxsize to get the offset
++ * in the data memory. */
++ uint32_t size; /**< size of valid data. Should be clamped to
++ * maxsize. */
++ int32_t stride; /**< stride of valid data */
++#define SPA_CHUNK_FLAG_NONE 0
++#define SPA_CHUNK_FLAG_CORRUPTED (1u<<0) /**< chunk data is corrupted in some way */
++ int32_t flags; /**< chunk flags */
++};
++
++/** Data for a buffer this stays constant for a buffer */
++struct spa_data {
++ uint32_t type; /**< memory type, one of enum spa_data_type, when
++ * allocating memory, the type contains a bitmask
++ * of allowed types */
++#define SPA_DATA_FLAG_NONE 0
++#define SPA_DATA_FLAG_READABLE (1u<<0) /**< data is readable */
++#define SPA_DATA_FLAG_WRITABLE (1u<<1) /**< data is writable */
++#define SPA_DATA_FLAG_DYNAMIC (1u<<2) /**< data pointer can be changed */
++#define SPA_DATA_FLAG_READWRITE (SPA_DATA_FLAG_READABLE|SPA_DATA_FLAG_WRITABLE)
++ uint32_t flags; /**< data flags */
++ int64_t fd; /**< optional fd for data */
++ uint32_t mapoffset; /**< offset to map fd at */
++ uint32_t maxsize; /**< max size of data */
++ void *data; /**< optional data pointer */
++ struct spa_chunk *chunk; /**< valid chunk of memory */
++};
++
++/** A Buffer */
++struct spa_buffer {
++ uint32_t n_metas; /**< number of metadata */
++ uint32_t n_datas; /**< number of data members */
++ struct spa_meta *metas; /**< array of metadata */
++ struct spa_data *datas; /**< array of data members */
++};
++
++/** Find metadata in a buffer */
++static inline struct spa_meta *spa_buffer_find_meta(const struct spa_buffer *b, uint32_t type)
++{
++ uint32_t i;
++
++ for (i = 0; i < b->n_metas; i++)
++ if (b->metas[i].type == type)
++ return &b->metas[i];
++
++ return NULL;
++}
++
++static inline void *spa_buffer_find_meta_data(const struct spa_buffer *b, uint32_t type, size_t size)
++{
++ struct spa_meta *m;
++ if ((m = spa_buffer_find_meta(b, type)) && m->size >= size)
++ return m->data;
++ return NULL;
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_BUFFER_H */
+diff --git a/third_party/pipewire/spa/buffer/meta.h b/third_party/pipewire/spa/buffer/meta.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/buffer/meta.h
+@@ -0,0 +1,151 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_META_H
++#define SPA_META_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/pod/pod.h>
++
++/** \page page_meta Metadata
++ *
++ * Metadata contains extra information on a buffer.
++ */
++enum spa_meta_type {
++ SPA_META_Invalid,
++ SPA_META_Header, /**< struct spa_meta_header */
++ SPA_META_VideoCrop, /**< struct spa_meta_region with cropping data */
++ SPA_META_VideoDamage, /**< array of struct spa_meta_region with damage */
++ SPA_META_Bitmap, /**< struct spa_meta_bitmap */
++ SPA_META_Cursor, /**< struct spa_meta_cursor */
++ SPA_META_Control, /**< metadata contains a spa_meta_control
++ * associated with the data */
++
++ SPA_META_LAST, /**< not part of ABI/API */
++};
++
++/**
++ * A metadata element.
++ *
++ * This structure is available on the buffer structure and contains
++ * the type of the metadata and a pointer/size to the actual metadata
++ * itself.
++ */
++struct spa_meta {
++ uint32_t type; /**< metadata type, one of enum spa_meta_type */
++ uint32_t size; /**< size of metadata */
++ void *data; /**< pointer to metadata */
++};
++
++#define spa_meta_first(m) ((m)->data)
++#define spa_meta_end(m) SPA_MEMBER((m)->data,(m)->size,void)
++#define spa_meta_check(p,m) (SPA_MEMBER(p,sizeof(*p),void) <= spa_meta_end(m))
++
++/**
++ * Describes essential buffer header metadata such as flags and
++ * timestamps.
++ */
++struct spa_meta_header {
++#define SPA_META_HEADER_FLAG_DISCONT (1 << 0) /**< data is not continuous with previous buffer */
++#define SPA_META_HEADER_FLAG_CORRUPTED (1 << 1) /**< data might be corrupted */
++#define SPA_META_HEADER_FLAG_MARKER (1 << 2) /**< media specific marker */
++#define SPA_META_HEADER_FLAG_HEADER (1 << 3) /**< data contains a codec specific header */
++#define SPA_META_HEADER_FLAG_GAP (1 << 4) /**< data contains media neutral data */
++#define SPA_META_HEADER_FLAG_DELTA_UNIT (1 << 5) /**< cannot be decoded independently */
++ uint32_t flags; /**< flags */
++ uint32_t offset; /**< offset in current cycle */
++ int64_t pts; /**< presentation timestamp */
++ int64_t dts_offset; /**< decoding timestamp as a difference with pts */
++ uint64_t seq; /**< sequence number, increments with a
++ * media specific frequency */
++};
++
++/** metadata structure for Region or an array of these for RegionArray */
++struct spa_meta_region {
++ struct spa_region region;
++};
++
++#define spa_meta_region_is_valid(m) ((m)->region.size.width != 0 && (m)->region.size.height != 0)
++
++/** iterate all the items in a metadata */
++#define spa_meta_for_each(pos,meta) \
++ for (pos = (__typeof(pos))spa_meta_first(meta); \
++ spa_meta_check(pos, meta); \
++ (pos)++)
++
++#define spa_meta_bitmap_is_valid(m) ((m)->format != 0)
++
++/**
++ * Bitmap information
++ *
++ * This metadata contains a bitmap image in the given format and size.
++ * It is typically used for cursor images or other small images that are
++ * better transferred inline.
++ */
++struct spa_meta_bitmap {
++ uint32_t format; /**< bitmap video format, one of enum spa_video_format. 0 is
++ * and invalid format and should be handled as if there is
++ * no new bitmap information. */
++ struct spa_rectangle size; /**< width and height of bitmap */
++ int32_t stride; /**< stride of bitmap data */
++ uint32_t offset; /**< offset of bitmap data in this structure. An offset of
++ * 0 means no image data (invisible), an offset >=
++ * sizeof(struct spa_meta_bitmap) contains valid bitmap
++ * info. */
++};
++
++#define spa_meta_cursor_is_valid(m) ((m)->id != 0)
++
++/**
++ * Cursor information
++ *
++ * Metadata to describe the position and appearance of a pointing device.
++ */
++struct spa_meta_cursor {
++ uint32_t id; /**< cursor id. an id of 0 is an invalid id and means that
++ * there is no new cursor data */
++ uint32_t flags; /**< extra flags */
++ struct spa_point position; /**< position on screen */
++ struct spa_point hotspot; /**< offsets for hotspot in bitmap, this field has no meaning
++ * when there is no valid bitmap (see below) */
++ uint32_t bitmap_offset; /**< offset of bitmap meta in this structure. When the offset
++ * is 0, there is no new bitmap information. When the offset is
++ * >= sizeof(struct spa_meta_cursor) there is a
++ * struct spa_meta_bitmap at the offset. */
++};
++
++/** a timed set of events associated with the buffer */
++struct spa_meta_control {
++ struct spa_pod_sequence sequence;
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_META_H */
+diff --git a/third_party/pipewire/spa/buffer/type-info.h b/third_party/pipewire/spa/buffer/type-info.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/buffer/type-info.h
+@@ -0,0 +1,83 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_BUFFER_TYPES_H
++#define SPA_BUFFER_TYPES_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/buffer/buffer.h>
++#include <spa/buffer/meta.h>
++#include <spa/utils/type.h>
++
++#define SPA_TYPE_INFO_Buffer SPA_TYPE_INFO_POINTER_BASE "Buffer"
++#define SPA_TYPE_INFO_BUFFER_BASE SPA_TYPE_INFO_Buffer ":"
++
++/** Buffers contain data of a certain type */
++#define SPA_TYPE_INFO_Data SPA_TYPE_INFO_ENUM_BASE "Data"
++#define SPA_TYPE_INFO_DATA_BASE SPA_TYPE_INFO_Data ":"
++
++/** base type for fd based memory */
++#define SPA_TYPE_INFO_DATA_Fd SPA_TYPE_INFO_DATA_BASE "Fd"
++#define SPA_TYPE_INFO_DATA_FD_BASE SPA_TYPE_INFO_DATA_Fd ":"
++
++static const struct spa_type_info spa_type_data_type[] = {
++ { SPA_DATA_Invalid, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "Invalid", NULL },
++ { SPA_DATA_MemPtr, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "MemPtr", NULL },
++ { SPA_DATA_MemFd, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_FD_BASE "MemFd", NULL },
++ { SPA_DATA_DmaBuf, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_FD_BASE "DmaBuf", NULL },
++ { SPA_DATA_MemId, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "MemId", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_Meta SPA_TYPE_INFO_POINTER_BASE "Meta"
++#define SPA_TYPE_INFO_META_BASE SPA_TYPE_INFO_Meta ":"
++
++#define SPA_TYPE_INFO_META_Array SPA_TYPE_INFO_META_BASE "Array"
++#define SPA_TYPE_INFO_META_ARRAY_BASE SPA_TYPE_INFO_META_Array ":"
++
++#define SPA_TYPE_INFO_META_Region SPA_TYPE_INFO_META_BASE "Region"
++#define SPA_TYPE_INFO_META_REGION_BASE SPA_TYPE_INFO_META_Region ":"
++
++#define SPA_TYPE_INFO_META_ARRAY_Region SPA_TYPE_INFO_META_ARRAY_BASE "Region"
++#define SPA_TYPE_INFO_META_ARRAY_REGION_BASE SPA_TYPE_INFO_META_ARRAY_Region ":"
++
++static const struct spa_type_info spa_type_meta_type[] = {
++ { SPA_META_Invalid, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Invalid", NULL },
++ { SPA_META_Header, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Header", NULL },
++ { SPA_META_VideoCrop, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_REGION_BASE "VideoCrop", NULL },
++ { SPA_META_VideoDamage, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_ARRAY_REGION_BASE "VideoDamage", NULL },
++ { SPA_META_Bitmap, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Bitmap", NULL },
++ { SPA_META_Cursor, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Cursor", NULL },
++ { SPA_META_Control, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Control", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_BUFFER_TYPES_H */
+diff --git a/third_party/pipewire/spa/control/control.h b/third_party/pipewire/spa/control/control.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/control/control.h
+@@ -0,0 +1,53 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_CONTROL_H
++#define SPA_CONTROL_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/pod/pod.h>
++
++/** Controls
++ *
++ */
++
++/** Different Control types */
++enum spa_control_type {
++ SPA_CONTROL_Invalid,
++ SPA_CONTROL_Properties, /**< data contains a SPA_TYPE_OBJECT_Props */
++ SPA_CONTROL_Midi, /**< data contains a spa_pod_bytes with raw midi data */
++ SPA_CONTROL_OSC, /**< data contains a spa_pod_bytes with an OSC packet */
++
++ SPA_CONTROL_LAST, /**< not part of ABI */
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_CONTROL_H */
+diff --git a/third_party/pipewire/spa/control/type-info.h b/third_party/pipewire/spa/control/type-info.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/control/type-info.h
+@@ -0,0 +1,52 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_CONTROL_TYPES_H
++#define SPA_CONTROL_TYPES_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/type-info.h>
++#include <spa/control/control.h>
++
++/* base for parameter object enumerations */
++#define SPA_TYPE_INFO_Control SPA_TYPE_INFO_ENUM_BASE "Control"
++#define SPA_TYPE_INFO_CONTROL_BASE SPA_TYPE_INFO_Control ":"
++
++static const struct spa_type_info spa_type_control[] = {
++ { SPA_CONTROL_Invalid, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "Invalid", NULL },
++ { SPA_CONTROL_Properties, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "Properties", NULL },
++ { SPA_CONTROL_Midi, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "Midi", NULL },
++ { SPA_CONTROL_OSC, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "OSC", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_CONTROL_TYPES_H */
+diff --git a/third_party/pipewire/spa/debug/buffer.h b/third_party/pipewire/spa/debug/buffer.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/debug/buffer.h
+@@ -0,0 +1,119 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_DEBUG_BUFFER_H
++#define SPA_DEBUG_BUFFER_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/debug/mem.h>
++#include <spa/debug/types.h>
++#include <spa/buffer/type-info.h>
++
++#ifndef spa_debug
++#define spa_debug(...) ({ fprintf(stderr, __VA_ARGS__);fputc('\n', stderr); })
++#endif
++
++static inline int spa_debug_buffer(int indent, const struct spa_buffer *buffer)
++{
++ uint32_t i;
++
++ spa_debug("%*s" "struct spa_buffer %p:", indent, "", buffer);
++ spa_debug("%*s" " n_metas: %u (at %p)", indent, "", buffer->n_metas, buffer->metas);
++ for (i = 0; i < buffer->n_metas; i++) {
++ struct spa_meta *m = &buffer->metas[i];
++ const char *type_name;
++
++ type_name = spa_debug_type_find_name(spa_type_meta_type, m->type);
++ spa_debug("%*s" " meta %d: type %d (%s), data %p, size %d:", indent, "", i, m->type,
++ type_name, m->data, m->size);
++
++ switch (m->type) {
++ case SPA_META_Header:
++ {
++ struct spa_meta_header *h = (struct spa_meta_header*)m->data;
++ spa_debug("%*s" " struct spa_meta_header:", indent, "");
++ spa_debug("%*s" " flags: %08x", indent, "", h->flags);
++ spa_debug("%*s" " offset: %u", indent, "", h->offset);
++ spa_debug("%*s" " seq: %" PRIu64, indent, "", h->seq);
++ spa_debug("%*s" " pts: %" PRIi64, indent, "", h->pts);
++ spa_debug("%*s" " dts_offset: %" PRIi64, indent, "", h->dts_offset);
++ break;
++ }
++ case SPA_META_VideoCrop:
++ {
++ struct spa_meta_region *h = (struct spa_meta_region*)m->data;
++ spa_debug("%*s" " struct spa_meta_region:", indent, "");
++ spa_debug("%*s" " x: %d", indent, "", h->region.position.x);
++ spa_debug("%*s" " y: %d", indent, "", h->region.position.y);
++ spa_debug("%*s" " width: %d", indent, "", h->region.size.width);
++ spa_debug("%*s" " height: %d", indent, "", h->region.size.height);
++ break;
++ }
++ case SPA_META_VideoDamage:
++ {
++ struct spa_meta_region *h;
++ spa_meta_for_each(h, m) {
++ spa_debug("%*s" " struct spa_meta_region:", indent, "");
++ spa_debug("%*s" " x: %d", indent, "", h->region.position.x);
++ spa_debug("%*s" " y: %d", indent, "", h->region.position.y);
++ spa_debug("%*s" " width: %d", indent, "", h->region.size.width);
++ spa_debug("%*s" " height: %d", indent, "", h->region.size.height);
++ }
++ break;
++ }
++ case SPA_META_Bitmap:
++ break;
++ case SPA_META_Cursor:
++ break;
++ default:
++ spa_debug("%*s" " Unknown:", indent, "");
++ spa_debug_mem(5, m->data, m->size);
++ }
++ }
++ spa_debug("%*s" " n_datas: \t%u (at %p)", indent, "", buffer->n_datas, buffer->datas);
++ for (i = 0; i < buffer->n_datas; i++) {
++ struct spa_data *d = &buffer->datas[i];
++ spa_debug("%*s" " type: %d (%s)", indent, "", d->type,
++ spa_debug_type_find_name(spa_type_data_type, d->type));
++ spa_debug("%*s" " flags: %d", indent, "", d->flags);
++ spa_debug("%*s" " data: %p", indent, "", d->data);
++ spa_debug("%*s" " fd: %" PRIi64, indent, "", d->fd);
++ spa_debug("%*s" " offset: %d", indent, "", d->mapoffset);
++ spa_debug("%*s" " maxsize: %u", indent, "", d->maxsize);
++ spa_debug("%*s" " chunk: %p", indent, "", d->chunk);
++ spa_debug("%*s" " offset: %d", indent, "", d->chunk->offset);
++ spa_debug("%*s" " size: %u", indent, "", d->chunk->size);
++ spa_debug("%*s" " stride: %d", indent, "", d->chunk->stride);
++ }
++ return 0;
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_DEBUG_BUFFER_H */
+diff --git a/third_party/pipewire/spa/debug/dict.h b/third_party/pipewire/spa/debug/dict.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/debug/dict.h
+@@ -0,0 +1,52 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_DEBUG_DICT_H
++#define SPA_DEBUG_DICT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/dict.h>
++
++#ifndef spa_debug
++#define spa_debug(...) ({ fprintf(stderr, __VA_ARGS__);fputc('\n', stderr); })
++#endif
++
++static inline int spa_debug_dict(int indent, const struct spa_dict *dict)
++{
++ const struct spa_dict_item *item;
++ spa_debug("%*sflags:%08x n_items:%d", indent, "", dict->flags, dict->n_items);
++ spa_dict_for_each(item, dict) {
++ spa_debug("%*s %s = \"%s\"", indent, "", item->key, item->value);
++ }
++ return 0;
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_DEBUG_DICT_H */
+diff --git a/third_party/pipewire/spa/debug/format.h b/third_party/pipewire/spa/debug/format.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/debug/format.h
+@@ -0,0 +1,201 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_DEBUG_FORMAT_H
++#define SPA_DEBUG_FORMAT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/pod/parser.h>
++#include <spa/debug/types.h>
++#include <spa/param/type-info.h>
++#include <spa/param/format-utils.h>
++
++static inline int
++spa_debug_format_value(const struct spa_type_info *info,
++ uint32_t type, void *body, uint32_t size)
++{
++ switch (type) {
++ case SPA_TYPE_Bool:
++ fprintf(stderr, "%s", *(int32_t *) body ? "true" : "false");
++ break;
++ case SPA_TYPE_Id:
++ {
++ const char *str = spa_debug_type_find_short_name(info, *(int32_t *) body);
++ char tmp[64];
++ if (str == NULL) {
++ snprintf(tmp, sizeof(tmp), "%d", *(int32_t*)body);
++ str = tmp;
++ }
++ fprintf(stderr, "%s", str);
++ break;
++ }
++ case SPA_TYPE_Int:
++ fprintf(stderr, "%d", *(int32_t *) body);
++ break;
++ case SPA_TYPE_Long:
++ fprintf(stderr, "%" PRIi64, *(int64_t *) body);
++ break;
++ case SPA_TYPE_Float:
++ fprintf(stderr, "%f", *(float *) body);
++ break;
++ case SPA_TYPE_Double:
++ fprintf(stderr, "%g", *(double *) body);
++ break;
++ case SPA_TYPE_String:
++ fprintf(stderr, "%s", (char *) body);
++ break;
++ case SPA_TYPE_Rectangle:
++ {
++ struct spa_rectangle *r = (struct spa_rectangle *)body;
++ fprintf(stderr, "%" PRIu32 "x%" PRIu32, r->width, r->height);
++ break;
++ }
++ case SPA_TYPE_Fraction:
++ {
++ struct spa_fraction *f = (struct spa_fraction *)body;
++ fprintf(stderr, "%" PRIu32 "/%" PRIu32, f->num, f->denom);
++ break;
++ }
++ case SPA_TYPE_Bitmap:
++ fprintf(stderr, "Bitmap");
++ break;
++ case SPA_TYPE_Bytes:
++ fprintf(stderr, "Bytes");
++ break;
++ case SPA_TYPE_Array:
++ {
++ void *p;
++ struct spa_pod_array_body *b = (struct spa_pod_array_body *)body;
++ int i = 0;
++ fprintf(stderr, "< ");
++ SPA_POD_ARRAY_BODY_FOREACH(b, size, p) {
++ if (i++ > 0)
++ fprintf(stderr, ", ");
++ spa_debug_format_value(info, b->child.type, p, b->child.size);
++ }
++ fprintf(stderr, " >");
++ break;
++ }
++ default:
++ fprintf(stderr, "INVALID type %d", type);
++ break;
++ }
++ return 0;
++}
++
++static inline int spa_debug_format(int indent,
++ const struct spa_type_info *info, const struct spa_pod *format)
++{
++ const char *media_type;
++ const char *media_subtype;
++ struct spa_pod_prop *prop;
++ uint32_t mtype, mstype;
++
++ if (info == NULL)
++ info = spa_type_format;
++
++ if (format == NULL || SPA_POD_TYPE(format) != SPA_TYPE_Object)
++ return -EINVAL;
++
++ if (spa_format_parse(format, &mtype, &mstype) < 0)
++ return -EINVAL;
++
++ media_type = spa_debug_type_find_name(spa_type_media_type, mtype);
++ media_subtype = spa_debug_type_find_name(spa_type_media_subtype, mstype);
++
++ fprintf(stderr, "%*s %s/%s\n", indent, "",
++ media_type ? spa_debug_type_short_name(media_type) : "unknown",
++ media_subtype ? spa_debug_type_short_name(media_subtype) : "unknown");
++
++ SPA_POD_OBJECT_FOREACH((struct spa_pod_object*)format, prop) {
++ const char *key;
++ const struct spa_type_info *ti;
++ uint32_t i, type, size, n_vals, choice;
++ const struct spa_pod *val;
++ void *vals;
++
++ if (prop->key == SPA_FORMAT_mediaType ||
++ prop->key == SPA_FORMAT_mediaSubtype)
++ continue;
++
++ val = spa_pod_get_values(&prop->value, &n_vals, &choice);
++
++ type = val->type;
++ size = val->size;
++ vals = SPA_POD_BODY(val);
++
++ if (type < SPA_TYPE_None || type >= SPA_TYPE_LAST)
++ continue;
++
++ ti = spa_debug_type_find(info, prop->key);
++ key = ti ? ti->name : NULL;
++
++ fprintf(stderr, "%*s %16s : (%s) ", indent, "",
++ key ? spa_debug_type_short_name(key) : "unknown",
++ spa_debug_type_short_name(spa_types[type].name));
++
++ if (choice == SPA_CHOICE_None) {
++ spa_debug_format_value(ti ? ti->values : NULL, type, vals, size);
++ } else {
++ const char *ssep, *esep, *sep;
++
++ switch (choice) {
++ case SPA_CHOICE_Range:
++ case SPA_CHOICE_Step:
++ ssep = "[ ";
++ sep = ", ";
++ esep = " ]";
++ break;
++ default:
++ case SPA_CHOICE_Enum:
++ case SPA_CHOICE_Flags:
++ ssep = "{ ";
++ sep = ", ";
++ esep = " }";
++ break;
++ }
++
++ fprintf(stderr, "%s", ssep);
++
++ for (i = 1; i < n_vals; i++) {
++ vals = SPA_MEMBER(vals, size, void);
++ if (i > 1)
++ fprintf(stderr, "%s", sep);
++ spa_debug_format_value(ti ? ti->values : NULL, type, vals, size);
++ }
++ fprintf(stderr, "%s", esep);
++ }
++ fprintf(stderr, "\n");
++ }
++ return 0;
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_DEBUG_FORMAT_H */
+diff --git a/third_party/pipewire/spa/debug/mem.h b/third_party/pipewire/spa/debug/mem.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/debug/mem.h
+@@ -0,0 +1,60 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_DEBUG_MEM_H
++#define SPA_DEBUG_MEM_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/dict.h>
++
++#ifndef spa_debug
++#define spa_debug(...) ({ fprintf(stderr, __VA_ARGS__);fputc('\n', stderr); })
++#endif
++
++static inline int spa_debug_mem(int indent, const void *data, size_t size)
++{
++ const uint8_t *t = (const uint8_t*)data;
++ char buffer[512];
++ size_t i;
++ int pos = 0;
++
++ for (i = 0; i < size; i++) {
++ if (i % 16 == 0)
++ pos = sprintf(buffer, "%p: ", &t[i]);
++ pos += sprintf(buffer + pos, "%02x ", t[i]);
++ if (i % 16 == 15 || i == size - 1) {
++ spa_debug("%*s" "%s", indent, "", buffer);
++ }
++ }
++ return 0;
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_DEBUG_MEM_H */
+diff --git a/third_party/pipewire/spa/debug/node.h b/third_party/pipewire/spa/debug/node.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/debug/node.h
+@@ -0,0 +1,57 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_DEBUG_NODE_H
++#define SPA_DEBUG_NODE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/node/node.h>
++#include <spa/debug/dict.h>
++
++#ifndef spa_debug
++#define spa_debug(...) ({ fprintf(stderr, __VA_ARGS__);fputc('\n', stderr); })
++#endif
++
++static inline int spa_debug_port_info(int indent, const struct spa_port_info *info)
++{
++ spa_debug("%*s" "struct spa_port_info %p:", indent, "", info);
++ spa_debug("%*s" " flags: \t%08" PRIx64, indent, "", info->flags);
++ spa_debug("%*s" " rate: \t%d/%d", indent, "", info->rate.num, info->rate.denom);
++ spa_debug("%*s" " props:", indent, "");
++ if (info->props)
++ spa_debug_dict(indent + 2, info->props);
++ else
++ spa_debug("%*s" " none", indent, "");
++ return 0;
++}
++
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_DEBUG_NODE_H */
+diff --git a/third_party/pipewire/spa/debug/pod.h b/third_party/pipewire/spa/debug/pod.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/debug/pod.h
+@@ -0,0 +1,207 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_DEBUG_POD_H
++#define SPA_DEBUG_POD_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/debug/mem.h>
++#include <spa/debug/types.h>
++#include <spa/pod/pod.h>
++#include <spa/pod/iter.h>
++
++#ifndef spa_debug
++#define spa_debug(...) ({ fprintf(stderr, __VA_ARGS__);fputc('\n', stderr); })
++#endif
++
++static inline int
++spa_debug_pod_value(int indent, const struct spa_type_info *info,
++ uint32_t type, void *body, uint32_t size)
++{
++ switch (type) {
++ case SPA_TYPE_Bool:
++ spa_debug("%*s" "Bool %s", indent, "", (*(int32_t *) body) ? "true" : "false");
++ break;
++ case SPA_TYPE_Id:
++ spa_debug("%*s" "Id %-8d (%s)", indent, "", *(int32_t *) body,
++ spa_debug_type_find_name(info, *(int32_t *) body));
++ break;
++ case SPA_TYPE_Int:
++ spa_debug("%*s" "Int %d", indent, "", *(int32_t *) body);
++ break;
++ case SPA_TYPE_Long:
++ spa_debug("%*s" "Long %" PRIi64 "", indent, "", *(int64_t *) body);
++ break;
++ case SPA_TYPE_Float:
++ spa_debug("%*s" "Float %f", indent, "", *(float *) body);
++ break;
++ case SPA_TYPE_Double:
++ spa_debug("%*s" "Double %f", indent, "", *(double *) body);
++ break;
++ case SPA_TYPE_String:
++ spa_debug("%*s" "String \"%s\"", indent, "", (char *) body);
++ break;
++ case SPA_TYPE_Fd:
++ spa_debug("%*s" "Fd %d", indent, "", *(int *) body);
++ break;
++ case SPA_TYPE_Pointer:
++ {
++ struct spa_pod_pointer_body *b = (struct spa_pod_pointer_body *)body;
++ spa_debug("%*s" "Pointer %s %p", indent, "",
++ spa_debug_type_find_name(SPA_TYPE_ROOT, b->type), b->value);
++ break;
++ }
++ case SPA_TYPE_Rectangle:
++ {
++ struct spa_rectangle *r = (struct spa_rectangle *)body;
++ spa_debug("%*s" "Rectangle %dx%d", indent, "", r->width, r->height);
++ break;
++ }
++ case SPA_TYPE_Fraction:
++ {
++ struct spa_fraction *f = (struct spa_fraction *)body;
++ spa_debug("%*s" "Fraction %d/%d", indent, "", f->num, f->denom);
++ break;
++ }
++ case SPA_TYPE_Bitmap:
++ spa_debug("%*s" "Bitmap", indent, "");
++ break;
++ case SPA_TYPE_Array:
++ {
++ struct spa_pod_array_body *b = (struct spa_pod_array_body *)body;
++ void *p;
++ const struct spa_type_info *ti = spa_debug_type_find(SPA_TYPE_ROOT, b->child.type);
++
++ spa_debug("%*s" "Array: child.size %d, child.type %s", indent, "",
++ b->child.size, ti ? ti->name : "unknown");
++
++ SPA_POD_ARRAY_BODY_FOREACH(b, size, p)
++ spa_debug_pod_value(indent + 2, info, b->child.type, p, b->child.size);
++ break;
++ }
++ case SPA_TYPE_Choice:
++ {
++ struct spa_pod_choice_body *b = (struct spa_pod_choice_body *)body;
++ void *p;
++ const struct spa_type_info *ti = spa_debug_type_find(spa_type_choice, b->type);
++
++ spa_debug("%*s" "Choice: type %s, flags %08x %d %d", indent, "",
++ ti ? ti->name : "unknown", b->flags, size, b->child.size);
++
++ SPA_POD_CHOICE_BODY_FOREACH(b, size, p)
++ spa_debug_pod_value(indent + 2, info, b->child.type, p, b->child.size);
++ break;
++ }
++ case SPA_TYPE_Struct:
++ {
++ struct spa_pod *b = (struct spa_pod *)body, *p;
++ spa_debug("%*s" "Struct: size %d", indent, "", size);
++ SPA_POD_FOREACH(b, size, p)
++ spa_debug_pod_value(indent + 2, info, p->type, SPA_POD_BODY(p), p->size);
++ break;
++ }
++ case SPA_TYPE_Object:
++ {
++ struct spa_pod_object_body *b = (struct spa_pod_object_body *)body;
++ struct spa_pod_prop *p;
++ const struct spa_type_info *ti, *ii;
++
++ ti = spa_debug_type_find(info, b->type);
++ ii = ti ? spa_debug_type_find(ti->values, 0) : NULL;
++ ii = ii ? spa_debug_type_find(ii->values, b->id) : NULL;
++
++ spa_debug("%*s" "Object: size %d, type %s (%d), id %s (%d)", indent, "", size,
++ ti ? ti->name : "unknown", b->type, ii ? ii->name : "unknown", b->id);
++
++ info = ti ? ti->values : info;
++
++ SPA_POD_OBJECT_BODY_FOREACH(b, size, p) {
++ ii = spa_debug_type_find(info, p->key);
++
++ spa_debug("%*s" "Prop: key %s (%d), flags %08x", indent+2, "",
++ ii ? ii->name : "unknown", p->key, p->flags);
++
++ spa_debug_pod_value(indent + 4, ii ? ii->values : NULL,
++ p->value.type,
++ SPA_POD_CONTENTS(struct spa_pod_prop, p),
++ p->value.size);
++ }
++ break;
++ }
++ case SPA_TYPE_Sequence:
++ {
++ struct spa_pod_sequence_body *b = (struct spa_pod_sequence_body *)body;
++ const struct spa_type_info *ti, *ii;
++ struct spa_pod_control *c;
++
++ ti = spa_debug_type_find(info, b->unit);
++
++ spa_debug("%*s" "Sequence: size %d, unit %s", indent, "", size,
++ ti ? ti->name : "unknown");
++
++ SPA_POD_SEQUENCE_BODY_FOREACH(b, size, c) {
++ ii = spa_debug_type_find(spa_type_control, c->type);
++
++ spa_debug("%*s" "Control: offset %d, type %s", indent+2, "",
++ c->offset, ii ? ii->name : "unknown");
++
++ spa_debug_pod_value(indent + 4, ii ? ii->values : NULL,
++ c->value.type,
++ SPA_POD_CONTENTS(struct spa_pod_control, c),
++ c->value.size);
++ }
++ break;
++ }
++ case SPA_TYPE_Bytes:
++ spa_debug("%*s" "Bytes", indent, "");
++ spa_debug_mem(indent + 2, body, size);
++ break;
++ case SPA_TYPE_None:
++ spa_debug("%*s" "None", indent, "");
++ spa_debug_mem(indent + 2, body, size);
++ break;
++ default:
++ spa_debug("%*s" "unhandled POD type %d", indent, "", type);
++ break;
++ }
++ return 0;
++}
++
++static inline int spa_debug_pod(int indent,
++ const struct spa_type_info *info, const struct spa_pod *pod)
++{
++ return spa_debug_pod_value(indent, info ? info : SPA_TYPE_ROOT,
++ SPA_POD_TYPE(pod),
++ SPA_POD_BODY(pod),
++ SPA_POD_BODY_SIZE(pod));
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_DEBUG_POD_H */
+diff --git a/third_party/pipewire/spa/debug/types.h b/third_party/pipewire/spa/debug/types.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/debug/types.h
+@@ -0,0 +1,98 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_DEBUG_TYPES_H
++#define SPA_DEBUG_TYPES_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/type-info.h>
++
++#include <string.h>
++
++static inline const struct spa_type_info *spa_debug_type_find(const struct spa_type_info *info, uint32_t type)
++{
++ const struct spa_type_info *res;
++
++ if (info == NULL)
++ info = SPA_TYPE_ROOT;
++
++ while (info && info->name) {
++ if (info->type == SPA_ID_INVALID) {
++ if (info->values && (res = spa_debug_type_find(info->values, type)))
++ return res;
++ }
++ else if (info->type == type)
++ return info;
++ info++;
++ }
++ return NULL;
++}
++
++static inline const char *spa_debug_type_short_name(const char *name)
++{
++ const char *h;
++ if ((h = strrchr(name, ':')) != NULL)
++ name = h + 1;
++ return name;
++}
++
++static inline const char *spa_debug_type_find_name(const struct spa_type_info *info, uint32_t type)
++{
++ if ((info = spa_debug_type_find(info, type)) == NULL)
++ return NULL;
++ return info->name;
++}
++
++static inline const char *spa_debug_type_find_short_name(const struct spa_type_info *info, uint32_t type)
++{
++ const char *str;
++ if ((str = spa_debug_type_find_name(info, type)) == NULL)
++ return NULL;
++ return spa_debug_type_short_name(str);
++}
++
++static inline uint32_t spa_debug_type_find_type(const struct spa_type_info *info, const char *name)
++{
++ if (info == NULL)
++ info = SPA_TYPE_ROOT;
++
++ while (info && info->name) {
++ uint32_t res;
++ if (strcmp(info->name, name) == 0)
++ return info->type;
++ if (info->values && (res = spa_debug_type_find_type(info->values, name)) != SPA_ID_INVALID)
++ return res;
++ info++;
++ }
++ return SPA_ID_INVALID;
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_DEBUG_NODE_H */
+diff --git a/third_party/pipewire/spa/graph/graph.h b/third_party/pipewire/spa/graph/graph.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/graph/graph.h
+@@ -0,0 +1,352 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_GRAPH_H
++#define SPA_GRAPH_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/list.h>
++#include <spa/utils/hook.h>
++#include <spa/node/node.h>
++#include <spa/node/io.h>
++
++#ifndef spa_debug
++#define spa_debug(...)
++#endif
++
++struct spa_graph;
++struct spa_graph_node;
++struct spa_graph_link;
++struct spa_graph_port;
++
++struct spa_graph_state {
++ int status; /**< current status */
++ int32_t required; /**< required number of signals */
++ int32_t pending; /**< number of pending signals */
++};
++
++static inline void spa_graph_state_reset(struct spa_graph_state *state)
++{
++ state->pending = state->required;
++}
++
++struct spa_graph_link {
++ struct spa_list link;
++ struct spa_graph_state *state;
++ int (*signal) (void *data);
++ void *signal_data;
++};
++
++#define spa_graph_link_signal(l) ((l)->signal((l)->signal_data))
++
++#define spa_graph_state_dec(s,c) (__atomic_sub_fetch(&(s)->pending, c, __ATOMIC_SEQ_CST) == 0)
++
++static inline int spa_graph_link_trigger(struct spa_graph_link *link)
++{
++ struct spa_graph_state *state = link->state;
++
++ spa_debug("link %p: state %p: pending %d/%d", link, state,
++ state->pending, state->required);
++
++ if (spa_graph_state_dec(state, 1))
++ spa_graph_link_signal(link);
++
++ return state->status;
++}
++struct spa_graph {
++ uint32_t flags; /* flags */
++ struct spa_graph_node *parent; /* parent node or NULL when driver */
++ struct spa_graph_state *state; /* state of graph */
++ struct spa_list nodes; /* list of nodes of this graph */
++};
++
++struct spa_graph_node_callbacks {
++#define SPA_VERSION_GRAPH_NODE_CALLBACKS 0
++ uint32_t version;
++
++ int (*process) (void *data, struct spa_graph_node *node);
++ int (*reuse_buffer) (void *data, struct spa_graph_node *node,
++ uint32_t port_id, uint32_t buffer_id);
++};
++
++struct spa_graph_node {
++ struct spa_list link; /**< link in graph nodes list */
++ struct spa_graph *graph; /**< owner graph */
++ struct spa_list ports[2]; /**< list of input and output ports */
++ struct spa_list links; /**< list of links to next nodes */
++ uint32_t flags; /**< node flags */
++ struct spa_graph_state *state; /**< state of the node */
++ struct spa_graph_link graph_link; /**< link in graph */
++ struct spa_graph *subgraph; /**< subgraph or NULL */
++ struct spa_callbacks callbacks;
++ struct spa_list sched_link; /**< link for scheduler */
++};
++
++#define spa_graph_node_call(n,method,version,...) \
++({ \
++ int __res = 0; \
++ spa_callbacks_call_res(&(n)->callbacks, \
++ struct spa_graph_node_callbacks, __res, \
++ method, version, ##__VA_ARGS__); \
++ __res; \
++})
++
++#define spa_graph_node_process(n) spa_graph_node_call(n, process, 0, n)
++#define spa_graph_node_reuse_buffer(n,p,i) spa_graph_node_call(n, reuse_buffer, 0, n, p, i)
++
++struct spa_graph_port {
++ struct spa_list link; /**< link in node port list */
++ struct spa_graph_node *node; /**< owner node */
++ enum spa_direction direction; /**< port direction */
++ uint32_t port_id; /**< port id */
++ uint32_t flags; /**< port flags */
++ struct spa_graph_port *peer; /**< peer */
++};
++
++static inline int spa_graph_node_trigger(struct spa_graph_node *node)
++{
++ struct spa_graph_link *l;
++ spa_debug("node %p trigger", node);
++ spa_list_for_each(l, &node->links, link)
++ spa_graph_link_trigger(l);
++ return 0;
++}
++
++static inline int spa_graph_run(struct spa_graph *graph)
++{
++ struct spa_graph_node *n, *t;
++ struct spa_list pending;
++
++ spa_graph_state_reset(graph->state);
++ spa_debug("graph %p run with state %p pending %d/%d", graph, graph->state,
++ graph->state->pending, graph->state->required);
++
++ spa_list_init(&pending);
++
++ spa_list_for_each(n, &graph->nodes, link) {
++ struct spa_graph_state *s = n->state;
++ spa_graph_state_reset(s);
++ spa_debug("graph %p node %p: state %p pending %d/%d status %d", graph, n,
++ s, s->pending, s->required, s->status);
++ if (--s->pending == 0)
++ spa_list_append(&pending, &n->sched_link);
++ }
++ spa_list_for_each_safe(n, t, &pending, sched_link)
++ spa_graph_node_process(n);
++
++ return 0;
++}
++
++static inline int spa_graph_finish(struct spa_graph *graph)
++{
++ spa_debug("graph %p finish", graph);
++ if (graph->parent)
++ return spa_graph_node_trigger(graph->parent);
++ return 0;
++}
++static inline int spa_graph_link_signal_node(void *data)
++{
++ struct spa_graph_node *node = (struct spa_graph_node *)data;
++ spa_debug("node %p call process", node);
++ return spa_graph_node_process(node);
++}
++
++static inline int spa_graph_link_signal_graph(void *data)
++{
++ struct spa_graph_node *node = (struct spa_graph_node *)data;
++ return spa_graph_finish(node->graph);
++}
++
++static inline void spa_graph_init(struct spa_graph *graph, struct spa_graph_state *state)
++{
++ spa_list_init(&graph->nodes);
++ graph->flags = 0;
++ graph->state = state;
++ spa_debug("graph %p init state %p", graph, state);
++}
++
++static inline void
++spa_graph_link_add(struct spa_graph_node *out,
++ struct spa_graph_state *state,
++ struct spa_graph_link *link)
++{
++ link->state = state;
++ state->required++;
++ spa_debug("node %p add link %p to state %p %d", out, link, state, state->required);
++ spa_list_append(&out->links, &link->link);
++}
++
++static inline void spa_graph_link_remove(struct spa_graph_link *link)
++{
++ link->state->required--;
++ spa_debug("link %p state %p remove %d", link, link->state, link->state->required);
++ spa_list_remove(&link->link);
++}
++
++static inline void
++spa_graph_node_init(struct spa_graph_node *node, struct spa_graph_state *state)
++{
++ spa_list_init(&node->ports[SPA_DIRECTION_INPUT]);
++ spa_list_init(&node->ports[SPA_DIRECTION_OUTPUT]);
++ spa_list_init(&node->links);
++ node->flags = 0;
++ node->subgraph = NULL;
++ node->state = state;
++ node->state->required = node->state->pending = 0;
++ node->state->status = SPA_STATUS_OK;
++ node->graph_link.signal = spa_graph_link_signal_graph;
++ node->graph_link.signal_data = node;
++ spa_debug("node %p init state %p", node, state);
++}
++
++
++static inline int spa_graph_node_impl_sub_process(void *data, struct spa_graph_node *node)
++{
++ struct spa_graph *graph = node->subgraph;
++ spa_debug("node %p: sub process %p", node, graph);
++ return spa_graph_run(graph);
++}
++
++static const struct spa_graph_node_callbacks spa_graph_node_sub_impl_default = {
++ SPA_VERSION_GRAPH_NODE_CALLBACKS,
++ .process = spa_graph_node_impl_sub_process,
++};
++
++static inline void spa_graph_node_set_subgraph(struct spa_graph_node *node,
++ struct spa_graph *subgraph)
++{
++ node->subgraph = subgraph;
++ subgraph->parent = node;
++ spa_debug("node %p set subgraph %p", node, subgraph);
++}
++
++static inline void
++spa_graph_node_set_callbacks(struct spa_graph_node *node,
++ const struct spa_graph_node_callbacks *callbacks,
++ void *data)
++{
++ node->callbacks = SPA_CALLBACKS_INIT(callbacks, data);
++}
++
++static inline void
++spa_graph_node_add(struct spa_graph *graph,
++ struct spa_graph_node *node)
++{
++ node->graph = graph;
++ spa_list_append(&graph->nodes, &node->link);
++ node->state->required++;
++ spa_debug("node %p add to graph %p, state %p required %d",
++ node, graph, node->state, node->state->required);
++ spa_graph_link_add(node, graph->state, &node->graph_link);
++}
++
++static inline void spa_graph_node_remove(struct spa_graph_node *node)
++{
++ spa_debug("node %p remove from graph %p, state %p required %d",
++ node, node->graph, node->state, node->state->required);
++ spa_graph_link_remove(&node->graph_link);
++ node->state->required--;
++ spa_list_remove(&node->link);
++}
++
++
++static inline void
++spa_graph_port_init(struct spa_graph_port *port,
++ enum spa_direction direction,
++ uint32_t port_id,
++ uint32_t flags)
++{
++ spa_debug("port %p init type %d id %d", port, direction, port_id);
++ port->direction = direction;
++ port->port_id = port_id;
++ port->flags = flags;
++}
++
++static inline void
++spa_graph_port_add(struct spa_graph_node *node,
++ struct spa_graph_port *port)
++{
++ spa_debug("port %p add to node %p", port, node);
++ port->node = node;
++ spa_list_append(&node->ports[port->direction], &port->link);
++}
++
++static inline void spa_graph_port_remove(struct spa_graph_port *port)
++{
++ spa_debug("port %p remove", port);
++ spa_list_remove(&port->link);
++}
++
++static inline void
++spa_graph_port_link(struct spa_graph_port *out, struct spa_graph_port *in)
++{
++ spa_debug("port %p link to %p %p %p", out, in, in->node, in->node->state);
++ out->peer = in;
++ in->peer = out;
++}
++
++static inline void
++spa_graph_port_unlink(struct spa_graph_port *port)
++{
++ spa_debug("port %p unlink from %p", port, port->peer);
++ if (port->peer) {
++ port->peer->peer = NULL;
++ port->peer = NULL;
++ }
++}
++
++static inline int spa_graph_node_impl_process(void *data, struct spa_graph_node *node)
++{
++ struct spa_node *n = (struct spa_node *)data;
++ struct spa_graph_state *state = node->state;
++
++ spa_debug("node %p: process state %p: %d, node %p", node, state, state->status, n);
++ if ((state->status = spa_node_process(n)) != SPA_STATUS_OK)
++ spa_graph_node_trigger(node);
++
++ return state->status;
++}
++
++static inline int spa_graph_node_impl_reuse_buffer(void *data, struct spa_graph_node *node,
++ uint32_t port_id, uint32_t buffer_id)
++{
++ struct spa_node *n = (struct spa_node *)data;
++ return spa_node_port_reuse_buffer(n, port_id, buffer_id);
++}
++
++static const struct spa_graph_node_callbacks spa_graph_node_impl_default = {
++ SPA_VERSION_GRAPH_NODE_CALLBACKS,
++ .process = spa_graph_node_impl_process,
++ .reuse_buffer = spa_graph_node_impl_reuse_buffer,
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_GRAPH_H */
+diff --git a/third_party/pipewire/spa/monitor/device.h b/third_party/pipewire/spa/monitor/device.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/monitor/device.h
+@@ -0,0 +1,297 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_DEVICE_H
++#define SPA_DEVICE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/hook.h>
++#include <spa/utils/dict.h>
++#include <spa/pod/event.h>
++
++/**
++ * spa_device:
++ *
++ * The device interface can be used to monitor all kinds of devices
++ * and create objects as a result. Objects a typically other
++ * Devices or Nodes.
++ *
++ */
++#define SPA_TYPE_INTERFACE_Device SPA_TYPE_INFO_INTERFACE_BASE "Device"
++
++#define SPA_VERSION_DEVICE 0
++struct spa_device { struct spa_interface iface; };
++
++/**
++ * Information about the device and parameters it supports
++ *
++ * This information is part of the info event on a device.
++ */
++struct spa_device_info {
++#define SPA_VERSION_DEVICE_INFO 0
++ uint32_t version;
++
++#define SPA_DEVICE_CHANGE_MASK_FLAGS (1u<<0)
++#define SPA_DEVICE_CHANGE_MASK_PROPS (1u<<1)
++#define SPA_DEVICE_CHANGE_MASK_PARAMS (1u<<2)
++ uint64_t change_mask;
++ uint64_t flags;
++ const struct spa_dict *props; /**< device properties */
++ struct spa_param_info *params; /**< supported parameters */
++ uint32_t n_params; /**< number of elements in params */
++};
++
++#define SPA_DEVICE_INFO_INIT() (struct spa_device_info){ SPA_VERSION_DEVICE_INFO, }
++
++/**
++ * Information about a device object
++ *
++ * This information is part of the object_info event on the device.
++ */
++struct spa_device_object_info {
++#define SPA_VERSION_DEVICE_OBJECT_INFO 0
++ uint32_t version;
++
++ const char *type; /**< the object type managed by this device */
++ const char *factory_name; /**< a factory name that implements the object */
++
++#define SPA_DEVICE_OBJECT_CHANGE_MASK_FLAGS (1u<<0)
++#define SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS (1u<<1)
++ uint64_t change_mask;
++ uint64_t flags;
++ const struct spa_dict *props; /**< extra object properties */
++};
++
++#define SPA_DEVICE_OBJECT_INFO_INIT() (struct spa_device_object_info){ SPA_VERSION_DEVICE_OBJECT_INFO, }
++
++/** the result of spa_device_enum_params() */
++#define SPA_RESULT_TYPE_DEVICE_PARAMS 1
++struct spa_result_device_params {
++ uint32_t id;
++ uint32_t index;
++ uint32_t next;
++ struct spa_pod *param;
++};
++
++#define SPA_DEVICE_EVENT_INFO 0
++#define SPA_DEVICE_EVENT_RESULT 1
++#define SPA_DEVICE_EVENT_EVENT 2
++#define SPA_DEVICE_EVENT_OBJECT_INFO 3
++#define SPA_DEVICE_EVENT_NUM 4
++
++/**
++ * spa_device_events:
++ *
++ * Events are always emitted from the main thread
++ */
++struct spa_device_events {
++ /** version of the structure */
++#define SPA_VERSION_DEVICE_EVENTS 0
++ uint32_t version;
++
++ /** notify extra information about the device */
++ void (*info) (void *data, const struct spa_device_info *info);
++
++ /** notify a result */
++ void (*result) (void *data, int seq, int res, uint32_t type, const void *result);
++
++ /** a device event */
++ void (*event) (void *data, const struct spa_event *event);
++
++ /** info changed for an object managed by the device, info is NULL when
++ * the object is removed */
++ void (*object_info) (void *data, uint32_t id,
++ const struct spa_device_object_info *info);
++};
++
++#define SPA_DEVICE_METHOD_ADD_LISTENER 0
++#define SPA_DEVICE_METHOD_SYNC 1
++#define SPA_DEVICE_METHOD_ENUM_PARAMS 2
++#define SPA_DEVICE_METHOD_SET_PARAM 3
++#define SPA_DEVICE_METHOD_NUM 4
++
++/**
++ * spa_device_methods:
++ */
++struct spa_device_methods {
++ /* the version of the methods. This can be used to expand this
++ * structure in the future */
++#define SPA_VERSION_DEVICE_METHODS 0
++ uint32_t version;
++
++ /**
++ * Set events to receive asynchronous notifications from
++ * the device.
++ *
++ * Setting the events will trigger the info event and an
++ * object_info event for each managed object on the new
++ * listener.
++ *
++ * \param device a #spa_device
++ * \param listener a listener
++ * \param events a #struct spa_device_events
++ * \param data data passed as first argument in functions of \a events
++ * \return 0 on success
++ * < 0 errno on error
++ */
++ int (*add_listener) (void *object,
++ struct spa_hook *listener,
++ const struct spa_device_events *events,
++ void *data);
++ /**
++ * Perform a sync operation.
++ *
++ * This method will emit the result event with the given sequence
++ * number synchronously or with the returned async return value
++ * asynchronously.
++ *
++ * Because all methods are serialized in the device, this can be used
++ * to wait for completion of all previous method calls.
++ *
++ * \param seq a sequence number
++ * \return 0 on success
++ * -EINVAL when node is NULL
++ * an async result
++ */
++ int (*sync) (void *object, int seq);
++
++ /**
++ * Enumerate the parameters of a device.
++ *
++ * Parameters are identified with an \a id. Some parameters can have
++ * multiple values, see the documentation of the parameter id.
++ *
++ * Parameters can be filtered by passing a non-NULL \a filter.
++ *
++ * The result callback will be called at most \max times with a
++ * struct spa_result_device_params as the result.
++ *
++ * This function must be called from the main thread.
++ *
++ * \param device a \ref spa_device
++ * \param seq a sequence number to pass to the result function
++ * \param id the param id to enumerate
++ * \param index the index of enumeration, pass 0 for the first item.
++ * \param max the maximum number of items to iterate
++ * \param filter and optional filter to use
++ * \return 0 when there are no more parameters to enumerate
++ * -EINVAL when invalid arguments are given
++ * -ENOENT the parameter \a id is unknown
++ * -ENOTSUP when there are no parameters
++ * implemented on \a device
++ */
++ int (*enum_params) (void *object, int seq,
++ uint32_t id, uint32_t index, uint32_t max,
++ const struct spa_pod *filter);
++
++ /**
++ * Set the configurable parameter in \a device.
++ *
++ * Usually, \a param will be obtained from enum_params and then
++ * modified but it is also possible to set another spa_pod
++ * as long as its keys and types match a supported object.
++ *
++ * Objects with property keys that are not known are ignored.
++ *
++ * This function must be called from the main thread.
++ *
++ * \param device a \ref spa_device
++ * \param id the parameter id to configure
++ * \param flags additional flags
++ * \param param the parameter to configure
++ *
++ * \return 0 on success
++ * -EINVAL when invalid arguments are given
++ * -ENOTSUP when there are no parameters implemented on \a device
++ * -ENOENT the parameter is unknown
++ */
++ int (*set_param) (void *object,
++ uint32_t id, uint32_t flags,
++ const struct spa_pod *param);
++};
++
++#define spa_device_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ struct spa_device *_o = o; \
++ spa_interface_call_res(&_o->iface, \
++ struct spa_device_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#define spa_device_add_listener(d,...) spa_device_method(d, add_listener, 0, __VA_ARGS__)
++#define spa_device_sync(d,...) spa_device_method(d, sync, 0, __VA_ARGS__)
++#define spa_device_enum_params(d,...) spa_device_method(d, enum_params, 0, __VA_ARGS__)
++#define spa_device_set_param(d,...) spa_device_method(d, set_param, 0, __VA_ARGS__)
++
++#define SPA_KEY_DEVICE_ENUM_API "device.enum.api" /**< the api used to discover this
++ * device */
++#define SPA_KEY_DEVICE_API "device.api" /**< the api used by the device
++ * Ex. "udev", "alsa", "v4l2". */
++#define SPA_KEY_DEVICE_NAME "device.name" /**< the name of the device */
++#define SPA_KEY_DEVICE_ALIAS "device.alias" /**< alternative name of the device */
++#define SPA_KEY_DEVICE_NICK "device.nick" /**< the device short name */
++#define SPA_KEY_DEVICE_DESCRIPTION "device.description" /**< a device description */
++#define SPA_KEY_DEVICE_ICON "device.icon" /**< icon for the device. A base64 blob
++ * containing PNG image data */
++#define SPA_KEY_DEVICE_ICON_NAME "device.icon-name" /**< an XDG icon name for the device.
++ * Ex. "sound-card-speakers-usb" */
++#define SPA_KEY_DEVICE_PLUGGED_USEC "device.plugged.usec" /**< when the device was plugged */
++
++#define SPA_KEY_DEVICE_BUS_ID "device.bus-id" /**< the device bus-id */
++#define SPA_KEY_DEVICE_BUS_PATH "device.bus-path" /**< bus path to the device in the OS'
++ * format.
++ * Ex. "pci-0000:00:14.0-usb-0:3.2:1.0" */
++#define SPA_KEY_DEVICE_BUS "device.bus" /**< bus of the device if applicable. One of
++ * "isa", "pci", "usb", "firewire",
++ * "bluetooth" */
++#define SPA_KEY_DEVICE_SUBSYSTEM "device.subsystem" /**< device subsystem */
++#define SPA_KEY_DEVICE_SYSFS_PATH "device.sysfs.path" /**< device sysfs path */
++
++#define SPA_KEY_DEVICE_VENDOR_ID "device.vendor.id" /**< vendor ID if applicable */
++#define SPA_KEY_DEVICE_VENDOR_NAME "device.vendor.name" /**< vendor name if applicable */
++#define SPA_KEY_DEVICE_PRODUCT_ID "device.product.id" /**< product ID if applicable */
++#define SPA_KEY_DEVICE_PRODUCT_NAME "device.product.name" /**< product name if applicable */
++#define SPA_KEY_DEVICE_SERIAL "device.serial" /**< Serial number if applicable */
++#define SPA_KEY_DEVICE_CLASS "device.class" /**< device class */
++#define SPA_KEY_DEVICE_CAPABILITIES "device.capabilities" /**< api specific device capabilities */
++#define SPA_KEY_DEVICE_FORM_FACTOR "device.form-factor" /**< form factor if applicable. One of
++ * "internal", "speaker", "handset", "tv",
++ * "webcam", "microphone", "headset",
++ * "headphone", "hands-free", "car", "hifi",
++ * "computer", "portable" */
++#define SPA_KEY_DEVICE_PROFILE "device.profile " /**< profile for the device */
++#define SPA_KEY_DEVICE_PROFILE_SET "device.profile-set" /**< profile set for the device */
++
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_DEVICE_H */
+diff --git a/third_party/pipewire/spa/monitor/event.h b/third_party/pipewire/spa/monitor/event.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/monitor/event.h
+@@ -0,0 +1,54 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2020 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_EVENT_DEVICE_H
++#define SPA_EVENT_DEVICE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/pod/event.h>
++
++/* object id of SPA_TYPE_EVENT_Device */
++enum spa_device_event {
++ SPA_DEVICE_EVENT_ObjectConfig,
++};
++
++#define SPA_DEVICE_EVENT_ID(ev) SPA_EVENT_ID(ev, SPA_TYPE_EVENT_Device)
++#define SPA_DEVICE_EVENT_INIT(id) SPA_EVENT_INIT(SPA_TYPE_EVENT_Device, id)
++
++/* properties for SPA_TYPE_EVENT_Device */
++enum spa_event_device {
++ SPA_EVENT_DEVICE_START,
++
++ SPA_EVENT_DEVICE_Object, /* an object id (Int) */
++ SPA_EVENT_DEVICE_Props, /* properties for an object (SPA_TYPE_OBJECT_Props) */
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_EVENT_DEVICE */
+diff --git a/third_party/pipewire/spa/monitor/utils.h b/third_party/pipewire/spa/monitor/utils.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/monitor/utils.h
+@@ -0,0 +1,95 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2019 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_DEVICE_UTILS_H
++#define SPA_DEVICE_UTILS_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/pod/builder.h>
++#include <spa/monitor/device.h>
++
++struct spa_result_device_params_data {
++ struct spa_pod_builder *builder;
++ struct spa_result_device_params data;
++};
++
++static inline void spa_result_func_device_params(void *data, int seq, int res,
++ uint32_t type, const void *result)
++{
++ struct spa_result_device_params_data *d =
++ (struct spa_result_device_params_data *)data;
++ const struct spa_result_device_params *r =
++ (const struct spa_result_device_params *)result;
++ uint32_t offset = d->builder->state.offset;
++ spa_pod_builder_raw_padded(d->builder, r->param, SPA_POD_SIZE(r->param));
++ d->data.next = r->next;
++ d->data.param = SPA_MEMBER(d->builder->data, offset, struct spa_pod);
++}
++
++static inline int spa_device_enum_params_sync(struct spa_device *device,
++ uint32_t id, uint32_t *index,
++ const struct spa_pod *filter,
++ struct spa_pod **param,
++ struct spa_pod_builder *builder)
++{
++ struct spa_result_device_params_data data = { builder, };
++ struct spa_hook listener = { 0 };
++ static const struct spa_device_events device_events = {
++ SPA_VERSION_DEVICE_EVENTS,
++ .result = spa_result_func_device_params,
++ };
++ int res;
++
++ spa_device_add_listener(device, &listener, &device_events, &data);
++ res = spa_device_enum_params(device, 0, id, *index, 1, filter);
++ spa_hook_remove(&listener);
++
++ if (data.data.param == NULL) {
++ if (res > 0)
++ res = 0;
++ } else {
++ *index = data.data.next;
++ *param = data.data.param;
++ res = 1;
++ }
++ return res;
++}
++
++#define spa_device_emit(hooks,method,version,...) \
++ spa_hook_list_call_simple(hooks, struct spa_device_events, \
++ method, version, ##__VA_ARGS__)
++
++#define spa_device_emit_info(hooks,i) spa_device_emit(hooks,info, 0, i)
++#define spa_device_emit_result(hooks,s,r,t,res) spa_device_emit(hooks,result, 0, s, r, t, res)
++#define spa_device_emit_event(hooks,e) spa_device_emit(hooks,event, 0, e)
++#define spa_device_emit_object_info(hooks,id,i) spa_device_emit(hooks,object_info, 0, id, i)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_DEVICE_UTILS_H */
+diff --git a/third_party/pipewire/spa/node/command.h b/third_party/pipewire/spa/node/command.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/node/command.h
+@@ -0,0 +1,54 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_COMMAND_NODE_H
++#define SPA_COMMAND_NODE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/pod/command.h>
++
++/* object id of SPA_TYPE_COMMAND_Node */
++enum spa_node_command {
++ SPA_NODE_COMMAND_Suspend,
++ SPA_NODE_COMMAND_Pause,
++ SPA_NODE_COMMAND_Start,
++ SPA_NODE_COMMAND_Enable,
++ SPA_NODE_COMMAND_Disable,
++ SPA_NODE_COMMAND_Flush,
++ SPA_NODE_COMMAND_Drain,
++ SPA_NODE_COMMAND_Marker,
++};
++
++#define SPA_NODE_COMMAND_ID(cmd) SPA_COMMAND_ID(cmd, SPA_TYPE_COMMAND_Node)
++#define SPA_NODE_COMMAND_INIT(id) SPA_COMMAND_INIT(SPA_TYPE_COMMAND_Node, id)
++
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_COMMAND_NODE_H */
+diff --git a/third_party/pipewire/spa/node/event.h b/third_party/pipewire/spa/node/event.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/node/event.h
+@@ -0,0 +1,48 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_EVENT_NODE_H
++#define SPA_EVENT_NODE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/pod/event.h>
++
++/* object id of SPA_TYPE_EVENT_Node */
++enum spa_node_event {
++ SPA_NODE_EVENT_Error,
++ SPA_NODE_EVENT_Buffering,
++ SPA_NODE_EVENT_RequestRefresh,
++};
++
++#define SPA_NODE_EVENT_ID(ev) SPA_EVENT_ID(ev, SPA_TYPE_EVENT_Node)
++#define SPA_NODE_EVENT_INIT(id) SPA_EVENT_INIT(SPA_TYPE_EVENT_Node, id)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_EVENT_NODE_H */
+diff --git a/third_party/pipewire/spa/node/io.h b/third_party/pipewire/spa/node/io.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/node/io.h
+@@ -0,0 +1,294 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_IO_H
++#define SPA_IO_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/pod/pod.h>
++
++/** IO areas
++ *
++ * IO information for a port on a node. This is allocated
++ * by the host and configured on a node or all ports for which
++ * IO is requested.
++ *
++ * The plugin will communicate with the host through the IO
++ * areas.
++ */
++
++/** Different IO area types */
++enum spa_io_type {
++ SPA_IO_Invalid,
++ SPA_IO_Buffers, /**< area to exchange buffers, struct spa_io_buffers */
++ SPA_IO_Range, /**< expected byte range, struct spa_io_range */
++ SPA_IO_Clock, /**< area to update clock information, struct spa_io_clock */
++ SPA_IO_Latency, /**< latency reporting, struct spa_io_latency */
++ SPA_IO_Control, /**< area for control messages, struct spa_io_sequence */
++ SPA_IO_Notify, /**< area for notify messages, struct spa_io_sequence */
++ SPA_IO_Position, /**< position information in the graph, struct spa_io_position */
++ SPA_IO_RateMatch, /**< rate matching between nodes, struct spa_io_rate_match */
++ SPA_IO_Memory, /**< memory pointer, struct spa_io_memory */
++};
++
++/**
++ * IO area to exchange buffers.
++ *
++ * A set of buffers should first be configured on the node/port.
++ * Further references to those buffers will be made by using the
++ * id of the buffer.
++ *
++ * If status is SPA_STATUS_OK, the host should ignore
++ * the io area.
++ *
++ * If status is SPA_STATUS_NEED_DATA, the host should:
++ * 1) recycle the buffer in buffer_id, if possible
++ * 2) prepare a new buffer and place the id in buffer_id.
++ *
++ * If status is SPA_STATUS_HAVE_DATA, the host should consume
++ * the buffer in buffer_id and set the state to
++ * SPA_STATUS_NEED_DATA when new data is requested.
++ *
++ * If status is SPA_STATUS_STOPPED, some error occurred on the
++ * port.
++ *
++ * If status is SPA_STATUS_DRAINED, data from the io area was
++ * used to drain.
++ *
++ * Status can also be a negative errno value to indicate errors.
++ * such as:
++ * -EINVAL: buffer_id is invalid
++ * -EPIPE: no more buffers available
++ */
++struct spa_io_buffers {
++#define SPA_STATUS_OK 0
++#define SPA_STATUS_NEED_DATA (1<<0)
++#define SPA_STATUS_HAVE_DATA (1<<1)
++#define SPA_STATUS_STOPPED (1<<2)
++#define SPA_STATUS_DRAINED (1<<3)
++ int32_t status; /**< the status code */
++ uint32_t buffer_id; /**< a buffer id */
++};
++
++#define SPA_IO_BUFFERS_INIT (struct spa_io_buffers) { SPA_STATUS_OK, SPA_ID_INVALID, }
++
++/**
++ * IO area to exchange a memory region
++ */
++struct spa_io_memory {
++ int32_t status; /**< the status code */
++ uint32_t size; /**< the size of \a data */
++ void *data; /**< a memory pointer */
++};
++#define SPA_IO_MEMORY_INIT (struct spa_io_memory) { SPA_STATUS_OK, 0, NULL, }
++
++/** A range, suitable for input ports that can suggest a range to output ports */
++struct spa_io_range {
++ uint64_t offset; /**< offset in range */
++ uint32_t min_size; /**< minimum size of data */
++ uint32_t max_size; /**< maximum size of data */
++};
++
++/**
++ * Absolute time reporting.
++ *
++ * Nodes that can report clocking information will receive this io block.
++ * The application sets the id. This is usually set as part of the
++ * position information but can also be set separately.
++ *
++ * The clock counts the elapsed time according to the clock provider
++ * since the provider was last started.
++ */
++struct spa_io_clock {
++ uint32_t flags; /**< clock flags */
++ uint32_t id; /**< unique clock id, set by application */
++ char name[64]; /**< clock name prefixed with API, set by node. The clock name
++ * is unique per clock and can be used to check if nodes
++ * share the same clock. */
++ uint64_t nsec; /**< time in nanoseconds against monotonic clock */
++ struct spa_fraction rate; /**< rate for position/duration/delay */
++ uint64_t position; /**< current position */
++ uint64_t duration; /**< duration of current cycle */
++ int64_t delay; /**< delay between position and hardware,
++ * positive for capture, negative for playback */
++ double rate_diff; /**< rate difference between clock and monotonic time */
++ uint64_t next_nsec; /**< extimated next wakeup time in nanoseconds */
++ uint32_t padding[8];
++};
++
++/* the size of the video in this cycle */
++struct spa_io_video_size {
++#define SPA_IO_VIDEO_SIZE_VALID (1<<0)
++ uint32_t flags; /**< optional flags */
++ uint32_t stride; /**< video stride in bytes */
++ struct spa_rectangle size; /**< the video size */
++ struct spa_fraction framerate; /**< the minimum framerate, the cycle duration is
++ * always smaller to ensure there is only one
++ * video frame per cycle. */
++ uint32_t padding[4];
++};
++
++/** latency reporting */
++struct spa_io_latency {
++ struct spa_fraction rate; /**< rate for min/max */
++ uint64_t min; /**< min latency */
++ uint64_t max; /**< max latency */
++};
++
++/** control stream, io area for SPA_IO_Control and SPA_IO_Notify */
++struct spa_io_sequence {
++ struct spa_pod_sequence sequence; /**< sequence of timed events */
++};
++
++/** bar and beat segment */
++struct spa_io_segment_bar {
++#define SPA_IO_SEGMENT_BAR_FLAG_VALID (1<<0)
++ uint32_t flags; /**< extra flags */
++ uint32_t offset; /**< offset in segment of this beat */
++ float signature_num; /**< time signature numerator */
++ float signature_denom; /**< time signature denominator */
++ double bpm; /**< beats per minute */
++ double beat; /**< current beat in segment */
++ uint32_t padding[8];
++};
++
++/** video frame segment */
++struct spa_io_segment_video {
++#define SPA_IO_SEGMENT_VIDEO_FLAG_VALID (1<<0)
++#define SPA_IO_SEGMENT_VIDEO_FLAG_DROP_FRAME (1<<1)
++#define SPA_IO_SEGMENT_VIDEO_FLAG_PULL_DOWN (1<<2)
++#define SPA_IO_SEGMENT_VIDEO_FLAG_INTERLACED (1<<3)
++ uint32_t flags; /**< flags */
++ uint32_t offset; /**< offset in segment */
++ struct spa_fraction framerate;
++ uint32_t hours;
++ uint32_t minutes;
++ uint32_t seconds;
++ uint32_t frames;
++ uint32_t field_count; /**< 0 for progressive, 1 and 2 for interlaced */
++ uint32_t padding[11];
++};
++
++/**
++ * A segment converts a running time to a segment (stream) position.
++ *
++ * The segment position is valid when the current running time is between
++ * start and start + duration. The position is then
++ * calculated as:
++ *
++ * (running time - start) * rate + position;
++ *
++ * Support for looping is done by specifying the LOOPING flags with a
++ * non-zero duration. When the running time reaches start + duration,
++ * duration is added to start and the loop repeats.
++ *
++ * Care has to be taken when the running time + clock.duration extends
++ * past the start + duration from the segment; the user should correctly
++ * wrap around and partially repeat the loop in the current cycle.
++ *
++ * Extra information can be placed in the segment by setting the valid flags
++ * and filling up the corresponding structures.
++ */
++struct spa_io_segment {
++ uint32_t version;
++#define SPA_IO_SEGMENT_FLAG_LOOPING (1<<0) /**< after the duration, the segment repeats */
++#define SPA_IO_SEGMENT_FLAG_NO_POSITION (1<<1) /**< position is invalid. The position can be invalid
++ * after a seek, for example, when the exact mapping
++ * of the extra segment info (bar, video, ...) to
++ * position has not been determined yet */
++ uint32_t flags; /**< extra flags */
++ uint64_t start; /**< value of running time when this
++ * info is active. Can be in the future for
++ * pending changes. It does not have to be in
++ * exact multiples of the clock duration. */
++ uint64_t duration; /**< duration when this info becomes invalid expressed
++ * in running time. If the duration is 0, this
++ * segment extends to the next segment. If the
++ * segment becomes invalid and the looping flag is
++ * set, the segment repeats. */
++ double rate; /**< overal rate of the segment, can be negative for
++ * backwards time reporting. */
++ uint64_t position; /**< The position when the running time == start.
++ * can be invalid when the owner of the extra segment
++ * information has not yet made the mapping. */
++
++ struct spa_io_segment_bar bar;
++ struct spa_io_segment_video video;
++};
++
++enum spa_io_position_state {
++ SPA_IO_POSITION_STATE_STOPPED,
++ SPA_IO_POSITION_STATE_STARTING,
++ SPA_IO_POSITION_STATE_RUNNING,
++};
++
++/** the maximum number of segments visible in the future */
++#define SPA_IO_POSITION_MAX_SEGMENTS 8
++
++/**
++ * The position information adds extra meaning to the raw clock times.
++ *
++ * It is set on all nodes and the clock id will contain the clock of the
++ * driving node in the graph.
++ *
++ * The position information contains 1 or more segments that convert the
++ * raw clock times to a stream time. They are sorted based on their
++ * start times, and thus the order in which they will activate in
++ * the future. This makes it possible to look ahead in the scheduled
++ * segments and anticipate the changes in the timeline.
++ */
++struct spa_io_position {
++ struct spa_io_clock clock; /**< clock position of driver, always valid and
++ * read only */
++ struct spa_io_video_size video; /**< size of the video in the current cycle */
++ int64_t offset; /**< an offset to subtract from the clock position
++ * to get a running time. This is the time that
++ * the state has been in the RUNNING state and the
++ * time that should be used to compare the segment
++ * start values against. */
++ uint32_t state; /**< one of enum spa_io_position_state */
++
++ uint32_t n_segments; /**< number of segments */
++ struct spa_io_segment segments[SPA_IO_POSITION_MAX_SEGMENTS]; /**< segments */
++};
++
++/** rate matching */
++struct spa_io_rate_match {
++ uint32_t delay; /**< extra delay in samples for resampler */
++ uint32_t size; /**< requested input size for resampler */
++ double rate; /**< rate for resampler */
++#define SPA_IO_RATE_MATCH_FLAG_ACTIVE (1 << 0)
++ uint32_t flags; /**< extra flags */
++ uint32_t padding[7];
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_IO_H */
+diff --git a/third_party/pipewire/spa/node/keys.h b/third_party/pipewire/spa/node/keys.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/node/keys.h
+@@ -0,0 +1,54 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2019 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_NODE_KEYS_H
++#define SPA_NODE_KEYS_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** node keys */
++#define SPA_KEY_NODE_NAME "node.name" /**< a node name */
++#define SPA_KEY_NODE_LATENCY "node.latency" /**< the requested node latency */
++
++#define SPA_KEY_NODE_DRIVER "node.driver" /**< the node can be a driver */
++#define SPA_KEY_NODE_ALWAYS_PROCESS "node.always-process" /**< call the process function even if
++ * not linked. */
++#define SPA_KEY_NODE_PAUSE_ON_IDLE "node.pause-on-idle" /**< if the node should be paused
++ * immediately when idle. */
++#define SPA_KEY_NODE_MONITOR "node.monitor" /**< the node has monitor ports */
++
++
++/** port keys */
++#define SPA_KEY_PORT_NAME "port.name" /**< a port name */
++#define SPA_KEY_PORT_ALIAS "port.alias" /**< a port alias */
++#define SPA_KEY_PORT_MONITOR "port.monitor" /**< this port is a monitor port */
++
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_NODE_KEYS_H */
+diff --git a/third_party/pipewire/spa/node/node.h b/third_party/pipewire/spa/node/node.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/node/node.h
+@@ -0,0 +1,662 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_NODE_H
++#define SPA_NODE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/type.h>
++#include <spa/utils/hook.h>
++#include <spa/buffer/buffer.h>
++#include <spa/node/event.h>
++#include <spa/node/command.h>
++
++
++/**
++ * A spa_node is a component that can consume and produce buffers.
++ */
++#define SPA_TYPE_INTERFACE_Node SPA_TYPE_INFO_INTERFACE_BASE "Node"
++
++#define SPA_VERSION_NODE 0
++struct spa_node { struct spa_interface iface; };
++
++/**
++ * Node information structure
++ *
++ * Contains the basic node information.
++ */
++struct spa_node_info {
++ uint32_t max_input_ports;
++ uint32_t max_output_ports;
++#define SPA_NODE_CHANGE_MASK_FLAGS (1u<<0)
++#define SPA_NODE_CHANGE_MASK_PROPS (1u<<1)
++#define SPA_NODE_CHANGE_MASK_PARAMS (1u<<2)
++ uint64_t change_mask;
++
++#define SPA_NODE_FLAG_RT (1u<<0) /**< node can do real-time processing */
++#define SPA_NODE_FLAG_IN_DYNAMIC_PORTS (1u<<1) /**< input ports can be added/removed */
++#define SPA_NODE_FLAG_OUT_DYNAMIC_PORTS (1u<<2) /**< output ports can be added/removed */
++#define SPA_NODE_FLAG_IN_PORT_CONFIG (1u<<3) /**< input ports can be reconfigured with
++ * PortConfig parameter */
++#define SPA_NODE_FLAG_OUT_PORT_CONFIG (1u<<4) /**< output ports can be reconfigured with
++ * PortConfig parameter */
++#define SPA_NODE_FLAG_NEED_CONFIGURE (1u<<5) /**< node needs configuration before it can
++ * be started. */
++#define SPA_NODE_FLAG_ASYNC (1u<<6) /**< the process function might not
++ * immediateley produce or consume data
++ * but might offload the work to a worker
++ * thread. */
++ uint64_t flags;
++ struct spa_dict *props; /**< extra node properties */
++ struct spa_param_info *params; /**< parameter information */
++ uint32_t n_params; /**< number of items in \a params */
++};
++
++#define SPA_NODE_INFO_INIT() (struct spa_node_info) { 0, }
++
++/**
++ * Port information structure
++ *
++ * Contains the basic port information.
++ */
++struct spa_port_info {
++#define SPA_PORT_CHANGE_MASK_FLAGS (1u<<0)
++#define SPA_PORT_CHANGE_MASK_RATE (1u<<1)
++#define SPA_PORT_CHANGE_MASK_PROPS (1u<<2)
++#define SPA_PORT_CHANGE_MASK_PARAMS (1u<<3)
++ uint64_t change_mask;
++
++#define SPA_PORT_FLAG_REMOVABLE (1u<<0) /**< port can be removed */
++#define SPA_PORT_FLAG_OPTIONAL (1u<<1) /**< processing on port is optional */
++#define SPA_PORT_FLAG_CAN_ALLOC_BUFFERS (1u<<2) /**< the port can allocate buffer data */
++#define SPA_PORT_FLAG_IN_PLACE (1u<<3) /**< the port can process data in-place and
++ * will need a writable input buffer */
++#define SPA_PORT_FLAG_NO_REF (1u<<4) /**< the port does not keep a ref on the buffer.
++ * This means the node will always completely
++ * consume the input buffer and it will be
++ * recycled after process. */
++#define SPA_PORT_FLAG_LIVE (1u<<5) /**< output buffers from this port are
++ * timestamped against a live clock. */
++#define SPA_PORT_FLAG_PHYSICAL (1u<<6) /**< connects to some device */
++#define SPA_PORT_FLAG_TERMINAL (1u<<7) /**< data was not created from this port
++ * or will not be made available on another
++ * port */
++#define SPA_PORT_FLAG_DYNAMIC_DATA (1u<<8) /**< data pointer on buffers can be changed.
++ * Only the buffer data marked as DYNAMIC
++ * can be changed. */
++ uint64_t flags; /**< port flags */
++ struct spa_fraction rate; /**< rate of sequence numbers on port */
++ const struct spa_dict *props; /**< extra port properties */
++ struct spa_param_info *params; /**< parameter information */
++ uint32_t n_params; /**< number of items in \a params */
++};
++
++#define SPA_PORT_INFO_INIT() (struct spa_port_info) { 0, }
++
++#define SPA_RESULT_TYPE_NODE_ERROR 1
++#define SPA_RESULT_TYPE_NODE_PARAMS 2
++
++/** an error result */
++struct spa_result_node_error {
++ const char *message;
++};
++
++/** the result of enum_params or port_enum_params. */
++struct spa_result_node_params {
++ uint32_t id; /**< id of parameter */
++ uint32_t index; /**< index of parameter */
++ uint32_t next; /**< next index of iteration */
++ struct spa_pod *param; /**< the result param */
++};
++
++#define SPA_NODE_EVENT_INFO 0
++#define SPA_NODE_EVENT_PORT_INFO 1
++#define SPA_NODE_EVENT_RESULT 2
++#define SPA_NODE_EVENT_EVENT 3
++#define SPA_NODE_EVENT_NUM 4
++
++/** events from the spa_node.
++ *
++ * All event are called from the main thread and multiple
++ * listeners can be registered for the events with
++ * spa_node_add_listener().
++ */
++struct spa_node_events {
++#define SPA_VERSION_NODE_EVENTS 0
++ uint32_t version; /**< version of this structure */
++
++ /** Emitted when info changes */
++ void (*info) (void *data, const struct spa_node_info *info);
++
++ /** Emitted when port info changes, NULL when port is removed */
++ void (*port_info) (void *data,
++ enum spa_direction direction, uint32_t port,
++ const struct spa_port_info *info);
++
++ /** notify a result.
++ *
++ * Some methods will trigger a result event with an optional
++ * result of the given type. Look at the documentation of the
++ * method to know when to expect a result event.
++ *
++ * The result event can be called synchronously, as an event
++ * called from inside the method itself, in which case the seq
++ * number passed to the method will be passed unchanged.
++ *
++ * The result event will be called asynchronously when the
++ * method returned an async return value. In this case, the seq
++ * number in the result will match the async return value of
++ * the method call. Users should match the seq number from
++ * request to the reply.
++ */
++ void (*result) (void *data, int seq, int res,
++ uint32_t type, const void *result);
++
++ /**
++ * \param node a spa_node
++ * \param event the event that was emitted
++ *
++ * This will be called when an out-of-bound event is notified
++ * on \a node.
++ */
++ void (*event) (void *data, const struct spa_event *event);
++};
++
++#define SPA_NODE_CALLBACK_READY 0
++#define SPA_NODE_CALLBACK_REUSE_BUFFER 1
++#define SPA_NODE_CALLBACK_XRUN 2
++#define SPA_NODE_CALLBACK_NUM 3
++
++/** Node callbacks
++ *
++ * Callbacks are called from the real-time data thread. Only
++ * one callback structure can be set on an spa_node.
++ */
++struct spa_node_callbacks {
++#define SPA_VERSION_NODE_CALLBACKS 0
++ uint32_t version;
++ /**
++ * \param node a spa_node
++ *
++ * The node is ready for processing.
++ *
++ * When this function is NULL, synchronous operation is requested
++ * on the ports.
++ */
++ int (*ready) (void *data, int state);
++
++ /**
++ * \param node a spa_node
++ * \param port_id an input port_id
++ * \param buffer_id the buffer id to be reused
++ *
++ * The node has a buffer that can be reused.
++ *
++ * When this function is NULL, the buffers to reuse will be set in
++ * the io area of the input ports.
++ */
++ int (*reuse_buffer) (void *data,
++ uint32_t port_id,
++ uint32_t buffer_id);
++
++ /**
++ * \param data user data
++ * \param trigger the timestamp in microseconds when the xrun happened
++ * \param delay the amount of microseconds of xrun.
++ * \param info an object with extra info (NULL for now)
++ *
++ * The node has encountered an over or underrun
++ *
++ * The info contains an object with more information
++ */
++ int (*xrun) (void *data, uint64_t trigger, uint64_t delay,
++ struct spa_pod *info);
++};
++
++
++/** flags that can be passed to set_param and port_set_param functions */
++#define SPA_NODE_PARAM_FLAG_TEST_ONLY (1 << 0) /**< Just check if the param is accepted */
++#define SPA_NODE_PARAM_FLAG_FIXATE (1 << 1) /**< Fixate the non-optional unset fields */
++#define SPA_NODE_PARAM_FLAG_NEAREST (1 << 2) /**< Allow set fields to be rounded to the
++ * nearest allowed field value. */
++
++/** flags to pass to the use_buffers functions */
++#define SPA_NODE_BUFFERS_FLAG_ALLOC (1 << 0) /**< Allocate memory for the buffers. This flag
++ * is ignored when the port does not have the
++ * SPA_PORT_FLAG_CAN_ALLOC_BUFFERS set. */
++
++
++#define SPA_NODE_METHOD_ADD_LISTENER 0
++#define SPA_NODE_METHOD_SET_CALLBACKS 1
++#define SPA_NODE_METHOD_SYNC 2
++#define SPA_NODE_METHOD_ENUM_PARAMS 3
++#define SPA_NODE_METHOD_SET_PARAM 4
++#define SPA_NODE_METHOD_SET_IO 5
++#define SPA_NODE_METHOD_SEND_COMMAND 6
++#define SPA_NODE_METHOD_ADD_PORT 7
++#define SPA_NODE_METHOD_REMOVE_PORT 8
++#define SPA_NODE_METHOD_PORT_ENUM_PARAMS 9
++#define SPA_NODE_METHOD_PORT_SET_PARAM 10
++#define SPA_NODE_METHOD_PORT_USE_BUFFERS 11
++#define SPA_NODE_METHOD_PORT_SET_IO 12
++#define SPA_NODE_METHOD_PORT_REUSE_BUFFER 13
++#define SPA_NODE_METHOD_PROCESS 14
++#define SPA_NODE_METHOD_NUM 15
++
++/**
++ * Node methods
++ */
++struct spa_node_methods {
++ /* the version of the node methods. This can be used to expand this
++ * structure in the future */
++#define SPA_VERSION_NODE_METHODS 0
++ uint32_t version;
++
++ /**
++ * Adds an event listener on \a node.
++ *
++ * Setting the events will trigger the info event and a
++ * port_info event for each managed port on the new
++ * listener.
++ *
++ * \param node a #spa_node
++ * \param listener a listener
++ * \param events a #struct spa_node_events
++ * \param data data passed as first argument in functions of \a events
++ * \return 0 on success
++ * < 0 errno on error
++ */
++ int (*add_listener) (void *object,
++ struct spa_hook *listener,
++ const struct spa_node_events *events,
++ void *data);
++ /**
++ * Set callbacks to on \a node.
++ * if \a callbacks is NULL, the current callbacks are removed.
++ *
++ * This function must be called from the main thread.
++ *
++ * All callbacks are called from the data thread.
++ *
++ * \param node a spa_node
++ * \param callbacks callbacks to set
++ * \return 0 on success
++ * -EINVAL when node is NULL
++ */
++ int (*set_callbacks) (void *object,
++ const struct spa_node_callbacks *callbacks,
++ void *data);
++ /**
++ * Perform a sync operation.
++ *
++ * This method will emit the result event with the given sequence
++ * number synchronously or with the returned async return value
++ * asynchronously.
++ *
++ * Because all methods are serialized in the node, this can be used
++ * to wait for completion of all previous method calls.
++ *
++ * \param seq a sequence number
++ * \return 0 on success
++ * -EINVAL when node is NULL
++ * an async result
++ */
++ int (*sync) (void *object, int seq);
++
++ /**
++ * Enumerate the parameters of a node.
++ *
++ * Parameters are identified with an \a id. Some parameters can have
++ * multiple values, see the documentation of the parameter id.
++ *
++ * Parameters can be filtered by passing a non-NULL \a filter.
++ *
++ * The function will emit the result event up to \a max times with
++ * the result value. The seq in the result will either be the \a seq
++ * number when executed synchronously or the async return value of
++ * this function when executed asynchronously.
++ *
++ * This function must be called from the main thread.
++ *
++ * \param node a \ref spa_node
++ * \param seq a sequence number to pass to the result event when
++ * this method is executed synchronously.
++ * \param id the param id to enumerate
++ * \param start the index of enumeration, pass 0 for the first item
++ * \param max the maximum number of parameters to enumerate
++ * \param filter and optional filter to use
++ *
++ * \return 0 when no more items can be iterated.
++ * -EINVAL when invalid arguments are given
++ * -ENOENT the parameter \a id is unknown
++ * -ENOTSUP when there are no parameters
++ * implemented on \a node
++ * an async return value when the result event will be
++ * emitted later.
++ */
++ int (*enum_params) (void *object, int seq,
++ uint32_t id, uint32_t start, uint32_t max,
++ const struct spa_pod *filter);
++
++ /**
++ * Set the configurable parameter in \a node.
++ *
++ * Usually, \a param will be obtained from enum_params and then
++ * modified but it is also possible to set another spa_pod
++ * as long as its keys and types match a supported object.
++ *
++ * Objects with property keys that are not known are ignored.
++ *
++ * This function must be called from the main thread.
++ *
++ * \param node a \ref spa_node
++ * \param id the parameter id to configure
++ * \param flags additional flags
++ * \param param the parameter to configure
++ *
++ * \return 0 on success
++ * -EINVAL when node is NULL
++ * -ENOTSUP when there are no parameters implemented on \a node
++ * -ENOENT the parameter is unknown
++ */
++ int (*set_param) (void *object,
++ uint32_t id, uint32_t flags,
++ const struct spa_pod *param);
++
++ /**
++ * Configure the given memory area with \a id on \a node. This
++ * structure is allocated by the host and is used to exchange
++ * data and parameters with the node.
++ *
++ * Setting an \a io of NULL will disable the node io.
++ *
++ * This function must be called from the main thread.
++ *
++ * \param id the id of the io area, the available ids can be
++ * enumerated with the node parameters.
++ * \param data a io area memory
++ * \param size the size of \a data
++ * \return 0 on success
++ * -EINVAL when invalid input is given
++ * -ENOENT when \a id is unknown
++ * -ENOSPC when \a size is too small
++ */
++ int (*set_io) (void *object,
++ uint32_t id, void *data, size_t size);
++
++ /**
++ * Send a command to a node.
++ *
++ * Upon completion, a command might change the state of a node.
++ *
++ * This function must be called from the main thread.
++ *
++ * \param node a spa_node
++ * \param command a spa_command
++ * \return 0 on success
++ * -EINVAL when node or command is NULL
++ * -ENOTSUP when this node can't process commands
++ * -EINVAL \a command is an invalid command
++ */
++ int (*send_command) (void *object, const struct spa_command *command);
++
++ /**
++ * Make a new port with \a port_id. The caller should use the lowest unused
++ * port id for the given \a direction.
++ *
++ * Port ids should be between 0 and max_ports as obtained from the info
++ * event.
++ *
++ * This function must be called from the main thread.
++ *
++ * \param node a spa_node
++ * \param direction a #enum spa_direction
++ * \param port_id an unused port id
++ * \param props extra properties
++ * \return 0 on success
++ * -EINVAL when node is NULL
++ */
++ int (*add_port) (void *object,
++ enum spa_direction direction, uint32_t port_id,
++ const struct spa_dict *props);
++
++ /**
++ * Remove a port with \a port_id.
++ *
++ * \param node a spa_node
++ * \param direction a #enum spa_direction
++ * \param port_id a port id
++ * \return 0 on success
++ * -EINVAL when node is NULL or when port_id is unknown or
++ * when the port can't be removed.
++ */
++ int (*remove_port) (void *object,
++ enum spa_direction direction, uint32_t port_id);
++
++ /**
++ * Enumerate all possible parameters of \a id on \a port_id of \a node
++ * that are compatible with \a filter.
++ *
++ * The result parameters can be queried and modified and ultimately be used
++ * to call port_set_param.
++ *
++ * The function will emit the result event up to \a max times with
++ * the result value. The seq in the result event will either be the
++ * \a seq number when executed synchronously or the async return
++ * value of this function when executed asynchronously.
++ *
++ * This function must be called from the main thread.
++ *
++ * \param node a spa_node
++ * \param seq a sequence number to pass to the result event when
++ * this method is executed synchronously.
++ * \param direction an spa_direction
++ * \param port_id the port to query
++ * \param id the parameter id to query
++ * \param start the first index to query, 0 to get the first item
++ * \param max the maximum number of params to query
++ * \param filter a parameter filter or NULL for no filter
++ *
++ * \return 0 when no more items can be iterated.
++ * -EINVAL when invalid parameters are given
++ * -ENOENT when \a id is unknown
++ * an async return value when the result event will be
++ * emitted later.
++ */
++ int (*port_enum_params) (void *object, int seq,
++ enum spa_direction direction, uint32_t port_id,
++ uint32_t id, uint32_t start, uint32_t max,
++ const struct spa_pod *filter);
++ /**
++ * Set a parameter on \a port_id of \a node.
++ *
++ * When \a param is NULL, the parameter will be unset.
++ *
++ * This function must be called from the main thread.
++ *
++ * \param node a #struct spa_node
++ * \param direction a #enum spa_direction
++ * \param port_id the port to configure
++ * \param id the parameter id to set
++ * \param flags optional flags
++ * \param param a #struct spa_pod with the parameter to set
++ * \return 0 on success
++ * 1 on success, the value of \a param might have been
++ * changed depending on \a flags and the final value can be found by
++ * doing port_enum_params.
++ * -EINVAL when node is NULL or invalid arguments are given
++ * -ESRCH when one of the mandatory param
++ * properties is not specified and SPA_NODE_PARAM_FLAG_FIXATE was
++ * not set in \a flags.
++ * -ESRCH when the type or size of a property is not correct.
++ * -ENOENT when the param id is not found
++ */
++ int (*port_set_param) (void *object,
++ enum spa_direction direction,
++ uint32_t port_id,
++ uint32_t id, uint32_t flags,
++ const struct spa_pod *param);
++
++ /**
++ * Tell the port to use the given buffers
++ *
++ * When \a flags contains SPA_NODE_BUFFERS_FLAG_ALLOC, the data
++ * in the buffers should point to an array of at least 1 data entry
++ * with the desired supported type that will be filled by this function.
++ *
++ * The port should also have a spa_io_buffers io area configured to exchange
++ * the buffers with the port.
++ *
++ * For an input port, all the buffers will remain dequeued.
++ * Once a buffer has been queued on a port in the spa_io_buffers,
++ * it should not be reused until the reuse_buffer callback is notified
++ * or when the buffer has been returned in the spa_io_buffers of
++ * the port.
++ *
++ * For output ports, all buffers will be queued in the port. When process
++ * returns SPA_STATUS_HAVE_DATA, buffers are available in one or more
++ * of the spa_io_buffers areas.
++ *
++ * When a buffer can be reused, port_reuse_buffer() should be called or the
++ * buffer_id should be placed in the spa_io_buffers area before calling
++ * process.
++ *
++ * Passing NULL as \a buffers will remove the reference that the port has
++ * on the buffers.
++ *
++ * When this function returns async, use the spa_node_sync operation to
++ * wait for completion.
++ *
++ * This function must be called from the main thread.
++ *
++ * \param object an object implementing the interface
++ * \param direction a port direction
++ * \param port_id a port id
++ * \param flags extra flags
++ * \param buffers an array of buffer pointers
++ * \param n_buffers number of elements in \a buffers
++ * \return 0 on success
++ */
++ int (*port_use_buffers) (void *object,
++ enum spa_direction direction,
++ uint32_t port_id,
++ uint32_t flags,
++ struct spa_buffer **buffers,
++ uint32_t n_buffers);
++
++ /**
++ * Configure the given memory area with \a id on \a port_id. This
++ * structure is allocated by the host and is used to exchange
++ * data and parameters with the port.
++ *
++ * Setting an \a io of NULL will disable the port io.
++ *
++ * This function must be called from the main thread.
++ *
++ * \param direction a spa_direction
++ * \param port_id a port id
++ * \param id the id of the io area, the available ids can be
++ * enumerated with the port parameters.
++ * \param data a io area memory
++ * \param size the size of \a data
++ * \return 0 on success
++ * -EINVAL when invalid input is given
++ * -ENOENT when \a id is unknown
++ * -ENOSPC when \a size is too small
++ */
++ int (*port_set_io) (void *object,
++ enum spa_direction direction,
++ uint32_t port_id,
++ uint32_t id,
++ void *data, size_t size);
++
++ /**
++ * Tell an output port to reuse a buffer.
++ *
++ * This function must be called from the data thread.
++ *
++ * \param node a spa_node
++ * \param port_id a port id
++ * \param buffer_id a buffer id to reuse
++ * \return 0 on success
++ * -EINVAL when node is NULL
++ */
++ int (*port_reuse_buffer) (void *object, uint32_t port_id, uint32_t buffer_id);
++
++ /**
++ * Process the node
++ *
++ * This function must be called from the data thread.
++ *
++ * Output io areas with SPA_STATUS_NEED_DATA will recycle the
++ * buffers if any.
++ *
++ * Input areas with SPA_STATUS_HAVE_DATA are consumed if possible
++ * and the status is set to SPA_STATUS_NEED_DATA or SPA_STATUS_OK.
++ *
++ * When the node has new output buffers, the SPA_STATUS_HAVE_DATA
++ * bit will be set.
++ *
++ * When the node can accept new input in the next cycle, the
++ * SPA_STATUS_NEED_DATA bit will be set.
++ */
++ int (*process) (void *object);
++};
++
++#define spa_node_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ struct spa_node *_n = o; \
++ spa_interface_call_res(&_n->iface, \
++ struct spa_node_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#define spa_node_add_listener(n,...) spa_node_method(n, add_listener, 0, __VA_ARGS__)
++#define spa_node_set_callbacks(n,...) spa_node_method(n, set_callbacks, 0, __VA_ARGS__)
++#define spa_node_sync(n,...) spa_node_method(n, sync, 0, __VA_ARGS__)
++#define spa_node_enum_params(n,...) spa_node_method(n, enum_params, 0, __VA_ARGS__)
++#define spa_node_set_param(n,...) spa_node_method(n, set_param, 0, __VA_ARGS__)
++#define spa_node_set_io(n,...) spa_node_method(n, set_io, 0, __VA_ARGS__)
++#define spa_node_send_command(n,...) spa_node_method(n, send_command, 0, __VA_ARGS__)
++#define spa_node_add_port(n,...) spa_node_method(n, add_port, 0, __VA_ARGS__)
++#define spa_node_remove_port(n,...) spa_node_method(n, remove_port, 0, __VA_ARGS__)
++#define spa_node_port_enum_params(n,...) spa_node_method(n, port_enum_params, 0, __VA_ARGS__)
++#define spa_node_port_set_param(n,...) spa_node_method(n, port_set_param, 0, __VA_ARGS__)
++#define spa_node_port_use_buffers(n,...) spa_node_method(n, port_use_buffers, 0, __VA_ARGS__)
++#define spa_node_port_set_io(n,...) spa_node_method(n, port_set_io, 0, __VA_ARGS__)
++
++#define spa_node_port_reuse_buffer(n,...) spa_node_method(n, port_reuse_buffer, 0, __VA_ARGS__)
++#define spa_node_process(n) spa_node_method(n, process, 0)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_NODE_H */
+diff --git a/third_party/pipewire/spa/node/type-info.h b/third_party/pipewire/spa/node/type-info.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/node/type-info.h
+@@ -0,0 +1,94 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_NODE_TYPES_H
++#define SPA_NODE_TYPES_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/type-info.h>
++
++#include <spa/node/command.h>
++#include <spa/node/event.h>
++#include <spa/node/io.h>
++
++#define SPA_TYPE_INFO_IO SPA_TYPE_INFO_ENUM_BASE "IO"
++#define SPA_TYPE_INFO_IO_BASE SPA_TYPE_INFO_IO ":"
++
++static const struct spa_type_info spa_type_io[] = {
++ { SPA_IO_Invalid, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Invalid", NULL },
++ { SPA_IO_Buffers, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Buffers", NULL },
++ { SPA_IO_Range, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Range", NULL },
++ { SPA_IO_Clock, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Clock", NULL },
++ { SPA_IO_Latency, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Latency", NULL },
++ { SPA_IO_Control, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Control", NULL },
++ { SPA_IO_Notify, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Notify", NULL },
++ { SPA_IO_Position, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Position", NULL },
++ { SPA_IO_RateMatch, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "RateMatch", NULL },
++ { SPA_IO_Memory, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Memory", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_NodeEvent SPA_TYPE_INFO_EVENT_BASE "Node"
++#define SPA_TYPE_INFO_NODE_EVENT_BASE SPA_TYPE_INFO_NodeEvent ":"
++
++static const struct spa_type_info spa_type_node_event_id[] = {
++ { SPA_NODE_EVENT_Error, SPA_TYPE_Int, SPA_TYPE_INFO_NODE_EVENT_BASE "Error", NULL },
++ { SPA_NODE_EVENT_Buffering, SPA_TYPE_Int, SPA_TYPE_INFO_NODE_EVENT_BASE "Buffering", NULL },
++ { SPA_NODE_EVENT_RequestRefresh, SPA_TYPE_Int, SPA_TYPE_INFO_NODE_EVENT_BASE "RequestRefresh", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++static const struct spa_type_info spa_type_node_event[] = {
++ { 0, SPA_TYPE_Id, SPA_TYPE_INFO_NODE_EVENT_BASE, spa_type_node_event_id },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_NodeCommand SPA_TYPE_INFO_COMMAND_BASE "Node"
++#define SPA_TYPE_INFO_NODE_COMMAND_BASE SPA_TYPE_INFO_NodeCommand ":"
++
++static const struct spa_type_info spa_type_node_command_id[] = {
++ { SPA_NODE_COMMAND_Suspend, SPA_TYPE_Int, SPA_TYPE_INFO_NODE_COMMAND_BASE "Suspend", NULL },
++ { SPA_NODE_COMMAND_Pause, SPA_TYPE_Int, SPA_TYPE_INFO_NODE_COMMAND_BASE "Pause", NULL },
++ { SPA_NODE_COMMAND_Start, SPA_TYPE_Int, SPA_TYPE_INFO_NODE_COMMAND_BASE "Start", NULL },
++ { SPA_NODE_COMMAND_Enable, SPA_TYPE_Int, SPA_TYPE_INFO_NODE_COMMAND_BASE "Enable", NULL },
++ { SPA_NODE_COMMAND_Disable, SPA_TYPE_Int, SPA_TYPE_INFO_NODE_COMMAND_BASE "Disable", NULL },
++ { SPA_NODE_COMMAND_Flush, SPA_TYPE_Int, SPA_TYPE_INFO_NODE_COMMAND_BASE "Flush", NULL },
++ { SPA_NODE_COMMAND_Drain, SPA_TYPE_Int, SPA_TYPE_INFO_NODE_COMMAND_BASE "Drain", NULL },
++ { SPA_NODE_COMMAND_Marker, SPA_TYPE_Int, SPA_TYPE_INFO_NODE_COMMAND_BASE "Marker", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++static const struct spa_type_info spa_type_node_command[] = {
++ { 0, SPA_TYPE_Id, SPA_TYPE_INFO_NODE_COMMAND_BASE, spa_type_node_command_id },
++ { 0, 0, NULL, NULL },
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_NODE_TYPES_H */
+diff --git a/third_party/pipewire/spa/node/utils.h b/third_party/pipewire/spa/node/utils.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/node/utils.h
+@@ -0,0 +1,144 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2019 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_NODE_UTILS_H
++#define SPA_NODE_UTILS_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/pod/builder.h>
++
++#include <spa/node/node.h>
++
++struct spa_result_node_params_data {
++ struct spa_pod_builder *builder;
++ struct spa_result_node_params data;
++};
++
++static inline void spa_result_func_node_params(void *data,
++ int seq, int res, uint32_t type, const void *result)
++{
++ struct spa_result_node_params_data *d =
++ (struct spa_result_node_params_data *) data;
++ const struct spa_result_node_params *r =
++ (const struct spa_result_node_params *) result;
++ uint32_t offset = d->builder->state.offset;
++ spa_pod_builder_raw_padded(d->builder, r->param, SPA_POD_SIZE(r->param));
++ d->data.next = r->next;
++ d->data.param = SPA_MEMBER(d->builder->data, offset, struct spa_pod);
++}
++
++static inline int spa_node_enum_params_sync(struct spa_node *node,
++ uint32_t id, uint32_t *index,
++ const struct spa_pod *filter,
++ struct spa_pod **param,
++ struct spa_pod_builder *builder)
++{
++ struct spa_result_node_params_data data = { builder, };
++ struct spa_hook listener = { 0 };
++ static const struct spa_node_events node_events = {
++ SPA_VERSION_NODE_EVENTS,
++ .result = spa_result_func_node_params,
++ };
++ int res;
++
++ res = spa_node_add_listener(node, &listener, &node_events, &data);
++ if (res >= 0) {
++ res = spa_node_enum_params(node, 0, id, *index, 1, filter);
++ spa_hook_remove(&listener);
++ }
++
++ if (data.data.param == NULL) {
++ if (res > 0)
++ res = 0;
++ } else {
++ *index = data.data.next;
++ *param = data.data.param;
++ res = 1;
++ }
++ return res;
++}
++
++static inline int spa_node_port_enum_params_sync(struct spa_node *node,
++ enum spa_direction direction, uint32_t port_id,
++ uint32_t id, uint32_t *index,
++ const struct spa_pod *filter,
++ struct spa_pod **param,
++ struct spa_pod_builder *builder)
++{
++ struct spa_result_node_params_data data = { builder, };
++ struct spa_hook listener = { 0 };
++ static const struct spa_node_events node_events = {
++ SPA_VERSION_NODE_EVENTS,
++ .result = spa_result_func_node_params,
++ };
++ int res;
++
++ res = spa_node_add_listener(node, &listener, &node_events, &data);
++ if (res >= 0) {
++ res = spa_node_port_enum_params(node, 0, direction, port_id,
++ id, *index, 1, filter);
++ spa_hook_remove(&listener);
++ }
++
++ if (data.data.param == NULL) {
++ if (res > 0)
++ res = 0;
++ } else {
++ *index = data.data.next;
++ *param = data.data.param;
++ res = 1;
++ }
++ return res;
++}
++
++#define spa_node_emit(hooks,method,version,...) \
++ spa_hook_list_call_simple(hooks, struct spa_node_events, \
++ method, version, ##__VA_ARGS__)
++
++#define spa_node_emit_info(hooks,...) spa_node_emit(hooks,info, 0, __VA_ARGS__)
++#define spa_node_emit_port_info(hooks,...) spa_node_emit(hooks,port_info, 0, __VA_ARGS__)
++#define spa_node_emit_result(hooks,...) spa_node_emit(hooks,result, 0, __VA_ARGS__)
++#define spa_node_emit_event(hooks,...) spa_node_emit(hooks,event, 0, __VA_ARGS__)
++
++
++#define spa_node_call(callbacks,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ spa_callbacks_call_res(callbacks, struct spa_node_callbacks, \
++ _res, method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#define spa_node_call_ready(hook,...) spa_node_call(hook, ready, 0, __VA_ARGS__)
++#define spa_node_call_reuse_buffer(hook,...) spa_node_call(hook, reuse_buffer, 0, __VA_ARGS__)
++#define spa_node_call_xrun(hook,...) spa_node_call(hook, xrun, 0, __VA_ARGS__)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_NODE_UTILS_H */
+diff --git a/third_party/pipewire/spa/param/audio/format-utils.h b/third_party/pipewire/spa/param/audio/format-utils.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/audio/format-utils.h
+@@ -0,0 +1,105 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_PARAM_AUDIO_FORMAT_UTILS_H
++#define SPA_PARAM_AUDIO_FORMAT_UTILS_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++
++#include <spa/pod/parser.h>
++#include <spa/pod/builder.h>
++#include <spa/param/audio/format.h>
++#include <spa/param/format-utils.h>
++
++static inline int
++spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info)
++{
++ struct spa_pod *position = NULL;
++ int res;
++ info->flags = 0;
++ res = spa_pod_parse_object(format,
++ SPA_TYPE_OBJECT_Format, NULL,
++ SPA_FORMAT_AUDIO_format, SPA_POD_Id(&info->format),
++ SPA_FORMAT_AUDIO_rate, SPA_POD_Int(&info->rate),
++ SPA_FORMAT_AUDIO_channels, SPA_POD_Int(&info->channels),
++ SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
++ if (position == NULL ||
++ !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS))
++ SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
++
++ return res;
++}
++
++static inline int
++spa_format_audio_dsp_parse(const struct spa_pod *format, struct spa_audio_info_dsp *info)
++{
++ int res;
++ res = spa_pod_parse_object(format,
++ SPA_TYPE_OBJECT_Format, NULL,
++ SPA_FORMAT_AUDIO_format, SPA_POD_Id(&info->format));
++ return res;
++}
++
++static inline struct spa_pod *
++spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_raw *info)
++{
++ struct spa_pod_frame f;
++ spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
++ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
++ SPA_FORMAT_AUDIO_format, SPA_POD_Id(info->format),
++ SPA_FORMAT_AUDIO_rate, SPA_POD_Int(info->rate),
++ SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info->channels),
++ 0);
++
++ if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) {
++ spa_pod_builder_prop(builder, SPA_FORMAT_AUDIO_position, 0);
++ spa_pod_builder_array(builder, sizeof(uint32_t), SPA_TYPE_Id,
++ info->channels, info->position);
++ }
++ return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
++}
++
++static inline struct spa_pod *
++spa_format_audio_dsp_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_dsp *info)
++{
++ struct spa_pod_frame f;
++ spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
++ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp),
++ SPA_FORMAT_AUDIO_format, SPA_POD_Id(info->format),
++ 0);
++ return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_PARAM_AUDIO_FORMAT_UTILS_H */
+diff --git a/third_party/pipewire/spa/param/audio/format.h b/third_party/pipewire/spa/param/audio/format.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/audio/format.h
+@@ -0,0 +1,48 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_PARAM_AUDIO_FORMAT_H
++#define SPA_PARAM_AUDIO_FORMAT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/param/format.h>
++#include <spa/param/audio/raw.h>
++
++struct spa_audio_info {
++ uint32_t media_type;
++ uint32_t media_subtype;
++ union {
++ struct spa_audio_info_raw raw;
++ struct spa_audio_info_dsp dsp;
++ } info;
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_PARAM_AUDIO_FORMAT_H */
+diff --git a/third_party/pipewire/spa/param/audio/layout.h b/third_party/pipewire/spa/param/audio/layout.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/audio/layout.h
+@@ -0,0 +1,184 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_AUDIO_LAYOUT_H
++#define SPA_AUDIO_LAYOUT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#ifndef __FreeBSD__
++#include <endian.h>
++#endif
++
++#include <spa/param/audio/raw.h>
++
++struct spa_audio_layout_info {
++ uint32_t n_channels;
++ uint32_t position[SPA_AUDIO_MAX_CHANNELS];
++};
++
++#define SPA_AUDIO_LAYOUT_Mono 1, { SPA_AUDIO_CHANNEL_MONO, }
++#define SPA_AUDIO_LAYOUT_Stereo 2, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, }
++#define SPA_AUDIO_LAYOUT_Quad 4, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, }
++#define SPA_AUDIO_LAYOUT_Pentagonal 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
++ SPA_AUDIO_CHANNEL_FC, }
++#define SPA_AUDIO_LAYOUT_Hexagonal 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RC, }
++#define SPA_AUDIO_LAYOUT_Octagonal 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RC, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_Cube 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR }, \
++ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
++ SPA_AUDIO_CHANNEL_TFL, SPA_AUDIO_CHANNEL_TFR, \
++ SPA_AUDIO_CHANNEL_TRL, SPA_AUDIO_CHANNEL_TRR, }
++
++
++#define SPA_AUDIO_LAYOUT_MPEG_1_0 SPA_AUDIO_LAYOUT_MONO
++#define SPA_AUDIO_LAYOUT_MPEG_2_0 SPA_AUDIO_LAYOUT_STEREO
++#define SPA_AUDIO_LAYOUT_MPEG_3_0A 3, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, }
++#define SPA_AUDIO_LAYOUT_MPEG_3_0B 3, { SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_FL, \
++ SPA_AUDIO_CHANNEL_FR, }
++#define SPA_AUDIO_LAYOUT_MPEG_4_0A 4, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RC, }
++#define SPA_AUDIO_LAYOUT_MPEG_4_0B 4, { SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_FL, \
++ SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_RC, }
++#define SPA_AUDIO_LAYOUT_MPEG_5_0A 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_SL, \
++ SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_MPEG_5_0B 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, \
++ SPA_AUDIO_CHANNEL_FC, }
++#define SPA_AUDIO_LAYOUT_MPEG_5_0C 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FC, \
++ SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_SL, \
++ SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_MPEG_5_0D 5, { SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_FL, \
++ SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_SL, \
++ SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_MPEG_5_1A 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_MPEG_5_1B 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, }
++#define SPA_AUDIO_LAYOUT_MPEG_5_1C 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FC, \
++ SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_SL, \
++ SPA_AUDIO_CHANNEL_SR, SPA_AUDIO_CHANNEL_LFE, }
++#define SPA_AUDIO_LAYOUT_MPEG_5_1D 6, { SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_FL, \
++ SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_SL, \
++ SPA_AUDIO_CHANNEL_SR, SPA_AUDIO_CHANNEL_LFE, }
++#define SPA_AUDIO_LAYOUT_MPEG_6_1A 7, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, \
++ SPA_AUDIO_CHANNEL_RC, }
++#define SPA_AUDIO_LAYOUT_MPEG_7_1A 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
++ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_MPEG_7_1B 8, { SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_SL, \
++ SPA_AUDIO_CHANNEL_SR, SPA_AUDIO_CHANNEL_FL, \
++ SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_RL, \
++ SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_LFE, }
++#define SPA_AUDIO_LAYOUT_MPEG_7_1C 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, \
++ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, }
++
++
++#define SPA_AUDIO_LAYOUT_2_1 3, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_LFE, }
++
++#define SPA_AUDIO_LAYOUT_2RC 3, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_RC, }
++#define SPA_AUDIO_LAYOUT_2FC 3, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, }
++
++#define SPA_AUDIO_LAYOUT_3_1 4, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, }
++#define SPA_AUDIO_LAYOUT_4_0 4, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RC, }
++#define SPA_AUDIO_LAYOUT_2_2 4, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
++
++#define SPA_AUDIO_LAYOUT_4_1 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
++ SPA_AUDIO_CHANNEL_RC, }
++#define SPA_AUDIO_LAYOUT_5_0 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_SL, \
++ SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_5_0R 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RL, \
++ SPA_AUDIO_CHANNEL_RR, }
++#define SPA_AUDIO_LAYOUT_5_1 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_5_1R 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
++ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, }
++#define SPA_AUDIO_LAYOUT_6_0 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RC, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_6_0F 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FLC, SPA_AUDIO_CHANNEL_FRC, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_6_1 7, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
++ SPA_AUDIO_CHANNEL_RC, SPA_AUDIO_CHANNEL_SL, \
++ SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_6_1F 7, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
++ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
++ SPA_AUDIO_CHANNEL_RC, }
++#define SPA_AUDIO_LAYOUT_7_0 7, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RL, \
++ SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_SL, \
++ SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_7_0F 7, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_FLC, \
++ SPA_AUDIO_CHANNEL_FRC, SPA_AUDIO_CHANNEL_SL, \
++ SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_7_1 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
++ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_7_1W 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
++ SPA_AUDIO_CHANNEL_FLC, SPA_AUDIO_CHANNEL_FRC, \
++ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
++#define SPA_AUDIO_LAYOUT_7_1WR 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
++ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
++ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
++ SPA_AUDIO_CHANNEL_FLC, SPA_AUDIO_CHANNEL_FRC, }
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_AUDIO_LAYOUT_H */
+diff --git a/third_party/pipewire/spa/param/audio/raw.h b/third_party/pipewire/spa/param/audio/raw.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/audio/raw.h
+@@ -0,0 +1,237 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_AUDIO_RAW_H
++#define SPA_AUDIO_RAW_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdint.h>
++
++#ifndef __FreeBSD__
++#include <endian.h>
++#endif
++
++#define SPA_AUDIO_MAX_CHANNELS 64u
++
++enum spa_audio_format {
++ SPA_AUDIO_FORMAT_UNKNOWN,
++ SPA_AUDIO_FORMAT_ENCODED,
++
++ /* interleaved formats */
++ SPA_AUDIO_FORMAT_START_Interleaved = 0x100,
++ SPA_AUDIO_FORMAT_S8,
++ SPA_AUDIO_FORMAT_U8,
++ SPA_AUDIO_FORMAT_S16_LE,
++ SPA_AUDIO_FORMAT_S16_BE,
++ SPA_AUDIO_FORMAT_U16_LE,
++ SPA_AUDIO_FORMAT_U16_BE,
++ SPA_AUDIO_FORMAT_S24_32_LE,
++ SPA_AUDIO_FORMAT_S24_32_BE,
++ SPA_AUDIO_FORMAT_U24_32_LE,
++ SPA_AUDIO_FORMAT_U24_32_BE,
++ SPA_AUDIO_FORMAT_S32_LE,
++ SPA_AUDIO_FORMAT_S32_BE,
++ SPA_AUDIO_FORMAT_U32_LE,
++ SPA_AUDIO_FORMAT_U32_BE,
++ SPA_AUDIO_FORMAT_S24_LE,
++ SPA_AUDIO_FORMAT_S24_BE,
++ SPA_AUDIO_FORMAT_U24_LE,
++ SPA_AUDIO_FORMAT_U24_BE,
++ SPA_AUDIO_FORMAT_S20_LE,
++ SPA_AUDIO_FORMAT_S20_BE,
++ SPA_AUDIO_FORMAT_U20_LE,
++ SPA_AUDIO_FORMAT_U20_BE,
++ SPA_AUDIO_FORMAT_S18_LE,
++ SPA_AUDIO_FORMAT_S18_BE,
++ SPA_AUDIO_FORMAT_U18_LE,
++ SPA_AUDIO_FORMAT_U18_BE,
++ SPA_AUDIO_FORMAT_F32_LE,
++ SPA_AUDIO_FORMAT_F32_BE,
++ SPA_AUDIO_FORMAT_F64_LE,
++ SPA_AUDIO_FORMAT_F64_BE,
++
++ /* planar formats */
++ SPA_AUDIO_FORMAT_START_Planar = 0x200,
++ SPA_AUDIO_FORMAT_U8P,
++ SPA_AUDIO_FORMAT_S16P,
++ SPA_AUDIO_FORMAT_S24_32P,
++ SPA_AUDIO_FORMAT_S32P,
++ SPA_AUDIO_FORMAT_S24P,
++ SPA_AUDIO_FORMAT_F32P,
++ SPA_AUDIO_FORMAT_F64P,
++
++ /* other formats start here */
++ SPA_AUDIO_FORMAT_START_Other = 0x400,
++
++ /* Aliases */
++
++ /* DSP formats */
++ SPA_AUDIO_FORMAT_DSP_S32 = SPA_AUDIO_FORMAT_S24_32P,
++ SPA_AUDIO_FORMAT_DSP_F32 = SPA_AUDIO_FORMAT_F32P,
++ SPA_AUDIO_FORMAT_DSP_F64 = SPA_AUDIO_FORMAT_F64P,
++
++ /* native endian */
++#if __BYTE_ORDER == __BIG_ENDIAN
++ SPA_AUDIO_FORMAT_S16 = SPA_AUDIO_FORMAT_S16_BE,
++ SPA_AUDIO_FORMAT_U16 = SPA_AUDIO_FORMAT_U16_BE,
++ SPA_AUDIO_FORMAT_S24_32 = SPA_AUDIO_FORMAT_S24_32_BE,
++ SPA_AUDIO_FORMAT_U24_32 = SPA_AUDIO_FORMAT_U24_32_BE,
++ SPA_AUDIO_FORMAT_S32 = SPA_AUDIO_FORMAT_S32_BE,
++ SPA_AUDIO_FORMAT_U32 = SPA_AUDIO_FORMAT_U32_BE,
++ SPA_AUDIO_FORMAT_S24 = SPA_AUDIO_FORMAT_S24_BE,
++ SPA_AUDIO_FORMAT_U24 = SPA_AUDIO_FORMAT_U24_BE,
++ SPA_AUDIO_FORMAT_S20 = SPA_AUDIO_FORMAT_S20_BE,
++ SPA_AUDIO_FORMAT_U20 = SPA_AUDIO_FORMAT_U20_BE,
++ SPA_AUDIO_FORMAT_S18 = SPA_AUDIO_FORMAT_S18_BE,
++ SPA_AUDIO_FORMAT_U18 = SPA_AUDIO_FORMAT_U18_BE,
++ SPA_AUDIO_FORMAT_F32 = SPA_AUDIO_FORMAT_F32_BE,
++ SPA_AUDIO_FORMAT_F64 = SPA_AUDIO_FORMAT_F64_BE,
++ SPA_AUDIO_FORMAT_S16_OE = SPA_AUDIO_FORMAT_S16_LE,
++ SPA_AUDIO_FORMAT_U16_OE = SPA_AUDIO_FORMAT_U16_LE,
++ SPA_AUDIO_FORMAT_S24_32_OE = SPA_AUDIO_FORMAT_S24_32_LE,
++ SPA_AUDIO_FORMAT_U24_32_OE = SPA_AUDIO_FORMAT_U24_32_LE,
++ SPA_AUDIO_FORMAT_S32_OE = SPA_AUDIO_FORMAT_S32_LE,
++ SPA_AUDIO_FORMAT_U32_OE = SPA_AUDIO_FORMAT_U32_LE,
++ SPA_AUDIO_FORMAT_S24_OE = SPA_AUDIO_FORMAT_S24_LE,
++ SPA_AUDIO_FORMAT_U24_OE = SPA_AUDIO_FORMAT_U24_LE,
++ SPA_AUDIO_FORMAT_S20_OE = SPA_AUDIO_FORMAT_S20_LE,
++ SPA_AUDIO_FORMAT_U20_OE = SPA_AUDIO_FORMAT_U20_LE,
++ SPA_AUDIO_FORMAT_S18_OE = SPA_AUDIO_FORMAT_S18_LE,
++ SPA_AUDIO_FORMAT_U18_OE = SPA_AUDIO_FORMAT_U18_LE,
++ SPA_AUDIO_FORMAT_F32_OE = SPA_AUDIO_FORMAT_F32_LE,
++ SPA_AUDIO_FORMAT_F64_OE = SPA_AUDIO_FORMAT_F64_LE,
++#elif __BYTE_ORDER == __LITTLE_ENDIAN
++ SPA_AUDIO_FORMAT_S16 = SPA_AUDIO_FORMAT_S16_LE,
++ SPA_AUDIO_FORMAT_U16 = SPA_AUDIO_FORMAT_U16_LE,
++ SPA_AUDIO_FORMAT_S24_32 = SPA_AUDIO_FORMAT_S24_32_LE,
++ SPA_AUDIO_FORMAT_U24_32 = SPA_AUDIO_FORMAT_U24_32_LE,
++ SPA_AUDIO_FORMAT_S32 = SPA_AUDIO_FORMAT_S32_LE,
++ SPA_AUDIO_FORMAT_U32 = SPA_AUDIO_FORMAT_U32_LE,
++ SPA_AUDIO_FORMAT_S24 = SPA_AUDIO_FORMAT_S24_LE,
++ SPA_AUDIO_FORMAT_U24 = SPA_AUDIO_FORMAT_U24_LE,
++ SPA_AUDIO_FORMAT_S20 = SPA_AUDIO_FORMAT_S20_LE,
++ SPA_AUDIO_FORMAT_U20 = SPA_AUDIO_FORMAT_U20_LE,
++ SPA_AUDIO_FORMAT_S18 = SPA_AUDIO_FORMAT_S18_LE,
++ SPA_AUDIO_FORMAT_U18 = SPA_AUDIO_FORMAT_U18_LE,
++ SPA_AUDIO_FORMAT_F32 = SPA_AUDIO_FORMAT_F32_LE,
++ SPA_AUDIO_FORMAT_F64 = SPA_AUDIO_FORMAT_F64_LE,
++ SPA_AUDIO_FORMAT_S16_OE = SPA_AUDIO_FORMAT_S16_BE,
++ SPA_AUDIO_FORMAT_U16_OE = SPA_AUDIO_FORMAT_U16_BE,
++ SPA_AUDIO_FORMAT_S24_32_OE = SPA_AUDIO_FORMAT_S24_32_BE,
++ SPA_AUDIO_FORMAT_U24_32_OE = SPA_AUDIO_FORMAT_U24_32_BE,
++ SPA_AUDIO_FORMAT_S32_OE = SPA_AUDIO_FORMAT_S32_BE,
++ SPA_AUDIO_FORMAT_U32_OE = SPA_AUDIO_FORMAT_U32_BE,
++ SPA_AUDIO_FORMAT_S24_OE = SPA_AUDIO_FORMAT_S24_BE,
++ SPA_AUDIO_FORMAT_U24_OE = SPA_AUDIO_FORMAT_U24_BE,
++ SPA_AUDIO_FORMAT_S20_OE = SPA_AUDIO_FORMAT_S20_BE,
++ SPA_AUDIO_FORMAT_U20_OE = SPA_AUDIO_FORMAT_U20_BE,
++ SPA_AUDIO_FORMAT_S18_OE = SPA_AUDIO_FORMAT_S18_BE,
++ SPA_AUDIO_FORMAT_U18_OE = SPA_AUDIO_FORMAT_U18_BE,
++ SPA_AUDIO_FORMAT_F32_OE = SPA_AUDIO_FORMAT_F32_BE,
++ SPA_AUDIO_FORMAT_F64_OE = SPA_AUDIO_FORMAT_F64_BE,
++#endif
++};
++
++#define SPA_AUDIO_FORMAT_IS_INTERLEAVED(fmt) ((fmt) > SPA_AUDIO_FORMAT_START_Interleaved && (fmt) < SPA_AUDIO_FORMAT_START_Planar)
++#define SPA_AUDIO_FORMAT_IS_PLANAR(fmt) ((fmt) > SPA_AUDIO_FORMAT_START_Planar && (fmt) < SPA_AUDIO_FORMAT_START_Other)
++
++enum spa_audio_channel {
++ SPA_AUDIO_CHANNEL_UNKNOWN, /**< unspecified */
++ SPA_AUDIO_CHANNEL_NA, /**< N/A, silent */
++
++ SPA_AUDIO_CHANNEL_MONO, /**< mono stream */
++
++ SPA_AUDIO_CHANNEL_FL, /**< front left */
++ SPA_AUDIO_CHANNEL_FR, /**< front right */
++ SPA_AUDIO_CHANNEL_FC, /**< front center */
++ SPA_AUDIO_CHANNEL_LFE, /**< LFE */
++ SPA_AUDIO_CHANNEL_SL, /**< side left */
++ SPA_AUDIO_CHANNEL_SR, /**< side right */
++ SPA_AUDIO_CHANNEL_FLC, /**< front left center */
++ SPA_AUDIO_CHANNEL_FRC, /**< front right center */
++ SPA_AUDIO_CHANNEL_RC, /**< rear center */
++ SPA_AUDIO_CHANNEL_RL, /**< rear left */
++ SPA_AUDIO_CHANNEL_RR, /**< rear right */
++ SPA_AUDIO_CHANNEL_TC, /**< top center */
++ SPA_AUDIO_CHANNEL_TFL, /**< top front left */
++ SPA_AUDIO_CHANNEL_TFC, /**< top front center */
++ SPA_AUDIO_CHANNEL_TFR, /**< top front right */
++ SPA_AUDIO_CHANNEL_TRL, /**< top rear left */
++ SPA_AUDIO_CHANNEL_TRC, /**< top rear center */
++ SPA_AUDIO_CHANNEL_TRR, /**< top rear right */
++ SPA_AUDIO_CHANNEL_RLC, /**< rear left center */
++ SPA_AUDIO_CHANNEL_RRC, /**< rear right center */
++ SPA_AUDIO_CHANNEL_FLW, /**< front left wide */
++ SPA_AUDIO_CHANNEL_FRW, /**< front right wide */
++ SPA_AUDIO_CHANNEL_LFE2, /**< LFE 2 */
++ SPA_AUDIO_CHANNEL_FLH, /**< front left high */
++ SPA_AUDIO_CHANNEL_FCH, /**< front center high */
++ SPA_AUDIO_CHANNEL_FRH, /**< front right high */
++ SPA_AUDIO_CHANNEL_TFLC, /**< top front left center */
++ SPA_AUDIO_CHANNEL_TFRC, /**< top front right center */
++ SPA_AUDIO_CHANNEL_TSL, /**< top side left */
++ SPA_AUDIO_CHANNEL_TSR, /**< top side right */
++ SPA_AUDIO_CHANNEL_LLFE, /**< left LFE */
++ SPA_AUDIO_CHANNEL_RLFE, /**< right LFE */
++ SPA_AUDIO_CHANNEL_BC, /**< bottom center */
++ SPA_AUDIO_CHANNEL_BLC, /**< bottom left center */
++ SPA_AUDIO_CHANNEL_BRC, /**< bottom right center */
++
++ SPA_AUDIO_CHANNEL_CUSTOM_START = 0x10000,
++};
++
++/** Extra audio flags */
++#define SPA_AUDIO_FLAG_NONE (0) /*< no valid flag */
++#define SPA_AUDIO_FLAG_UNPOSITIONED (1 << 0) /*< the position array explicitly
++ * contains unpositioned channels. */
++/** Audio information description */
++struct spa_audio_info_raw {
++ enum spa_audio_format format; /*< format, one of enum spa_audio_format */
++ uint32_t flags; /*< extra flags */
++ uint32_t rate; /*< sample rate */
++ uint32_t channels; /*< number of channels */
++ uint32_t position[SPA_AUDIO_MAX_CHANNELS]; /*< channel position from enum spa_audio_channel */
++};
++
++#define SPA_AUDIO_INFO_RAW_INIT(...) (struct spa_audio_info_raw) { __VA_ARGS__ }
++
++#define SPA_KEY_AUDIO_CHANNEL "audio.channel" /**< an audio channel as string,
++ * Ex. "FL" */
++#define SPA_KEY_AUDIO_CHANNELS "audio.channels" /**< an audio channel count as int */
++#define SPA_KEY_AUDIO_RATE "audio.rate" /**< an audio sample rate as int */
++
++struct spa_audio_info_dsp {
++ enum spa_audio_format format; /*< format, one of the DSP formats in enum spa_audio_format_dsp */
++};
++
++#define SPA_AUDIO_INFO_DSP_INIT(...) (struct spa_audio_info_dsp) { __VA_ARGS__ }
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_AUDIO_RAW_H */
+diff --git a/third_party/pipewire/spa/param/audio/type-info.h b/third_party/pipewire/spa/param/audio/type-info.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/audio/type-info.h
+@@ -0,0 +1,140 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_AUDIO_TYPES_H
++#define SPA_AUDIO_TYPES_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/param/audio/raw.h>
++
++#define SPA_TYPE_INFO_AudioFormat SPA_TYPE_INFO_ENUM_BASE "AudioFormat"
++#define SPA_TYPE_INFO_AUDIO_FORMAT_BASE SPA_TYPE_INFO_AudioFormat ":"
++
++static const struct spa_type_info spa_type_audio_format[] = {
++ { SPA_AUDIO_FORMAT_UNKNOWN, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "UNKNOWN", NULL },
++ { SPA_AUDIO_FORMAT_ENCODED, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "ENCODED", NULL },
++ { SPA_AUDIO_FORMAT_S8, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S8", NULL },
++ { SPA_AUDIO_FORMAT_U8, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U8", NULL },
++ { SPA_AUDIO_FORMAT_S16_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16LE", NULL },
++ { SPA_AUDIO_FORMAT_S16_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16BE", NULL },
++ { SPA_AUDIO_FORMAT_U16_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16LE", NULL },
++ { SPA_AUDIO_FORMAT_U16_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16BE", NULL },
++ { SPA_AUDIO_FORMAT_S24_32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32LE", NULL },
++ { SPA_AUDIO_FORMAT_S24_32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32BE", NULL },
++ { SPA_AUDIO_FORMAT_U24_32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32LE", NULL },
++ { SPA_AUDIO_FORMAT_U24_32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32BE", NULL },
++ { SPA_AUDIO_FORMAT_S32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32LE", NULL },
++ { SPA_AUDIO_FORMAT_S32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32BE", NULL },
++ { SPA_AUDIO_FORMAT_U32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32LE", NULL },
++ { SPA_AUDIO_FORMAT_U32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32BE", NULL },
++ { SPA_AUDIO_FORMAT_S24_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24LE", NULL },
++ { SPA_AUDIO_FORMAT_S24_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24BE", NULL },
++ { SPA_AUDIO_FORMAT_U24_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24LE", NULL },
++ { SPA_AUDIO_FORMAT_U24_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24BE", NULL },
++ { SPA_AUDIO_FORMAT_S20_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20LE", NULL },
++ { SPA_AUDIO_FORMAT_S20_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20BE", NULL },
++ { SPA_AUDIO_FORMAT_U20_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20LE", NULL },
++ { SPA_AUDIO_FORMAT_U20_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20BE", NULL },
++ { SPA_AUDIO_FORMAT_S18_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18LE", NULL },
++ { SPA_AUDIO_FORMAT_S18_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18BE", NULL },
++ { SPA_AUDIO_FORMAT_U18_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18LE", NULL },
++ { SPA_AUDIO_FORMAT_U18_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18BE", NULL },
++ { SPA_AUDIO_FORMAT_F32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32LE", NULL },
++ { SPA_AUDIO_FORMAT_F32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32BE", NULL },
++ { SPA_AUDIO_FORMAT_F64_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64LE", NULL },
++ { SPA_AUDIO_FORMAT_F64_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64BE", NULL },
++
++ { SPA_AUDIO_FORMAT_U8P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U8P", NULL },
++ { SPA_AUDIO_FORMAT_S16P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16P", NULL },
++ { SPA_AUDIO_FORMAT_S24_32P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32P", NULL },
++ { SPA_AUDIO_FORMAT_S32P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32P", NULL },
++ { SPA_AUDIO_FORMAT_S24P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24P", NULL },
++ { SPA_AUDIO_FORMAT_F32P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32P", NULL },
++ { SPA_AUDIO_FORMAT_F64P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64P", NULL },
++
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_AudioFlags SPA_TYPE_INFO_FLAGS_BASE "AudioFlags"
++#define SPA_TYPE_INFO_AUDIO_FLAGS_BASE SPA_TYPE_INFO_AudioFlags ":"
++
++static const struct spa_type_info spa_type_audio_flags[] = {
++ { SPA_AUDIO_FLAG_NONE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FLAGS_BASE "none", NULL },
++ { SPA_AUDIO_FLAG_UNPOSITIONED, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FLAGS_BASE "unpositioned", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_AudioChannel SPA_TYPE_INFO_ENUM_BASE "AudioChannel"
++#define SPA_TYPE_INFO_AUDIO_CHANNEL_BASE SPA_TYPE_INFO_AudioChannel ":"
++
++static const struct spa_type_info spa_type_audio_channel[] = {
++ { SPA_AUDIO_CHANNEL_UNKNOWN, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "UNK", NULL },
++ { SPA_AUDIO_CHANNEL_NA, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "NA", NULL },
++ { SPA_AUDIO_CHANNEL_MONO, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "MONO", NULL },
++ { SPA_AUDIO_CHANNEL_FL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FL", NULL },
++ { SPA_AUDIO_CHANNEL_FR, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FR", NULL },
++ { SPA_AUDIO_CHANNEL_FC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FC", NULL },
++ { SPA_AUDIO_CHANNEL_LFE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "LFE", NULL },
++ { SPA_AUDIO_CHANNEL_SL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "SL", NULL },
++ { SPA_AUDIO_CHANNEL_SR, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "SR", NULL },
++ { SPA_AUDIO_CHANNEL_FLC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FLC", NULL },
++ { SPA_AUDIO_CHANNEL_FRC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FRC", NULL },
++ { SPA_AUDIO_CHANNEL_RC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "RC", NULL },
++ { SPA_AUDIO_CHANNEL_RL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "RL", NULL },
++ { SPA_AUDIO_CHANNEL_RR, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "RR", NULL },
++ { SPA_AUDIO_CHANNEL_TC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TC", NULL },
++ { SPA_AUDIO_CHANNEL_TFL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TFL", NULL },
++ { SPA_AUDIO_CHANNEL_TFC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TFC", NULL },
++ { SPA_AUDIO_CHANNEL_TFR, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TFR", NULL },
++ { SPA_AUDIO_CHANNEL_TRL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TRL", NULL },
++ { SPA_AUDIO_CHANNEL_TRC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TRC", NULL },
++ { SPA_AUDIO_CHANNEL_TRR, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TRR", NULL },
++ { SPA_AUDIO_CHANNEL_RLC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "RLC", NULL },
++ { SPA_AUDIO_CHANNEL_RRC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "RRC", NULL },
++ { SPA_AUDIO_CHANNEL_FLW, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FLW", NULL },
++ { SPA_AUDIO_CHANNEL_FRW, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FRW", NULL },
++ { SPA_AUDIO_CHANNEL_LFE2, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "LFE2", NULL },
++ { SPA_AUDIO_CHANNEL_FLH, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FLH", NULL },
++ { SPA_AUDIO_CHANNEL_FCH, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FCH", NULL },
++ { SPA_AUDIO_CHANNEL_FRH, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "FRH", NULL },
++ { SPA_AUDIO_CHANNEL_TFLC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TFLC", NULL },
++ { SPA_AUDIO_CHANNEL_TFRC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TFRC", NULL },
++ { SPA_AUDIO_CHANNEL_TSL, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TSL", NULL },
++ { SPA_AUDIO_CHANNEL_TSR, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "TSR", NULL },
++ { SPA_AUDIO_CHANNEL_LLFE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "LLFR", NULL },
++ { SPA_AUDIO_CHANNEL_RLFE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "RLFE", NULL },
++ { SPA_AUDIO_CHANNEL_BC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "BC", NULL },
++ { SPA_AUDIO_CHANNEL_BLC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "BLC", NULL },
++ { SPA_AUDIO_CHANNEL_BRC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "BRC", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_AUDIO_RAW_TYPES_H */
+diff --git a/third_party/pipewire/spa/param/format-utils.h b/third_party/pipewire/spa/param/format-utils.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/format-utils.h
+@@ -0,0 +1,49 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_PARAM_FORMAT_UTILS_H
++#define SPA_PARAM_FORMAT_UTILS_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++
++#include <spa/pod/parser.h>
++#include <spa/param/format.h>
++
++static inline int
++spa_format_parse(const struct spa_pod *format, uint32_t *media_type, uint32_t *media_subtype)
++{
++ return spa_pod_parse_object(format,
++ SPA_TYPE_OBJECT_Format, NULL,
++ SPA_FORMAT_mediaType, SPA_POD_Id(media_type),
++ SPA_FORMAT_mediaSubtype, SPA_POD_Id(media_subtype));
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_PARAM_FORMAT_UTILS_H */
+diff --git a/third_party/pipewire/spa/param/format.h b/third_party/pipewire/spa/param/format.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/format.h
+@@ -0,0 +1,147 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_PARAM_FORMAT_H
++#define SPA_PARAM_FORMAT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/param/param.h>
++
++/** media type for SPA_TYPE_OBJECT_Format */
++enum spa_media_type {
++ SPA_MEDIA_TYPE_unknown,
++ SPA_MEDIA_TYPE_audio,
++ SPA_MEDIA_TYPE_video,
++ SPA_MEDIA_TYPE_image,
++ SPA_MEDIA_TYPE_binary,
++ SPA_MEDIA_TYPE_stream,
++ SPA_MEDIA_TYPE_application,
++};
++
++/** media subtype for SPA_TYPE_OBJECT_Format */
++enum spa_media_subtype {
++ SPA_MEDIA_SUBTYPE_unknown,
++ SPA_MEDIA_SUBTYPE_raw,
++ SPA_MEDIA_SUBTYPE_dsp,
++
++ SPA_MEDIA_SUBTYPE_START_Audio = 0x10000,
++ SPA_MEDIA_SUBTYPE_mp3,
++ SPA_MEDIA_SUBTYPE_aac,
++ SPA_MEDIA_SUBTYPE_vorbis,
++ SPA_MEDIA_SUBTYPE_wma,
++ SPA_MEDIA_SUBTYPE_ra,
++ SPA_MEDIA_SUBTYPE_sbc,
++ SPA_MEDIA_SUBTYPE_adpcm,
++ SPA_MEDIA_SUBTYPE_g723,
++ SPA_MEDIA_SUBTYPE_g726,
++ SPA_MEDIA_SUBTYPE_g729,
++ SPA_MEDIA_SUBTYPE_amr,
++ SPA_MEDIA_SUBTYPE_gsm,
++
++ SPA_MEDIA_SUBTYPE_START_Video = 0x20000,
++ SPA_MEDIA_SUBTYPE_h264,
++ SPA_MEDIA_SUBTYPE_mjpg,
++ SPA_MEDIA_SUBTYPE_dv,
++ SPA_MEDIA_SUBTYPE_mpegts,
++ SPA_MEDIA_SUBTYPE_h263,
++ SPA_MEDIA_SUBTYPE_mpeg1,
++ SPA_MEDIA_SUBTYPE_mpeg2,
++ SPA_MEDIA_SUBTYPE_mpeg4,
++ SPA_MEDIA_SUBTYPE_xvid,
++ SPA_MEDIA_SUBTYPE_vc1,
++ SPA_MEDIA_SUBTYPE_vp8,
++ SPA_MEDIA_SUBTYPE_vp9,
++ SPA_MEDIA_SUBTYPE_bayer,
++
++ SPA_MEDIA_SUBTYPE_START_Image = 0x30000,
++ SPA_MEDIA_SUBTYPE_jpeg,
++
++ SPA_MEDIA_SUBTYPE_START_Binary = 0x40000,
++
++ SPA_MEDIA_SUBTYPE_START_Stream = 0x50000,
++ SPA_MEDIA_SUBTYPE_midi,
++
++ SPA_MEDIA_SUBTYPE_START_Application = 0x60000,
++ SPA_MEDIA_SUBTYPE_control, /**< control stream, data contains
++ * spa_pod_sequence with control info. */
++};
++
++/** properties for audio SPA_TYPE_OBJECT_Format */
++enum spa_format {
++ SPA_FORMAT_START,
++
++ SPA_FORMAT_mediaType, /**< media type (Id enum spa_media_type) */
++ SPA_FORMAT_mediaSubtype, /**< media subtype (Id enum spa_media_subtype) */
++
++ /* Audio format keys */
++ SPA_FORMAT_START_Audio = 0x10000,
++ SPA_FORMAT_AUDIO_format, /**< audio format, (Id enum spa_audio_format) */
++ SPA_FORMAT_AUDIO_flags, /**< optional flags (Int) */
++ SPA_FORMAT_AUDIO_rate, /**< sample rate (Int) */
++ SPA_FORMAT_AUDIO_channels, /**< number of audio channels (Int) */
++ SPA_FORMAT_AUDIO_position, /**< channel positions (Id enum spa_audio_position) */
++
++ /* Video Format keys */
++ SPA_FORMAT_START_Video = 0x20000,
++ SPA_FORMAT_VIDEO_format, /**< video format (Id enum spa_video_format) */
++ SPA_FORMAT_VIDEO_modifier, /**< format modifier (Long) */
++ SPA_FORMAT_VIDEO_size, /**< size (Rectangle) */
++ SPA_FORMAT_VIDEO_framerate, /**< frame rate (Fraction) */
++ SPA_FORMAT_VIDEO_maxFramerate, /**< maximum frame rate (Fraction) */
++ SPA_FORMAT_VIDEO_views, /**< number of views (Int) */
++ SPA_FORMAT_VIDEO_interlaceMode, /**< (Id enum spa_video_interlace_mode) */
++ SPA_FORMAT_VIDEO_pixelAspectRatio, /**< (Rectangle) */
++ SPA_FORMAT_VIDEO_multiviewMode, /**< (Id enum spa_video_multiview_mode) */
++ SPA_FORMAT_VIDEO_multiviewFlags, /**< (Id enum spa_video_multiview_flags) */
++ SPA_FORMAT_VIDEO_chromaSite, /**< /Id enum spa_video_chroma_site) */
++ SPA_FORMAT_VIDEO_colorRange, /**< /Id enum spa_video_color_range) */
++ SPA_FORMAT_VIDEO_colorMatrix, /**< /Id enum spa_video_color_matrix) */
++ SPA_FORMAT_VIDEO_transferFunction, /**< /Id enum spa_video_transfer_function) */
++ SPA_FORMAT_VIDEO_colorPrimaries, /**< /Id enum spa_video_color_primaries) */
++ SPA_FORMAT_VIDEO_profile, /**< (Int) */
++ SPA_FORMAT_VIDEO_level, /**< (Int) */
++ SPA_FORMAT_VIDEO_H264_streamFormat, /**< (Id enum spa_h264_stream_format) */
++ SPA_FORMAT_VIDEO_H264_alignment, /**< (Id enum spa_h264_alignment) */
++
++ /* Image Format keys */
++ SPA_FORMAT_START_Image = 0x30000,
++ /* Binary Format keys */
++ SPA_FORMAT_START_Binary = 0x40000,
++ /* Stream Format keys */
++ SPA_FORMAT_START_Stream = 0x50000,
++ /* Application Format keys */
++ SPA_FORMAT_START_Application = 0x60000,
++};
++
++#define SPA_KEY_FORMAT_DSP "format.dsp" /**< a predefined DSP format,
++ * Ex. "32 bit float mono audio" */
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_PARAM_FORMAT_H */
+diff --git a/third_party/pipewire/spa/param/param.h b/third_party/pipewire/spa/param/param.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/param.h
+@@ -0,0 +1,166 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_PARAM_H
++#define SPA_PARAM_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++
++/** different parameter types that can be queried */
++enum spa_param_type {
++ SPA_PARAM_Invalid, /**< invalid */
++ SPA_PARAM_PropInfo, /**< property information as SPA_TYPE_OBJECT_PropInfo */
++ SPA_PARAM_Props, /**< properties as SPA_TYPE_OBJECT_Props */
++ SPA_PARAM_EnumFormat, /**< available formats as SPA_TYPE_OBJECT_Format */
++ SPA_PARAM_Format, /**< configured format as SPA_TYPE_OBJECT_Format */
++ SPA_PARAM_Buffers, /**< buffer configurations as SPA_TYPE_OBJECT_ParamBuffers*/
++ SPA_PARAM_Meta, /**< allowed metadata for buffers as SPA_TYPE_OBJECT_ParamMeta*/
++ SPA_PARAM_IO, /**< configurable IO areas as SPA_TYPE_OBJECT_ParamIO */
++ SPA_PARAM_EnumProfile, /**< profile enumeration as SPA_TYPE_OBJECT_ParamProfile */
++ SPA_PARAM_Profile, /**< profile configuration as SPA_TYPE_OBJECT_ParamProfile */
++ SPA_PARAM_EnumPortConfig, /**< port configuration enumeration as SPA_TYPE_OBJECT_ParamPortConfig */
++ SPA_PARAM_PortConfig, /**< port configuration as SPA_TYPE_OBJECT_ParamPortConfig */
++ SPA_PARAM_EnumRoute, /**< routing enumeration as SPA_TYPE_OBJECT_ParamRoute */
++ SPA_PARAM_Route, /**< routing configuration as SPA_TYPE_OBJECT_ParamRoute */
++ SPA_PARAM_Control, /**< Control parameter, a SPA_TYPE_Sequence */
++};
++
++/** information about a parameter */
++struct spa_param_info {
++ uint32_t id; /**< enum spa_param_type */
++#define SPA_PARAM_INFO_SERIAL (1<<0) /**< bit to signal update even when the
++ * read/write flags don't change */
++#define SPA_PARAM_INFO_READ (1<<1)
++#define SPA_PARAM_INFO_WRITE (1<<2)
++#define SPA_PARAM_INFO_READWRITE (SPA_PARAM_INFO_WRITE|SPA_PARAM_INFO_READ)
++ uint32_t flags;
++ uint32_t user; /**< private user field. You can use this to keep
++ * state. */
++ uint32_t padding[5];
++};
++
++#define SPA_PARAM_INFO(id,flags) (struct spa_param_info){ (id), (flags) }
++
++/** properties for SPA_TYPE_OBJECT_ParamBuffers */
++enum spa_param_buffers {
++ SPA_PARAM_BUFFERS_START,
++ SPA_PARAM_BUFFERS_buffers, /**< number of buffers (Int) */
++ SPA_PARAM_BUFFERS_blocks, /**< number of data blocks per buffer (Int) */
++ SPA_PARAM_BUFFERS_size, /**< size of a data block memory (Int)*/
++ SPA_PARAM_BUFFERS_stride, /**< stride of data block memory (Int) */
++ SPA_PARAM_BUFFERS_align, /**< alignment of data block memory (Int) */
++ SPA_PARAM_BUFFERS_dataType, /**< possible memory types (Int, mask of enum spa_data_type) */
++};
++
++/** properties for SPA_TYPE_OBJECT_ParamMeta */
++enum spa_param_meta {
++ SPA_PARAM_META_START,
++ SPA_PARAM_META_type, /**< the metadata, one of enum spa_meta_type (Id enum spa_meta_type) */
++ SPA_PARAM_META_size, /**< the expected maximum size the meta (Int) */
++};
++
++/** properties for SPA_TYPE_OBJECT_ParamIO */
++enum spa_param_io {
++ SPA_PARAM_IO_START,
++ SPA_PARAM_IO_id, /**< type ID, uniquely identifies the io area (Id enum spa_io_type) */
++ SPA_PARAM_IO_size, /**< size of the io area (Int) */
++};
++
++enum spa_param_availability {
++ SPA_PARAM_AVAILABILITY_unknown, /**< unknown availability */
++ SPA_PARAM_AVAILABILITY_no, /**< not available */
++ SPA_PARAM_AVAILABILITY_yes, /**< available */
++};
++
++/** properties for SPA_TYPE_OBJECT_ParamProfile */
++enum spa_param_profile {
++ SPA_PARAM_PROFILE_START,
++ SPA_PARAM_PROFILE_index, /**< profile index (Int) */
++ SPA_PARAM_PROFILE_name, /**< profile name (String) */
++ SPA_PARAM_PROFILE_description, /**< profile description (String) */
++ SPA_PARAM_PROFILE_priority, /**< profile priority (Int) */
++ SPA_PARAM_PROFILE_available, /**< availability of the profile
++ * (Id enum spa_param_availability) */
++ SPA_PARAM_PROFILE_info, /**< info (Struct(
++ * Int : n_items,
++ * (String : key,
++ * String : value)*)) */
++ SPA_PARAM_PROFILE_classes, /**< node classes provided by this profile
++ * (Struct(
++ * Int : number of items following
++ * Struct(
++ * String : class name (eg. "Audio/Source"),
++ * Int : number of nodes)*)) */
++};
++
++enum spa_param_port_config_mode {
++ SPA_PARAM_PORT_CONFIG_MODE_none, /**< no configuration */
++ SPA_PARAM_PORT_CONFIG_MODE_passthrough, /**< passthrough configuration */
++ SPA_PARAM_PORT_CONFIG_MODE_convert, /**< convert configuration */
++ SPA_PARAM_PORT_CONFIG_MODE_dsp, /**< dsp configuration, depending on the external
++ * format. For audio, ports will be configured for
++ * the given number of channels with F32 format. */
++};
++
++/** properties for SPA_TYPE_OBJECT_ParamPortConfig */
++enum spa_param_port_config {
++ SPA_PARAM_PORT_CONFIG_START,
++ SPA_PARAM_PORT_CONFIG_direction, /**< direction, input/output (Id enum spa_direction) */
++ SPA_PARAM_PORT_CONFIG_mode, /**< (Id enum spa_param_port_config_mode) mode */
++ SPA_PARAM_PORT_CONFIG_monitor, /**< (Bool) enable monitor output ports on input ports */
++ SPA_PARAM_PORT_CONFIG_control, /**< (Bool) enable control ports */
++ SPA_PARAM_PORT_CONFIG_format, /**< (Object) format filter */
++};
++
++/** properties for SPA_TYPE_OBJECT_ParamRoute */
++enum spa_param_route {
++ SPA_PARAM_ROUTE_START,
++ SPA_PARAM_ROUTE_index, /**< index of the routing destination (Int) */
++ SPA_PARAM_ROUTE_direction, /**< direction, input/output (Id enum spa_direction) */
++ SPA_PARAM_ROUTE_device, /**< device id (Int) */
++ SPA_PARAM_ROUTE_name, /**< name of the routing destination (String) */
++ SPA_PARAM_ROUTE_description, /**< description of the destination (String) */
++ SPA_PARAM_ROUTE_priority, /**< priority of the destination (Int) */
++ SPA_PARAM_ROUTE_available, /**< availability of the destination
++ * (Id enum spa_param_availability) */
++ SPA_PARAM_ROUTE_info, /**< info (Struct(
++ * Int : n_items,
++ * (String : key,
++ * String : value)*)) */
++ SPA_PARAM_ROUTE_profiles, /**< associated profile indexes (Array of Int) */
++ SPA_PARAM_ROUTE_props, /**< properties SPA_TYPE_OBJECT_Props */
++ SPA_PARAM_ROUTE_devices, /**< associated device indexes (Array of Int) */
++ SPA_PARAM_ROUTE_profile, /**< profile id (Int) */
++};
++
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_PARAM_H */
+diff --git a/third_party/pipewire/spa/param/profiler.h b/third_party/pipewire/spa/param/profiler.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/profiler.h
+@@ -0,0 +1,84 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2020 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_PARAM_PROFILER_H
++#define SPA_PARAM_PROFILER_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/param/param.h>
++
++/** properties for SPA_TYPE_OBJECT_Profiler */
++enum spa_profiler {
++ SPA_PROFILER_START,
++
++ SPA_PROFILER_START_Driver = 0x10000, /**< driver related profiler properties */
++ SPA_PROFILER_info, /**< Generic info, counter and CPU load,
++ * (Struct(
++ * Long : counter,
++ * Float : cpu_load fast,
++ * Float : cpu_load medium,
++ * Float : cpu_load slow)) */
++ SPA_PROFILER_clock, /**< clock information
++ * (Struct(
++ * Int : clock flags,
++ * Int : clock id,
++ * String: clock name,
++ * Long : clock nsec,
++ * Fraction : clock rate,
++ * Long : clock position,
++ * Long : clock duration,
++ * Long : clock delay,
++ * Double : clock rate_diff,
++ * Long : clock next_nsec)) */
++ SPA_PROFILER_driverBlock, /**< generic driver info block
++ * (Struct(
++ * Int : driver_id,
++ * String : name,
++ * Long : driver prev_signal,
++ * Long : driver signal,
++ * Long : driver awake,
++ * Long : driver finish,
++ * Int : driver status)) */
++
++ SPA_PROFILER_START_Follower = 0x20000, /**< follower related profiler properties */
++ SPA_PROFILER_followerBlock, /**< generic follower info block
++ * (Struct(
++ * Int : id,
++ * String : name,
++ * Long : prev_signal,
++ * Long : signal,
++ * Long : awake,
++ * Long : finish,
++ * Int : status)) */
++ SPA_PROFILER_START_CUSTOM = 0x1000000,
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_PARAM_PROFILER_H */
+diff --git a/third_party/pipewire/spa/param/props.h b/third_party/pipewire/spa/param/props.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/props.h
+@@ -0,0 +1,99 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_PARAM_PROPS_H
++#define SPA_PARAM_PROPS_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/param/param.h>
++
++/** properties of SPA_TYPE_OBJECT_PropInfo */
++enum spa_prop_info {
++ SPA_PROP_INFO_START,
++ SPA_PROP_INFO_id, /**< associated id of the property */
++ SPA_PROP_INFO_name, /**< name of the property */
++ SPA_PROP_INFO_type, /**< type and range/enums of property */
++ SPA_PROP_INFO_labels, /**< labels of property if any, this is a
++ * struct with pairs of values, the first one
++ * is of the type of the property, the second
++ * one is a string with a user readable label
++ * for the value. */
++};
++
++/** predefined properties for SPA_TYPE_OBJECT_Props */
++enum spa_prop {
++ SPA_PROP_START,
++
++ SPA_PROP_unknown, /**< an unknown property */
++
++ SPA_PROP_START_Device = 0x100, /**< device related properties */
++ SPA_PROP_device,
++ SPA_PROP_deviceName,
++ SPA_PROP_deviceFd,
++ SPA_PROP_card,
++ SPA_PROP_cardName,
++
++ SPA_PROP_minLatency,
++ SPA_PROP_maxLatency,
++ SPA_PROP_periods,
++ SPA_PROP_periodSize,
++ SPA_PROP_periodEvent,
++ SPA_PROP_live,
++ SPA_PROP_rate,
++ SPA_PROP_quality,
++
++ SPA_PROP_START_Audio = 0x10000, /**< audio related properties */
++ SPA_PROP_waveType,
++ SPA_PROP_frequency,
++ SPA_PROP_volume, /**< a volume (Float), 0.0 silence, 1.0 normal */
++ SPA_PROP_mute, /**< mute (Bool) */
++ SPA_PROP_patternType,
++ SPA_PROP_ditherType,
++ SPA_PROP_truncate,
++ SPA_PROP_channelVolumes, /**< a volume array, one volume per
++ * channel (Array of Float) */
++ SPA_PROP_volumeBase, /**< a volume base (Float) */
++ SPA_PROP_volumeStep, /**< a volume step (Float) */
++
++ SPA_PROP_START_Video = 0x20000, /**< video related properties */
++ SPA_PROP_brightness,
++ SPA_PROP_contrast,
++ SPA_PROP_saturation,
++ SPA_PROP_hue,
++ SPA_PROP_gamma,
++ SPA_PROP_exposure,
++ SPA_PROP_gain,
++ SPA_PROP_sharpness,
++
++ SPA_PROP_START_CUSTOM = 0x1000000,
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_PARAM_PROPS_H */
+diff --git a/third_party/pipewire/spa/param/type-info.h b/third_party/pipewire/spa/param/type-info.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/type-info.h
+@@ -0,0 +1,362 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_PARAM_TYPES_H
++#define SPA_PARAM_TYPES_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/param/props.h>
++#include <spa/param/format.h>
++#include <spa/buffer/type-info.h>
++
++/* base for parameter object enumerations */
++#define SPA_TYPE_INFO_ParamId SPA_TYPE_INFO_ENUM_BASE "ParamId"
++#define SPA_TYPE_INFO_PARAM_ID_BASE SPA_TYPE_INFO_ParamId ":"
++
++static const struct spa_type_info spa_type_param[] = {
++ { SPA_PARAM_Invalid, SPA_TYPE_None, SPA_TYPE_INFO_PARAM_ID_BASE "Invalid", NULL },
++ { SPA_PARAM_PropInfo, SPA_TYPE_OBJECT_Props, SPA_TYPE_INFO_PARAM_ID_BASE "PropInfo", NULL },
++ { SPA_PARAM_Props, SPA_TYPE_OBJECT_Props, SPA_TYPE_INFO_PARAM_ID_BASE "Props", NULL },
++ { SPA_PARAM_EnumFormat, SPA_TYPE_OBJECT_Format, SPA_TYPE_INFO_PARAM_ID_BASE "EnumFormat", NULL },
++ { SPA_PARAM_Format, SPA_TYPE_OBJECT_Format, SPA_TYPE_INFO_PARAM_ID_BASE "Format", NULL },
++ { SPA_PARAM_Buffers, SPA_TYPE_OBJECT_ParamBuffers, SPA_TYPE_INFO_PARAM_ID_BASE "Buffers", NULL },
++ { SPA_PARAM_Meta, SPA_TYPE_OBJECT_ParamMeta, SPA_TYPE_INFO_PARAM_ID_BASE "Meta", NULL },
++ { SPA_PARAM_IO, SPA_TYPE_OBJECT_ParamIO, SPA_TYPE_INFO_PARAM_ID_BASE "IO", NULL },
++ { SPA_PARAM_EnumProfile, SPA_TYPE_OBJECT_ParamProfile, SPA_TYPE_INFO_PARAM_ID_BASE "EnumProfile", NULL },
++ { SPA_PARAM_Profile, SPA_TYPE_OBJECT_ParamProfile, SPA_TYPE_INFO_PARAM_ID_BASE "Profile", NULL },
++ { SPA_PARAM_EnumPortConfig, SPA_TYPE_OBJECT_ParamPortConfig, SPA_TYPE_INFO_PARAM_ID_BASE "EnumPortConfig", NULL },
++ { SPA_PARAM_PortConfig, SPA_TYPE_OBJECT_ParamPortConfig, SPA_TYPE_INFO_PARAM_ID_BASE "PortConfig", NULL },
++ { SPA_PARAM_EnumRoute, SPA_TYPE_OBJECT_ParamRoute, SPA_TYPE_INFO_PARAM_ID_BASE "EnumRoute", NULL },
++ { SPA_PARAM_Route, SPA_TYPE_OBJECT_ParamRoute, SPA_TYPE_INFO_PARAM_ID_BASE "Route", NULL },
++ { SPA_PARAM_Control, SPA_TYPE_Sequence, SPA_TYPE_INFO_PARAM_ID_BASE "Control", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++/* base for parameter objects */
++#define SPA_TYPE_INFO_Param SPA_TYPE_INFO_OBJECT_BASE "Param"
++#define SPA_TYPE_INFO_PARAM_BASE SPA_TYPE_INFO_Param ":"
++
++#define SPA_TYPE_INFO_Props SPA_TYPE_INFO_PARAM_BASE "Props"
++#define SPA_TYPE_INFO_PROPS_BASE SPA_TYPE_INFO_Props ":"
++
++static const struct spa_type_info spa_type_props[] = {
++ { SPA_PROP_START, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE, spa_type_param, },
++ { SPA_PROP_unknown, SPA_TYPE_None, SPA_TYPE_INFO_PROPS_BASE "unknown", NULL },
++ { SPA_PROP_device, SPA_TYPE_String, SPA_TYPE_INFO_PROPS_BASE "device", NULL },
++ { SPA_PROP_deviceName, SPA_TYPE_String, SPA_TYPE_INFO_PROPS_BASE "deviceName", NULL },
++ { SPA_PROP_deviceFd, SPA_TYPE_Fd, SPA_TYPE_INFO_PROPS_BASE "deviceFd", NULL },
++ { SPA_PROP_card, SPA_TYPE_String, SPA_TYPE_INFO_PROPS_BASE "card", NULL },
++ { SPA_PROP_cardName, SPA_TYPE_String, SPA_TYPE_INFO_PROPS_BASE "cardName", NULL },
++ { SPA_PROP_minLatency, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "minLatency", NULL },
++ { SPA_PROP_maxLatency, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "maxLatency", NULL },
++ { SPA_PROP_periods, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "periods", NULL },
++ { SPA_PROP_periodSize, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "periodSize", NULL },
++ { SPA_PROP_periodEvent, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "periodEvent", NULL },
++ { SPA_PROP_live, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "live", NULL },
++ { SPA_PROP_rate, SPA_TYPE_Double, SPA_TYPE_INFO_PROPS_BASE "rate", NULL },
++ { SPA_PROP_quality, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "quality", NULL },
++
++ { SPA_PROP_waveType, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "waveType", NULL },
++ { SPA_PROP_frequency, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "frequency", NULL },
++ { SPA_PROP_volume, SPA_TYPE_Float, SPA_TYPE_INFO_PROPS_BASE "volume", NULL },
++ { SPA_PROP_mute, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "mute", NULL },
++ { SPA_PROP_patternType, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "patternType", NULL },
++ { SPA_PROP_ditherType, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "ditherType", NULL },
++ { SPA_PROP_truncate, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "truncate", NULL },
++ { SPA_PROP_channelVolumes, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "channelVolumes", NULL },
++ { SPA_PROP_volumeBase, SPA_TYPE_Float, SPA_TYPE_INFO_PROPS_BASE "volumeBase", NULL },
++ { SPA_PROP_volumeStep, SPA_TYPE_Float, SPA_TYPE_INFO_PROPS_BASE "volumeStep", NULL },
++
++ { SPA_PROP_brightness, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "brightness", NULL },
++ { SPA_PROP_contrast, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "contrast", NULL },
++ { SPA_PROP_saturation, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "saturation", NULL },
++ { SPA_PROP_hue, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "hue", NULL },
++ { SPA_PROP_gamma, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "gamma", NULL },
++ { SPA_PROP_exposure, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "exposure", NULL },
++ { SPA_PROP_gain, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "gain", NULL },
++ { SPA_PROP_sharpness, SPA_TYPE_Int, SPA_TYPE_INFO_PROPS_BASE "sharpness", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++/** Enum Property info */
++#define SPA_TYPE_INFO_PropInfo SPA_TYPE_INFO_PARAM_BASE "PropInfo"
++#define SPA_TYPE_INFO_PROP_INFO_BASE SPA_TYPE_INFO_PropInfo ":"
++
++static const struct spa_type_info spa_type_prop_info[] = {
++ { SPA_PROP_INFO_START, SPA_TYPE_Id, SPA_TYPE_INFO_PROP_INFO_BASE, spa_type_param, },
++ { SPA_PROP_INFO_id, SPA_TYPE_Id, SPA_TYPE_INFO_PROP_INFO_BASE "id", spa_type_props },
++ { SPA_PROP_INFO_name, SPA_TYPE_String, SPA_TYPE_INFO_PROP_INFO_BASE "name", NULL },
++ { SPA_PROP_INFO_type, SPA_TYPE_Pod, SPA_TYPE_INFO_PROP_INFO_BASE "type", NULL },
++ { SPA_PROP_INFO_labels, SPA_TYPE_Struct, SPA_TYPE_INFO_PROP_INFO_BASE "labels", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_PARAM_Meta SPA_TYPE_INFO_PARAM_BASE "Meta"
++#define SPA_TYPE_INFO_PARAM_META_BASE SPA_TYPE_INFO_PARAM_Meta ":"
++
++static const struct spa_type_info spa_type_param_meta[] = {
++ { SPA_PARAM_META_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_META_BASE, spa_type_param },
++ { SPA_PARAM_META_type, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_META_BASE "type", spa_type_meta_type },
++ { SPA_PARAM_META_size, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_META_BASE "size", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++/** Base for parameters that describe IO areas to exchange data,
++ * control and properties with a node.
++ */
++#define SPA_TYPE_INFO_PARAM_IO SPA_TYPE_INFO_PARAM_BASE "IO"
++#define SPA_TYPE_INFO_PARAM_IO_BASE SPA_TYPE_INFO_PARAM_IO ":"
++
++static const struct spa_type_info spa_type_param_io[] = {
++ { SPA_PARAM_IO_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_IO_BASE, spa_type_param, },
++ { SPA_PARAM_IO_id, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_IO_BASE "id", spa_type_io },
++ { SPA_PARAM_IO_size, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_IO_BASE "size", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_Format SPA_TYPE_INFO_PARAM_BASE "Format"
++#define SPA_TYPE_INFO_FORMAT_BASE SPA_TYPE_INFO_Format ":"
++
++#define SPA_TYPE_INFO_MediaType SPA_TYPE_INFO_ENUM_BASE "MediaType"
++#define SPA_TYPE_INFO_MEDIA_TYPE_BASE SPA_TYPE_INFO_MediaType ":"
++
++#include <spa/param/audio/type-info.h>
++#include <spa/param/video/type-info.h>
++
++static const struct spa_type_info spa_type_media_type[] = {
++ { SPA_MEDIA_TYPE_unknown, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "unknown", NULL },
++ { SPA_MEDIA_TYPE_audio, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "audio", NULL },
++ { SPA_MEDIA_TYPE_video, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "video", NULL },
++ { SPA_MEDIA_TYPE_image, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "image", NULL },
++ { SPA_MEDIA_TYPE_binary, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "binary", NULL },
++ { SPA_MEDIA_TYPE_stream, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "stream", NULL },
++ { SPA_MEDIA_TYPE_application, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_TYPE_BASE "application", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_MediaSubtype SPA_TYPE_INFO_ENUM_BASE "MediaSubtype"
++#define SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE SPA_TYPE_INFO_MediaSubtype ":"
++
++static const struct spa_type_info spa_type_media_subtype[] = {
++ { SPA_MEDIA_SUBTYPE_unknown, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "unknown", NULL },
++ /* generic subtypes */
++ { SPA_MEDIA_SUBTYPE_raw, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "raw", NULL },
++ { SPA_MEDIA_SUBTYPE_dsp, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "dsp", NULL },
++ /* audio subtypes */
++ { SPA_MEDIA_SUBTYPE_mp3, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mp3", NULL },
++ { SPA_MEDIA_SUBTYPE_aac, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "aac", NULL },
++ { SPA_MEDIA_SUBTYPE_vorbis, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "vorbis", NULL },
++ { SPA_MEDIA_SUBTYPE_wma, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "wma", NULL },
++ { SPA_MEDIA_SUBTYPE_ra, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "ra", NULL },
++ { SPA_MEDIA_SUBTYPE_sbc, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "sbc", NULL },
++ { SPA_MEDIA_SUBTYPE_adpcm, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "adpcm", NULL },
++ { SPA_MEDIA_SUBTYPE_g723, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "g723", NULL },
++ { SPA_MEDIA_SUBTYPE_g726, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "g726", NULL },
++ { SPA_MEDIA_SUBTYPE_g729, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "g729", NULL },
++ { SPA_MEDIA_SUBTYPE_amr, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "amr", NULL },
++ { SPA_MEDIA_SUBTYPE_gsm, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "gsm", NULL },
++ /* video subtypes */
++ { SPA_MEDIA_SUBTYPE_h264, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "h264", NULL },
++ { SPA_MEDIA_SUBTYPE_mjpg, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mjpg", NULL },
++ { SPA_MEDIA_SUBTYPE_dv, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "dv", NULL },
++ { SPA_MEDIA_SUBTYPE_mpegts, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mpegts", NULL },
++ { SPA_MEDIA_SUBTYPE_h263, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "h263", NULL },
++ { SPA_MEDIA_SUBTYPE_mpeg1, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mpeg1", NULL },
++ { SPA_MEDIA_SUBTYPE_mpeg2, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mpeg2", NULL },
++ { SPA_MEDIA_SUBTYPE_mpeg4, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "mpeg4", NULL },
++ { SPA_MEDIA_SUBTYPE_xvid, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "xvid", NULL },
++ { SPA_MEDIA_SUBTYPE_vc1, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "vc1", NULL },
++ { SPA_MEDIA_SUBTYPE_vp8, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "vp8", NULL },
++ { SPA_MEDIA_SUBTYPE_vp9, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "vp9", NULL },
++ { SPA_MEDIA_SUBTYPE_bayer, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "bayer", NULL },
++ /* image subtypes */
++ { SPA_MEDIA_SUBTYPE_jpeg, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "jpeg", NULL },
++ /* stream subtypes */
++ { SPA_MEDIA_SUBTYPE_midi, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "midi", NULL },
++ /* application subtypes */
++ { SPA_MEDIA_SUBTYPE_control, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "control", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_FormatAudio SPA_TYPE_INFO_FORMAT_BASE "Audio"
++#define SPA_TYPE_INFO_FORMAT_AUDIO_BASE SPA_TYPE_INFO_FormatAudio ":"
++
++#define SPA_TYPE_INFO_FormatVideo SPA_TYPE_INFO_FORMAT_BASE "Video"
++#define SPA_TYPE_INFO_FORMAT_VIDEO_BASE SPA_TYPE_INFO_FormatVideo ":"
++
++#define SPA_TYPE_INFO_FORMAT_VIDEO_H264 SPA_TYPE_INFO_FORMAT_VIDEO_BASE "H264"
++#define SPA_TYPE_INFO_FORMAT_VIDEO_H264_BASE SPA_TYPE_INFO_FORMAT_VIDEO_H264 ":"
++
++static const struct spa_type_info spa_type_format[] = {
++ { SPA_FORMAT_START, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_BASE, spa_type_param, },
++
++ { SPA_FORMAT_mediaType, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_BASE "mediaType",
++ spa_type_media_type, },
++ { SPA_FORMAT_mediaSubtype, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_BASE "mediaSubtype",
++ spa_type_media_subtype, },
++
++ { SPA_FORMAT_AUDIO_format, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "format",
++ spa_type_audio_format },
++ { SPA_FORMAT_AUDIO_flags, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "flags",
++ spa_type_audio_flags },
++ { SPA_FORMAT_AUDIO_rate, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "rate", NULL },
++ { SPA_FORMAT_AUDIO_channels, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "channels", NULL },
++ { SPA_FORMAT_AUDIO_position, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "position",
++ spa_type_audio_channel },
++
++ { SPA_FORMAT_VIDEO_format, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "format",
++ spa_type_video_format, },
++ { SPA_FORMAT_VIDEO_modifier, SPA_TYPE_Long, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "modifier", NULL },
++ { SPA_FORMAT_VIDEO_size, SPA_TYPE_Rectangle, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "size", NULL },
++ { SPA_FORMAT_VIDEO_framerate, SPA_TYPE_Fraction, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "framerate", NULL },
++ { SPA_FORMAT_VIDEO_maxFramerate, SPA_TYPE_Fraction, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "maxFramerate", NULL },
++ { SPA_FORMAT_VIDEO_views, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "views", NULL },
++ { SPA_FORMAT_VIDEO_interlaceMode, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "interlaceMode", NULL },
++ { SPA_FORMAT_VIDEO_pixelAspectRatio, SPA_TYPE_Fraction, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "pixelAspectRatio", NULL },
++ { SPA_FORMAT_VIDEO_multiviewMode, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "multiviewMode", NULL },
++ { SPA_FORMAT_VIDEO_multiviewFlags, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "multiviewFlags", NULL },
++ { SPA_FORMAT_VIDEO_chromaSite, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "chromaSite", NULL },
++ { SPA_FORMAT_VIDEO_colorRange, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "colorRange", NULL },
++ { SPA_FORMAT_VIDEO_colorMatrix, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "colorMatrix", NULL },
++ { SPA_FORMAT_VIDEO_transferFunction, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "transferFunction", NULL },
++ { SPA_FORMAT_VIDEO_colorPrimaries, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "colorPrimaries", NULL },
++ { SPA_FORMAT_VIDEO_profile, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "profile", NULL },
++ { SPA_FORMAT_VIDEO_level, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_VIDEO_BASE "level", NULL },
++
++ { SPA_FORMAT_VIDEO_H264_streamFormat, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_H264_BASE "streamFormat", NULL },
++ { SPA_FORMAT_VIDEO_H264_alignment, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_VIDEO_H264_BASE "alignment", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_PARAM_Buffers SPA_TYPE_INFO_PARAM_BASE "Buffers"
++#define SPA_TYPE_INFO_PARAM_BUFFERS_BASE SPA_TYPE_INFO_PARAM_Buffers ":"
++
++#define SPA_TYPE_INFO_PARAM_BlockInfo SPA_TYPE_INFO_PARAM_BUFFERS_BASE "BlockInfo"
++#define SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE SPA_TYPE_INFO_PARAM_BlockInfo ":"
++
++static const struct spa_type_info spa_type_param_buffers[] = {
++ { SPA_PARAM_BUFFERS_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_BUFFERS_BASE, spa_type_param, },
++ { SPA_PARAM_BUFFERS_buffers, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BUFFERS_BASE "buffers", NULL },
++ { SPA_PARAM_BUFFERS_blocks, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BUFFERS_BASE "blocks", NULL },
++ { SPA_PARAM_BUFFERS_size, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "size", NULL },
++ { SPA_PARAM_BUFFERS_stride, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "stride", NULL },
++ { SPA_PARAM_BUFFERS_align, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "align", NULL },
++ { SPA_PARAM_BUFFERS_dataType, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BLOCK_INFO_BASE "dataType", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_ParamAvailability SPA_TYPE_INFO_ENUM_BASE "ParamAvailability"
++#define SPA_TYPE_INFO_PARAM_AVAILABILITY_BASE SPA_TYPE_INFO_ParamAvailability ":"
++
++static const struct spa_type_info spa_type_param_availability[] = {
++ { SPA_PARAM_AVAILABILITY_unknown, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_AVAILABILITY_BASE "unknown", NULL },
++ { SPA_PARAM_AVAILABILITY_no, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_AVAILABILITY_BASE "no", NULL },
++ { SPA_PARAM_AVAILABILITY_yes, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_AVAILABILITY_BASE "yes", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_PARAM_Profile SPA_TYPE_INFO_PARAM_BASE "Profile"
++#define SPA_TYPE_INFO_PARAM_PROFILE_BASE SPA_TYPE_INFO_PARAM_Profile ":"
++
++static const struct spa_type_info spa_type_param_profile[] = {
++ { SPA_PARAM_PROFILE_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_PROFILE_BASE, spa_type_param, },
++ { SPA_PARAM_PROFILE_index, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PROFILE_BASE "index", NULL },
++ { SPA_PARAM_PROFILE_name, SPA_TYPE_String, SPA_TYPE_INFO_PARAM_PROFILE_BASE "name", NULL },
++ { SPA_PARAM_PROFILE_description, SPA_TYPE_String, SPA_TYPE_INFO_PARAM_PROFILE_BASE "description", NULL },
++ { SPA_PARAM_PROFILE_priority, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PROFILE_BASE "priority", NULL },
++ { SPA_PARAM_PROFILE_available, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_PROFILE_BASE "available", spa_type_param_availability, },
++ { SPA_PARAM_PROFILE_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_PROFILE_BASE "info", NULL, },
++ { SPA_PARAM_PROFILE_classes, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_PROFILE_BASE "classes", NULL, },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_ParamPortConfigMode SPA_TYPE_INFO_ENUM_BASE "ParamPortConfigMode"
++#define SPA_TYPE_INFO_PARAM_PORT_CONFIG_MODE_BASE SPA_TYPE_INFO_ParamPortConfigMode ":"
++
++static const struct spa_type_info spa_type_param_port_config_mode[] = {
++ { SPA_PARAM_PORT_CONFIG_MODE_none, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PORT_CONFIG_MODE_BASE "none", NULL },
++ { SPA_PARAM_PORT_CONFIG_MODE_passthrough, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PORT_CONFIG_MODE_BASE "passthrough", NULL },
++ { SPA_PARAM_PORT_CONFIG_MODE_convert, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PORT_CONFIG_MODE_BASE "convert", NULL },
++ { SPA_PARAM_PORT_CONFIG_MODE_dsp, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PORT_CONFIG_MODE_BASE "dsp", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#define SPA_TYPE_INFO_PARAM_PortConfig SPA_TYPE_INFO_PARAM_BASE "PortConfig"
++#define SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE SPA_TYPE_INFO_PARAM_PortConfig ":"
++
++static const struct spa_type_info spa_type_param_port_config[] = {
++ { SPA_PARAM_PORT_CONFIG_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE, spa_type_param, },
++ { SPA_PARAM_PORT_CONFIG_direction, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE "direction", spa_type_direction, },
++ { SPA_PARAM_PORT_CONFIG_mode, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE "mode", spa_type_param_port_config_mode },
++ { SPA_PARAM_PORT_CONFIG_monitor, SPA_TYPE_Bool, SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE "monitor", NULL },
++ { SPA_PARAM_PORT_CONFIG_control, SPA_TYPE_Bool, SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE "control", NULL },
++ { SPA_PARAM_PORT_CONFIG_format, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_PORT_CONFIG_BASE "format", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++
++#define SPA_TYPE_INFO_PARAM_Route SPA_TYPE_INFO_PARAM_BASE "Route"
++#define SPA_TYPE_INFO_PARAM_ROUTE_BASE SPA_TYPE_INFO_PARAM_Route ":"
++
++static const struct spa_type_info spa_type_param_route[] = {
++ { SPA_PARAM_ROUTE_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_ROUTE_BASE, spa_type_param, },
++ { SPA_PARAM_ROUTE_index, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "index", NULL, },
++ { SPA_PARAM_ROUTE_direction, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_ROUTE_BASE "direction", spa_type_direction, },
++ { SPA_PARAM_ROUTE_device, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "device", NULL, },
++ { SPA_PARAM_ROUTE_name, SPA_TYPE_String, SPA_TYPE_INFO_PARAM_ROUTE_BASE "name", NULL, },
++ { SPA_PARAM_ROUTE_description, SPA_TYPE_String, SPA_TYPE_INFO_PARAM_ROUTE_BASE "description", NULL, },
++ { SPA_PARAM_ROUTE_priority, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "priority", NULL, },
++ { SPA_PARAM_ROUTE_available, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_ROUTE_BASE "available", spa_type_param_availability, },
++ { SPA_PARAM_ROUTE_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_ROUTE_BASE "info", NULL, },
++ { SPA_PARAM_ROUTE_profiles, SPA_TYPE_Array, SPA_TYPE_INFO_PARAM_ROUTE_BASE "profiles", NULL, },
++ { SPA_PARAM_ROUTE_props, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_ROUTE_BASE "props", NULL, },
++ { SPA_PARAM_ROUTE_devices, SPA_TYPE_Array, SPA_TYPE_INFO_PARAM_ROUTE_BASE "devices", NULL, },
++ { SPA_PARAM_ROUTE_profile, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "profile", NULL, },
++ { 0, 0, NULL, NULL },
++};
++
++#include <spa/param/profiler.h>
++
++#define SPA_TYPE_INFO_Profiler SPA_TYPE_INFO_OBJECT_BASE "Profiler"
++#define SPA_TYPE_INFO_PROFILER_BASE SPA_TYPE_INFO_Profiler ":"
++
++static const struct spa_type_info spa_type_profiler[] = {
++ { SPA_PROFILER_START, SPA_TYPE_Id, SPA_TYPE_INFO_PROFILER_BASE, spa_type_param, },
++ { SPA_PROFILER_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "info", NULL, },
++ { SPA_PROFILER_clock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "clock", NULL, },
++ { SPA_PROFILER_driverBlock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "driverBlock", NULL, },
++ { SPA_PROFILER_followerBlock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "followerBlock", NULL, },
++ { 0, 0, NULL, NULL },
++};
++
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_PARAM_TYPES_H */
+diff --git a/third_party/pipewire/spa/param/video/chroma.h b/third_party/pipewire/spa/param/video/chroma.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/video/chroma.h
+@@ -0,0 +1,60 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_VIDEO_CHROMA_H
++#define SPA_VIDEO_CHROMA_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** Various Chroma sitings.
++ * @SPA_VIDEO_CHROMA_SITE_UNKNOWN: unknown cositing
++ * @SPA_VIDEO_CHROMA_SITE_NONE: no cositing
++ * @SPA_VIDEO_CHROMA_SITE_H_COSITED: chroma is horizontally cosited
++ * @SPA_VIDEO_CHROMA_SITE_V_COSITED: chroma is vertically cosited
++ * @SPA_VIDEO_CHROMA_SITE_ALT_LINE: choma samples are sited on alternate lines
++ * @SPA_VIDEO_CHROMA_SITE_COSITED: chroma samples cosited with luma samples
++ * @SPA_VIDEO_CHROMA_SITE_JPEG: jpeg style cositing, also for mpeg1 and mjpeg
++ * @SPA_VIDEO_CHROMA_SITE_MPEG2: mpeg2 style cositing
++ * @SPA_VIDEO_CHROMA_SITE_DV: DV style cositing
++ */
++enum spa_video_chroma_site {
++ SPA_VIDEO_CHROMA_SITE_UNKNOWN = 0,
++ SPA_VIDEO_CHROMA_SITE_NONE = (1 << 0),
++ SPA_VIDEO_CHROMA_SITE_H_COSITED = (1 << 1),
++ SPA_VIDEO_CHROMA_SITE_V_COSITED = (1 << 2),
++ SPA_VIDEO_CHROMA_SITE_ALT_LINE = (1 << 3),
++ /* some common chroma cositing */
++ SPA_VIDEO_CHROMA_SITE_COSITED = (SPA_VIDEO_CHROMA_SITE_H_COSITED | SPA_VIDEO_CHROMA_SITE_V_COSITED),
++ SPA_VIDEO_CHROMA_SITE_JPEG = (SPA_VIDEO_CHROMA_SITE_NONE),
++ SPA_VIDEO_CHROMA_SITE_MPEG2 = (SPA_VIDEO_CHROMA_SITE_H_COSITED),
++ SPA_VIDEO_CHROMA_SITE_DV = (SPA_VIDEO_CHROMA_SITE_COSITED | SPA_VIDEO_CHROMA_SITE_ALT_LINE),
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_VIDEO_CHROMA_H */
+diff --git a/third_party/pipewire/spa/param/video/color.h b/third_party/pipewire/spa/param/video/color.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/video/color.h
+@@ -0,0 +1,162 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_VIDEO_COLOR_H
++#define SPA_VIDEO_COLOR_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/**
++ * spa_video_color_range:
++ * @SPA_VIDEO_COLOR_RANGE_UNKNOWN: unknown range
++ * @SPA_VIDEO_COLOR_RANGE_0_255: [0..255] for 8 bit components
++ * @SPA_VIDEO_COLOR_RANGE_16_235: [16..235] for 8 bit components. Chroma has
++ * [16..240] range.
++ *
++ * Possible color range values. These constants are defined for 8 bit color
++ * values and can be scaled for other bit depths.
++ */
++enum spa_video_color_range {
++ SPA_VIDEO_COLOR_RANGE_UNKNOWN = 0,
++ SPA_VIDEO_COLOR_RANGE_0_255,
++ SPA_VIDEO_COLOR_RANGE_16_235
++};
++
++/**
++ * spa_video_color_matrix:
++ * @SPA_VIDEO_COLOR_MATRIX_UNKNOWN: unknown matrix
++ * @SPA_VIDEO_COLOR_MATRIX_RGB: identity matrix
++ * @SPA_VIDEO_COLOR_MATRIX_FCC: FCC color matrix
++ * @SPA_VIDEO_COLOR_MATRIX_BT709: ITU-R BT.709 color matrix
++ * @SPA_VIDEO_COLOR_MATRIX_BT601: ITU-R BT.601 color matrix
++ * @SPA_VIDEO_COLOR_MATRIX_SMPTE240M: SMPTE 240M color matrix
++ * @SPA_VIDEO_COLOR_MATRIX_BT2020: ITU-R BT.2020 color matrix. Since: 1.6.
++ *
++ * The color matrix is used to convert between Y'PbPr and
++ * non-linear RGB (R'G'B')
++ */
++enum spa_video_color_matrix {
++ SPA_VIDEO_COLOR_MATRIX_UNKNOWN = 0,
++ SPA_VIDEO_COLOR_MATRIX_RGB,
++ SPA_VIDEO_COLOR_MATRIX_FCC,
++ SPA_VIDEO_COLOR_MATRIX_BT709,
++ SPA_VIDEO_COLOR_MATRIX_BT601,
++ SPA_VIDEO_COLOR_MATRIX_SMPTE240M,
++ SPA_VIDEO_COLOR_MATRIX_BT2020
++};
++
++/**
++ * spa_video_transfer_function:
++ * @SPA_VIDEO_TRANSFER_UNKNOWN: unknown transfer function
++ * @SPA_VIDEO_TRANSFER_GAMMA10: linear RGB, gamma 1.0 curve
++ * @SPA_VIDEO_TRANSFER_GAMMA18: Gamma 1.8 curve
++ * @SPA_VIDEO_TRANSFER_GAMMA20: Gamma 2.0 curve
++ * @SPA_VIDEO_TRANSFER_GAMMA22: Gamma 2.2 curve
++ * @SPA_VIDEO_TRANSFER_BT709: Gamma 2.2 curve with a linear segment in the lower
++ * range
++ * @SPA_VIDEO_TRANSFER_SMPTE240M: Gamma 2.2 curve with a linear segment in the
++ * lower range
++ * @SPA_VIDEO_TRANSFER_SRGB: Gamma 2.4 curve with a linear segment in the lower
++ * range
++ * @SPA_VIDEO_TRANSFER_GAMMA28: Gamma 2.8 curve
++ * @SPA_VIDEO_TRANSFER_LOG100: Logarithmic transfer characteristic
++ * 100:1 range
++ * @SPA_VIDEO_TRANSFER_LOG316: Logarithmic transfer characteristic
++ * 316.22777:1 range
++ * @SPA_VIDEO_TRANSFER_BT2020_12: Gamma 2.2 curve with a linear segment in the lower
++ * range. Used for BT.2020 with 12 bits per
++ * component. Since: 1.6.
++ * @SPA_VIDEO_TRANSFER_ADOBERGB: Gamma 2.19921875. Since: 1.8
++ *
++ * The video transfer function defines the formula for converting between
++ * non-linear RGB (R'G'B') and linear RGB
++ */
++enum spa_video_transfer_function {
++ SPA_VIDEO_TRANSFER_UNKNOWN = 0,
++ SPA_VIDEO_TRANSFER_GAMMA10,
++ SPA_VIDEO_TRANSFER_GAMMA18,
++ SPA_VIDEO_TRANSFER_GAMMA20,
++ SPA_VIDEO_TRANSFER_GAMMA22,
++ SPA_VIDEO_TRANSFER_BT709,
++ SPA_VIDEO_TRANSFER_SMPTE240M,
++ SPA_VIDEO_TRANSFER_SRGB,
++ SPA_VIDEO_TRANSFER_GAMMA28,
++ SPA_VIDEO_TRANSFER_LOG100,
++ SPA_VIDEO_TRANSFER_LOG316,
++ SPA_VIDEO_TRANSFER_BT2020_12,
++ SPA_VIDEO_TRANSFER_ADOBERGB
++};
++
++/**
++ * spa_video_color_primaries:
++ * @SPA_VIDEO_COLOR_PRIMARIES_UNKNOWN: unknown color primaries
++ * @SPA_VIDEO_COLOR_PRIMARIES_BT709: BT709 primaries
++ * @SPA_VIDEO_COLOR_PRIMARIES_BT470M: BT470M primaries
++ * @SPA_VIDEO_COLOR_PRIMARIES_BT470BG: BT470BG primaries
++ * @SPA_VIDEO_COLOR_PRIMARIES_SMPTE170M: SMPTE170M primaries
++ * @SPA_VIDEO_COLOR_PRIMARIES_SMPTE240M: SMPTE240M primaries
++ * @SPA_VIDEO_COLOR_PRIMARIES_FILM: Generic film
++ * @SPA_VIDEO_COLOR_PRIMARIES_BT2020: BT2020 primaries. Since: 1.6.
++ * @SPA_VIDEO_COLOR_PRIMARIES_ADOBERGB: Adobe RGB primaries. Since: 1.8
++ *
++ * The color primaries define the how to transform linear RGB values to and from
++ * the CIE XYZ colorspace.
++ */
++enum spa_video_color_primaries {
++ SPA_VIDEO_COLOR_PRIMARIES_UNKNOWN = 0,
++ SPA_VIDEO_COLOR_PRIMARIES_BT709,
++ SPA_VIDEO_COLOR_PRIMARIES_BT470M,
++ SPA_VIDEO_COLOR_PRIMARIES_BT470BG,
++ SPA_VIDEO_COLOR_PRIMARIES_SMPTE170M,
++ SPA_VIDEO_COLOR_PRIMARIES_SMPTE240M,
++ SPA_VIDEO_COLOR_PRIMARIES_FILM,
++ SPA_VIDEO_COLOR_PRIMARIES_BT2020,
++ SPA_VIDEO_COLOR_PRIMARIES_ADOBERGB
++};
++
++/**
++ * spa_video_colorimetry:
++ * @range: the color range. This is the valid range for the samples.
++ * It is used to convert the samples to Y'PbPr values.
++ * @matrix: the color matrix. Used to convert between Y'PbPr and
++ * non-linear RGB (R'G'B')
++ * @transfer: the transfer function. used to convert between R'G'B' and RGB
++ * @primaries: color primaries. used to convert between R'G'B' and CIE XYZ
++ *
++ * Structure describing the color info.
++ */
++struct spa_video_colorimetry {
++ enum spa_video_color_range range;
++ enum spa_video_color_matrix matrix;
++ enum spa_video_transfer_function transfer;
++ enum spa_video_color_primaries primaries;
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_VIDEO_COLOR_H */
+diff --git a/third_party/pipewire/spa/param/video/encoded.h b/third_party/pipewire/spa/param/video/encoded.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/video/encoded.h
+@@ -0,0 +1,65 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_VIDEO_ENCODED_H
++#define SPA_VIDEO_ENCODED_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/param/format.h>
++
++enum spa_h264_stream_format {
++ SPA_H264_STREAM_FORMAT_UNKNOWN = 0,
++ SPA_H264_STREAM_FORMAT_AVC,
++ SPA_H264_STREAM_FORMAT_AVC3,
++ SPA_H264_STREAM_FORMAT_BYTESTREAM
++};
++
++enum spa_h264_alignment {
++ SPA_H264_ALIGNMENT_UNKNOWN = 0,
++ SPA_H264_ALIGNMENT_AU,
++ SPA_H264_ALIGNMENT_NAL
++};
++
++struct spa_video_info_h264 {
++ struct spa_rectangle size;
++ struct spa_fraction framerate;
++ struct spa_fraction max_framerate;
++ enum spa_h264_stream_format stream_format;
++ enum spa_h264_alignment alignment;
++};
++
++struct spa_video_info_mjpg {
++ struct spa_rectangle size;
++ struct spa_fraction framerate;
++ struct spa_fraction max_framerate;
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_VIDEO_ENCODED_H */
+diff --git a/third_party/pipewire/spa/param/video/format-utils.h b/third_party/pipewire/spa/param/video/format-utils.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/video/format-utils.h
+@@ -0,0 +1,167 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_PARAM_VIDEO_FORMAT_UTILS_H
++#define SPA_PARAM_VIDEO_FORMAT_UTILS_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/pod/parser.h>
++#include <spa/pod/builder.h>
++#include <spa/param/video/format.h>
++#include <spa/param/format-utils.h>
++
++static inline int
++spa_format_video_raw_parse(const struct spa_pod *format,
++ struct spa_video_info_raw *info)
++{
++ return spa_pod_parse_object(format,
++ SPA_TYPE_OBJECT_Format, NULL,
++ SPA_FORMAT_VIDEO_format, SPA_POD_Id(&info->format),
++ SPA_FORMAT_VIDEO_modifier, SPA_POD_OPT_Long(&info->modifier),
++ SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&info->size),
++ SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&info->framerate),
++ SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_OPT_Fraction(&info->max_framerate),
++ SPA_FORMAT_VIDEO_views, SPA_POD_OPT_Int(&info->views),
++ SPA_FORMAT_VIDEO_interlaceMode, SPA_POD_OPT_Id(&info->interlace_mode),
++ SPA_FORMAT_VIDEO_pixelAspectRatio, SPA_POD_OPT_Fraction(&info->pixel_aspect_ratio),
++ SPA_FORMAT_VIDEO_multiviewMode, SPA_POD_OPT_Id(&info->multiview_mode),
++ SPA_FORMAT_VIDEO_multiviewFlags, SPA_POD_OPT_Id(&info->multiview_flags),
++ SPA_FORMAT_VIDEO_chromaSite, SPA_POD_OPT_Id(&info->chroma_site),
++ SPA_FORMAT_VIDEO_colorRange, SPA_POD_OPT_Id(&info->color_range),
++ SPA_FORMAT_VIDEO_colorMatrix, SPA_POD_OPT_Id(&info->color_matrix),
++ SPA_FORMAT_VIDEO_transferFunction, SPA_POD_OPT_Id(&info->transfer_function),
++ SPA_FORMAT_VIDEO_colorPrimaries, SPA_POD_OPT_Id(&info->color_primaries));
++}
++
++static inline int
++spa_format_video_dsp_parse(const struct spa_pod *format,
++ struct spa_video_info_dsp *info)
++{
++ return spa_pod_parse_object(format,
++ SPA_TYPE_OBJECT_Format, NULL,
++ SPA_FORMAT_VIDEO_format, SPA_POD_Id(&info->format),
++ SPA_FORMAT_VIDEO_modifier, SPA_POD_OPT_Long(&info->modifier));
++}
++
++static inline struct spa_pod *
++spa_format_video_raw_build(struct spa_pod_builder *builder, uint32_t id,
++ struct spa_video_info_raw *info)
++{
++ struct spa_pod_frame f;
++ spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
++ spa_pod_builder_add(builder,
++ 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_Id(info->format),
++ SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&info->size),
++ SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&info->framerate),
++ 0);
++ if (info->modifier != 0)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_modifier, SPA_POD_Long(info->modifier), 0);
++ if (info->max_framerate.denom != 0)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_Fraction(info->max_framerate), 0);
++ if (info->views != 0)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_views, SPA_POD_Int(info->views), 0);
++ if (info->interlace_mode != 0)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_interlaceMode, SPA_POD_Id(info->interlace_mode), 0);
++ if (info->pixel_aspect_ratio.denom != 0)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_pixelAspectRatio,SPA_POD_Fraction(info->pixel_aspect_ratio), 0);
++ if (info->multiview_mode != 0)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_multiviewMode, SPA_POD_Id(info->multiview_mode), 0);
++ if (info->multiview_flags != 0)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_multiviewFlags,SPA_POD_Id(info->multiview_flags), 0);
++ if (info->chroma_site != 0)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_chromaSite, SPA_POD_Id(info->chroma_site), 0);
++ if (info->color_range != 0)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_colorRange, SPA_POD_Id(info->color_range), 0);
++ if (info->color_matrix != 0)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_colorMatrix, SPA_POD_Id(info->color_matrix), 0);
++ if (info->transfer_function != 0)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_transferFunction,SPA_POD_Id(info->transfer_function), 0);
++ if (info->color_primaries != 0)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_colorPrimaries,SPA_POD_Id(info->color_primaries), 0);
++ return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
++}
++
++static inline struct spa_pod *
++spa_format_video_dsp_build(struct spa_pod_builder *builder, uint32_t id,
++ struct spa_video_info_dsp *info)
++{
++ struct spa_pod_frame f;
++ spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
++ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp),
++ SPA_FORMAT_VIDEO_format, SPA_POD_Id(info->format),
++ 0);
++ if (info->modifier)
++ spa_pod_builder_add(builder,
++ SPA_FORMAT_VIDEO_modifier, SPA_POD_Long(info->modifier), 0);
++ return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
++}
++
++static inline int
++spa_format_video_h264_parse(const struct spa_pod *format,
++ struct spa_video_info_h264 *info)
++{
++ return spa_pod_parse_object(format,
++ SPA_TYPE_OBJECT_Format, NULL,
++ SPA_FORMAT_VIDEO_size, SPA_POD_OPT_Rectangle(&info->size),
++ SPA_FORMAT_VIDEO_framerate, SPA_POD_OPT_Fraction(&info->framerate),
++ SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_OPT_Fraction(&info->max_framerate),
++ SPA_FORMAT_VIDEO_H264_streamFormat, SPA_POD_OPT_Id(&info->stream_format),
++ SPA_FORMAT_VIDEO_H264_alignment, SPA_POD_OPT_Id(&info->alignment));
++}
++
++static inline int
++spa_format_video_mjpg_parse(const struct spa_pod *format,
++ struct spa_video_info_mjpg *info)
++{
++ return spa_pod_parse_object(format,
++ SPA_TYPE_OBJECT_Format, NULL,
++ SPA_FORMAT_VIDEO_size, SPA_POD_OPT_Rectangle(&info->size),
++ SPA_FORMAT_VIDEO_framerate, SPA_POD_OPT_Fraction(&info->framerate),
++ SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_OPT_Fraction(&info->max_framerate));
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_PARAM_VIDEO_FORMAT_UTILS_H */
+diff --git a/third_party/pipewire/spa/param/video/format.h b/third_party/pipewire/spa/param/video/format.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/video/format.h
+@@ -0,0 +1,50 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_PARAM_VIDEO_FORMAT_H
++#define SPA_PARAM_VIDEO_FORMAT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/param/video/raw.h>
++#include <spa/param/video/encoded.h>
++
++struct spa_video_info {
++ uint32_t media_type;
++ uint32_t media_subtype;
++ union {
++ struct spa_video_info_raw raw;
++ struct spa_video_info_dsp dsp;
++ struct spa_video_info_h264 h264;
++ struct spa_video_info_mjpg mjpg;
++ } info;
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_PARAM_VIDEO_FORMAT_H */
+diff --git a/third_party/pipewire/spa/param/video/multiview.h b/third_party/pipewire/spa/param/video/multiview.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/video/multiview.h
+@@ -0,0 +1,140 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_VIDEO_MULTIVIEW_H
++#define SPA_VIDEO_MULTIVIEW_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/**
++ * spa_video_multiview_mode:
++ * @SPA_VIDEO_MULTIVIEW_MODE_NONE: A special value indicating
++ * no multiview information. Used in spa_video_info and other places to
++ * indicate that no specific multiview handling has been requested or
++ * provided. This value is never carried on caps.
++ * @SPA_VIDEO_MULTIVIEW_MODE_MONO: All frames are monoscopic.
++ * @SPA_VIDEO_MULTIVIEW_MODE_LEFT: All frames represent a left-eye view.
++ * @SPA_VIDEO_MULTIVIEW_MODE_RIGHT: All frames represent a right-eye view.
++ * @SPA_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE: Left and right eye views are
++ * provided in the left and right half of the frame respectively.
++ * @SPA_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX: Left and right eye
++ * views are provided in the left and right half of the frame, but
++ * have been sampled using quincunx method, with half-pixel offset
++ * between the 2 views.
++ * @SPA_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED: Alternating vertical
++ * columns of pixels represent the left and right eye view respectively.
++ * @SPA_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED: Alternating horizontal
++ * rows of pixels represent the left and right eye view respectively.
++ * @SPA_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM: The top half of the frame
++ * contains the left eye, and the bottom half the right eye.
++ * @SPA_VIDEO_MULTIVIEW_MODE_CHECKERBOARD: Pixels are arranged with
++ * alternating pixels representing left and right eye views in a
++ * checkerboard fashion.
++ * @SPA_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME: Left and right eye views
++ * are provided in separate frames alternately.
++ * @SPA_VIDEO_MULTIVIEW_MODE_MULTIVIEW_FRAME_BY_FRAME: Multiple
++ * independent views are provided in separate frames in sequence.
++ * This method only applies to raw video buffers at the moment.
++ * Specific view identification is via the #spa_video_multiview_meta
++ * on raw video buffers.
++ * @SPA_VIDEO_MULTIVIEW_MODE_SEPARATED: Multiple views are
++ * provided as separate #spa_data framebuffers attached to each
++ * #spa_buffer, described by the #spa_video_multiview_meta
++ *
++ * All possible stereoscopic 3D and multiview representations.
++ * In conjunction with #soa_video_multiview_flags, describes how
++ * multiview content is being transported in the stream.
++ */
++enum spa_video_multiview_mode {
++ SPA_VIDEO_MULTIVIEW_MODE_NONE = -1,
++ SPA_VIDEO_MULTIVIEW_MODE_MONO = 0,
++ /* Single view modes */
++ SPA_VIDEO_MULTIVIEW_MODE_LEFT,
++ SPA_VIDEO_MULTIVIEW_MODE_RIGHT,
++ /* Stereo view modes */
++ SPA_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE,
++ SPA_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX,
++ SPA_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED,
++ SPA_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED,
++ SPA_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM,
++ SPA_VIDEO_MULTIVIEW_MODE_CHECKERBOARD,
++ /* Padding for new frame packing modes */
++
++ SPA_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME = 32,
++ /* Multivew mode(s) */
++ SPA_VIDEO_MULTIVIEW_MODE_MULTIVIEW_FRAME_BY_FRAME,
++ SPA_VIDEO_MULTIVIEW_MODE_SEPARATED
++ /* future expansion for annotated modes */
++};
++
++/**
++ * spa_video_multiview_flags:
++ * @SPA_VIDEO_MULTIVIEW_FLAGS_NONE: No flags
++ * @SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST: For stereo streams, the
++ * normal arrangement of left and right views is reversed.
++ * @SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED: The left view is vertically
++ * mirrored.
++ * @SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED: The left view is horizontally
++ * mirrored.
++ * @SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED: The right view is
++ * vertically mirrored.
++ * @SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED: The right view is
++ * horizontally mirrored.
++ * @SPA_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT: For frame-packed
++ * multiview modes, indicates that the individual
++ * views have been encoded with half the true width or height
++ * and should be scaled back up for display. This flag
++ * is used for overriding input layout interpretation
++ * by adjusting pixel-aspect-ratio.
++ * For side-by-side, column interleaved or checkerboard packings, the
++ * pixel width will be doubled. For row interleaved and top-bottom
++ * encodings, pixel height will be doubled.
++ * @SPA_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO: The video stream contains both
++ * mono and multiview portions, signalled on each buffer by the
++ * absence or presence of the @SPA_VIDEO_BUFFER_FLAG_MULTIPLE_VIEW
++ * buffer flag.
++ *
++ * spa_video_multiview_flags are used to indicate extra properties of a
++ * stereo/multiview stream beyond the frame layout and buffer mapping
++ * that is conveyed in the #spa_video_multiview_mode.
++ */
++enum spa_video_multiview_flags {
++ SPA_VIDEO_MULTIVIEW_FLAGS_NONE = 0,
++ SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST = (1 << 0),
++ SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED = (1 << 1),
++ SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED = (1 << 2),
++ SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED = (1 << 3),
++ SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED = (1 << 4),
++ SPA_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT = (1 << 14),
++ SPA_VIDEO_MULTIVIEW_FLAGS_MIXED_MONO = (1 << 15)
++};
++
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_VIDEO_MULTIVIEW_H */
+diff --git a/third_party/pipewire/spa/param/video/raw.h b/third_party/pipewire/spa/param/video/raw.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/video/raw.h
+@@ -0,0 +1,221 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_VIDEO_RAW_H
++#define SPA_VIDEO_RAW_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/param/video/chroma.h>
++#include <spa/param/video/color.h>
++#include <spa/param/video/multiview.h>
++
++#define SPA_VIDEO_MAX_PLANES 4
++#define SPA_VIDEO_MAX_COMPONENTS 4
++
++enum spa_video_format {
++ SPA_VIDEO_FORMAT_UNKNOWN,
++ SPA_VIDEO_FORMAT_ENCODED,
++
++ SPA_VIDEO_FORMAT_I420,
++ SPA_VIDEO_FORMAT_YV12,
++ SPA_VIDEO_FORMAT_YUY2,
++ SPA_VIDEO_FORMAT_UYVY,
++ SPA_VIDEO_FORMAT_AYUV,
++ SPA_VIDEO_FORMAT_RGBx,
++ SPA_VIDEO_FORMAT_BGRx,
++ SPA_VIDEO_FORMAT_xRGB,
++ SPA_VIDEO_FORMAT_xBGR,
++ SPA_VIDEO_FORMAT_RGBA,
++ SPA_VIDEO_FORMAT_BGRA,
++ SPA_VIDEO_FORMAT_ARGB,
++ SPA_VIDEO_FORMAT_ABGR,
++ SPA_VIDEO_FORMAT_RGB,
++ SPA_VIDEO_FORMAT_BGR,
++ SPA_VIDEO_FORMAT_Y41B,
++ SPA_VIDEO_FORMAT_Y42B,
++ SPA_VIDEO_FORMAT_YVYU,
++ SPA_VIDEO_FORMAT_Y444,
++ SPA_VIDEO_FORMAT_v210,
++ SPA_VIDEO_FORMAT_v216,
++ SPA_VIDEO_FORMAT_NV12,
++ SPA_VIDEO_FORMAT_NV21,
++ SPA_VIDEO_FORMAT_GRAY8,
++ SPA_VIDEO_FORMAT_GRAY16_BE,
++ SPA_VIDEO_FORMAT_GRAY16_LE,
++ SPA_VIDEO_FORMAT_v308,
++ SPA_VIDEO_FORMAT_RGB16,
++ SPA_VIDEO_FORMAT_BGR16,
++ SPA_VIDEO_FORMAT_RGB15,
++ SPA_VIDEO_FORMAT_BGR15,
++ SPA_VIDEO_FORMAT_UYVP,
++ SPA_VIDEO_FORMAT_A420,
++ SPA_VIDEO_FORMAT_RGB8P,
++ SPA_VIDEO_FORMAT_YUV9,
++ SPA_VIDEO_FORMAT_YVU9,
++ SPA_VIDEO_FORMAT_IYU1,
++ SPA_VIDEO_FORMAT_ARGB64,
++ SPA_VIDEO_FORMAT_AYUV64,
++ SPA_VIDEO_FORMAT_r210,
++ SPA_VIDEO_FORMAT_I420_10BE,
++ SPA_VIDEO_FORMAT_I420_10LE,
++ SPA_VIDEO_FORMAT_I422_10BE,
++ SPA_VIDEO_FORMAT_I422_10LE,
++ SPA_VIDEO_FORMAT_Y444_10BE,
++ SPA_VIDEO_FORMAT_Y444_10LE,
++ SPA_VIDEO_FORMAT_GBR,
++ SPA_VIDEO_FORMAT_GBR_10BE,
++ SPA_VIDEO_FORMAT_GBR_10LE,
++ SPA_VIDEO_FORMAT_NV16,
++ SPA_VIDEO_FORMAT_NV24,
++ SPA_VIDEO_FORMAT_NV12_64Z32,
++ SPA_VIDEO_FORMAT_A420_10BE,
++ SPA_VIDEO_FORMAT_A420_10LE,
++ SPA_VIDEO_FORMAT_A422_10BE,
++ SPA_VIDEO_FORMAT_A422_10LE,
++ SPA_VIDEO_FORMAT_A444_10BE,
++ SPA_VIDEO_FORMAT_A444_10LE,
++ SPA_VIDEO_FORMAT_NV61,
++ SPA_VIDEO_FORMAT_P010_10BE,
++ SPA_VIDEO_FORMAT_P010_10LE,
++ SPA_VIDEO_FORMAT_IYU2,
++ SPA_VIDEO_FORMAT_VYUY,
++ SPA_VIDEO_FORMAT_GBRA,
++ SPA_VIDEO_FORMAT_GBRA_10BE,
++ SPA_VIDEO_FORMAT_GBRA_10LE,
++ SPA_VIDEO_FORMAT_GBR_12BE,
++ SPA_VIDEO_FORMAT_GBR_12LE,
++ SPA_VIDEO_FORMAT_GBRA_12BE,
++ SPA_VIDEO_FORMAT_GBRA_12LE,
++ SPA_VIDEO_FORMAT_I420_12BE,
++ SPA_VIDEO_FORMAT_I420_12LE,
++ SPA_VIDEO_FORMAT_I422_12BE,
++ SPA_VIDEO_FORMAT_I422_12LE,
++ SPA_VIDEO_FORMAT_Y444_12BE,
++ SPA_VIDEO_FORMAT_Y444_12LE,
++
++ SPA_VIDEO_FORMAT_RGBA_F16,
++ SPA_VIDEO_FORMAT_RGBA_F32,
++
++ /* Aliases */
++ SPA_VIDEO_FORMAT_DSP_F32 = SPA_VIDEO_FORMAT_RGBA_F32,
++};
++
++/**
++ * spa_video_flags:
++ * @SPA_VIDEO_FLAG_NONE: no flags
++ * @SPA_VIDEO_FLAG_VARIABLE_FPS: a variable fps is selected, fps_n and fps_d
++ * denote the maximum fps of the video
++ * @SPA_VIDEO_FLAG_PREMULTIPLIED_ALPHA: Each color has been scaled by the alpha
++ * value.
++ *
++ * Extra video flags
++ */
++enum spa_video_flags {
++ SPA_VIDEO_FLAG_NONE = 0,
++ SPA_VIDEO_FLAG_VARIABLE_FPS = (1 << 0),
++ SPA_VIDEO_FLAG_PREMULTIPLIED_ALPHA = (1 << 1)
++};
++
++/**
++ * spa_video_interlace_mode:
++ * @SPA_VIDEO_INTERLACE_MODE_PROGRESSIVE: all frames are progressive
++ * @SPA_VIDEO_INTERLACE_MODE_INTERLEAVED: 2 fields are interleaved in one video
++ * frame. Extra buffer flags describe the field order.
++ * @SPA_VIDEO_INTERLACE_MODE_MIXED: frames contains both interlaced and
++ * progressive video, the buffer flags describe the frame and fields.
++ * @SPA_VIDEO_INTERLACE_MODE_FIELDS: 2 fields are stored in one buffer, use the
++ * frame ID to get access to the required field. For multiview (the
++ * 'views' property > 1) the fields of view N can be found at frame ID
++ * (N * 2) and (N * 2) + 1.
++ * Each field has only half the amount of lines as noted in the
++ * height property. This mode requires multiple spa_data
++ * to describe the fields.
++ *
++ * The possible values of the #spa_video_interlace_mode describing the interlace
++ * mode of the stream.
++ */
++enum spa_video_interlace_mode {
++ SPA_VIDEO_INTERLACE_MODE_PROGRESSIVE = 0,
++ SPA_VIDEO_INTERLACE_MODE_INTERLEAVED,
++ SPA_VIDEO_INTERLACE_MODE_MIXED,
++ SPA_VIDEO_INTERLACE_MODE_FIELDS
++};
++
++/**
++ * spa_video_info_raw:
++ * @format: the format
++ * @modifier: format modifier
++ * @size: the frame size of the video
++ * @framerate: the framerate of the video 0/1 means variable rate
++ * @max_framerate: the maximum framerate of the video. This is only valid when
++ * @framerate is 0/1
++ * @views: the number of views in this video
++ * @interlace_mode: the interlace mode
++ * @pixel_aspect_ratio: The pixel aspect ratio
++ * @multiview_mode: multiview mode
++ * @multiview_flags: multiview flags
++ * @chroma_site: the chroma siting
++ * @color_range: the color range. This is the valid range for the samples.
++ * It is used to convert the samples to Y'PbPr values.
++ * @color_matrix: the color matrix. Used to convert between Y'PbPr and
++ * non-linear RGB (R'G'B')
++ * @transfer_function: the transfer function. used to convert between R'G'B' and RGB
++ * @color_primaries: color primaries. used to convert between R'G'B' and CIE XYZ
++ */
++struct spa_video_info_raw {
++ enum spa_video_format format;
++ int64_t modifier;
++ struct spa_rectangle size;
++ struct spa_fraction framerate;
++ struct spa_fraction max_framerate;
++ uint32_t views;
++ enum spa_video_interlace_mode interlace_mode;
++ struct spa_fraction pixel_aspect_ratio;
++ enum spa_video_multiview_mode multiview_mode;
++ enum spa_video_multiview_flags multiview_flags;
++ enum spa_video_chroma_site chroma_site;
++ enum spa_video_color_range color_range;
++ enum spa_video_color_matrix color_matrix;
++ enum spa_video_transfer_function transfer_function;
++ enum spa_video_color_primaries color_primaries;
++};
++
++#define SPA_VIDEO_INFO_RAW_INIT(...) (struct spa_video_info_raw) { __VA_ARGS__ }
++
++struct spa_video_info_dsp {
++ enum spa_video_format format;
++ int64_t modifier;
++};
++
++#define SPA_VIDEO_INFO_DSP_INIT(...) (struct spa_video_info_dsp) { __VA_ARGS__ }
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_VIDEO_RAW_H */
+diff --git a/third_party/pipewire/spa/param/video/type-info.h b/third_party/pipewire/spa/param/video/type-info.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/param/video/type-info.h
+@@ -0,0 +1,124 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_VIDEO_TYPES_H
++#define SPA_VIDEO_TYPES_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/param/video/raw.h>
++
++#define SPA_TYPE_INFO_VideoFormat SPA_TYPE_INFO_ENUM_BASE "VideoFormat"
++#define SPA_TYPE_INFO_VIDEO_FORMAT_BASE SPA_TYPE_INFO_VideoFormat ":"
++
++static const struct spa_type_info spa_type_video_format[] = {
++ { SPA_VIDEO_FORMAT_ENCODED, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "encoded", NULL },
++ { SPA_VIDEO_FORMAT_I420, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", NULL },
++ { SPA_VIDEO_FORMAT_YV12, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YV12", NULL },
++ { SPA_VIDEO_FORMAT_YUY2, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YUY2", NULL },
++ { SPA_VIDEO_FORMAT_UYVY, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "UYVY", NULL },
++ { SPA_VIDEO_FORMAT_AYUV, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "AYUV", NULL },
++ { SPA_VIDEO_FORMAT_RGBx, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBx", NULL },
++ { SPA_VIDEO_FORMAT_BGRx, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGRx", NULL },
++ { SPA_VIDEO_FORMAT_xRGB, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "xRGB", NULL },
++ { SPA_VIDEO_FORMAT_xBGR, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "xBGR", NULL },
++ { SPA_VIDEO_FORMAT_RGBA, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBA", NULL },
++ { SPA_VIDEO_FORMAT_BGRA, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGRA", NULL },
++ { SPA_VIDEO_FORMAT_ARGB, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ARGB", NULL },
++ { SPA_VIDEO_FORMAT_ABGR, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ABGR", NULL },
++ { SPA_VIDEO_FORMAT_RGB, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGB", NULL },
++ { SPA_VIDEO_FORMAT_BGR, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGR", NULL },
++ { SPA_VIDEO_FORMAT_Y41B, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y41B", NULL },
++ { SPA_VIDEO_FORMAT_Y42B, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y42B", NULL },
++ { SPA_VIDEO_FORMAT_YVYU, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YVYU", NULL },
++ { SPA_VIDEO_FORMAT_Y444, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444", NULL },
++ { SPA_VIDEO_FORMAT_v210, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "v210", NULL },
++ { SPA_VIDEO_FORMAT_v216, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "v216", NULL },
++ { SPA_VIDEO_FORMAT_NV12, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV12", NULL },
++ { SPA_VIDEO_FORMAT_NV21, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV21", NULL },
++ { SPA_VIDEO_FORMAT_GRAY8, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GRAY8", NULL },
++ { SPA_VIDEO_FORMAT_GRAY16_BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GRAY16_BE", NULL },
++ { SPA_VIDEO_FORMAT_GRAY16_LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GRAY16_LE", NULL },
++ { SPA_VIDEO_FORMAT_v308, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "v308", NULL },
++ { SPA_VIDEO_FORMAT_RGB16, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGB16", NULL },
++ { SPA_VIDEO_FORMAT_BGR16, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGR16", NULL },
++ { SPA_VIDEO_FORMAT_RGB15, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGB15", NULL },
++ { SPA_VIDEO_FORMAT_BGR15, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGR15", NULL },
++ { SPA_VIDEO_FORMAT_UYVP, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "UYVP", NULL },
++ { SPA_VIDEO_FORMAT_A420, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A420", NULL },
++ { SPA_VIDEO_FORMAT_RGB8P, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGB8P", NULL },
++ { SPA_VIDEO_FORMAT_YUV9, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YUV9", NULL },
++ { SPA_VIDEO_FORMAT_YVU9, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YVU9", NULL },
++ { SPA_VIDEO_FORMAT_IYU1, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "IYU1", NULL },
++ { SPA_VIDEO_FORMAT_ARGB64, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ARGB64", NULL },
++ { SPA_VIDEO_FORMAT_AYUV64, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "AYUV64", NULL },
++ { SPA_VIDEO_FORMAT_r210, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "r210", NULL },
++ { SPA_VIDEO_FORMAT_I420_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420_10BE", NULL },
++ { SPA_VIDEO_FORMAT_I420_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420_10LE", NULL },
++ { SPA_VIDEO_FORMAT_I422_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I422_10BE", NULL },
++ { SPA_VIDEO_FORMAT_I422_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I422_10LE", NULL },
++ { SPA_VIDEO_FORMAT_Y444_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444_10BE", NULL },
++ { SPA_VIDEO_FORMAT_Y444_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444_10LE", NULL },
++ { SPA_VIDEO_FORMAT_GBR, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR", NULL },
++ { SPA_VIDEO_FORMAT_GBR_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR_10BE", NULL },
++ { SPA_VIDEO_FORMAT_GBR_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR_10LE", NULL },
++ { SPA_VIDEO_FORMAT_NV16, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV16", NULL },
++ { SPA_VIDEO_FORMAT_NV24, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV24", NULL },
++ { SPA_VIDEO_FORMAT_NV12_64Z32, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV12_64Z32", NULL },
++ { SPA_VIDEO_FORMAT_A420_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A420_10BE", NULL },
++ { SPA_VIDEO_FORMAT_A420_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A420_10LE", NULL },
++ { SPA_VIDEO_FORMAT_A422_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A422_10BE", NULL },
++ { SPA_VIDEO_FORMAT_A422_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A422_10LE", NULL },
++ { SPA_VIDEO_FORMAT_A444_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A444_10BE", NULL },
++ { SPA_VIDEO_FORMAT_A444_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A444_10LE", NULL },
++ { SPA_VIDEO_FORMAT_NV61, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV61", NULL },
++ { SPA_VIDEO_FORMAT_P010_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "P010_10BE", NULL },
++ { SPA_VIDEO_FORMAT_P010_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "P010_10LE", NULL },
++ { SPA_VIDEO_FORMAT_IYU2, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "IYU2", NULL },
++ { SPA_VIDEO_FORMAT_VYUY, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "VYUY", NULL },
++ { SPA_VIDEO_FORMAT_GBRA, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA", NULL },
++ { SPA_VIDEO_FORMAT_GBRA_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA_10BE", NULL },
++ { SPA_VIDEO_FORMAT_GBRA_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA_10LE", NULL },
++ { SPA_VIDEO_FORMAT_GBR_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR_12BE", NULL },
++ { SPA_VIDEO_FORMAT_GBR_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR_12LE", NULL },
++ { SPA_VIDEO_FORMAT_GBRA_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA_12BE", NULL },
++ { SPA_VIDEO_FORMAT_GBRA_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA_12LE", NULL },
++ { SPA_VIDEO_FORMAT_I420_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420_12BE", NULL },
++ { SPA_VIDEO_FORMAT_I420_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420_12LE", NULL },
++ { SPA_VIDEO_FORMAT_I422_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I422_12BE", NULL },
++ { SPA_VIDEO_FORMAT_I422_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I422_12LE", NULL },
++ { SPA_VIDEO_FORMAT_Y444_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444_12BE", NULL },
++ { SPA_VIDEO_FORMAT_Y444_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444_12LE", NULL },
++ { SPA_VIDEO_FORMAT_RGBA_F16, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBA_F16", NULL },
++ { SPA_VIDEO_FORMAT_RGBA_F32, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBA_F32", NULL },
++ { 0, 0, NULL, NULL },
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_VIDEO_RAW_TYPES_H */
+diff --git a/third_party/pipewire/spa/pod/builder.h b/third_party/pipewire/spa/pod/builder.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/pod/builder.h
+@@ -0,0 +1,671 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_POD_BUILDER_H
++#define SPA_POD_BUILDER_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdarg.h>
++
++#include <spa/utils/hook.h>
++#include <spa/pod/iter.h>
++#include <spa/pod/vararg.h>
++
++struct spa_pod_builder_state {
++ uint32_t offset;
++#define SPA_POD_BUILDER_FLAG_BODY (1<<0)
++#define SPA_POD_BUILDER_FLAG_FIRST (1<<1)
++ uint32_t flags;
++ struct spa_pod_frame *frame;
++};
++
++struct spa_pod_builder;
++
++struct spa_pod_builder_callbacks {
++#define SPA_VERSION_POD_BUILDER_CALLBACKS 0
++ uint32_t version;
++
++ int (*overflow) (void *data, uint32_t size);
++};
++
++struct spa_pod_builder {
++ void *data;
++ uint32_t size;
++ uint32_t _padding;
++ struct spa_pod_builder_state state;
++ struct spa_callbacks callbacks;
++};
++
++#define SPA_POD_BUILDER_INIT(buffer,size) (struct spa_pod_builder){ buffer, size, }
++
++static inline void
++spa_pod_builder_get_state(struct spa_pod_builder *builder, struct spa_pod_builder_state *state)
++{
++ *state = builder->state;
++}
++
++static inline void
++spa_pod_builder_set_callbacks(struct spa_pod_builder *builder,
++ const struct spa_pod_builder_callbacks *callbacks, void *data)
++{
++ builder->callbacks = SPA_CALLBACKS_INIT(callbacks, data);
++}
++
++static inline void
++spa_pod_builder_reset(struct spa_pod_builder *builder, struct spa_pod_builder_state *state)
++{
++ struct spa_pod_frame *f;
++ uint32_t size = builder->state.offset - state->offset;
++ builder->state = *state;
++ for (f = builder->state.frame; f ; f = f->parent)
++ f->pod.size -= size;
++}
++
++static inline void spa_pod_builder_init(struct spa_pod_builder *builder, void *data, uint32_t size)
++{
++ *builder = SPA_POD_BUILDER_INIT(data, size);
++}
++
++static inline struct spa_pod *
++spa_pod_builder_deref(struct spa_pod_builder *builder, uint32_t offset)
++{
++ uint32_t size = builder->size;
++ if (offset + 8 <= size) {
++ struct spa_pod *pod = SPA_MEMBER(builder->data, offset, struct spa_pod);
++ if (offset + SPA_POD_SIZE(pod) <= size)
++ return pod;
++ }
++ return NULL;
++}
++
++static inline struct spa_pod *
++spa_pod_builder_frame(struct spa_pod_builder *builder, struct spa_pod_frame *frame)
++{
++ if (frame->offset + SPA_POD_SIZE(&frame->pod) <= builder->size)
++ return SPA_MEMBER(builder->data, frame->offset, struct spa_pod);
++ return NULL;
++}
++
++static inline void
++spa_pod_builder_push(struct spa_pod_builder *builder,
++ struct spa_pod_frame *frame,
++ const struct spa_pod *pod,
++ uint32_t offset)
++{
++ frame->pod = *pod;
++ frame->offset = offset;
++ frame->parent = builder->state.frame;
++ frame->flags = builder->state.flags;
++ builder->state.frame = frame;
++
++ if (frame->pod.type == SPA_TYPE_Array || frame->pod.type == SPA_TYPE_Choice)
++ builder->state.flags = SPA_POD_BUILDER_FLAG_FIRST | SPA_POD_BUILDER_FLAG_BODY;
++}
++
++static inline int spa_pod_builder_raw(struct spa_pod_builder *builder, const void *data, uint32_t size)
++{
++ int res = 0;
++ struct spa_pod_frame *f;
++ uint32_t offset = builder->state.offset;
++
++ if (offset + size > builder->size) {
++ res = -ENOSPC;
++ spa_callbacks_call_res(&builder->callbacks, struct spa_pod_builder_callbacks, res,
++ overflow, 0, offset + size);
++ }
++ if (res == 0 && data)
++ memcpy(SPA_MEMBER(builder->data, offset, void), data, size);
++
++ builder->state.offset += size;
++
++ for (f = builder->state.frame; f ; f = f->parent)
++ f->pod.size += size;
++
++ return res;
++}
++
++static inline int spa_pod_builder_pad(struct spa_pod_builder *builder, uint32_t size)
++{
++ uint64_t zeroes = 0;
++ size = SPA_ROUND_UP_N(size, 8) - size;
++ return size ? spa_pod_builder_raw(builder, &zeroes, size) : 0;
++}
++
++static inline int
++spa_pod_builder_raw_padded(struct spa_pod_builder *builder, const void *data, uint32_t size)
++{
++ int r, res = spa_pod_builder_raw(builder, data, size);
++ if ((r = spa_pod_builder_pad(builder, size)) < 0)
++ res = r;
++ return res;
++}
++
++static inline void *spa_pod_builder_pop(struct spa_pod_builder *builder, struct spa_pod_frame *frame)
++{
++ struct spa_pod *pod;
++
++ if ((pod = (struct spa_pod*)spa_pod_builder_frame(builder, frame)) != NULL)
++ *pod = frame->pod;
++
++ builder->state.frame = frame->parent;
++ builder->state.flags = frame->flags;
++ spa_pod_builder_pad(builder, builder->state.offset);
++ return pod;
++}
++
++static inline int
++spa_pod_builder_primitive(struct spa_pod_builder *builder, const struct spa_pod *p)
++{
++ const void *data;
++ uint32_t size;
++ int r, res;
++
++ if (builder->state.flags == SPA_POD_BUILDER_FLAG_BODY) {
++ data = SPA_POD_BODY_CONST(p);
++ size = SPA_POD_BODY_SIZE(p);
++ } else {
++ data = p;
++ size = SPA_POD_SIZE(p);
++ SPA_FLAG_CLEAR(builder->state.flags, SPA_POD_BUILDER_FLAG_FIRST);
++ }
++ res = spa_pod_builder_raw(builder, data, size);
++ if (builder->state.flags != SPA_POD_BUILDER_FLAG_BODY)
++ if ((r = spa_pod_builder_pad(builder, size)) < 0)
++ res = r;
++ return res;
++}
++
++#define SPA_POD_INIT(size,type) (struct spa_pod) { size, type }
++
++#define SPA_POD_INIT_None() SPA_POD_INIT(0, SPA_TYPE_None)
++
++static inline int spa_pod_builder_none(struct spa_pod_builder *builder)
++{
++ const struct spa_pod p = SPA_POD_INIT_None();
++ return spa_pod_builder_primitive(builder, &p);
++}
++
++#define SPA_POD_INIT_Bool(val) (struct spa_pod_bool){ { sizeof(uint32_t), SPA_TYPE_Bool }, val ? 1 : 0, 0 }
++
++static inline int spa_pod_builder_bool(struct spa_pod_builder *builder, bool val)
++{
++ const struct spa_pod_bool p = SPA_POD_INIT_Bool(val);
++ return spa_pod_builder_primitive(builder, &p.pod);
++}
++
++#define SPA_POD_INIT_Id(val) (struct spa_pod_id){ { sizeof(uint32_t), SPA_TYPE_Id }, (uint32_t)val, 0 }
++
++static inline int spa_pod_builder_id(struct spa_pod_builder *builder, uint32_t val)
++{
++ const struct spa_pod_id p = SPA_POD_INIT_Id(val);
++ return spa_pod_builder_primitive(builder, &p.pod);
++}
++
++#define SPA_POD_INIT_Int(val) (struct spa_pod_int){ { sizeof(int32_t), SPA_TYPE_Int }, (int32_t)val, 0 }
++
++static inline int spa_pod_builder_int(struct spa_pod_builder *builder, int32_t val)
++{
++ const struct spa_pod_int p = SPA_POD_INIT_Int(val);
++ return spa_pod_builder_primitive(builder, &p.pod);
++}
++
++#define SPA_POD_INIT_Long(val) (struct spa_pod_long){ { sizeof(int64_t), SPA_TYPE_Long }, (int64_t)val }
++
++static inline int spa_pod_builder_long(struct spa_pod_builder *builder, int64_t val)
++{
++ const struct spa_pod_long p = SPA_POD_INIT_Long(val);
++ return spa_pod_builder_primitive(builder, &p.pod);
++}
++
++#define SPA_POD_INIT_Float(val) (struct spa_pod_float){ { sizeof(float), SPA_TYPE_Float }, val }
++
++static inline int spa_pod_builder_float(struct spa_pod_builder *builder, float val)
++{
++ const struct spa_pod_float p = SPA_POD_INIT_Float(val);
++ return spa_pod_builder_primitive(builder, &p.pod);
++}
++
++#define SPA_POD_INIT_Double(val) (struct spa_pod_double){ { sizeof(double), SPA_TYPE_Double }, val }
++
++static inline int spa_pod_builder_double(struct spa_pod_builder *builder, double val)
++{
++ const struct spa_pod_double p = SPA_POD_INIT_Double(val);
++ return spa_pod_builder_primitive(builder, &p.pod);
++}
++
++#define SPA_POD_INIT_String(len) (struct spa_pod_string){ { len, SPA_TYPE_String } }
++
++static inline int
++spa_pod_builder_write_string(struct spa_pod_builder *builder, const char *str, uint32_t len)
++{
++ int r, res;
++ res = spa_pod_builder_raw(builder, str, len);
++ if ((r = spa_pod_builder_raw(builder, "", 1)) < 0)
++ res = r;
++ if ((r = spa_pod_builder_pad(builder, builder->state.offset)) < 0)
++ res = r;
++ return res;
++}
++
++static inline int
++spa_pod_builder_string_len(struct spa_pod_builder *builder, const char *str, uint32_t len)
++{
++ const struct spa_pod_string p = SPA_POD_INIT_String(len+1);
++ int r, res = spa_pod_builder_raw(builder, &p, sizeof(p));
++ if ((r = spa_pod_builder_write_string(builder, str, len)) < 0)
++ res = r;
++ return res;
++}
++
++static inline int spa_pod_builder_string(struct spa_pod_builder *builder, const char *str)
++{
++ uint32_t len = str ? strlen(str) : 0;
++ return spa_pod_builder_string_len(builder, str ? str : "", len);
++}
++
++#define SPA_POD_INIT_Bytes(len) (struct spa_pod_bytes){ { len, SPA_TYPE_Bytes } }
++
++static inline int
++spa_pod_builder_bytes(struct spa_pod_builder *builder, const void *bytes, uint32_t len)
++{
++ const struct spa_pod_bytes p = SPA_POD_INIT_Bytes(len);
++ int r, res = spa_pod_builder_raw(builder, &p, sizeof(p));
++ if ((r = spa_pod_builder_raw_padded(builder, bytes, len)) < 0)
++ res = r;
++ return res;
++}
++static inline void *
++spa_pod_builder_reserve_bytes(struct spa_pod_builder *builder, uint32_t len)
++{
++ uint32_t offset = builder->state.offset;
++ if (spa_pod_builder_bytes(builder, NULL, len) < 0)
++ return NULL;
++ return SPA_POD_BODY(spa_pod_builder_deref(builder, offset));
++}
++
++#define SPA_POD_INIT_Pointer(type,value) (struct spa_pod_pointer){ { sizeof(struct spa_pod_pointer_body), SPA_TYPE_Pointer }, { type, 0, value } }
++
++static inline int
++spa_pod_builder_pointer(struct spa_pod_builder *builder, uint32_t type, const void *val)
++{
++ const struct spa_pod_pointer p = SPA_POD_INIT_Pointer(type, val);
++ return spa_pod_builder_primitive(builder, &p.pod);
++}
++
++#define SPA_POD_INIT_Fd(fd) (struct spa_pod_fd){ { sizeof(int64_t), SPA_TYPE_Fd }, fd }
++
++static inline int spa_pod_builder_fd(struct spa_pod_builder *builder, int64_t fd)
++{
++ const struct spa_pod_fd p = SPA_POD_INIT_Fd(fd);
++ return spa_pod_builder_primitive(builder, &p.pod);
++}
++
++#define SPA_POD_INIT_Rectangle(val) (struct spa_pod_rectangle){ { sizeof(struct spa_rectangle), SPA_TYPE_Rectangle }, val }
++
++static inline int
++spa_pod_builder_rectangle(struct spa_pod_builder *builder, uint32_t width, uint32_t height)
++{
++ const struct spa_pod_rectangle p = SPA_POD_INIT_Rectangle(SPA_RECTANGLE(width, height));
++ return spa_pod_builder_primitive(builder, &p.pod);
++}
++
++#define SPA_POD_INIT_Fraction(val) (struct spa_pod_fraction){ { sizeof(struct spa_fraction), SPA_TYPE_Fraction }, val }
++
++static inline int
++spa_pod_builder_fraction(struct spa_pod_builder *builder, uint32_t num, uint32_t denom)
++{
++ const struct spa_pod_fraction p = SPA_POD_INIT_Fraction(SPA_FRACTION(num, denom));
++ return spa_pod_builder_primitive(builder, &p.pod);
++}
++
++static inline int
++spa_pod_builder_push_array(struct spa_pod_builder *builder, struct spa_pod_frame *frame)
++{
++ const struct spa_pod_array p =
++ { {sizeof(struct spa_pod_array_body) - sizeof(struct spa_pod), SPA_TYPE_Array},
++ {{0, 0}} };
++ uint32_t offset = builder->state.offset;
++ int res = spa_pod_builder_raw(builder, &p, sizeof(p) - sizeof(struct spa_pod));
++ spa_pod_builder_push(builder, frame, &p.pod, offset);
++ return res;
++}
++
++static inline int
++spa_pod_builder_array(struct spa_pod_builder *builder,
++ uint32_t child_size, uint32_t child_type, uint32_t n_elems, const void *elems)
++{
++ const struct spa_pod_array p = {
++ {(uint32_t)(sizeof(struct spa_pod_array_body) + n_elems * child_size), SPA_TYPE_Array},
++ {{child_size, child_type}}
++ };
++ int r, res = spa_pod_builder_raw(builder, &p, sizeof(p));
++ if ((r = spa_pod_builder_raw_padded(builder, elems, child_size * n_elems)) < 0)
++ res = r;
++ return res;
++}
++
++#define SPA_POD_INIT_CHOICE_BODY(type, flags, child_size, child_type) \
++ (struct spa_pod_choice_body) { type, flags, { child_size, child_type }}
++
++#define SPA_POD_INIT_Choice(type, ctype, child_type, n_vals, ...) \
++ (struct { struct spa_pod_choice choice; ctype vals[n_vals];}) \
++ { { { n_vals * sizeof(ctype) + sizeof(struct spa_pod_choice_body), SPA_TYPE_Choice }, \
++ { type, 0, { sizeof(ctype), child_type } } }, { __VA_ARGS__ } }
++
++static inline int
++spa_pod_builder_push_choice(struct spa_pod_builder *builder, struct spa_pod_frame *frame,
++ uint32_t type, uint32_t flags)
++{
++ const struct spa_pod_choice p =
++ { {sizeof(struct spa_pod_choice_body) - sizeof(struct spa_pod), SPA_TYPE_Choice},
++ { type, flags, {0, 0}} };
++ uint32_t offset = builder->state.offset;
++ int res = spa_pod_builder_raw(builder, &p, sizeof(p) - sizeof(struct spa_pod));
++ spa_pod_builder_push(builder, frame, &p.pod, offset);
++ return res;
++}
++
++#define SPA_POD_INIT_Struct(size) (struct spa_pod_struct){ { size, SPA_TYPE_Struct } }
++
++static inline int
++spa_pod_builder_push_struct(struct spa_pod_builder *builder, struct spa_pod_frame *frame)
++{
++ const struct spa_pod_struct p = SPA_POD_INIT_Struct(0);
++ uint32_t offset = builder->state.offset;
++ int res = spa_pod_builder_raw(builder, &p, sizeof(p));
++ spa_pod_builder_push(builder, frame, &p.pod, offset);
++ return res;
++}
++
++#define SPA_POD_INIT_Object(size,type,id,...) (struct spa_pod_object){ { size, SPA_TYPE_Object }, { type, id }, ##__VA_ARGS__ }
++
++static inline int
++spa_pod_builder_push_object(struct spa_pod_builder *builder, struct spa_pod_frame *frame,
++ uint32_t type, uint32_t id)
++{
++ const struct spa_pod_object p =
++ SPA_POD_INIT_Object(sizeof(struct spa_pod_object_body), type, id);
++ uint32_t offset = builder->state.offset;
++ int res = spa_pod_builder_raw(builder, &p, sizeof(p));
++ spa_pod_builder_push(builder, frame, &p.pod, offset);
++ return res;
++}
++
++#define SPA_POD_INIT_Prop(key,flags,size,type) \
++ (struct spa_pod_prop){ key, flags, { size, type } }
++
++static inline int
++spa_pod_builder_prop(struct spa_pod_builder *builder, uint32_t key, uint32_t flags)
++{
++ const struct { uint32_t key; uint32_t flags; } p = { key, flags };
++ return spa_pod_builder_raw(builder, &p, sizeof(p));
++}
++
++#define SPA_POD_INIT_Sequence(size,unit) \
++ (struct spa_pod_sequence){ { size, SPA_TYPE_Sequence}, {unit, 0 } }
++
++static inline int
++spa_pod_builder_push_sequence(struct spa_pod_builder *builder, struct spa_pod_frame *frame, uint32_t unit)
++{
++ const struct spa_pod_sequence p =
++ SPA_POD_INIT_Sequence(sizeof(struct spa_pod_sequence_body), unit);
++ uint32_t offset = builder->state.offset;
++ int res = spa_pod_builder_raw(builder, &p, sizeof(p));
++ spa_pod_builder_push(builder, frame, &p.pod, offset);
++ return res;
++}
++
++static inline uint32_t
++spa_pod_builder_control(struct spa_pod_builder *builder, uint32_t offset, uint32_t type)
++{
++ const struct { uint32_t offset; uint32_t type; } p = { offset, type };
++ return spa_pod_builder_raw(builder, &p, sizeof(p));
++}
++
++static inline uint32_t spa_choice_from_id(char id)
++{
++ switch (id) {
++ case 'r':
++ return SPA_CHOICE_Range;
++ case 's':
++ return SPA_CHOICE_Step;
++ case 'e':
++ return SPA_CHOICE_Enum;
++ case 'f':
++ return SPA_CHOICE_Flags;
++ case 'n':
++ default:
++ return SPA_CHOICE_None;
++ }
++}
++
++#define SPA_POD_BUILDER_COLLECT(builder,type,args) \
++do { \
++ switch (type) { \
++ case 'b': \
++ spa_pod_builder_bool(builder, !!va_arg(args, int)); \
++ break; \
++ case 'I': \
++ spa_pod_builder_id(builder, va_arg(args, uint32_t)); \
++ break; \
++ case 'i': \
++ spa_pod_builder_int(builder, va_arg(args, int)); \
++ break; \
++ case 'l': \
++ spa_pod_builder_long(builder, va_arg(args, int64_t)); \
++ break; \
++ case 'f': \
++ spa_pod_builder_float(builder, va_arg(args, double)); \
++ break; \
++ case 'd': \
++ spa_pod_builder_double(builder, va_arg(args, double)); \
++ break; \
++ case 's': \
++ { \
++ char *strval = va_arg(args, char *); \
++ if (strval != NULL) { \
++ size_t len = strlen(strval); \
++ spa_pod_builder_string_len(builder, strval, len); \
++ } \
++ else \
++ spa_pod_builder_none(builder); \
++ break; \
++ } \
++ case 'S': \
++ { \
++ char *strval = va_arg(args, char *); \
++ size_t len = va_arg(args, int); \
++ spa_pod_builder_string_len(builder, strval, len); \
++ break; \
++ } \
++ case 'y': \
++ { \
++ void *ptr = va_arg(args, void *); \
++ int len = va_arg(args, int); \
++ spa_pod_builder_bytes(builder, ptr, len); \
++ break; \
++ } \
++ case 'R': \
++ { \
++ struct spa_rectangle *rectval = \
++ va_arg(args, struct spa_rectangle *); \
++ spa_pod_builder_rectangle(builder, \
++ rectval->width, rectval->height); \
++ break; \
++ } \
++ case 'F': \
++ { \
++ struct spa_fraction *fracval = \
++ va_arg(args, struct spa_fraction *); \
++ spa_pod_builder_fraction(builder, fracval->num, fracval->denom);\
++ break; \
++ } \
++ case 'a': \
++ { \
++ int child_size = va_arg(args, int); \
++ int child_type = va_arg(args, int); \
++ int n_elems = va_arg(args, int); \
++ void *elems = va_arg(args, void *); \
++ spa_pod_builder_array(builder, child_size, \
++ child_type, n_elems, elems); \
++ break; \
++ } \
++ case 'p': \
++ { \
++ int t = va_arg(args, uint32_t); \
++ spa_pod_builder_pointer(builder, t, va_arg(args, void *)); \
++ break; \
++ } \
++ case 'h': \
++ spa_pod_builder_fd(builder, va_arg(args, int)); \
++ break; \
++ case 'P': \
++ case 'O': \
++ case 'T': \
++ case 'V': \
++ { \
++ struct spa_pod *pod = va_arg(args, struct spa_pod *); \
++ if (pod == NULL) \
++ spa_pod_builder_none(builder); \
++ else \
++ spa_pod_builder_primitive(builder, pod); \
++ break; \
++ } \
++ } \
++} while(false)
++
++static inline int
++spa_pod_builder_addv(struct spa_pod_builder *builder, va_list args)
++{
++ int res = 0;
++ struct spa_pod_frame *f = builder->state.frame;
++ uint32_t ftype = f ? f->pod.type : (uint32_t)SPA_TYPE_None;
++
++ do {
++ const char *format;
++ int n_values = 1;
++ struct spa_pod_frame f;
++ bool choice;
++
++ switch (ftype) {
++ case SPA_TYPE_Object:
++ {
++ uint32_t key = va_arg(args, uint32_t);
++ if (key == 0)
++ goto exit;
++ spa_pod_builder_prop(builder, key, 0);
++ break;
++ }
++ case SPA_TYPE_Sequence:
++ {
++ uint32_t offset = va_arg(args, uint32_t);
++ uint32_t type = va_arg(args, uint32_t);
++ if (type == 0)
++ goto exit;
++ spa_pod_builder_control(builder, offset, type);
++ }
++ default:
++ break;
++ }
++ if ((format = va_arg(args, const char *)) == NULL)
++ break;
++
++ choice = *format == '?';
++ if (choice) {
++ uint32_t type = spa_choice_from_id(*++format);
++ if (*format != '\0')
++ format++;
++
++ spa_pod_builder_push_choice(builder, &f, type, 0);
++
++ n_values = va_arg(args, int);
++ }
++ while (n_values-- > 0)
++ SPA_POD_BUILDER_COLLECT(builder, *format, args);
++
++ if (choice)
++ spa_pod_builder_pop(builder, &f);
++ } while (true);
++
++ exit:
++ return res;
++}
++
++static inline int spa_pod_builder_add(struct spa_pod_builder *builder, ...)
++{
++ int res;
++ va_list args;
++
++ va_start(args, builder);
++ res = spa_pod_builder_addv(builder, args);
++ va_end(args);
++
++ return res;
++}
++
++#define spa_pod_builder_add_object(b,type,id,...) \
++({ \
++ struct spa_pod_frame _f; \
++ spa_pod_builder_push_object(b, &_f, type, id); \
++ spa_pod_builder_add(b, ##__VA_ARGS__, 0); \
++ spa_pod_builder_pop(b, &_f); \
++})
++
++#define spa_pod_builder_add_struct(b,...) \
++({ \
++ struct spa_pod_frame _f; \
++ spa_pod_builder_push_struct(b, &_f); \
++ spa_pod_builder_add(b, ##__VA_ARGS__, NULL); \
++ spa_pod_builder_pop(b, &_f); \
++})
++
++#define spa_pod_builder_add_sequence(b,unit,...) \
++({ \
++ struct spa_pod_frame _f; \
++ spa_pod_builder_push_sequence(b, &_f, unit); \
++ spa_pod_builder_add(b, ##__VA_ARGS__, 0, 0); \
++ spa_pod_builder_pop(b, &_f); \
++})
++
++/** Copy a pod structure */
++static inline struct spa_pod *
++spa_pod_copy(const struct spa_pod *pod)
++{
++ size_t size;
++ struct spa_pod *c;
++
++ size = SPA_POD_SIZE(pod);
++ if ((c = (struct spa_pod *) malloc(size)) == NULL)
++ return NULL;
++ return (struct spa_pod *) memcpy(c, pod, size);
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_POD_BUILDER_H */
+diff --git a/third_party/pipewire/spa/pod/command.h b/third_party/pipewire/spa/pod/command.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/pod/command.h
+@@ -0,0 +1,61 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_COMMAND_H
++#define SPA_COMMAND_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/pod/pod.h>
++
++struct spa_command_body {
++ struct spa_pod_object_body body;
++};
++
++struct spa_command {
++ struct spa_pod pod;
++ struct spa_command_body body;
++};
++
++#define SPA_COMMAND_TYPE(cmd) ((cmd)->body.body.type)
++#define SPA_COMMAND_ID(cmd,type) (SPA_COMMAND_TYPE(cmd) == type ? \
++ (cmd)->body.body.id : SPA_ID_INVALID)
++
++#define SPA_COMMAND_INIT_FULL(t,size,type,id,...) (t) \
++ { { size, SPA_TYPE_Object }, \
++ { { type, id }, ##__VA_ARGS__ } } \
++
++#define SPA_COMMAND_INIT(type,id) \
++ SPA_COMMAND_INIT_FULL(struct spa_command, \
++ sizeof(struct spa_command_body), type, id)
++
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_COMMAND_H */
+diff --git a/third_party/pipewire/spa/pod/compare.h b/third_party/pipewire/spa/pod/compare.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/pod/compare.h
+@@ -0,0 +1,181 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_POD_COMPARE_H
++#define SPA_POD_COMPARE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdarg.h>
++#include <errno.h>
++#include <stdint.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <string.h>
++
++#include <spa/param/props.h>
++#include <spa/pod/iter.h>
++#include <spa/pod/builder.h>
++
++static inline int spa_pod_compare_value(uint32_t type, const void *r1, const void *r2, uint32_t size)
++{
++ switch (type) {
++ case SPA_TYPE_None:
++ return 0;
++ case SPA_TYPE_Bool:
++ case SPA_TYPE_Id:
++ return *(uint32_t *) r1 == *(uint32_t *) r2 ? 0 : 1;
++ case SPA_TYPE_Int:
++ return *(int32_t *) r1 - *(int32_t *) r2;
++ case SPA_TYPE_Long:
++ return *(int64_t *) r1 - *(int64_t *) r2;
++ case SPA_TYPE_Float:
++ return *(float *) r1 - *(float *) r2;
++ case SPA_TYPE_Double:
++ return *(double *) r1 - *(double *) r2;
++ case SPA_TYPE_String:
++ return strcmp((char *)r1, (char *)r2);
++ case SPA_TYPE_Bytes:
++ return memcmp((char *)r1, (char *)r2, size);
++ case SPA_TYPE_Rectangle:
++ {
++ const struct spa_rectangle *rec1 = (struct spa_rectangle *) r1,
++ *rec2 = (struct spa_rectangle *) r2;
++ if (rec1->width == rec2->width && rec1->height == rec2->height)
++ return 0;
++ else if (rec1->width < rec2->width || rec1->height < rec2->height)
++ return -1;
++ else
++ return 1;
++ }
++ case SPA_TYPE_Fraction:
++ {
++ const struct spa_fraction *f1 = (struct spa_fraction *) r1,
++ *f2 = (struct spa_fraction *) r2;
++ int64_t n1, n2;
++ n1 = ((int64_t) f1->num) * f2->denom;
++ n2 = ((int64_t) f2->num) * f1->denom;
++ if (n1 < n2)
++ return -1;
++ else if (n1 > n2)
++ return 1;
++ else
++ return 0;
++ }
++ default:
++ break;
++ }
++ return 0;
++}
++
++static inline int spa_pod_compare(const struct spa_pod *pod1,
++ const struct spa_pod *pod2)
++{
++ int res = 0;
++ uint32_t n_vals1, n_vals2;
++ uint32_t choice1, choice2;
++
++ spa_return_val_if_fail(pod1 != NULL, -EINVAL);
++ spa_return_val_if_fail(pod2 != NULL, -EINVAL);
++
++ pod1 = spa_pod_get_values(pod1, &n_vals1, &choice1);
++ pod2 = spa_pod_get_values(pod2, &n_vals2, &choice2);
++
++ if (n_vals1 != n_vals2)
++ return -EINVAL;
++
++ if (SPA_POD_TYPE(pod1) != SPA_POD_TYPE(pod2))
++ return -EINVAL;
++
++ switch (SPA_POD_TYPE(pod1)) {
++ case SPA_TYPE_Struct:
++ {
++ const struct spa_pod *p1, *p2;
++ size_t p1s, p2s;
++
++ p1 = (const struct spa_pod*)SPA_POD_BODY_CONST(pod1);
++ p1s = SPA_POD_BODY_SIZE(pod1);
++ p2 = (const struct spa_pod*)SPA_POD_BODY_CONST(pod2);
++ p2s = SPA_POD_BODY_SIZE(pod2);
++
++ while (true) {
++ if (!spa_pod_is_inside(pod1, p1s, p1) ||
++ !spa_pod_is_inside(pod2, p2s, p2))
++ return -EINVAL;
++
++ if ((res = spa_pod_compare(p1, p2)) != 0)
++ return res;
++
++ p1 = (const struct spa_pod*)spa_pod_next(p1);
++ p2 = (const struct spa_pod*)spa_pod_next(p2);
++ }
++ break;
++ }
++ case SPA_TYPE_Object:
++ {
++ const struct spa_pod_prop *p1, *p2;
++ const struct spa_pod_object *o1, *o2;
++
++ o1 = (const struct spa_pod_object*)pod1;
++ o2 = (const struct spa_pod_object*)pod2;
++
++ p2 = NULL;
++ SPA_POD_OBJECT_FOREACH(o1, p1) {
++ if ((p2 = spa_pod_object_find_prop(o2, p2, p1->key)) == NULL)
++ return 1;
++ if ((res = spa_pod_compare(&p1->value, &p2->value)) != 0)
++ return res;
++ }
++ p1 = NULL;
++ SPA_POD_OBJECT_FOREACH(o2, p2) {
++ if ((p1 = spa_pod_object_find_prop(o1, p1, p2->key)) == NULL)
++ return -1;
++ }
++ break;
++ }
++ case SPA_TYPE_Array:
++ {
++ if (SPA_POD_BODY_SIZE(pod1) != SPA_POD_BODY_SIZE(pod2))
++ return -EINVAL;
++ res = memcmp(SPA_POD_BODY(pod1), SPA_POD_BODY(pod2), SPA_POD_BODY_SIZE(pod2));
++ break;
++ }
++ default:
++ if (SPA_POD_BODY_SIZE(pod1) != SPA_POD_BODY_SIZE(pod2))
++ return -EINVAL;
++ res = spa_pod_compare_value(SPA_POD_TYPE(pod1),
++ SPA_POD_BODY(pod1), SPA_POD_BODY(pod2),
++ SPA_POD_BODY_SIZE(pod1));
++ break;
++ }
++ return res;
++}
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+diff --git a/third_party/pipewire/spa/pod/event.h b/third_party/pipewire/spa/pod/event.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/pod/event.h
+@@ -0,0 +1,59 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_EVENT_H
++#define SPA_EVENT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/pod/pod.h>
++
++struct spa_event_body {
++ struct spa_pod_object_body body;
++};
++
++struct spa_event {
++ struct spa_pod pod;
++ struct spa_event_body body;
++};
++
++#define SPA_EVENT_TYPE(ev) ((ev)->body.body.type)
++#define SPA_EVENT_ID(ev,type) (SPA_EVENT_TYPE(ev) == type ? \
++ (ev)->body.body.id : SPA_ID_INVALID)
++
++#define SPA_EVENT_INIT_FULL(t,size,type,id,...) (t) \
++ { { size, SPA_TYPE_OBJECT }, \
++ { { type, id }, ##__VA_ARGS__ } } \
++
++#define SPA_EVENT_INIT(type,id) \
++ SPA_EVENT_INIT_FULL(struct spa_event, \
++ sizeof(struct spa_event_body), type, id)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_EVENT_H */
+diff --git a/third_party/pipewire/spa/pod/filter.h b/third_party/pipewire/spa/pod/filter.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/pod/filter.h
+@@ -0,0 +1,395 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include <errno.h>
++#include <stdint.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <string.h>
++
++#include <spa/param/props.h>
++#include <spa/pod/iter.h>
++#include <spa/pod/builder.h>
++#include <spa/pod/compare.h>
++
++static inline int spa_pod_choice_fix_default(struct spa_pod_choice *choice)
++{
++ void *val, *alt;
++ int i, nvals;
++ uint32_t type, size;
++
++ nvals = SPA_POD_CHOICE_N_VALUES(choice);
++ type = SPA_POD_CHOICE_VALUE_TYPE(choice);
++ size = SPA_POD_CHOICE_VALUE_SIZE(choice);
++ alt = val = SPA_POD_CHOICE_VALUES(choice);
++
++ switch (choice->body.type) {
++ case SPA_CHOICE_None:
++ break;
++ case SPA_CHOICE_Range:
++ case SPA_CHOICE_Step:
++ if (nvals > 1) {
++ alt = SPA_MEMBER(alt, size, void);
++ if (spa_pod_compare_value(type, val, alt, size) < 0)
++ memcpy(val, alt, size);
++ }
++ if (nvals > 2) {
++ alt = SPA_MEMBER(alt, size, void);
++ if (spa_pod_compare_value(type, val, alt, size) > 0)
++ memcpy(val, alt, size);
++ }
++ break;
++ case SPA_CHOICE_Flags:
++ case SPA_CHOICE_Enum:
++ {
++ void *best = NULL;
++
++ for (i = 1; i < nvals; i++) {
++ alt = SPA_MEMBER(alt, size, void);
++ if (spa_pod_compare_value(type, val, alt, size) == 0) {
++ best = alt;
++ break;
++ }
++ if (best == NULL)
++ best = alt;
++ }
++ if (best)
++ memcpy(val, best, size);
++
++ if (nvals <= 1)
++ choice->body.type = SPA_CHOICE_None;
++ break;
++ }
++ }
++ return 0;
++}
++
++static inline int spa_pod_filter_flags_value(struct spa_pod_builder *b,
++ uint32_t type, const void *r1, const void *r2, uint32_t size)
++{
++ switch (type) {
++ case SPA_TYPE_Int:
++ {
++ int32_t val = (*(int32_t *) r1) & (*(int32_t *) r2);
++ if (val == 0)
++ return 0;
++ spa_pod_builder_int(b, val);
++ break;
++ }
++ case SPA_TYPE_Long:
++ {
++ int64_t val = (*(int64_t *) r1) & (*(int64_t *) r2);
++ if (val == 0)
++ return 0;
++ spa_pod_builder_long(b, val);
++ break;
++ }
++ default:
++ return -ENOTSUP;
++ }
++ return 1;
++}
++
++
++static inline int
++spa_pod_filter_prop(struct spa_pod_builder *b,
++ const struct spa_pod_prop *p1,
++ const struct spa_pod_prop *p2)
++{
++ const struct spa_pod *v1, *v2;
++ struct spa_pod_choice *nc;
++ uint32_t j, k, nalt1, nalt2;
++ void *alt1, *alt2, *a1, *a2;
++ uint32_t type, size, p1c, p2c;
++ struct spa_pod_frame f;
++
++ v1 = spa_pod_get_values(&p1->value, &nalt1, &p1c);
++ alt1 = SPA_POD_BODY(v1);
++ v2 = spa_pod_get_values(&p2->value, &nalt2, &p2c);
++ alt2 = SPA_POD_BODY(v2);
++
++ type = v1->type;
++ size = v1->size;
++
++ /* incompatible property types */
++ if (type != v2->type || size != v2->size || p1->key != p2->key)
++ return -EINVAL;
++
++ if (p1c == SPA_CHOICE_None || p1c == SPA_CHOICE_Flags) {
++ nalt1 = 1;
++ } else {
++ alt1 = SPA_MEMBER(alt1, size, void);
++ nalt1--;
++ }
++
++ if (p2c == SPA_CHOICE_None || p2c == SPA_CHOICE_Flags) {
++ nalt2 = 1;
++ } else {
++ alt2 = SPA_MEMBER(alt2, size, void);
++ nalt2--;
++ }
++
++ /* start with copying the property */
++ spa_pod_builder_prop(b, p1->key, 0);
++ spa_pod_builder_push_choice(b, &f, 0, 0);
++ nc = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f);
++
++ /* default value */
++ spa_pod_builder_primitive(b, v1);
++
++ if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_None) ||
++ (p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Enum) ||
++ (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_None) ||
++ (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Enum)) {
++ int n_copied = 0;
++ /* copy all equal values but don't copy the default value again */
++ for (j = 0, a1 = alt1; j < nalt1; j++, a1 = SPA_MEMBER(a1, size, void)) {
++ for (k = 0, a2 = alt2; k < nalt2; k++, a2 = SPA_MEMBER(a2,size,void)) {
++ if (spa_pod_compare_value(type, a1, a2, size) == 0) {
++ if (p1c == SPA_CHOICE_Enum || j > 0)
++ spa_pod_builder_raw(b, a1, size);
++ n_copied++;
++ }
++ }
++ }
++ if (n_copied == 0)
++ return -EINVAL;
++ nc->body.type = SPA_CHOICE_Enum;
++ }
++
++ if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Range) ||
++ (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Range)) {
++ int n_copied = 0;
++ /* copy all values inside the range */
++ for (j = 0, a1 = alt1, a2 = alt2; j < nalt1; j++, a1 = SPA_MEMBER(a1,size,void)) {
++ if (spa_pod_compare_value(type, a1, a2, size) < 0)
++ continue;
++ if (spa_pod_compare_value(type, a1, SPA_MEMBER(a2,size,void), size) > 0)
++ continue;
++ spa_pod_builder_raw(b, a1, size);
++ n_copied++;
++ }
++ if (n_copied == 0)
++ return -EINVAL;
++ nc->body.type = SPA_CHOICE_Enum;
++ }
++
++ if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Step) ||
++ (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Step)) {
++ return -ENOTSUP;
++ }
++
++ if ((p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_None) ||
++ (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Enum)) {
++ int n_copied = 0;
++ /* copy all values inside the range */
++ for (k = 0, a1 = alt1, a2 = alt2; k < nalt2; k++, a2 = SPA_MEMBER(a2,size,void)) {
++ if (spa_pod_compare_value(type, a2, a1, size) < 0)
++ continue;
++ if (spa_pod_compare_value(type, a2, SPA_MEMBER(a1,size,void), size) > 0)
++ continue;
++ spa_pod_builder_raw(b, a2, size);
++ n_copied++;
++ }
++ if (n_copied == 0)
++ return -EINVAL;
++ nc->body.type = SPA_CHOICE_Enum;
++ }
++
++ if ((p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Range) ||
++ (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Step) ||
++ (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Range) ||
++ (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Step)) {
++ if (spa_pod_compare_value(type, alt1, alt2, size) < 0)
++ spa_pod_builder_raw(b, alt2, size);
++ else
++ spa_pod_builder_raw(b, alt1, size);
++
++ alt1 = SPA_MEMBER(alt1,size,void);
++ alt2 = SPA_MEMBER(alt2,size,void);
++
++ if (spa_pod_compare_value(type, alt1, alt2, size) < 0)
++ spa_pod_builder_raw(b, alt1, size);
++ else
++ spa_pod_builder_raw(b, alt2, size);
++
++ nc->body.type = SPA_CHOICE_Range;
++ }
++
++ if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Flags) ||
++ (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_None) ||
++ (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Flags)) {
++ if (spa_pod_filter_flags_value(b, type, alt1, alt2, size) != 1)
++ return -EINVAL;
++ nc->body.type = SPA_CHOICE_Flags;
++ }
++
++ if (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Flags)
++ return -ENOTSUP;
++
++ if (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Flags)
++ return -ENOTSUP;
++
++ if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_None)
++ return -ENOTSUP;
++ if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Enum)
++ return -ENOTSUP;
++ if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Flags)
++ return -ENOTSUP;
++
++ if (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Range)
++ return -ENOTSUP;
++ if (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Step)
++ return -ENOTSUP;
++ if (p1c == SPA_CHOICE_Flags && p2c == SPA_CHOICE_Enum)
++ return -ENOTSUP;
++
++ spa_pod_builder_pop(b, &f);
++ spa_pod_choice_fix_default(nc);
++
++ return 0;
++}
++
++static inline int spa_pod_filter_part(struct spa_pod_builder *b,
++ const struct spa_pod *pod, uint32_t pod_size,
++ const struct spa_pod *filter, uint32_t filter_size)
++{
++ const struct spa_pod *pp, *pf;
++ int res = 0;
++
++ pf = filter;
++
++ SPA_POD_FOREACH(pod, pod_size, pp) {
++ bool do_copy = false, do_advance = false;
++ uint32_t filter_offset = 0;
++ struct spa_pod_frame f;
++
++ switch (SPA_POD_TYPE(pp)) {
++ case SPA_TYPE_Object:
++ if (pf != NULL) {
++ struct spa_pod_object *op = (struct spa_pod_object *) pp;
++ struct spa_pod_object *of = (struct spa_pod_object *) pf;
++ const struct spa_pod_prop *p1, *p2;
++
++ if (SPA_POD_TYPE(pf) != SPA_POD_TYPE(pp))
++ return -EINVAL;
++
++ spa_pod_builder_push_object(b, &f, op->body.type, op->body.id);
++ p2 = NULL;
++ SPA_POD_OBJECT_FOREACH(op, p1) {
++ p2 = spa_pod_object_find_prop(of, p2, p1->key);
++ if (p2 != NULL)
++ res = spa_pod_filter_prop(b, p1, p2);
++ else
++ spa_pod_builder_raw_padded(b, p1, SPA_POD_PROP_SIZE(p1));
++ if (res < 0)
++ break;
++ }
++ if (res >= 0) {
++ p1 = NULL;
++ SPA_POD_OBJECT_FOREACH(of, p2) {
++ p1 = spa_pod_object_find_prop(op, p1, p2->key);
++ if (p1 != NULL)
++ continue;
++
++ spa_pod_builder_raw_padded(b, p2, SPA_POD_PROP_SIZE(p2));
++ }
++ }
++ spa_pod_builder_pop(b, &f);
++ do_advance = true;
++ }
++ else
++ do_copy = true;
++ break;
++
++ case SPA_TYPE_Struct:
++ if (pf != NULL) {
++ if (SPA_POD_TYPE(pf) != SPA_POD_TYPE(pp))
++ return -EINVAL;
++
++ filter_offset = sizeof(struct spa_pod_struct);
++ spa_pod_builder_push_struct(b, &f);
++ res = spa_pod_filter_part(b,
++ SPA_MEMBER(pp,filter_offset,const struct spa_pod),
++ SPA_POD_SIZE(pp) - filter_offset,
++ SPA_MEMBER(pf,filter_offset,const struct spa_pod),
++ SPA_POD_SIZE(pf) - filter_offset);
++ spa_pod_builder_pop(b, &f);
++ do_advance = true;
++ }
++ else
++ do_copy = true;
++ break;
++
++ default:
++ if (pf != NULL) {
++ if (SPA_POD_SIZE(pp) != SPA_POD_SIZE(pf))
++ return -EINVAL;
++ if (memcmp(pp, pf, SPA_POD_SIZE(pp)) != 0)
++ return -EINVAL;
++ do_advance = true;
++ }
++ do_copy = true;
++ break;
++ }
++ if (do_copy)
++ spa_pod_builder_raw_padded(b, pp, SPA_POD_SIZE(pp));
++ if (do_advance) {
++ pf = (const struct spa_pod*)spa_pod_next(pf);
++ if (!spa_pod_is_inside(filter, filter_size, pf))
++ pf = NULL;
++ }
++ if (res < 0)
++ break;
++ }
++ return res;
++}
++
++static inline int
++spa_pod_filter(struct spa_pod_builder *b,
++ struct spa_pod **result,
++ const struct spa_pod *pod,
++ const struct spa_pod *filter)
++{
++ int res;
++ struct spa_pod_builder_state state;
++
++ spa_return_val_if_fail(pod != NULL, -EINVAL);
++ spa_return_val_if_fail(b != NULL, -EINVAL);
++
++ spa_pod_builder_get_state(b, &state);
++ if (filter == NULL)
++ res = spa_pod_builder_raw_padded(b, pod, SPA_POD_SIZE(pod));
++ else
++ res = spa_pod_filter_part(b, pod, SPA_POD_SIZE(pod), filter, SPA_POD_SIZE(filter));
++
++ if (res < 0) {
++ spa_pod_builder_reset(b, &state);
++ } else if (result) {
++ *result = (struct spa_pod*)spa_pod_builder_deref(b, state.offset);
++ if (*result == NULL)
++ res = -ENOSPC;
++ }
++ return res;
++}
+diff --git a/third_party/pipewire/spa/pod/iter.h b/third_party/pipewire/spa/pod/iter.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/pod/iter.h
+@@ -0,0 +1,446 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_POD_ITER_H
++#define SPA_POD_ITER_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <errno.h>
++#include <sys/types.h>
++
++#include <spa/pod/pod.h>
++
++struct spa_pod_frame {
++ struct spa_pod pod;
++ struct spa_pod_frame *parent;
++ uint32_t offset;
++ uint32_t flags;
++};
++
++static inline bool spa_pod_is_inside(const void *pod, uint32_t size, const void *iter)
++{
++ return SPA_POD_BODY(iter) <= SPA_MEMBER(pod, size, void) &&
++ SPA_MEMBER(iter, SPA_POD_SIZE(iter), void) <= SPA_MEMBER(pod, size, void);
++}
++
++static inline void *spa_pod_next(const void *iter)
++{
++ return SPA_MEMBER(iter, SPA_ROUND_UP_N(SPA_POD_SIZE(iter), 8), void);
++}
++
++static inline struct spa_pod_prop *spa_pod_prop_first(const struct spa_pod_object_body *body)
++{
++ return SPA_MEMBER(body, sizeof(struct spa_pod_object_body), struct spa_pod_prop);
++}
++
++static inline bool spa_pod_prop_is_inside(const struct spa_pod_object_body *body,
++ uint32_t size, const struct spa_pod_prop *iter)
++{
++ return SPA_POD_CONTENTS(struct spa_pod_prop, iter) <= SPA_MEMBER(body, size, void) &&
++ SPA_MEMBER(iter, SPA_POD_PROP_SIZE(iter), void) <= SPA_MEMBER(body, size, void);
++}
++
++static inline struct spa_pod_prop *spa_pod_prop_next(const struct spa_pod_prop *iter)
++{
++ return SPA_MEMBER(iter, SPA_ROUND_UP_N(SPA_POD_PROP_SIZE(iter), 8), struct spa_pod_prop);
++}
++
++static inline struct spa_pod_control *spa_pod_control_first(const struct spa_pod_sequence_body *body)
++{
++ return SPA_MEMBER(body, sizeof(struct spa_pod_sequence_body), struct spa_pod_control);
++}
++
++static inline bool spa_pod_control_is_inside(const struct spa_pod_sequence_body *body,
++ uint32_t size, const struct spa_pod_control *iter)
++{
++ return SPA_POD_CONTENTS(struct spa_pod_control, iter) <= SPA_MEMBER(body, size, void) &&
++ SPA_MEMBER(iter, SPA_POD_CONTROL_SIZE(iter), void) <= SPA_MEMBER(body, size, void);
++}
++
++static inline struct spa_pod_control *spa_pod_control_next(const struct spa_pod_control *iter)
++{
++ return SPA_MEMBER(iter, SPA_ROUND_UP_N(SPA_POD_CONTROL_SIZE(iter), 8), struct spa_pod_control);
++}
++
++#define SPA_POD_ARRAY_BODY_FOREACH(body, _size, iter) \
++ for ((iter) = (__typeof__(iter))SPA_MEMBER((body), sizeof(struct spa_pod_array_body), void); \
++ (iter) < (__typeof__(iter))SPA_MEMBER((body), (_size), void); \
++ (iter) = (__typeof__(iter))SPA_MEMBER((iter), (body)->child.size, void))
++
++#define SPA_POD_ARRAY_FOREACH(obj, iter) \
++ SPA_POD_ARRAY_BODY_FOREACH(&(obj)->body, SPA_POD_BODY_SIZE(obj), iter)
++
++#define SPA_POD_CHOICE_BODY_FOREACH(body, _size, iter) \
++ for ((iter) = (__typeof__(iter))SPA_MEMBER((body), sizeof(struct spa_pod_choice_body), void); \
++ (iter) < (__typeof__(iter))SPA_MEMBER((body), (_size), void); \
++ (iter) = (__typeof__(iter))SPA_MEMBER((iter), (body)->child.size, void))
++
++#define SPA_POD_CHOICE_FOREACH(obj, iter) \
++ SPA_POD_CHOICE_BODY_FOREACH(&(obj)->body, SPA_POD_BODY_SIZE(obj), iter)
++
++#define SPA_POD_FOREACH(pod, size, iter) \
++ for ((iter) = (pod); \
++ spa_pod_is_inside(pod, size, iter); \
++ (iter) = (__typeof__(iter))spa_pod_next(iter))
++
++#define SPA_POD_STRUCT_FOREACH(obj, iter) \
++ SPA_POD_FOREACH(SPA_POD_BODY(obj), SPA_POD_BODY_SIZE(obj), iter)
++
++#define SPA_POD_OBJECT_BODY_FOREACH(body, size, iter) \
++ for ((iter) = spa_pod_prop_first(body); \
++ spa_pod_prop_is_inside(body, size, iter); \
++ (iter) = spa_pod_prop_next(iter))
++
++#define SPA_POD_OBJECT_FOREACH(obj, iter) \
++ SPA_POD_OBJECT_BODY_FOREACH(&(obj)->body, SPA_POD_BODY_SIZE(obj), iter)
++
++#define SPA_POD_SEQUENCE_BODY_FOREACH(body, size, iter) \
++ for ((iter) = spa_pod_control_first(body); \
++ spa_pod_control_is_inside(body, size, iter); \
++ (iter) = spa_pod_control_next(iter))
++
++#define SPA_POD_SEQUENCE_FOREACH(seq, iter) \
++ SPA_POD_SEQUENCE_BODY_FOREACH(&(seq)->body, SPA_POD_BODY_SIZE(seq), iter)
++
++
++static inline void *spa_pod_from_data(void *data, size_t maxsize, off_t offset, size_t size)
++{
++ void *pod;
++ if (size < sizeof(struct spa_pod) || offset + size > maxsize)
++ return NULL;
++ pod = SPA_MEMBER(data, offset, void);
++ if (SPA_POD_SIZE(pod) > size)
++ return NULL;
++ return pod;
++}
++
++static inline int spa_pod_is_none(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_None);
++}
++
++static inline int spa_pod_is_bool(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Bool && SPA_POD_BODY_SIZE(pod) >= sizeof(int32_t));
++}
++
++static inline int spa_pod_get_bool(const struct spa_pod *pod, bool *value)
++{
++ if (!spa_pod_is_bool(pod))
++ return -EINVAL;
++ *value = !!SPA_POD_VALUE(struct spa_pod_bool, pod);
++ return 0;
++}
++
++static inline int spa_pod_is_id(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Id && SPA_POD_BODY_SIZE(pod) >= sizeof(uint32_t));
++}
++
++static inline int spa_pod_get_id(const struct spa_pod *pod, uint32_t *value)
++{
++ if (!spa_pod_is_id(pod))
++ return -EINVAL;
++ *value = SPA_POD_VALUE(struct spa_pod_id, pod);
++ return 0;
++}
++
++static inline int spa_pod_is_int(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Int && SPA_POD_BODY_SIZE(pod) >= sizeof(int32_t));
++}
++
++static inline int spa_pod_get_int(const struct spa_pod *pod, int32_t *value)
++{
++ if (!spa_pod_is_int(pod))
++ return -EINVAL;
++ *value = SPA_POD_VALUE(struct spa_pod_int, pod);
++ return 0;
++}
++
++static inline int spa_pod_is_long(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Long && SPA_POD_BODY_SIZE(pod) >= sizeof(int64_t));
++}
++
++static inline int spa_pod_get_long(const struct spa_pod *pod, int64_t *value)
++{
++ if (!spa_pod_is_long(pod))
++ return -EINVAL;
++ *value = SPA_POD_VALUE(struct spa_pod_long, pod);
++ return 0;
++}
++
++static inline int spa_pod_is_float(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Float && SPA_POD_BODY_SIZE(pod) >= sizeof(float));
++}
++
++static inline int spa_pod_get_float(const struct spa_pod *pod, float *value)
++{
++ if (!spa_pod_is_float(pod))
++ return -EINVAL;
++ *value = SPA_POD_VALUE(struct spa_pod_float, pod);
++ return 0;
++}
++
++static inline int spa_pod_is_double(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Double && SPA_POD_BODY_SIZE(pod) >= sizeof(double));
++}
++
++static inline int spa_pod_get_double(const struct spa_pod *pod, double *value)
++{
++ if (!spa_pod_is_double(pod))
++ return -EINVAL;
++ *value = SPA_POD_VALUE(struct spa_pod_double, pod);
++ return 0;
++}
++
++static inline int spa_pod_is_string(const struct spa_pod *pod)
++{
++ const char *s = (const char *)SPA_POD_CONTENTS(struct spa_pod_string, pod);
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_String &&
++ SPA_POD_BODY_SIZE(pod) > 0 &&
++ s[SPA_POD_BODY_SIZE(pod)-1] == '\0');
++}
++
++static inline int spa_pod_get_string(const struct spa_pod *pod, const char **value)
++{
++ if (!spa_pod_is_string(pod))
++ return -EINVAL;
++ *value = (const char *)SPA_POD_CONTENTS(struct spa_pod_string, pod);
++ return 0;
++}
++
++static inline int spa_pod_copy_string(const struct spa_pod *pod, size_t maxlen, char *dest)
++{
++ const char *s = (const char *)SPA_POD_CONTENTS(struct spa_pod_string, pod);
++ if (!spa_pod_is_string(pod) || maxlen < 1)
++ return -EINVAL;
++ strncpy(dest, s, maxlen-1);
++ dest[maxlen-1]= '\0';
++ return 0;
++}
++
++static inline int spa_pod_is_bytes(const struct spa_pod *pod)
++{
++ return SPA_POD_TYPE(pod) == SPA_TYPE_Bytes;
++}
++
++static inline int spa_pod_get_bytes(const struct spa_pod *pod, const void **value, uint32_t *len)
++{
++ if (!spa_pod_is_bytes(pod))
++ return -EINVAL;
++ *value = (const void *)SPA_POD_CONTENTS(struct spa_pod_bytes, pod);
++ *len = SPA_POD_BODY_SIZE(pod);
++ return 0;
++}
++
++static inline int spa_pod_is_pointer(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Pointer &&
++ SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_pointer_body));
++}
++
++static inline int spa_pod_get_pointer(const struct spa_pod *pod, uint32_t *type, const void **value)
++{
++ if (!spa_pod_is_pointer(pod))
++ return -EINVAL;
++ *type = ((struct spa_pod_pointer*)pod)->body.type;
++ *value = ((struct spa_pod_pointer*)pod)->body.value;
++ return 0;
++}
++
++static inline int spa_pod_is_fd(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Fd &&
++ SPA_POD_BODY_SIZE(pod) >= sizeof(int64_t));
++}
++
++static inline int spa_pod_get_fd(const struct spa_pod *pod, int64_t *value)
++{
++ if (!spa_pod_is_fd(pod))
++ return -EINVAL;
++ *value = SPA_POD_VALUE(struct spa_pod_fd, pod);
++ return 0;
++}
++
++static inline int spa_pod_is_rectangle(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Rectangle &&
++ SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_rectangle));
++}
++
++static inline int spa_pod_get_rectangle(const struct spa_pod *pod, struct spa_rectangle *value)
++{
++ if (!spa_pod_is_rectangle(pod))
++ return -EINVAL;
++ *value = SPA_POD_VALUE(struct spa_pod_rectangle, pod);
++ return 0;
++}
++
++static inline int spa_pod_is_fraction(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Fraction &&
++ SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_fraction));
++}
++
++static inline int spa_pod_get_fraction(const struct spa_pod *pod, struct spa_fraction *value)
++{
++ spa_return_val_if_fail(spa_pod_is_fraction(pod), -EINVAL);
++ *value = SPA_POD_VALUE(struct spa_pod_fraction, pod);
++ return 0;
++}
++
++static inline int spa_pod_is_bitmap(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Bitmap &&
++ SPA_POD_BODY_SIZE(pod) >= sizeof(uint8_t));
++}
++
++static inline int spa_pod_is_array(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Array &&
++ SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_array_body));
++}
++
++static inline void *spa_pod_get_array(const struct spa_pod *pod, uint32_t *n_values)
++{
++ spa_return_val_if_fail(spa_pod_is_array(pod), NULL);
++ *n_values = SPA_POD_ARRAY_N_VALUES(pod);
++ return SPA_POD_ARRAY_VALUES(pod);
++}
++
++static inline uint32_t spa_pod_copy_array(const struct spa_pod *pod, uint32_t type,
++ void *values, uint32_t max_values)
++{
++ uint32_t n_values;
++ void *v = spa_pod_get_array(pod, &n_values);
++ if (v == NULL || max_values == 0 || SPA_POD_ARRAY_VALUE_TYPE(pod) != type)
++ return 0;
++ n_values = SPA_MIN(n_values, max_values);
++ memcpy(values, v, SPA_POD_ARRAY_VALUE_SIZE(pod) * n_values);
++ return n_values;
++}
++
++static inline int spa_pod_is_choice(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Choice &&
++ SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_choice_body));
++}
++
++static inline struct spa_pod *spa_pod_get_values(const struct spa_pod *pod, uint32_t *n_vals, uint32_t *choice)
++{
++ if (pod->type == SPA_TYPE_Choice) {
++ *choice = SPA_POD_CHOICE_TYPE(pod);
++ *n_vals = *choice == SPA_CHOICE_None ? 1 : SPA_POD_CHOICE_N_VALUES(pod);
++ return (struct spa_pod*)SPA_POD_CHOICE_CHILD(pod);
++ } else {
++ *n_vals = 1;
++ *choice = SPA_CHOICE_None;
++ return (struct spa_pod*)pod;
++ }
++}
++
++static inline int spa_pod_is_struct(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Struct);
++}
++
++static inline int spa_pod_is_object(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Object &&
++ SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_object_body));
++}
++
++static inline bool spa_pod_is_object_type(const struct spa_pod *pod, uint32_t type)
++{
++ return (pod && spa_pod_is_object(pod) && SPA_POD_OBJECT_TYPE(pod) == type);
++}
++
++static inline bool spa_pod_is_object_id(const struct spa_pod *pod, uint32_t id)
++{
++ return (pod && spa_pod_is_object(pod) && SPA_POD_OBJECT_ID(pod) == id);
++}
++
++static inline int spa_pod_is_sequence(const struct spa_pod *pod)
++{
++ return (SPA_POD_TYPE(pod) == SPA_TYPE_Sequence &&
++ SPA_POD_BODY_SIZE(pod) >= sizeof(struct spa_pod_sequence_body));
++}
++
++static inline const struct spa_pod_prop *spa_pod_object_find_prop(const struct spa_pod_object *pod,
++ const struct spa_pod_prop *start, uint32_t key)
++{
++ const struct spa_pod_prop *first, *res;
++
++ first = spa_pod_prop_first(&pod->body);
++ start = start ? spa_pod_prop_next(start) : first;
++
++ for (res = start; spa_pod_prop_is_inside(&pod->body, pod->pod.size, res);
++ res = spa_pod_prop_next(res)) {
++ if (res->key == key)
++ return res;
++ }
++ for (res = first; res != start; res = spa_pod_prop_next(res)) {
++ if (res->key == key)
++ return res;
++ }
++ return NULL;
++}
++
++static inline const struct spa_pod_prop *spa_pod_find_prop(const struct spa_pod *pod,
++ const struct spa_pod_prop *start, uint32_t key)
++{
++ if (!spa_pod_is_object(pod))
++ return NULL;
++ return spa_pod_object_find_prop((const struct spa_pod_object *)pod, start, key);
++}
++
++static inline int spa_pod_object_fixate(struct spa_pod_object *pod)
++{
++ struct spa_pod_prop *res;
++ SPA_POD_OBJECT_FOREACH(pod, res) {
++ if (res->value.type == SPA_TYPE_Choice)
++ ((struct spa_pod_choice*)&res->value)->body.type = SPA_CHOICE_None;
++ }
++ return 0;
++}
++
++static inline int spa_pod_fixate(struct spa_pod *pod)
++{
++ if (!spa_pod_is_object(pod))
++ return -EINVAL;
++ return spa_pod_object_fixate((struct spa_pod_object *)pod);
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_POD_H */
+diff --git a/third_party/pipewire/spa/pod/parser.h b/third_party/pipewire/spa/pod/parser.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/pod/parser.h
+@@ -0,0 +1,573 @@
++/* Spa
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_POD_PARSER_H
++#define SPA_POD_PARSER_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <errno.h>
++#include <stdarg.h>
++
++#include <spa/pod/iter.h>
++#include <spa/pod/vararg.h>
++
++struct spa_pod_parser_state {
++ uint32_t offset;
++ uint32_t flags;
++ struct spa_pod_frame *frame;
++};
++
++struct spa_pod_parser {
++ const void *data;
++ uint32_t size;
++ uint32_t _padding;
++ struct spa_pod_parser_state state;
++};
++
++#define SPA_POD_PARSER_INIT(buffer,size) (struct spa_pod_parser){ buffer, size, }
++
++static inline void spa_pod_parser_init(struct spa_pod_parser *parser,
++ const void *data, uint32_t size)
++{
++ *parser = SPA_POD_PARSER_INIT(data, size);
++}
++
++static inline void spa_pod_parser_pod(struct spa_pod_parser *parser,
++ const struct spa_pod *pod)
++{
++ spa_pod_parser_init(parser, pod, SPA_POD_SIZE(pod));
++}
++
++static inline void
++spa_pod_parser_get_state(struct spa_pod_parser *parser, struct spa_pod_parser_state *state)
++{
++ *state = parser->state;
++}
++
++static inline void
++spa_pod_parser_reset(struct spa_pod_parser *parser, struct spa_pod_parser_state *state)
++{
++ parser->state = *state;
++}
++
++static inline struct spa_pod *
++spa_pod_parser_deref(struct spa_pod_parser *parser, uint32_t offset, uint32_t size)
++{
++ if (offset + 8 <= size) {
++ struct spa_pod *pod = SPA_MEMBER(parser->data, offset, struct spa_pod);
++ if (offset + SPA_POD_SIZE(pod) <= size)
++ return pod;
++ }
++ return NULL;
++}
++
++static inline struct spa_pod *spa_pod_parser_frame(struct spa_pod_parser *parser, struct spa_pod_frame *frame)
++{
++ return SPA_MEMBER(parser->data, frame->offset, struct spa_pod);
++}
++
++static inline void spa_pod_parser_push(struct spa_pod_parser *parser,
++ struct spa_pod_frame *frame, const struct spa_pod *pod, uint32_t offset)
++{
++ frame->pod = *pod;
++ frame->offset = offset;
++ frame->parent = parser->state.frame;
++ frame->flags = parser->state.flags;
++ parser->state.frame = frame;
++}
++
++static inline struct spa_pod *spa_pod_parser_current(struct spa_pod_parser *parser)
++{
++ struct spa_pod_frame *f = parser->state.frame;
++ uint32_t size = f ? f->offset + SPA_POD_SIZE(&f->pod) : parser->size;
++ return spa_pod_parser_deref(parser, parser->state.offset, size);
++}
++
++static inline void spa_pod_parser_advance(struct spa_pod_parser *parser, const struct spa_pod *pod)
++{
++ parser->state.offset += SPA_ROUND_UP_N(SPA_POD_SIZE(pod), 8);
++}
++
++static inline struct spa_pod *spa_pod_parser_next(struct spa_pod_parser *parser)
++{
++ struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod)
++ spa_pod_parser_advance(parser, pod);
++ return pod;
++}
++
++static inline int spa_pod_parser_pop(struct spa_pod_parser *parser,
++ struct spa_pod_frame *frame)
++{
++ parser->state.frame = frame->parent;
++ parser->state.offset = frame->offset + SPA_ROUND_UP_N(SPA_POD_SIZE(&frame->pod), 8);
++ return 0;
++}
++
++static inline int spa_pod_parser_get_bool(struct spa_pod_parser *parser, bool *value)
++{
++ int res = -EPIPE;
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod != NULL && (res = spa_pod_get_bool(pod, value)) >= 0)
++ spa_pod_parser_advance(parser, pod);
++ return res;
++}
++
++static inline int spa_pod_parser_get_id(struct spa_pod_parser *parser, uint32_t *value)
++{
++ int res = -EPIPE;
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod != NULL && (res = spa_pod_get_id(pod, value)) >= 0)
++ spa_pod_parser_advance(parser, pod);
++ return res;
++}
++
++static inline int spa_pod_parser_get_int(struct spa_pod_parser *parser, int32_t *value)
++{
++ int res = -EPIPE;
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod != NULL && (res = spa_pod_get_int(pod, value)) >= 0)
++ spa_pod_parser_advance(parser, pod);
++ return res;
++}
++
++static inline int spa_pod_parser_get_long(struct spa_pod_parser *parser, int64_t *value)
++{
++ int res = -EPIPE;
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod != NULL && (res = spa_pod_get_long(pod, value)) >= 0)
++ spa_pod_parser_advance(parser, pod);
++ return res;
++}
++
++static inline int spa_pod_parser_get_float(struct spa_pod_parser *parser, float *value)
++{
++ int res = -EPIPE;
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod != NULL && (res = spa_pod_get_float(pod, value)) >= 0)
++ spa_pod_parser_advance(parser, pod);
++ return res;
++}
++
++static inline int spa_pod_parser_get_double(struct spa_pod_parser *parser, double *value)
++{
++ int res = -EPIPE;
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod != NULL && (res = spa_pod_get_double(pod, value)) >= 0)
++ spa_pod_parser_advance(parser, pod);
++ return res;
++}
++
++static inline int spa_pod_parser_get_string(struct spa_pod_parser *parser, const char **value)
++{
++ int res = -EPIPE;
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod != NULL && (res = spa_pod_get_string(pod, value)) >= 0)
++ spa_pod_parser_advance(parser, pod);
++ return res;
++}
++
++static inline int spa_pod_parser_get_bytes(struct spa_pod_parser *parser, const void **value, uint32_t *len)
++{
++ int res = -EPIPE;
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod != NULL && (res = spa_pod_get_bytes(pod, value, len)) >= 0)
++ spa_pod_parser_advance(parser, pod);
++ return res;
++}
++
++static inline int spa_pod_parser_get_pointer(struct spa_pod_parser *parser, uint32_t *type, const void **value)
++{
++ int res = -EPIPE;
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod != NULL && (res = spa_pod_get_pointer(pod, type, value)) >= 0)
++ spa_pod_parser_advance(parser, pod);
++ return res;
++}
++
++static inline int spa_pod_parser_get_fd(struct spa_pod_parser *parser, int64_t *value)
++{
++ int res = -EPIPE;
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod != NULL && (res = spa_pod_get_fd(pod, value)) >= 0)
++ spa_pod_parser_advance(parser, pod);
++ return res;
++}
++
++static inline int spa_pod_parser_get_rectangle(struct spa_pod_parser *parser, struct spa_rectangle *value)
++{
++ int res = -EPIPE;
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod != NULL && (res = spa_pod_get_rectangle(pod, value)) >= 0)
++ spa_pod_parser_advance(parser, pod);
++ return res;
++}
++
++static inline int spa_pod_parser_get_fraction(struct spa_pod_parser *parser, struct spa_fraction *value)
++{
++ int res = -EPIPE;
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod != NULL && (res = spa_pod_get_fraction(pod, value)) >= 0)
++ spa_pod_parser_advance(parser, pod);
++ return res;
++}
++
++static inline int spa_pod_parser_get_pod(struct spa_pod_parser *parser, struct spa_pod **value)
++{
++ struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod == NULL)
++ return -EPIPE;
++ *value = pod;
++ spa_pod_parser_advance(parser, pod);
++ return 0;
++}
++static inline int spa_pod_parser_push_struct(struct spa_pod_parser *parser,
++ struct spa_pod_frame *frame)
++{
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod == NULL)
++ return -EPIPE;
++ if (!spa_pod_is_struct(pod))
++ return -EINVAL;
++ spa_pod_parser_push(parser, frame, pod, parser->state.offset);
++ parser->state.offset += sizeof(struct spa_pod_struct);
++ return 0;
++}
++
++static inline int spa_pod_parser_push_object(struct spa_pod_parser *parser,
++ struct spa_pod_frame *frame, uint32_t type, uint32_t *id)
++{
++ const struct spa_pod *pod = spa_pod_parser_current(parser);
++ if (pod == NULL)
++ return -EPIPE;
++ if (!spa_pod_is_object(pod))
++ return -EINVAL;
++ if (type != SPA_POD_OBJECT_TYPE(pod))
++ return -EPROTO;
++ if (id != NULL)
++ *id = SPA_POD_OBJECT_ID(pod);
++ spa_pod_parser_push(parser, frame, pod, parser->state.offset);
++ parser->state.offset = parser->size;
++ return 0;
++}
++
++static inline bool spa_pod_parser_can_collect(const struct spa_pod *pod, char type)
++{
++ if (pod == NULL)
++ return false;
++
++ if (spa_pod_is_choice(pod) &&
++ SPA_POD_CHOICE_TYPE(pod) == SPA_CHOICE_None &&
++ spa_pod_parser_can_collect(SPA_POD_CHOICE_CHILD(pod), type))
++ return true;
++
++ switch (type) {
++ case 'P':
++ return true;
++ case 'b':
++ return spa_pod_is_bool(pod);
++ case 'I':
++ return spa_pod_is_id(pod);
++ case 'i':
++ return spa_pod_is_int(pod);
++ case 'l':
++ return spa_pod_is_long(pod);
++ case 'f':
++ return spa_pod_is_float(pod);
++ case 'd':
++ return spa_pod_is_double(pod);
++ case 's':
++ return spa_pod_is_string(pod) || spa_pod_is_none(pod);
++ case 'S':
++ return spa_pod_is_string(pod);
++ case 'y':
++ return spa_pod_is_bytes(pod);
++ case 'R':
++ return spa_pod_is_rectangle(pod);
++ case 'F':
++ return spa_pod_is_fraction(pod);
++ case 'B':
++ return spa_pod_is_bitmap(pod);
++ case 'a':
++ return spa_pod_is_array(pod);
++ case 'p':
++ return spa_pod_is_pointer(pod);
++ case 'h':
++ return spa_pod_is_fd(pod);
++ case 'T':
++ return spa_pod_is_struct(pod) || spa_pod_is_none(pod);
++ case 'O':
++ return spa_pod_is_object(pod) || spa_pod_is_none(pod);
++ case 'V':
++ return spa_pod_is_choice(pod);
++ default:
++ return false;
++ }
++}
++
++#define SPA_POD_PARSER_COLLECT(pod,_type,args) \
++do { \
++ switch (_type) { \
++ case 'b': \
++ *va_arg(args, bool*) = SPA_POD_VALUE(struct spa_pod_bool, pod); \
++ break; \
++ case 'I': \
++ case 'i': \
++ *va_arg(args, int32_t*) = SPA_POD_VALUE(struct spa_pod_int, pod); \
++ break; \
++ case 'l': \
++ *va_arg(args, int64_t*) = SPA_POD_VALUE(struct spa_pod_long, pod); \
++ break; \
++ case 'f': \
++ *va_arg(args, float*) = SPA_POD_VALUE(struct spa_pod_float, pod); \
++ break; \
++ case 'd': \
++ *va_arg(args, double*) = SPA_POD_VALUE(struct spa_pod_double, pod); \
++ break; \
++ case 's': \
++ *va_arg(args, char**) = \
++ (pod == NULL || (SPA_POD_TYPE(pod) == SPA_TYPE_None) \
++ ? NULL \
++ : (char *)SPA_POD_CONTENTS(struct spa_pod_string, pod)); \
++ break; \
++ case 'S': \
++ { \
++ char *dest = va_arg(args, char*); \
++ uint32_t maxlen = va_arg(args, uint32_t); \
++ strncpy(dest, (char *)SPA_POD_CONTENTS(struct spa_pod_string, pod), maxlen-1); \
++ break; \
++ } \
++ case 'y': \
++ *(va_arg(args, void **)) = SPA_POD_CONTENTS(struct spa_pod_bytes, pod); \
++ *(va_arg(args, uint32_t *)) = SPA_POD_BODY_SIZE(pod); \
++ break; \
++ case 'R': \
++ *va_arg(args, struct spa_rectangle*) = \
++ SPA_POD_VALUE(struct spa_pod_rectangle, pod); \
++ break; \
++ case 'F': \
++ *va_arg(args, struct spa_fraction*) = \
++ SPA_POD_VALUE(struct spa_pod_fraction, pod); \
++ break; \
++ case 'B': \
++ *va_arg(args, uint32_t **) = \
++ (uint32_t *) SPA_POD_CONTENTS(struct spa_pod_bitmap, pod); \
++ break; \
++ case 'a': \
++ *va_arg(args, uint32_t*) = SPA_POD_ARRAY_VALUE_SIZE(pod); \
++ *va_arg(args, uint32_t*) = SPA_POD_ARRAY_VALUE_TYPE(pod); \
++ *va_arg(args, uint32_t*) = SPA_POD_ARRAY_N_VALUES(pod); \
++ *va_arg(args, void**) = SPA_POD_ARRAY_VALUES(pod); \
++ break; \
++ case 'p': \
++ { \
++ struct spa_pod_pointer_body *b = \
++ (struct spa_pod_pointer_body *) SPA_POD_BODY(pod); \
++ *(va_arg(args, uint32_t *)) = b->type; \
++ *(va_arg(args, const void **)) = b->value; \
++ break; \
++ } \
++ case 'h': \
++ *va_arg(args, int64_t*) = SPA_POD_VALUE(struct spa_pod_fd, pod); \
++ break; \
++ case 'P': \
++ case 'T': \
++ case 'O': \
++ case 'V': \
++ { \
++ const struct spa_pod **d = va_arg(args, const struct spa_pod**); \
++ if (d) \
++ *d = (pod == NULL || (SPA_POD_TYPE(pod) == SPA_TYPE_None) \
++ ? NULL : pod); \
++ break; \
++ } \
++ default: \
++ break; \
++ } \
++} while(false)
++
++#define SPA_POD_PARSER_SKIP(_type,args) \
++do { \
++ switch (_type) { \
++ case 'S': \
++ va_arg(args, char*); \
++ va_arg(args, uint32_t); \
++ break; \
++ case 'a': \
++ va_arg(args, void*); \
++ va_arg(args, void*); \
++ /* fallthrough */ \
++ case 'p': \
++ case 'y': \
++ va_arg(args, void*); \
++ /* fallthrough */ \
++ case 'b': \
++ case 'I': \
++ case 'i': \
++ case 'l': \
++ case 'f': \
++ case 'd': \
++ case 's': \
++ case 'R': \
++ case 'F': \
++ case 'B': \
++ case 'h': \
++ case 'V': \
++ case 'P': \
++ case 'T': \
++ case 'O': \
++ va_arg(args, void*); \
++ break; \
++ } \
++} while(false)
++
++static inline int spa_pod_parser_getv(struct spa_pod_parser *parser, va_list args)
++{
++ struct spa_pod_frame *f = parser->state.frame;
++ uint32_t ftype = f ? f->pod.type : (uint32_t)SPA_TYPE_Struct;
++ const struct spa_pod_prop *prop = NULL;
++ int count = 0;
++
++ do {
++ bool optional;
++ const struct spa_pod *pod = NULL;
++ const char *format;
++
++ if (ftype == SPA_TYPE_Object) {
++ uint32_t key = va_arg(args, uint32_t);
++ const struct spa_pod_object *object;
++
++ if (key == 0)
++ break;
++
++ object = (const struct spa_pod_object *)spa_pod_parser_frame(parser, f);
++ prop = spa_pod_object_find_prop(object, prop, key);
++ pod = prop ? &prop->value : NULL;
++ }
++
++ if ((format = va_arg(args, char *)) == NULL)
++ break;
++
++ if (ftype == SPA_TYPE_Struct)
++ pod = spa_pod_parser_next(parser);
++
++ if ((optional = (*format == '?')))
++ format++;
++
++ if (!spa_pod_parser_can_collect(pod, *format)) {
++ if (!optional) {
++ if (pod == NULL)
++ return -ESRCH;
++ else
++ return -EPROTO;
++ }
++ SPA_POD_PARSER_SKIP(*format, args);
++ } else {
++ if (pod->type == SPA_TYPE_Choice && *format != 'V' &&
++ SPA_POD_CHOICE_TYPE(pod) == SPA_CHOICE_None)
++ pod = SPA_POD_CHOICE_CHILD(pod);
++
++ SPA_POD_PARSER_COLLECT(pod, *format, args);
++ count++;
++ }
++ } while (true);
++
++ return count;
++}
++
++static inline int spa_pod_parser_get(struct spa_pod_parser *parser, ...)
++{
++ int res;
++ va_list args;
++
++ va_start(args, parser);
++ res = spa_pod_parser_getv(parser, args);
++ va_end(args);
++
++ return res;
++}
++
++#define SPA_POD_OPT_Bool(val) "?" SPA_POD_Bool(val)
++#define SPA_POD_OPT_Id(val) "?" SPA_POD_Id(val)
++#define SPA_POD_OPT_Int(val) "?" SPA_POD_Int(val)
++#define SPA_POD_OPT_Long(val) "?" SPA_POD_Long(val)
++#define SPA_POD_OPT_Float(val) "?" SPA_POD_Float(val)
++#define SPA_POD_OPT_Double(val) "?" SPA_POD_Double(val)
++#define SPA_POD_OPT_String(val) "?" SPA_POD_String(val)
++#define SPA_POD_OPT_Stringn(val,len) "?" SPA_POD_Stringn(val,len)
++#define SPA_POD_OPT_Bytes(val,len) "?" SPA_POD_Bytes(val,len)
++#define SPA_POD_OPT_Rectangle(val) "?" SPA_POD_Rectangle(val)
++#define SPA_POD_OPT_Fraction(val) "?" SPA_POD_Fraction(val)
++#define SPA_POD_OPT_Array(csize,ctype,n_vals,vals) "?" SPA_POD_Array(csize,ctype,n_vals,vals)
++#define SPA_POD_OPT_Pointer(type,val) "?" SPA_POD_Pointer(type,val)
++#define SPA_POD_OPT_Fd(val) "?" SPA_POD_Fd(val)
++#define SPA_POD_OPT_Pod(val) "?" SPA_POD_Pod(val)
++#define SPA_POD_OPT_PodObject(val) "?" SPA_POD_PodObject(val)
++#define SPA_POD_OPT_PodStruct(val) "?" SPA_POD_PodStruct(val)
++#define SPA_POD_OPT_PodChoice(val) "?" SPA_POD_PodChoice(val)
++
++#define spa_pod_parser_get_object(p,type,id,...) \
++({ \
++ struct spa_pod_frame _f; \
++ int _res; \
++ if ((_res = spa_pod_parser_push_object(p, &_f, type, id)) == 0) { \
++ _res = spa_pod_parser_get(p,##__VA_ARGS__, 0); \
++ spa_pod_parser_pop(p, &_f); \
++ } \
++ _res; \
++})
++
++#define spa_pod_parser_get_struct(p,...) \
++({ \
++ struct spa_pod_frame _f; \
++ int _res; \
++ if ((_res = spa_pod_parser_push_struct(p, &_f)) == 0) { \
++ _res = spa_pod_parser_get(p,##__VA_ARGS__, NULL); \
++ spa_pod_parser_pop(p, &_f); \
++ } \
++ _res; \
++})
++
++#define spa_pod_parse_object(pod,type,id,...) \
++({ \
++ struct spa_pod_parser _p; \
++ spa_pod_parser_pod(&_p, pod); \
++ spa_pod_parser_get_object(&_p,type,id,##__VA_ARGS__); \
++})
++
++#define spa_pod_parse_struct(pod,...) \
++({ \
++ struct spa_pod_parser _p; \
++ spa_pod_parser_pod(&_p, pod); \
++ spa_pod_parser_get_struct(&_p,##__VA_ARGS__); \
++})
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_POD_PARSER_H */
+diff --git a/third_party/pipewire/spa/pod/pod.h b/third_party/pipewire/spa/pod/pod.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/pod/pod.h
+@@ -0,0 +1,231 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_POD_H
++#define SPA_POD_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/type.h>
++
++#define SPA_POD_BODY_SIZE(pod) (((struct spa_pod*)(pod))->size)
++#define SPA_POD_TYPE(pod) (((struct spa_pod*)(pod))->type)
++#define SPA_POD_SIZE(pod) (sizeof(struct spa_pod) + SPA_POD_BODY_SIZE(pod))
++#define SPA_POD_CONTENTS_SIZE(type,pod) (SPA_POD_SIZE(pod)-sizeof(type))
++
++#define SPA_POD_CONTENTS(type,pod) SPA_MEMBER((pod),sizeof(type),void)
++#define SPA_POD_CONTENTS_CONST(type,pod) SPA_MEMBER((pod),sizeof(type),const void)
++#define SPA_POD_BODY(pod) SPA_MEMBER((pod),sizeof(struct spa_pod),void)
++#define SPA_POD_BODY_CONST(pod) SPA_MEMBER((pod),sizeof(struct spa_pod),const void)
++
++struct spa_pod {
++ uint32_t size; /* size of the body */
++ uint32_t type; /* a basic id of enum spa_type */
++};
++
++#define SPA_POD_VALUE(type,pod) (((type*)pod)->value)
++
++struct spa_pod_bool {
++ struct spa_pod pod;
++ int32_t value;
++ int32_t _padding;
++};
++
++struct spa_pod_id {
++ struct spa_pod pod;
++ uint32_t value;
++ int32_t _padding;
++};
++
++struct spa_pod_int {
++ struct spa_pod pod;
++ int32_t value;
++ int32_t _padding;
++};
++
++struct spa_pod_long {
++ struct spa_pod pod;
++ int64_t value;
++};
++
++struct spa_pod_float {
++ struct spa_pod pod;
++ float value;
++ int32_t _padding;
++};
++
++struct spa_pod_double {
++ struct spa_pod pod;
++ double value;
++};
++
++struct spa_pod_string {
++ struct spa_pod pod;
++ /* value here */
++};
++
++struct spa_pod_bytes {
++ struct spa_pod pod;
++ /* value here */
++};
++
++struct spa_pod_rectangle {
++ struct spa_pod pod;
++ struct spa_rectangle value;
++};
++
++struct spa_pod_fraction {
++ struct spa_pod pod;
++ struct spa_fraction value;
++};
++
++struct spa_pod_bitmap {
++ struct spa_pod pod;
++ /* array of uint8_t follows with the bitmap */
++};
++
++#define SPA_POD_ARRAY_CHILD(arr) (&((struct spa_pod_array*)(arr))->body.child)
++#define SPA_POD_ARRAY_VALUE_TYPE(arr) (SPA_POD_TYPE(SPA_POD_ARRAY_CHILD(arr)))
++#define SPA_POD_ARRAY_VALUE_SIZE(arr) (SPA_POD_BODY_SIZE(SPA_POD_ARRAY_CHILD(arr)))
++#define SPA_POD_ARRAY_N_VALUES(arr) ((SPA_POD_BODY_SIZE(arr) - sizeof(struct spa_pod_array_body)) / SPA_POD_ARRAY_VALUE_SIZE(arr))
++#define SPA_POD_ARRAY_VALUES(arr) SPA_POD_CONTENTS(struct spa_pod_array, arr)
++
++struct spa_pod_array_body {
++ struct spa_pod child;
++ /* array with elements of child.size follows */
++};
++
++struct spa_pod_array {
++ struct spa_pod pod;
++ struct spa_pod_array_body body;
++};
++
++#define SPA_POD_CHOICE_CHILD(choice) (&((struct spa_pod_choice*)(choice))->body.child)
++#define SPA_POD_CHOICE_TYPE(choice) (((struct spa_pod_choice*)(choice))->body.type)
++#define SPA_POD_CHOICE_FLAGS(choice) (((struct spa_pod_choice*)(choice))->body.flags)
++#define SPA_POD_CHOICE_VALUE_TYPE(choice) (SPA_POD_TYPE(SPA_POD_CHOICE_CHILD(choice)))
++#define SPA_POD_CHOICE_VALUE_SIZE(choice) (SPA_POD_BODY_SIZE(SPA_POD_CHOICE_CHILD(choice)))
++#define SPA_POD_CHOICE_N_VALUES(choice) ((SPA_POD_BODY_SIZE(choice) - sizeof(struct spa_pod_choice_body)) / SPA_POD_CHOICE_VALUE_SIZE(choice))
++#define SPA_POD_CHOICE_VALUES(choice) (SPA_POD_CONTENTS(struct spa_pod_choice, choice))
++
++enum spa_choice_type {
++ SPA_CHOICE_None, /**< no choice, first value is current */
++ SPA_CHOICE_Range, /**< range: default, min, max */
++ SPA_CHOICE_Step, /**< range with step: default, min, max, step */
++ SPA_CHOICE_Enum, /**< list: default, alternative,... */
++ SPA_CHOICE_Flags, /**< flags: default, possible flags,... */
++};
++
++struct spa_pod_choice_body {
++ uint32_t type; /**< type of choice, one of enum spa_choice_type */
++ uint32_t flags; /**< extra flags */
++ struct spa_pod child;
++ /* array with elements of child.size follows. Note that there might be more
++ * elements than required by \a type, which should be ignored. */
++};
++
++struct spa_pod_choice {
++ struct spa_pod pod;
++ struct spa_pod_choice_body body;
++};
++
++struct spa_pod_struct {
++ struct spa_pod pod;
++ /* one or more spa_pod follow */
++};
++
++#define SPA_POD_OBJECT_TYPE(obj) (((struct spa_pod_object*)(obj))->body.type)
++#define SPA_POD_OBJECT_ID(obj) (((struct spa_pod_object*)(obj))->body.id)
++
++struct spa_pod_object_body {
++ uint32_t type; /**< one of enum spa_type */
++ uint32_t id; /**< id of the object, depends on the object type */
++ /* contents follow, series of spa_pod_prop */
++};
++
++struct spa_pod_object {
++ struct spa_pod pod;
++ struct spa_pod_object_body body;
++};
++
++struct spa_pod_pointer_body {
++ uint32_t type; /**< pointer id, one of enum spa_type */
++ uint32_t _padding;
++ const void *value;
++};
++
++struct spa_pod_pointer {
++ struct spa_pod pod;
++ struct spa_pod_pointer_body body;
++};
++
++struct spa_pod_fd {
++ struct spa_pod pod;
++ int64_t value;
++};
++
++#define SPA_POD_PROP_SIZE(prop) (sizeof(struct spa_pod_prop) + (prop)->value.size)
++
++/* props can be inside an object */
++struct spa_pod_prop {
++ uint32_t key; /**< key of property, list of valid keys depends on the
++ * object type */
++#define SPA_POD_PROP_FLAG_READONLY (1u<<0) /**< is read-only */
++#define SPA_POD_PROP_FLAG_HARDWARE (1u<<1) /**< some sort of hardware parameter */
++ uint32_t flags; /**< flags for property */
++ struct spa_pod value;
++ /* value follows */
++};
++
++#define SPA_POD_CONTROL_SIZE(ev) (sizeof(struct spa_pod_control) + (ev)->value.size)
++
++/* controls can be inside a sequence and mark timed values */
++struct spa_pod_control {
++ uint32_t offset; /**< media offset */
++ uint32_t type; /**< type of control, enum spa_control_type */
++ struct spa_pod value; /**< control value, depends on type */
++ /* value contents follow */
++};
++
++struct spa_pod_sequence_body {
++ uint32_t unit;
++ uint32_t pad;
++ /* series of struct spa_pod_control follows */
++};
++
++/** a sequence of timed controls */
++struct spa_pod_sequence {
++ struct spa_pod pod;
++ struct spa_pod_sequence_body body;
++};
++
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_POD_H */
+diff --git a/third_party/pipewire/spa/pod/vararg.h b/third_party/pipewire/spa/pod/vararg.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/pod/vararg.h
+@@ -0,0 +1,104 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2019 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_POD_VARARG_H
++#define SPA_POD_VARARG_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdarg.h>
++
++#include <spa/pod/pod.h>
++
++#define SPA_POD_Prop(key,...) \
++ key, ##__VA_ARGS__
++
++#define SPA_POD_Control(offset,type,...) \
++ offset, type, ##__VA_ARGS__
++
++#define SPA_CHOICE_RANGE(def,min,max) 3,(def),(min),(max)
++#define SPA_CHOICE_STEP(def,min,max,step) 4,(def),(min),(max),(step)
++#define SPA_CHOICE_ENUM(n_vals,...) (n_vals),##__VA_ARGS__
++#define SPA_CHOICE_FLAGS(flags) 1, (flags)
++#define SPA_CHOICE_BOOL(def) 3,(def),(def),!(def)
++
++#define SPA_POD_Bool(val) "b", val
++#define SPA_POD_CHOICE_Bool(def) "?eb", SPA_CHOICE_BOOL(def)
++
++#define SPA_POD_Id(val) "I", val
++#define SPA_POD_CHOICE_ENUM_Id(n_vals,...) "?eI", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__)
++
++#define SPA_POD_Int(val) "i", val
++#define SPA_POD_CHOICE_ENUM_Int(n_vals,...) "?ei", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__)
++#define SPA_POD_CHOICE_RANGE_Int(def,min,max) "?ri", SPA_CHOICE_RANGE(def, min, max)
++#define SPA_POD_CHOICE_STEP_Int(def,min,max,step) "?si", SPA_CHOICE_STEP(def, min, max, step)
++#define SPA_POD_CHOICE_FLAGS_Int(flags) "?fi", SPA_CHOICE_FLAGS(flags)
++
++#define SPA_POD_Long(val) "l", val
++#define SPA_POD_CHOICE_ENUM_Long(n_vals,...) "?el", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__)
++#define SPA_POD_CHOICE_RANGE_Long(def,min,max) "?rl", SPA_CHOICE_RANGE(def, min, max)
++#define SPA_POD_CHOICE_STEP_Long(def,min,max,step) "?sl", SPA_CHOICE_STEP(def, min, max, step)
++#define SPA_POD_CHOICE_FLAGS_Long(flags) "?fl", SPA_CHOICE_FLAGS(flags)
++
++#define SPA_POD_Float(val) "f", val
++#define SPA_POD_CHOICE_ENUM_Float(n_vals,...) "?ef", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__)
++#define SPA_POD_CHOICE_RANGE_Float(def,min,max) "?rf", SPA_CHOICE_RANGE(def, min, max)
++#define SPA_POD_CHOICE_STEP_Float(def,min,max,step) "?sf", SPA_CHOICE_STEP(def, min, max, step)
++
++#define SPA_POD_Double(val) "d", val
++#define SPA_POD_CHOICE_ENUM_Double(n_vals,...) "?ed", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__)
++#define SPA_POD_CHOICE_RANGE_Double(def,min,max) "?rd", SPA_CHOICE_RANGE(def, min, max)
++#define SPA_POD_CHOICE_STEP_Double(def,min,max,step) "?sd", SPA_CHOICE_STEP(def, min, max, step)
++
++#define SPA_POD_String(val) "s",val
++#define SPA_POD_Stringn(val,len) "S",val,len
++
++#define SPA_POD_Bytes(val,len) "y",val,len
++
++#define SPA_POD_Rectangle(val) "R",val
++#define SPA_POD_CHOICE_ENUM_Rectangle(n_vals,...) "?eR", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__)
++#define SPA_POD_CHOICE_RANGE_Rectangle(def,min,max) "?rR", SPA_CHOICE_RANGE((def),(min),(max))
++#define SPA_POD_CHOICE_STEP_Rectangle(def,min,max,step) "?sR", SPA_CHOICE_STEP((def),(min),(max),(step))
++
++#define SPA_POD_Fraction(val) "F",val
++#define SPA_POD_CHOICE_ENUM_Fraction(n_vals,...) "?eF", SPA_CHOICE_ENUM(n_vals, __VA_ARGS__)
++#define SPA_POD_CHOICE_RANGE_Fraction(def,min,max) "?rF", SPA_CHOICE_RANGE((def),(min),(max))
++#define SPA_POD_CHOICE_STEP_Fraction(def,min,max,step) "?sF", SPA_CHOICE_STEP(def, min, max, step)
++
++#define SPA_POD_Array(csize,ctype,n_vals,vals) "a", csize,ctype,n_vals,vals
++#define SPA_POD_Pointer(type,val) "p", type,val
++#define SPA_POD_Fd(val) "h", val
++#define SPA_POD_None() "P", NULL
++#define SPA_POD_Pod(val) "P", val
++#define SPA_POD_PodObject(val) "O", val
++#define SPA_POD_PodStruct(val) "T", val
++#define SPA_POD_PodChoice(val) "V", val
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_POD_VARARG_H */
+diff --git a/third_party/pipewire/spa/support/cpu.h b/third_party/pipewire/spa/support/cpu.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/support/cpu.h
+@@ -0,0 +1,126 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_CPU_H
++#define SPA_CPU_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdarg.h>
++
++#include <spa/utils/defs.h>
++#include <spa/utils/hook.h>
++
++/**
++ * The CPU features interface
++ */
++#define SPA_TYPE_INTERFACE_CPU SPA_TYPE_INFO_INTERFACE_BASE "CPU"
++
++#define SPA_VERSION_CPU 0
++struct spa_cpu { struct spa_interface iface; };
++
++/* x86 specific */
++#define SPA_CPU_FLAG_MMX (1<<0) /**< standard MMX */
++#define SPA_CPU_FLAG_MMXEXT (1<<1) /**< SSE integer or AMD MMX ext */
++#define SPA_CPU_FLAG_3DNOW (1<<2) /**< AMD 3DNOW */
++#define SPA_CPU_FLAG_SSE (1<<3) /**< SSE */
++#define SPA_CPU_FLAG_SSE2 (1<<4) /**< SSE2 */
++#define SPA_CPU_FLAG_3DNOWEXT (1<<5) /**< AMD 3DNowExt */
++#define SPA_CPU_FLAG_SSE3 (1<<6) /**< Prescott SSE3 */
++#define SPA_CPU_FLAG_SSSE3 (1<<7) /**< Conroe SSSE3 */
++#define SPA_CPU_FLAG_SSE41 (1<<8) /**< Penryn SSE4.1 */
++#define SPA_CPU_FLAG_SSE42 (1<<9) /**< Nehalem SSE4.2 */
++#define SPA_CPU_FLAG_AESNI (1<<10) /**< Advanced Encryption Standard */
++#define SPA_CPU_FLAG_AVX (1<<11) /**< AVX */
++#define SPA_CPU_FLAG_XOP (1<<12) /**< Bulldozer XOP */
++#define SPA_CPU_FLAG_FMA4 (1<<13) /**< Bulldozer FMA4 */
++#define SPA_CPU_FLAG_CMOV (1<<14) /**< supports cmov */
++#define SPA_CPU_FLAG_AVX2 (1<<15) /**< AVX2 */
++#define SPA_CPU_FLAG_FMA3 (1<<16) /**< Haswell FMA3 */
++#define SPA_CPU_FLAG_BMI1 (1<<17) /**< Bit Manipulation Instruction Set 1 */
++#define SPA_CPU_FLAG_BMI2 (1<<18) /**< Bit Manipulation Instruction Set 2 */
++#define SPA_CPU_FLAG_AVX512 (1<<19) /**< AVX-512 */
++#define SPA_CPU_FLAG_SLOW_UNALIGNED (1<<20) /**< unaligned loads/stores are slow */
++
++/* PPC specific */
++#define SPA_CPU_FLAG_ALTIVEC (1<<0) /**< standard */
++#define SPA_CPU_FLAG_VSX (1<<1) /**< ISA 2.06 */
++#define SPA_CPU_FLAG_POWER8 (1<<2) /**< ISA 2.07 */
++
++/* ARM specific */
++#define SPA_CPU_FLAG_ARMV5TE (1 << 0)
++#define SPA_CPU_FLAG_ARMV6 (1 << 1)
++#define SPA_CPU_FLAG_ARMV6T2 (1 << 2)
++#define SPA_CPU_FLAG_VFP (1 << 3)
++#define SPA_CPU_FLAG_VFPV3 (1 << 4)
++#define SPA_CPU_FLAG_NEON (1 << 5)
++#define SPA_CPU_FLAG_ARMV8 (1 << 6)
++
++#define SPA_CPU_FORCE_AUTODETECT ((uint32_t)-1)
++/**
++ * methods
++ */
++struct spa_cpu_methods {
++ /** the version of the methods. This can be used to expand this
++ structure in the future */
++#define SPA_VERSION_CPU_METHODS 0
++ uint32_t version;
++
++ /** get CPU flags */
++ uint32_t (*get_flags) (void *object);
++
++ /** force CPU flags, use SPA_CPU_FORCE_AUTODETECT to autodetect CPU flags */
++ int (*force_flags) (void *object, uint32_t flags);
++
++ /** get number of CPU cores */
++ uint32_t (*get_count) (void *object);
++
++ /** get maximum required alignment of data */
++ uint32_t (*get_max_align) (void *object);
++};
++
++#define spa_cpu_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ struct spa_cpu *_c = o; \
++ spa_interface_call_res(&_c->iface, \
++ struct spa_cpu_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++#define spa_cpu_get_flags(c) spa_cpu_method(c, get_flags, 0)
++#define spa_cpu_force_flags(c,f) spa_cpu_method(c, force_flags, 0, f)
++#define spa_cpu_get_count(c) spa_cpu_method(c, get_count, 0)
++#define spa_cpu_get_max_align(c) spa_cpu_method(c, get_max_align, 0)
++
++/** keys can be given when initializing the cpu handle */
++#define SPA_KEY_CPU_FORCE "cpu.force" /**< force cpu flags */
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_CPU_H */
+diff --git a/third_party/pipewire/spa/support/dbus.h b/third_party/pipewire/spa/support/dbus.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/support/dbus.h
+@@ -0,0 +1,100 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_DBUS_H
++#define SPA_DBUS_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/support/loop.h>
++
++#define SPA_TYPE_INTERFACE_DBus SPA_TYPE_INFO_INTERFACE_BASE "DBus"
++
++#define SPA_VERSION_DBUS 0
++struct spa_dbus { struct spa_interface iface; };
++
++enum spa_dbus_type {
++ SPA_DBUS_TYPE_SESSION, /**< The login session bus */
++ SPA_DBUS_TYPE_SYSTEM, /**< The systemwide bus */
++ SPA_DBUS_TYPE_STARTER /**< The bus that started us, if any */
++};
++
++struct spa_dbus_connection {
++#define SPA_VERSION_DBUS_CONNECTION 0
++ uint32_t version;
++ /**
++ * Get the DBusConnection from a wrapper
++ *
++ * \param conn the spa_dbus_connection wrapper
++ * \return a pointer of type DBusConnection
++ */
++ void *(*get) (struct spa_dbus_connection *conn);
++ /**
++ * Destroy a dbus connection wrapper
++ *
++ * \param conn the wrapper to destroy
++ */
++ void (*destroy) (struct spa_dbus_connection *conn);
++};
++
++#define spa_dbus_connection_get(c) (c)->get((c))
++#define spa_dbus_connection_destroy(c) (c)->destroy((c))
++
++struct spa_dbus_methods {
++#define SPA_VERSION_DBUS_METHODS 0
++ uint32_t version;
++
++ /**
++ * Get a new connection wrapper for the given bus type.
++ *
++ * The connection wrapper is completely configured to operate
++ * in the main context of the handle that manages the spa_dbus
++ * interface.
++ *
++ * \param dbus the dbus manager
++ * \param type the bus type to wrap
++ * \param error location for the DBusError
++ * \return a new dbus connection wrapper or NULL on error
++ */
++ struct spa_dbus_connection * (*get_connection) (void *object,
++ enum spa_dbus_type type);
++};
++
++static inline struct spa_dbus_connection *
++spa_dbus_get_connection(struct spa_dbus *dbus, enum spa_dbus_type type)
++{
++ struct spa_dbus_connection *res = NULL;
++ spa_interface_call_res(&dbus->iface,
++ struct spa_dbus_methods, res,
++ get_connection, 0, type);
++ return res;
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_DBUS_H */
+diff --git a/third_party/pipewire/spa/support/log-impl.h b/third_party/pipewire/spa/support/log-impl.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/support/log-impl.h
+@@ -0,0 +1,86 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_LOG_IMPL_H
++#define SPA_LOG_IMPL_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdio.h>
++
++#include <spa/utils/type.h>
++#include <spa/support/log.h>
++
++static inline SPA_PRINTF_FUNC(6, 0) void spa_log_impl_logv(void *object,
++ enum spa_log_level level,
++ const char *file,
++ int line,
++ const char *func,
++ const char *fmt,
++ va_list args)
++{
++ char text[512], location[1024];
++ static const char *levels[] = { "-", "E", "W", "I", "D", "T" };
++
++ vsnprintf(text, sizeof(text), fmt, args);
++ snprintf(location, sizeof(location), "[%s][%s:%i %s()] %s\n",
++ levels[level], strrchr(file, '/') + 1, line, func, text);
++ fputs(location, stderr);
++}
++static inline SPA_PRINTF_FUNC(6,7) void spa_log_impl_log(void *object,
++ enum spa_log_level level,
++ const char *file,
++ int line,
++ const char *func,
++ const char *fmt, ...)
++{
++ va_list args;
++ va_start(args, fmt);
++ spa_log_impl_logv(object, level, file, line, func, fmt, args);
++ va_end(args);
++}
++
++#define SPA_LOG_IMPL_DEFINE(name) \
++struct { \
++ struct spa_log log; \
++ struct spa_log_methods methods; \
++} name
++
++#define SPA_LOG_IMPL_INIT(name) \
++ { { { SPA_TYPE_INTERFACE_Log, SPA_VERSION_LOG, \
++ SPA_CALLBACKS_INIT(&name.methods, &name) }, \
++ SPA_LOG_LEVEL_INFO, }, \
++ { SPA_VERSION_LOG_METHODS, \
++ spa_log_impl_log, \
++ spa_log_impl_logv,} }
++
++#define SPA_LOG_IMPL(name) \
++ SPA_LOG_IMPL_DEFINE(name) = SPA_LOG_IMPL_INIT(name)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++#endif /* SPA_LOG_IMPL_H */
+diff --git a/third_party/pipewire/spa/support/log.h b/third_party/pipewire/spa/support/log.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/support/log.h
+@@ -0,0 +1,179 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_LOG_H
++#define SPA_LOG_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdarg.h>
++
++#include <spa/utils/defs.h>
++#include <spa/utils/hook.h>
++
++enum spa_log_level {
++ SPA_LOG_LEVEL_NONE = 0,
++ SPA_LOG_LEVEL_ERROR,
++ SPA_LOG_LEVEL_WARN,
++ SPA_LOG_LEVEL_INFO,
++ SPA_LOG_LEVEL_DEBUG,
++ SPA_LOG_LEVEL_TRACE,
++};
++
++/**
++ * The Log interface
++ */
++#define SPA_TYPE_INTERFACE_Log SPA_TYPE_INFO_INTERFACE_BASE "Log"
++
++#define SPA_VERSION_LOG 0
++
++struct spa_log {
++ /** the version of this log. This can be used to expand this
++ * structure in the future */
++ struct spa_interface iface;
++ /**
++ * Logging level, everything above this level is not logged
++ */
++ enum spa_log_level level;
++};
++
++struct spa_log_methods {
++#define SPA_VERSION_LOG_METHODS 0
++ uint32_t version;
++ /**
++ * Log a message with the given log level.
++ *
++ * \param log a spa_log
++ * \param level a spa_log_level
++ * \param file the file name
++ * \param line the line number
++ * \param func the function name
++ * \param fmt printf style format
++ * \param ... format arguments
++ */
++ void (*log) (void *object,
++ enum spa_log_level level,
++ const char *file,
++ int line,
++ const char *func,
++ const char *fmt, ...) SPA_PRINTF_FUNC(6, 7);
++
++ /**
++ * Log a message with the given log level.
++ *
++ * \param log a spa_log
++ * \param level a spa_log_level
++ * \param file the file name
++ * \param line the line number
++ * \param func the function name
++ * \param fmt printf style format
++ * \param args format arguments
++ */
++ void (*logv) (void *object,
++ enum spa_log_level level,
++ const char *file,
++ int line,
++ const char *func,
++ const char *fmt,
++ va_list args) SPA_PRINTF_FUNC(6, 0);
++};
++
++#define spa_log_level_enabled(l,lev) ((l) && (l)->level >= (lev))
++
++#if defined(__USE_ISOC11) || defined(__USE_ISOC99) || \
++ (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
++
++#define spa_log_log(l,lev,...) \
++({ \
++ struct spa_log *_l = l; \
++ if (SPA_UNLIKELY(spa_log_level_enabled(_l, lev))) \
++ spa_interface_call(&_l->iface, \
++ struct spa_log_methods, log, 0, lev, \
++ __VA_ARGS__); \
++})
++
++#define spa_log_logv(l,lev,...) \
++({ \
++ struct spa_log *_l = l; \
++ if (SPA_UNLIKELY(spa_log_level_enabled(_l, lev))) \
++ spa_interface_call(&_l->iface, \
++ struct spa_log_methods, logv, 0, lev, \
++ __VA_ARGS__); \
++})
++
++#define spa_log_error(l,...) spa_log_log(l,SPA_LOG_LEVEL_ERROR,__FILE__,__LINE__,__func__,__VA_ARGS__)
++#define spa_log_warn(l,...) spa_log_log(l,SPA_LOG_LEVEL_WARN,__FILE__,__LINE__,__func__,__VA_ARGS__)
++#define spa_log_info(l,...) spa_log_log(l,SPA_LOG_LEVEL_INFO,__FILE__,__LINE__,__func__,__VA_ARGS__)
++#define spa_log_debug(l,...) spa_log_log(l,SPA_LOG_LEVEL_DEBUG,__FILE__,__LINE__,__func__,__VA_ARGS__)
++#define spa_log_trace(l,...) spa_log_log(l,SPA_LOG_LEVEL_TRACE,__FILE__,__LINE__,__func__,__VA_ARGS__)
++
++#ifndef FASTPATH
++#define spa_log_trace_fp(l,...) spa_log_log(l,SPA_LOG_LEVEL_TRACE,__FILE__,__LINE__,__func__,__VA_ARGS__)
++#else
++#define spa_log_trace_fp(l,...)
++#endif
++
++#else
++
++#define SPA_LOG_FUNC(name,lev) \
++static inline SPA_PRINTF_FUNC(2,3) void spa_log_##name (struct spa_log *l, const char *format, ...) \
++{ \
++ if (SPA_UNLIKELY(spa_log_level_enabled(l, lev))) { \
++ va_list varargs; \
++ va_start (varargs, format); \
++ spa_interface_call(&l->iface, \
++ struct spa_log_methods, logv, 0, lev, \
++ __FILE__,__LINE__,__func__,format,varargs); \
++ va_end (varargs); \
++ } \
++}
++
++SPA_LOG_FUNC(error, SPA_LOG_LEVEL_ERROR)
++SPA_LOG_FUNC(warn, SPA_LOG_LEVEL_WARN)
++SPA_LOG_FUNC(info, SPA_LOG_LEVEL_INFO)
++SPA_LOG_FUNC(debug, SPA_LOG_LEVEL_DEBUG)
++SPA_LOG_FUNC(trace, SPA_LOG_LEVEL_TRACE)
++
++#ifndef FASTPATH
++SPA_LOG_FUNC(trace_fp, SPA_LOG_LEVEL_TRACE)
++#else
++static inline void spa_log_trace_fp (struct spa_log *l, const char *format, ...) { }
++#endif
++
++#endif
++
++/** keys can be given when initializing the logger handle */
++#define SPA_KEY_LOG_LEVEL "log.level" /**< the default log level */
++#define SPA_KEY_LOG_COLORS "log.colors" /**< enable colors in the logger */
++#define SPA_KEY_LOG_FILE "log.file" /**< log to the specified file instead of
++ * stderr. */
++#define SPA_KEY_LOG_TIMESTAMP "log.timestamp" /**< log timestamps */
++#define SPA_KEY_LOG_LINE "log.line" /**< log file and line numbers */
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++#endif /* SPA_LOG_H */
+diff --git a/third_party/pipewire/spa/support/loop.h b/third_party/pipewire/spa/support/loop.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/support/loop.h
+@@ -0,0 +1,312 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_LOOP_H
++#define SPA_LOOP_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/hook.h>
++#include <spa/support/system.h>
++
++#define SPA_TYPE_INTERFACE_Loop SPA_TYPE_INFO_INTERFACE_BASE "Loop"
++#define SPA_TYPE_INTERFACE_DataLoop SPA_TYPE_INFO_INTERFACE_BASE "DataLoop"
++#define SPA_VERSION_LOOP 0
++struct spa_loop { struct spa_interface iface; };
++
++#define SPA_TYPE_INTERFACE_LoopControl SPA_TYPE_INFO_INTERFACE_BASE "LoopControl"
++#define SPA_VERSION_LOOP_CONTROL 0
++struct spa_loop_control { struct spa_interface iface; };
++
++#define SPA_TYPE_INTERFACE_LoopUtils SPA_TYPE_INFO_INTERFACE_BASE "LoopUtils"
++#define SPA_VERSION_LOOP_UTILS 0
++struct spa_loop_utils { struct spa_interface iface; };
++
++struct spa_source;
++
++typedef void (*spa_source_func_t) (struct spa_source *source);
++
++struct spa_source {
++ struct spa_loop *loop;
++ spa_source_func_t func;
++ void *data;
++ int fd;
++ uint32_t mask;
++ uint32_t rmask;
++};
++
++typedef int (*spa_invoke_func_t) (struct spa_loop *loop,
++ bool async,
++ uint32_t seq,
++ const void *data,
++ size_t size,
++ void *user_data);
++
++/**
++ * Register sources and work items to an event loop
++ */
++struct spa_loop_methods {
++ /* the version of this structure. This can be used to expand this
++ * structure in the future */
++#define SPA_VERSION_LOOP_METHODS 0
++ uint32_t version;
++
++ /** add a source to the loop */
++ int (*add_source) (void *object,
++ struct spa_source *source);
++
++ /** update the source io mask */
++ int (*update_source) (void *object,
++ struct spa_source *source);
++
++ /** remove a source from the loop */
++ int (*remove_source) (void *object,
++ struct spa_source *source);
++
++ /** invoke a function in the context of this loop */
++ int (*invoke) (void *object,
++ spa_invoke_func_t func,
++ uint32_t seq,
++ const void *data,
++ size_t size,
++ bool block,
++ void *user_data);
++};
++
++#define spa_loop_method(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ struct spa_loop *_o = o; \
++ spa_interface_call_res(&_o->iface, \
++ struct spa_loop_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#define spa_loop_add_source(l,...) spa_loop_method(l,add_source,0,##__VA_ARGS__)
++#define spa_loop_update_source(l,...) spa_loop_method(l,update_source,0,##__VA_ARGS__)
++#define spa_loop_remove_source(l,...) spa_loop_method(l,remove_source,0,##__VA_ARGS__)
++#define spa_loop_invoke(l,...) spa_loop_method(l,invoke,0,##__VA_ARGS__)
++
++
++/** Control hooks. These hooks can't be removed from their
++ * callbacks and must be removed from a safe place (when the loop
++ * is not running or when it is locked). */
++struct spa_loop_control_hooks {
++#define SPA_VERSION_LOOP_CONTROL_HOOKS 0
++ uint32_t version;
++ /** Executed right before waiting for events. It is typically used to
++ * release locks. */
++ void (*before) (void *data);
++ /** Executed right after waiting for events. It is typically used to
++ * reacquire locks. */
++ void (*after) (void *data);
++};
++
++#define spa_loop_control_hook_before(l) \
++({ \
++ struct spa_hook_list *_l = l; \
++ struct spa_hook *_h; \
++ spa_list_for_each_reverse(_h, &_l->list, link) \
++ spa_callbacks_call(&_h->cb, struct spa_loop_control_hooks, before, 0); \
++})
++
++#define spa_loop_control_hook_after(l) \
++({ \
++ struct spa_hook_list *_l = l; \
++ struct spa_hook *_h; \
++ spa_list_for_each(_h, &_l->list, link) \
++ spa_callbacks_call(&_h->cb, struct spa_loop_control_hooks, after, 0); \
++})
++
++/**
++ * Control an event loop
++ */
++struct spa_loop_control_methods {
++ /* the version of this structure. This can be used to expand this
++ * structure in the future */
++#define SPA_VERSION_LOOP_CONTROL_METHODS 0
++ uint32_t version;
++
++ int (*get_fd) (void *object);
++
++ /** Add a hook
++ * \param ctrl the control to change
++ * \param hooks the hooks to add
++ *
++ * Adds hooks to the loop controlled by \a ctrl.
++ */
++ void (*add_hook) (void *object,
++ struct spa_hook *hook,
++ const struct spa_loop_control_hooks *hooks,
++ void *data);
++
++ /** Enter a loop
++ * \param ctrl the control
++ *
++ * Start an iteration of the loop. This function should be called
++ * before calling iterate and is typically used to capture the thread
++ * that this loop will run in.
++ */
++ void (*enter) (void *object);
++ /** Leave a loop
++ * \param ctrl the control
++ *
++ * Ends the iteration of a loop. This should be called after calling
++ * iterate.
++ */
++ void (*leave) (void *object);
++
++ /** Perform one iteration of the loop.
++ * \param ctrl the control
++ * \param timeout an optional timeout in milliseconds.
++ * 0 for no timeout, -1 for infinite timeout.
++ *
++ * This function will block
++ * up to \a timeout milliseconds and then dispatch the fds with activity.
++ * The number of dispatched fds is returned.
++ */
++ int (*iterate) (void *object, int timeout);
++};
++
++#define spa_loop_control_method_v(o,method,version,...) \
++({ \
++ struct spa_loop_control *_o = o; \
++ spa_interface_call(&_o->iface, \
++ struct spa_loop_control_methods, \
++ method, version, ##__VA_ARGS__); \
++})
++
++#define spa_loop_control_method_r(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ struct spa_loop_control *_o = o; \
++ spa_interface_call_res(&_o->iface, \
++ struct spa_loop_control_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++#define spa_loop_control_get_fd(l) spa_loop_control_method_r(l,get_fd,0)
++#define spa_loop_control_add_hook(l,...) spa_loop_control_method_v(l,add_hook,0,__VA_ARGS__)
++#define spa_loop_control_enter(l) spa_loop_control_method_v(l,enter,0)
++#define spa_loop_control_leave(l) spa_loop_control_method_v(l,leave,0)
++#define spa_loop_control_iterate(l,...) spa_loop_control_method_r(l,iterate,0,__VA_ARGS__)
++
++typedef void (*spa_source_io_func_t) (void *data, int fd, uint32_t mask);
++typedef void (*spa_source_idle_func_t) (void *data);
++typedef void (*spa_source_event_func_t) (void *data, uint64_t count);
++typedef void (*spa_source_timer_func_t) (void *data, uint64_t expirations);
++typedef void (*spa_source_signal_func_t) (void *data, int signal_number);
++
++/**
++ * Create sources for an event loop
++ */
++struct spa_loop_utils_methods {
++ /* the version of this structure. This can be used to expand this
++ * structure in the future */
++#define SPA_VERSION_LOOP_UTILS_METHODS 0
++ uint32_t version;
++
++ struct spa_source *(*add_io) (void *object,
++ int fd,
++ uint32_t mask,
++ bool close,
++ spa_source_io_func_t func, void *data);
++
++ int (*update_io) (void *object, struct spa_source *source, uint32_t mask);
++
++ struct spa_source *(*add_idle) (void *object,
++ bool enabled,
++ spa_source_idle_func_t func, void *data);
++ int (*enable_idle) (void *object, struct spa_source *source, bool enabled);
++
++ struct spa_source *(*add_event) (void *object,
++ spa_source_event_func_t func, void *data);
++ int (*signal_event) (void *object, struct spa_source *source);
++
++ struct spa_source *(*add_timer) (void *object,
++ spa_source_timer_func_t func, void *data);
++ int (*update_timer) (void *object,
++ struct spa_source *source,
++ struct timespec *value,
++ struct timespec *interval,
++ bool absolute);
++ struct spa_source *(*add_signal) (void *object,
++ int signal_number,
++ spa_source_signal_func_t func, void *data);
++
++ /** destroy a source allocated with this interface. This function
++ * should only be called when the loop is not running or from the
++ * context of the running loop */
++ void (*destroy_source) (void *object, struct spa_source *source);
++};
++
++#define spa_loop_utils_method_v(o,method,version,...) \
++({ \
++ struct spa_loop_utils *_o = o; \
++ spa_interface_call(&_o->iface, \
++ struct spa_loop_utils_methods, \
++ method, version, ##__VA_ARGS__); \
++})
++
++#define spa_loop_utils_method_r(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ struct spa_loop_utils *_o = o; \
++ spa_interface_call_res(&_o->iface, \
++ struct spa_loop_utils_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++#define spa_loop_utils_method_s(o,method,version,...) \
++({ \
++ struct spa_source *_res = NULL; \
++ struct spa_loop_utils *_o = o; \
++ spa_interface_call_res(&_o->iface, \
++ struct spa_loop_utils_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++
++#define spa_loop_utils_add_io(l,...) spa_loop_utils_method_s(l,add_io,0,__VA_ARGS__)
++#define spa_loop_utils_update_io(l,...) spa_loop_utils_method_r(l,update_io,0,__VA_ARGS__)
++#define spa_loop_utils_add_idle(l,...) spa_loop_utils_method_s(l,add_idle,0,__VA_ARGS__)
++#define spa_loop_utils_enable_idle(l,...) spa_loop_utils_method_r(l,enable_idle,0,__VA_ARGS__)
++#define spa_loop_utils_add_event(l,...) spa_loop_utils_method_s(l,add_event,0,__VA_ARGS__)
++#define spa_loop_utils_signal_event(l,...) spa_loop_utils_method_r(l,signal_event,0,__VA_ARGS__)
++#define spa_loop_utils_add_timer(l,...) spa_loop_utils_method_s(l,add_timer,0,__VA_ARGS__)
++#define spa_loop_utils_update_timer(l,...) spa_loop_utils_method_r(l,update_timer,0,__VA_ARGS__)
++#define spa_loop_utils_add_signal(l,...) spa_loop_utils_method_s(l,add_signal,0,__VA_ARGS__)
++#define spa_loop_utils_destroy_source(l,...) spa_loop_utils_method_v(l,destroy_source,0,__VA_ARGS__)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_LOOP_H */
+diff --git a/third_party/pipewire/spa/support/plugin.h b/third_party/pipewire/spa/support/plugin.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/support/plugin.h
+@@ -0,0 +1,215 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_PLUGIN_H
++#define SPA_PLUGIN_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/dict.h>
++
++struct spa_handle {
++ /** Version of this struct */
++#define SPA_VERSION_HANDLE 0
++ uint32_t version;
++
++ /**
++ * Get the interface provided by \a handle with \a type.
++ *
++ * \a interface is always a struct spa_interface but depending on
++ * \a type, the struct might contain other information.
++ *
++ * \param handle a spa_handle
++ * \param type the interface type
++ * \param interface result to hold the interface.
++ * \return 0 on success
++ * -ENOTSUP when there are no interfaces
++ * -EINVAL when handle or info is NULL
++ */
++ int (*get_interface) (struct spa_handle *handle, const char *type, void **interface);
++ /**
++ * Clean up the memory of \a handle. After this, \a handle should not be used
++ * anymore.
++ *
++ * \param handle a pointer to memory
++ * \return 0 on success
++ */
++ int (*clear) (struct spa_handle *handle);
++};
++
++#define spa_handle_get_interface(h,...) (h)->get_interface((h),__VA_ARGS__)
++#define spa_handle_clear(h) (h)->clear((h))
++
++/**
++ * This structure lists the information about available interfaces on
++ * handles.
++ */
++struct spa_interface_info {
++ const char *type; /*< the type of the interface, can be
++ * used to get the interface */
++};
++
++/**
++ * Extra supporting infrastructure passed to the init() function of
++ * a factory. It can be extra information or interfaces such as logging.
++ */
++struct spa_support {
++ const char *type; /*< the type of the support item */
++ void *data; /*< specific data for the item */
++};
++
++/** Find a support item of the given type */
++static inline void *spa_support_find(const struct spa_support *support,
++ uint32_t n_support,
++ const char *type)
++{
++ uint32_t i;
++ for (i = 0; i < n_support; i++) {
++ if (strcmp(support[i].type, type) == 0)
++ return support[i].data;
++ }
++ return NULL;
++}
++
++#define SPA_SUPPORT_INIT(type,data) (struct spa_support) { (type), (data) }
++
++struct spa_handle_factory {
++ /** The version of this structure */
++#define SPA_VERSION_HANDLE_FACTORY 1
++ uint32_t version;
++ /**
++ * The name of the factory contains a logical name that describes
++ * the function of the handle. Other plugins might contain an alternative
++ * implementation with the same name.
++ *
++ * See utils/names.h for the list of standard names.
++ *
++ * Examples include:
++ *
++ * api.alsa.pcm.sink: an object to write PCM samples to an alsa PLAYBACK
++ * device
++ * api.v4l2.source: an object to read from a v4l2 source.
++ */
++ const char *name;
++ /**
++ * Extra information about the handles of this factory.
++ */
++ const struct spa_dict *info;
++ /**
++ * Get the size of handles from this factory.
++ *
++ * \param factory a spa_handle_factory
++ * \param params extra parameters that determine the size of the
++ * handle.
++ */
++ size_t (*get_size) (const struct spa_handle_factory *factory,
++ const struct spa_dict *params);
++
++ /**
++ * Initialize an instance of this factory. The caller should allocate
++ * memory at least size bytes and pass this as \a handle.
++ *
++ * \a support can optionally contain extra interfaces or data items that the
++ * plugin can use such as a logger.
++ *
++ * \param factory a spa_handle_factory
++ * \param handle a pointer to memory
++ * \param info extra handle specific information, usually obtained
++ * from a spa_device. This can be used to configure the handle.
++ * \param support support items
++ * \param n_support number of elements in \a support
++ * \return 0 on success
++ * < 0 errno type error
++ */
++ int (*init) (const struct spa_handle_factory *factory,
++ struct spa_handle *handle,
++ const struct spa_dict *info,
++ const struct spa_support *support,
++ uint32_t n_support);
++
++ /**
++ * spa_handle_factory::enum_interface_info:
++ * \param factory: a #spa_handle_factory
++ * \param info: result to hold spa_interface_info.
++ * \param index: index to keep track of the enumeration, 0 for first item
++ *
++ * Enumerate the interface information for \a factory.
++ *
++ * \return 1 when an item is available
++ * 0 when no more items are available
++ * < 0 errno type error
++ */
++ int (*enum_interface_info) (const struct spa_handle_factory *factory,
++ const struct spa_interface_info **info,
++ uint32_t *index);
++};
++
++#define spa_handle_factory_get_size(h,...) (h)->get_size((h),__VA_ARGS__)
++#define spa_handle_factory_init(h,...) (h)->init((h),__VA_ARGS__)
++#define spa_handle_factory_enum_interface_info(h,...) (h)->enum_interface_info((h),__VA_ARGS__)
++
++/**
++ * The function signature of the entry point in a plugin.
++ *
++ * \param factory a location to hold the factory result
++ * \param index index to keep track of the enumeration
++ * \return 1 on success
++ * 0 when there are no more factories
++ * -EINVAL when factory is NULL
++ */
++typedef int (*spa_handle_factory_enum_func_t) (const struct spa_handle_factory **factory,
++ uint32_t *index);
++
++#define SPA_HANDLE_FACTORY_ENUM_FUNC_NAME "spa_handle_factory_enum"
++
++/**
++ * The entry point in a plugin.
++ *
++ * \param factory a location to hold the factory result
++ * \param index index to keep track of the enumeration
++ * \return 1 on success
++ * 0 when no more items are available
++ * < 0 errno type error
++ */
++int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index);
++
++
++
++#define SPA_KEY_FACTORY_NAME "factory.name" /**< the name of a factory */
++#define SPA_KEY_FACTORY_AUTHOR "factory.author" /**< a comma separated list of factory authors */
++#define SPA_KEY_FACTORY_DESCRIPTION "factory.description" /**< description of a factory */
++#define SPA_KEY_FACTORY_USAGE "factory.usage" /**< usage of a factory */
++
++#define SPA_KEY_LIBRARY_NAME "library.name" /**< the name of a library. This is usually
++ * the filename of the plugin without the
++ * path or the plugin extension. */
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_PLUGIN_H */
+diff --git a/third_party/pipewire/spa/support/system.h b/third_party/pipewire/spa/support/system.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/support/system.h
+@@ -0,0 +1,152 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2019 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_SYSTEM_H
++#define SPA_SYSTEM_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++struct itimerspec;
++
++#include <time.h>
++#include <sys/types.h>
++
++#include <spa/utils/defs.h>
++#include <spa/utils/hook.h>
++
++/**
++ * a collection of core system functions
++ */
++#define SPA_TYPE_INTERFACE_System SPA_TYPE_INFO_INTERFACE_BASE "System"
++#define SPA_TYPE_INTERFACE_DataSystem SPA_TYPE_INFO_INTERFACE_BASE "DataSystem"
++
++#define SPA_VERSION_SYSTEM 0
++struct spa_system { struct spa_interface iface; };
++
++/* IO events */
++#define SPA_IO_IN (1 << 0)
++#define SPA_IO_OUT (1 << 2)
++#define SPA_IO_ERR (1 << 3)
++#define SPA_IO_HUP (1 << 4)
++
++/* flags */
++#define SPA_FD_CLOEXEC (1<<0)
++#define SPA_FD_NONBLOCK (1<<1)
++#define SPA_FD_EVENT_SEMAPHORE (1<<2)
++#define SPA_FD_TIMER_ABSTIME (1<<3)
++#define SPA_FD_TIMER_CANCEL_ON_SET (1<<4)
++
++struct spa_poll_event {
++ uint32_t events;
++ void *data;
++};
++
++struct spa_system_methods {
++#define SPA_VERSION_SYSTEM_METHODS 0
++ uint32_t version;
++
++ /* read/write/ioctl */
++ ssize_t (*read) (void *object, int fd, void *buf, size_t count);
++ ssize_t (*write) (void *object, int fd, const void *buf, size_t count);
++ int (*ioctl) (void *object, int fd, unsigned long request, ...);
++ int (*close) (void *object, int fd);
++
++ /* clock */
++ int (*clock_gettime) (void *object,
++ int clockid, struct timespec *value);
++ int (*clock_getres) (void *object,
++ int clockid, struct timespec *res);
++
++ /* poll */
++ int (*pollfd_create) (void *object, int flags);
++ int (*pollfd_add) (void *object, int pfd, int fd, uint32_t events, void *data);
++ int (*pollfd_mod) (void *object, int pfd, int fd, uint32_t events, void *data);
++ int (*pollfd_del) (void *object, int pfd, int fd);
++ int (*pollfd_wait) (void *object, int pfd,
++ struct spa_poll_event *ev, int n_ev, int timeout);
++
++ /* timers */
++ int (*timerfd_create) (void *object, int clockid, int flags);
++ int (*timerfd_settime) (void *object,
++ int fd, int flags,
++ const struct itimerspec *new_value,
++ struct itimerspec *old_value);
++ int (*timerfd_gettime) (void *object,
++ int fd, struct itimerspec *curr_value);
++ int (*timerfd_read) (void *object, int fd, uint64_t *expirations);
++
++ /* events */
++ int (*eventfd_create) (void *object, int flags);
++ int (*eventfd_write) (void *object, int fd, uint64_t count);
++ int (*eventfd_read) (void *object, int fd, uint64_t *count);
++
++ /* signals */
++ int (*signalfd_create) (void *object, int signal, int flags);
++ int (*signalfd_read) (void *object, int fd, int *signal);
++};
++
++#define spa_system_method_r(o,method,version,...) \
++({ \
++ int _res = -ENOTSUP; \
++ struct spa_system *_o = o; \
++ spa_interface_call_res(&_o->iface, \
++ struct spa_system_methods, _res, \
++ method, version, ##__VA_ARGS__); \
++ _res; \
++})
++
++
++#define spa_system_read(s,...) spa_system_method_r(s,read,0,__VA_ARGS__)
++#define spa_system_write(s,...) spa_system_method_r(s,write,0,__VA_ARGS__)
++#define spa_system_ioctl(s,...) spa_system_method_r(s,ioctl,0,__VA_ARGS__)
++#define spa_system_close(s,...) spa_system_method_r(s,close,0,__VA_ARGS__)
++
++#define spa_system_clock_gettime(s,...) spa_system_method_r(s,clock_gettime,0,__VA_ARGS__)
++#define spa_system_clock_getres(s,...) spa_system_method_r(s,clock_getres,0,__VA_ARGS__)
++
++#define spa_system_pollfd_create(s,...) spa_system_method_r(s,pollfd_create,0,__VA_ARGS__)
++#define spa_system_pollfd_add(s,...) spa_system_method_r(s,pollfd_add,0,__VA_ARGS__)
++#define spa_system_pollfd_mod(s,...) spa_system_method_r(s,pollfd_mod,0,__VA_ARGS__)
++#define spa_system_pollfd_del(s,...) spa_system_method_r(s,pollfd_del,0,__VA_ARGS__)
++#define spa_system_pollfd_wait(s,...) spa_system_method_r(s,pollfd_wait,0,__VA_ARGS__)
++
++#define spa_system_timerfd_create(s,...) spa_system_method_r(s,timerfd_create,0,__VA_ARGS__)
++#define spa_system_timerfd_settime(s,...) spa_system_method_r(s,timerfd_settime,0,__VA_ARGS__)
++#define spa_system_timerfd_gettime(s,...) spa_system_method_r(s,timerfd_gettime,0,__VA_ARGS__)
++#define spa_system_timerfd_read(s,...) spa_system_method_r(s,timerfd_read,0,__VA_ARGS__)
++
++#define spa_system_eventfd_create(s,...) spa_system_method_r(s,eventfd_create,0,__VA_ARGS__)
++#define spa_system_eventfd_write(s,...) spa_system_method_r(s,eventfd_write,0,__VA_ARGS__)
++#define spa_system_eventfd_read(s,...) spa_system_method_r(s,eventfd_read,0,__VA_ARGS__)
++
++#define spa_system_signalfd_create(s,...) spa_system_method_r(s,signalfd_create,0,__VA_ARGS__)
++#define spa_system_signalfd_read(s,...) spa_system_method_r(s,signalfd_read,0,__VA_ARGS__)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_SYSTEM_H */
+diff --git a/third_party/pipewire/spa/utils/defs.h b/third_party/pipewire/spa/utils/defs.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/utils/defs.h
+@@ -0,0 +1,265 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_UTILS_DEFS_H
++#define SPA_UTILS_DEFS_H
++
++#ifdef __cplusplus
++extern "C" {
++#else
++#include <stdbool.h>
++#endif
++#include <inttypes.h>
++#include <stdlib.h>
++#include <string.h>
++#include <stddef.h>
++#include <stdio.h>
++
++#define SPA_FLAG_MASK(field,mask,flag) (((field) & (mask)) == (flag))
++#define SPA_FLAG_IS_SET(field,flag) SPA_FLAG_MASK(field,flag,flag)
++#define SPA_FLAG_SET(field,flag) ((field) |= (flag))
++#define SPA_FLAG_CLEAR(field,flag) ((field) &= ~(flag))
++#define SPA_FLAG_UPDATE(field,flag,val) ((val) ? SPA_FLAG_SET(field,flag) : SPA_FLAG_CLEAR(field,flag))
++
++enum spa_direction {
++ SPA_DIRECTION_INPUT = 0,
++ SPA_DIRECTION_OUTPUT = 1,
++};
++
++#define SPA_DIRECTION_REVERSE(d) ((d) ^ 1)
++
++#define SPA_RECTANGLE(width,height) (struct spa_rectangle){ width, height }
++struct spa_rectangle {
++ uint32_t width;
++ uint32_t height;
++};
++
++#define SPA_POINT(x,y) (struct spa_point){ x, y }
++struct spa_point {
++ int32_t x;
++ int32_t y;
++};
++
++#define SPA_REGION(x,y,width,height) (struct spa_region){ SPA_POINT(x,y), SPA_RECTANGLE(width,height) }
++struct spa_region {
++ struct spa_point position;
++ struct spa_rectangle size;
++};
++
++#define SPA_FRACTION(num,denom) (struct spa_fraction){ num, denom }
++struct spa_fraction {
++ uint32_t num;
++ uint32_t denom;
++};
++
++#define SPA_N_ELEMENTS(arr) (sizeof(arr) / sizeof((arr)[0]))
++
++#define SPA_MIN(a,b) \
++({ \
++ __typeof__(a) _a = (a); \
++ __typeof__(b) _b = (b); \
++ SPA_LIKELY(_a < _b) ? _a : _b; \
++})
++#define SPA_MAX(a,b) \
++({ \
++ __typeof__(a) _a = (a); \
++ __typeof__(b) _b = (b); \
++ SPA_LIKELY(_a > _b) ? _a : _b; \
++})
++#define SPA_CLAMP(v,low,high) \
++({ \
++ __typeof__(v) _v = (v); \
++ __typeof__(low) _low = (low); \
++ __typeof__(high) _high = (high); \
++ SPA_MIN(SPA_MAX(_v, _low), _high); \
++})
++
++#define SPA_SWAP(a,b) \
++({ \
++ __typeof__(a) _t = (a); \
++ a = b; b = _t; \
++})
++
++#define SPA_TYPECHECK(type,x) \
++({ type _dummy; \
++ typeof(x) _dummy2; \
++ (void)(&_dummy == &_dummy2); \
++ x; \
++})
++
++#define SPA_MEMBER(b,o,t) ((t*)((uint8_t*)(b) + (int)(o)))
++#define SPA_MEMBER_ALIGN(b,o,a,t) SPA_PTR_ALIGN(SPA_MEMBER(b,o,t),a,t)
++
++#define SPA_CONTAINER_OF(p,t,m) (t*)((uint8_t*)p - offsetof (t,m))
++
++#define SPA_PTRDIFF(p1,p2) ((uint8_t*)(p1) - (uint8_t*)(p2))
++
++#define SPA_PTR_TO_INT(p) ((int) ((intptr_t) (p)))
++#define SPA_INT_TO_PTR(u) ((void*) ((intptr_t) (u)))
++
++#define SPA_PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
++#define SPA_UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u)))
++
++#define SPA_TIME_INVALID ((int64_t)INT64_MIN)
++#define SPA_IDX_INVALID ((unsigned int)-1)
++#define SPA_ID_INVALID ((uint32_t)0xffffffff)
++
++#define SPA_NSEC_PER_SEC (1000000000ll)
++#define SPA_NSEC_PER_MSEC (1000000ll)
++#define SPA_NSEC_PER_USEC (1000ll)
++#define SPA_USEC_PER_SEC (1000000ll)
++#define SPA_USEC_PER_MSEC (1000ll)
++#define SPA_MSEC_PER_SEC (1000ll)
++
++#define SPA_TIMESPEC_TO_NSEC(ts) ((ts)->tv_sec * SPA_NSEC_PER_SEC + (ts)->tv_nsec)
++#define SPA_TIMESPEC_TO_USEC(ts) ((ts)->tv_sec * SPA_USEC_PER_SEC + (ts)->tv_nsec / SPA_NSEC_PER_USEC)
++#define SPA_TIMEVAL_TO_NSEC(tv) ((tv)->tv_sec * SPA_NSEC_PER_SEC + (tv)->tv_usec * SPA_NSEC_PER_USEC)
++#define SPA_TIMEVAL_TO_USEC(tv) ((tv)->tv_sec * SPA_USEC_PER_SEC + (tv)->tv_usec)
++
++#ifdef __GNUC__
++#define SPA_PRINTF_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1)))
++#define SPA_ALIGNED(align) __attribute__((aligned(align)))
++#define SPA_DEPRECATED __attribute__ ((deprecated))
++#define SPA_EXPORT __attribute__((visibility("default")))
++#define SPA_SENTINEL __attribute__((__sentinel__))
++#define SPA_UNUSED __attribute__ ((unused))
++#else
++#define SPA_PRINTF_FUNC(fmt, arg1)
++#define SPA_ALIGNED(align)
++#define SPA_DEPRECATED
++#define SPA_EXPORT
++#define SPA_SENTINEL
++#define SPA_UNUSED
++#endif
++
++#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
++#define SPA_RESTRICT restrict
++#elif defined(__GNUC__) && __GNUC__ >= 4
++#define SPA_RESTRICT __restrict__
++#else
++#define SPA_RESTRICT
++#endif
++
++#define SPA_ROUND_DOWN_N(num,align) ((num) & ~((align) - 1))
++#define SPA_ROUND_UP_N(num,align) SPA_ROUND_DOWN_N((num) + ((align) - 1),align)
++
++#define SPA_PTR_ALIGNMENT(p,align) ((intptr_t)(p) & ((align)-1))
++#define SPA_IS_ALIGNED(p,align) (SPA_PTR_ALIGNMENT(p,align) == 0)
++#define SPA_PTR_ALIGN(p,align,type) (type*)SPA_ROUND_UP_N((intptr_t)(p), (intptr_t)(align))
++
++#ifndef SPA_LIKELY
++#ifdef __GNUC__
++#define SPA_LIKELY(x) (__builtin_expect(!!(x),1))
++#define SPA_UNLIKELY(x) (__builtin_expect(!!(x),0))
++#else
++#define SPA_LIKELY(x) (x)
++#define SPA_UNLIKELY(x) (x)
++#endif
++#endif
++
++#define SPA_STRINGIFY_1(...) #__VA_ARGS__
++#define SPA_STRINGIFY(...) SPA_STRINGIFY_1(__VA_ARGS__)
++
++#define spa_return_if_fail(expr) \
++ do { \
++ if (SPA_UNLIKELY(!(expr))) { \
++ fprintf(stderr, "'%s' failed at %s:%u %s()\n", \
++ #expr , __FILE__, __LINE__, __func__); \
++ return; \
++ } \
++ } while(false)
++
++#define spa_return_val_if_fail(expr, val) \
++ do { \
++ if (SPA_UNLIKELY(!(expr))) { \
++ fprintf(stderr, "'%s' failed at %s:%u %s()\n", \
++ #expr , __FILE__, __LINE__, __func__); \
++ return (val); \
++ } \
++ } while(false)
++
++/* spa_assert_se() is an assert which guarantees side effects of x,
++ * i.e. is never optimized away, regardless of NDEBUG or FASTPATH. */
++#define spa_assert_se(expr) \
++ do { \
++ if (SPA_UNLIKELY(!(expr))) { \
++ fprintf(stderr, "'%s' failed at %s:%u %s()\n", \
++ #expr , __FILE__, __LINE__, __func__); \
++ abort(); \
++ } \
++ } while (false)
++
++#define spa_assert(expr) \
++ do { \
++ if (SPA_UNLIKELY(!(expr))) { \
++ fprintf(stderr, "'%s' failed at %s:%u %s()\n", \
++ #expr , __FILE__, __LINE__, __func__); \
++ abort(); \
++ } \
++ } while (false)
++
++#define spa_assert_not_reached() \
++ do { \
++ fprintf(stderr, "Code should not be reached at %s:%u %s()\n", \
++ __FILE__, __LINE__, __func__); \
++ abort(); \
++ } while (false)
++
++/* Does exactly nothing */
++#define spa_nop() do {} while (false)
++
++#define spa_memzero(x,l) (memset((x), 0, (l)))
++#define spa_zero(x) (spa_memzero(&(x), sizeof(x)))
++
++#ifdef SPA_DEBUG_MEMCPY
++#define spa_memcpy(d,s,n) \
++({ \
++ fprintf(stderr, "%s:%u %s() memcpy(%p, %p, %zd)\n", \
++ __FILE__, __LINE__, __func__, (d), (s), (size_t)(n)); \
++ memcpy(d,s,n); \
++})
++#define spa_memmove(d,s,n) \
++({ \
++ fprintf(stderr, "%s:%u %s() memmove(%p, %p, %zd)\n", \
++ __FILE__, __LINE__, __func__, (d), (s), (size_t)(n)); \
++ memmove(d,s,n); \
++})
++#else
++#define spa_memcpy(d,s,n) memcpy(d,s,n)
++#define spa_memmove(d,s,n) memmove(d,s,n)
++#endif
++
++#define spa_aprintf(_fmt, ...) \
++({ \
++ char *_strp; \
++ if (asprintf(&(_strp), (_fmt), ## __VA_ARGS__ ) == -1) \
++ _strp = NULL; \
++ _strp; \
++})
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_UTILS_DEFS_H */
+diff --git a/third_party/pipewire/spa/utils/dict.h b/third_party/pipewire/spa/utils/dict.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/utils/dict.h
+@@ -0,0 +1,104 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_DICT_H
++#define SPA_DICT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <string.h>
++
++#include <spa/utils/defs.h>
++
++struct spa_dict_item {
++ const char *key;
++ const char *value;
++};
++
++#define SPA_DICT_ITEM_INIT(key,value) (struct spa_dict_item) { key, value }
++
++struct spa_dict {
++#define SPA_DICT_FLAG_SORTED (1<<0) /**< items are sorted */
++ uint32_t flags;
++ uint32_t n_items;
++ const struct spa_dict_item *items;
++};
++
++#define SPA_DICT_INIT(items,n_items) (struct spa_dict) { 0, n_items, items }
++#define SPA_DICT_INIT_ARRAY(items) (struct spa_dict) { 0, SPA_N_ELEMENTS(items), items }
++
++#define spa_dict_for_each(item, dict) \
++ for ((item) = (dict)->items; \
++ (item) < &(dict)->items[(dict)->n_items]; \
++ (item)++)
++
++static inline int spa_dict_item_compare(const void *i1, const void *i2)
++{
++ const struct spa_dict_item *it1 = (const struct spa_dict_item *)i1,
++ *it2 = (const struct spa_dict_item *)i2;
++ return strcmp(it1->key, it2->key);
++}
++
++static inline void spa_dict_qsort(struct spa_dict *dict)
++{
++ qsort((void*)dict->items, dict->n_items, sizeof(struct spa_dict_item),
++ spa_dict_item_compare);
++ SPA_FLAG_SET(dict->flags, SPA_DICT_FLAG_SORTED);
++}
++
++static inline const struct spa_dict_item *spa_dict_lookup_item(const struct spa_dict *dict,
++ const char *key)
++{
++ const struct spa_dict_item *item;
++
++ if (SPA_FLAG_IS_SET(dict->flags, SPA_DICT_FLAG_SORTED)) {
++ struct spa_dict_item k = SPA_DICT_ITEM_INIT(key, NULL);
++ item = (const struct spa_dict_item *)bsearch(&k,
++ (const void *) dict->items, dict->n_items,
++ sizeof(struct spa_dict_item),
++ spa_dict_item_compare);
++ if (item != NULL)
++ return item;
++ } else {
++ spa_dict_for_each(item, dict) {
++ if (!strcmp(item->key, key))
++ return item;
++ }
++ }
++ return NULL;
++}
++
++static inline const char *spa_dict_lookup(const struct spa_dict *dict, const char *key)
++{
++ const struct spa_dict_item *item = spa_dict_lookup_item(dict, key);
++ return item ? item->value : NULL;
++}
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_DICT_H */
+diff --git a/third_party/pipewire/spa/utils/hook.h b/third_party/pipewire/spa/utils/hook.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/utils/hook.h
+@@ -0,0 +1,198 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_HOOK_H
++#define SPA_HOOK_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/list.h>
++
++/** \class spa_hook
++ *
++ * \brief a list of hooks
++ *
++ * The hook list provides a way to keep track of hooks.
++ */
++/** A list of hooks */
++struct spa_hook_list {
++ struct spa_list list;
++};
++
++/** Callbacks, contains the structure with functions and the data passed
++ * to the functions. The structure should also contain a version field that
++ * is checked. */
++struct spa_callbacks {
++ const void *funcs;
++ void *data;
++};
++
++/** Check if a callback \c has method \m of version \v */
++#define SPA_CALLBACK_CHECK(c,m,v) ((c) && ((v) == 0 || (c)->version > (v)-1) && (c)->m)
++
++#define SPA_CALLBACKS_INIT(_funcs,_data) (struct spa_callbacks){ _funcs, _data, }
++
++struct spa_interface {
++ const char *type;
++ uint32_t version;
++ struct spa_callbacks cb;
++};
++
++#define SPA_INTERFACE_INIT(_type,_version,_funcs,_data) \
++ (struct spa_interface){ _type, _version, SPA_CALLBACKS_INIT(_funcs,_data), }
++
++/** A hook, contains the structure with functions and the data passed
++ * to the functions. */
++struct spa_hook {
++ struct spa_list link;
++ struct spa_callbacks cb;
++ /** callback and data for the hook list */
++ void (*removed) (struct spa_hook *hook);
++ void *priv;
++};
++
++/** Initialize a hook list */
++static inline void spa_hook_list_init(struct spa_hook_list *list)
++{
++ spa_list_init(&list->list);
++}
++
++static inline bool spa_hook_list_is_empty(struct spa_hook_list *list)
++{
++ return spa_list_is_empty(&list->list);
++}
++
++/** Append a hook \memberof spa_hook */
++static inline void spa_hook_list_append(struct spa_hook_list *list,
++ struct spa_hook *hook,
++ const void *funcs, void *data)
++{
++ hook->cb = SPA_CALLBACKS_INIT(funcs, data);
++ spa_list_append(&list->list, &hook->link);
++}
++
++/** Prepend a hook \memberof spa_hook */
++static inline void spa_hook_list_prepend(struct spa_hook_list *list,
++ struct spa_hook *hook,
++ const void *funcs, void *data)
++{
++ hook->cb = SPA_CALLBACKS_INIT(funcs, data);
++ spa_list_prepend(&list->list, &hook->link);
++}
++
++/** Remove a hook \memberof spa_hook */
++static inline void spa_hook_remove(struct spa_hook *hook)
++{
++ spa_list_remove(&hook->link);
++ if (hook->removed)
++ hook->removed(hook);
++}
++
++static inline void
++spa_hook_list_isolate(struct spa_hook_list *list,
++ struct spa_hook_list *save,
++ struct spa_hook *hook,
++ const void *funcs, void *data)
++{
++ /* init save list and move hooks to it */
++ spa_hook_list_init(save);
++ spa_list_insert_list(&save->list, &list->list);
++ /* init hooks and add single hook */
++ spa_hook_list_init(list);
++ spa_hook_list_append(list, hook, funcs, data);
++}
++
++static inline void
++spa_hook_list_join(struct spa_hook_list *list,
++ struct spa_hook_list *save)
++{
++ spa_list_insert_list(&list->list, &save->list);
++}
++
++#define spa_callbacks_call(callbacks,type,method,vers,...) \
++({ \
++ const type *_f = (const type *) (callbacks)->funcs; \
++ if (SPA_LIKELY(SPA_CALLBACK_CHECK(_f,method,vers))) \
++ _f->method((callbacks)->data, ## __VA_ARGS__); \
++})
++
++#define spa_callbacks_call_res(callbacks,type,res,method,vers,...) \
++({ \
++ const type *_f = (const type *) (callbacks)->funcs; \
++ if (SPA_LIKELY(SPA_CALLBACK_CHECK(_f,method,vers))) \
++ res = _f->method((callbacks)->data, ## __VA_ARGS__); \
++ res; \
++})
++
++#define spa_interface_call(iface,type,method,vers,...) \
++ spa_callbacks_call(&(iface)->cb,type,method,vers,##__VA_ARGS__)
++
++#define spa_interface_call_res(iface,type,res,method,vers,...) \
++ spa_callbacks_call_res(&(iface)->cb,type,res,method,vers,##__VA_ARGS__)
++
++#define spa_hook_list_call_simple(l,type,method,vers,...) \
++({ \
++ struct spa_hook_list *_l = l; \
++ struct spa_hook *_h, *_t; \
++ spa_list_for_each_safe(_h, _t, &_l->list, link) \
++ spa_callbacks_call(&_h->cb,type,method,vers, ## __VA_ARGS__); \
++})
++
++/** Call all hooks in a list, starting from the given one and optionally stopping
++ * after calling the first non-NULL function, returns the number of methods
++ * called */
++#define spa_hook_list_do_call(l,start,type,method,vers,once,...) \
++({ \
++ struct spa_hook_list *list = l; \
++ struct spa_list *s = start ? (struct spa_list *)start : &list->list; \
++ struct spa_hook cursor = { 0 }, *ci; \
++ int count = 0; \
++ spa_list_cursor_start(cursor, s, link); \
++ spa_list_for_each_cursor(ci, cursor, &list->list, link) { \
++ const type *_f = (const type *)ci->cb.funcs; \
++ if (SPA_LIKELY(SPA_CALLBACK_CHECK(_f,method,vers))) { \
++ _f->method(ci->cb.data, ## __VA_ARGS__); \
++ count++; \
++ if (once) \
++ break; \
++ } \
++ } \
++ spa_list_cursor_end(cursor, link); \
++ count; \
++})
++
++#define spa_hook_list_call(l,t,m,v,...) spa_hook_list_do_call(l,NULL,t,m,v,false,##__VA_ARGS__)
++#define spa_hook_list_call_once(l,t,m,v,...) spa_hook_list_do_call(l,NULL,t,m,v,true,##__VA_ARGS__)
++
++#define spa_hook_list_call_start(l,s,t,m,v,...) spa_hook_list_do_call(l,s,t,m,v,false,##__VA_ARGS__)
++#define spa_hook_list_call_once_start(l,s,t,m,v,...) spa_hook_list_do_call(l,s,t,m,v,true,##__VA_ARGS__)
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* SPA_HOOK_H */
+diff --git a/third_party/pipewire/spa/utils/keys.h b/third_party/pipewire/spa/utils/keys.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/utils/keys.h
+@@ -0,0 +1,124 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2019 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_UTILS_KEYS_H
++#define SPA_UTILS_KEYS_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** for objects */
++#define SPA_KEY_OBJECT_PATH "object.path" /**< a unique path to
++ * identity the object */
++
++#define SPA_KEY_MEDIA_CLASS "media.class" /**< Media class
++ * Ex. "Audio/Device",
++ * "Video/Source",... */
++#define SPA_KEY_MEDIA_ROLE "media.role" /**< Role: Movie, Music, Camera,
++ * Screen, Communication, Game,
++ * Notification, DSP, Production,
++ * Accessibility, Test */
++/** keys for udev api */
++#define SPA_KEY_API_UDEV "api.udev" /**< key for the udev api */
++#define SPA_KEY_API_UDEV_MATCH "api.udev.match" /**< udev subsystem match */
++
++/** keys for alsa api */
++#define SPA_KEY_API_ALSA "api.alsa" /**< key for the alsa api */
++#define SPA_KEY_API_ALSA_PATH "api.alsa.path" /**< alsa device path as can be
++ * used in snd_pcm_open() and
++ * snd_ctl_open(). */
++#define SPA_KEY_API_ALSA_CARD "api.alsa.card" /**< alsa card number */
++#define SPA_KEY_API_ALSA_USE_UCM "api.alsa.use-ucm" /**< if UCM should be used */
++#define SPA_KEY_API_ALSA_IGNORE_DB "api.alsa.ignore-dB" /**< if decibel info should be ignored */
++
++/** info from alsa card_info */
++#define SPA_KEY_API_ALSA_CARD_ID "api.alsa.card.id" /**< id from card_info */
++#define SPA_KEY_API_ALSA_CARD_COMPONENTS \
++ "api.alsa.card.components" /**< components from card_info */
++#define SPA_KEY_API_ALSA_CARD_DRIVER "api.alsa.card.driver" /**< driver from card_info */
++#define SPA_KEY_API_ALSA_CARD_NAME "api.alsa.card.name" /**< name from card_info */
++#define SPA_KEY_API_ALSA_CARD_LONGNAME "api.alsa.card.longname" /**< longname from card_info */
++#define SPA_KEY_API_ALSA_CARD_MIXERNAME "api.alsa.card.mixername" /**< mixername from card_info */
++
++/** info from alsa pcm_info */
++#define SPA_KEY_API_ALSA_PCM_ID "api.alsa.pcm.id" /**< id from pcm_info */
++#define SPA_KEY_API_ALSA_PCM_CARD "api.alsa.pcm.card" /**< card from pcm_info */
++#define SPA_KEY_API_ALSA_PCM_NAME "api.alsa.pcm.name" /**< name from pcm_info */
++#define SPA_KEY_API_ALSA_PCM_SUBNAME "api.alsa.pcm.subname" /**< subdevice_name from pcm_info */
++#define SPA_KEY_API_ALSA_PCM_STREAM "api.alsa.pcm.stream" /**< stream type from pcm_info */
++#define SPA_KEY_API_ALSA_PCM_CLASS "api.alsa.pcm.class" /**< class from pcm_info as string */
++#define SPA_KEY_API_ALSA_PCM_DEVICE "api.alsa.pcm.device" /**< device from pcm_info */
++#define SPA_KEY_API_ALSA_PCM_SUBDEVICE "api.alsa.pcm.subdevice" /**< subdevice from pcm_info */
++#define SPA_KEY_API_ALSA_PCM_SUBCLASS "api.alsa.pcm.subclass" /**< subclass from pcm_info as string */
++#define SPA_KEY_API_ALSA_PCM_SYNC_ID "api.alsa.pcm.sync-id" /**< sync id */
++
++/** keys for v4l2 api */
++#define SPA_KEY_API_V4L2 "api.v4l2" /**< key for the v4l2 api */
++#define SPA_KEY_API_V4L2_PATH "api.v4l2.path" /**< v4l2 device path as can be
++ * used in open() */
++
++/** keys for libcamera api */
++#define SPA_KEY_API_LIBCAMERA "api.libcamera" /**< key for the libcamera api */
++#define SPA_KEY_API_LIBCAMERA_PATH "api.libcamera.path" /**< libcamera device path as can be
++ * used in open() */
++
++/** info from libcamera_capability */
++#define SPA_KEY_API_LIBCAMERA_CAP_DRIVER "api.libcamera.cap.driver" /**< driver from capbility */
++#define SPA_KEY_API_LIBCAMERA_CAP_CARD "api.libcamera.cap.card" /**< caps from capability */
++#define SPA_KEY_API_LIBCAMERA_CAP_BUS_INFO "api.libcamera.cap.bus_info"/**< bus_info from capability */
++#define SPA_KEY_API_LIBCAMERA_CAP_VERSION "api.libcamera.cap.version" /**< version from capability as %u.%u.%u */
++#define SPA_KEY_API_LIBCAMERA_CAP_CAPABILITIES \
++ "api.libcamera.cap.capabilities" /**< capabilities from capability */
++#define SPA_KEY_API_LIBCAMERA_CAP_DEVICE_CAPS \
++ "api.libcamera.cap.device-caps" /**< device_caps from capability */
++/** info from v4l2_capability */
++#define SPA_KEY_API_V4L2_CAP_DRIVER "api.v4l2.cap.driver" /**< driver from capbility */
++#define SPA_KEY_API_V4L2_CAP_CARD "api.v4l2.cap.card" /**< caps from capability */
++#define SPA_KEY_API_V4L2_CAP_BUS_INFO "api.v4l2.cap.bus_info" /**< bus_info from capability */
++#define SPA_KEY_API_V4L2_CAP_VERSION "api.v4l2.cap.version" /**< version from capability as %u.%u.%u */
++#define SPA_KEY_API_V4L2_CAP_CAPABILITIES \
++ "api.v4l2.cap.capabilities" /**< capabilities from capability */
++#define SPA_KEY_API_V4L2_CAP_DEVICE_CAPS \
++ "api.v4l2.cap.device-caps" /**< device_caps from capability */
++
++
++/** keys for bluez5 api */
++#define SPA_KEY_API_BLUEZ5 "api.bluez5" /**< key for the bluez5 api */
++#define SPA_KEY_API_BLUEZ5_PATH "api.bluez5.path" /**< a bluez5 path */
++#define SPA_KEY_API_BLUEZ5_DEVICE "api.bluez5.device" /**< an internal bluez5 device */
++#define SPA_KEY_API_BLUEZ5_TRANSPORT "api.bluez5.transport" /**< an internal bluez5 transport */
++#define SPA_KEY_API_BLUEZ5_PROFILE "api.bluez5.profile" /**< a bluetooth profile */
++#define SPA_KEY_API_BLUEZ5_ADDRESS "api.bluez5.address" /**< a bluetooth address */
++
++/** keys for jack api */
++#define SPA_KEY_API_JACK "api.jack" /**< key for the JACK api */
++#define SPA_KEY_API_JACK_SERVER "api.jack.server" /**< a jack server name */
++#define SPA_KEY_API_JACK_CLIENT "api.jack.client" /**< an internal jack client */
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_UTILS_KEYS_H */
+diff --git a/third_party/pipewire/spa/utils/list.h b/third_party/pipewire/spa/utils/list.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/utils/list.h
+@@ -0,0 +1,138 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_LIST_H
++#define SPA_LIST_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++struct spa_list {
++ struct spa_list *next;
++ struct spa_list *prev;
++};
++
++#define SPA_LIST_INIT(list) (struct spa_list){ list, list };
++
++static inline void spa_list_init(struct spa_list *list)
++{
++ *list = SPA_LIST_INIT(list);
++}
++
++#define spa_list_is_empty(l) ((l)->next == (l))
++
++static inline void spa_list_insert(struct spa_list *list, struct spa_list *elem)
++{
++ elem->prev = list;
++ elem->next = list->next;
++ list->next = elem;
++ elem->next->prev = elem;
++}
++
++static inline void spa_list_insert_list(struct spa_list *list, struct spa_list *other)
++{
++ if (spa_list_is_empty(other))
++ return;
++ other->next->prev = list;
++ other->prev->next = list->next;
++ list->next->prev = other->prev;
++ list->next = other->next;
++}
++
++static inline void spa_list_remove(struct spa_list *elem)
++{
++ elem->prev->next = elem->next;
++ elem->next->prev = elem->prev;
++}
++
++#define spa_list_first(head, type, member) \
++ SPA_CONTAINER_OF((head)->next, type, member)
++
++#define spa_list_last(head, type, member) \
++ SPA_CONTAINER_OF((head)->prev, type, member)
++
++#define spa_list_append(list, item) \
++ spa_list_insert((list)->prev, item)
++
++#define spa_list_prepend(list, item) \
++ spa_list_insert(list, item)
++
++#define spa_list_is_end(pos, head, member) \
++ (&(pos)->member == (head))
++
++#define spa_list_next(pos, member) \
++ SPA_CONTAINER_OF((pos)->member.next, __typeof__(*pos), member)
++
++#define spa_list_prev(pos, member) \
++ SPA_CONTAINER_OF((pos)->member.prev, __typeof__(*pos), member)
++
++#define spa_list_consume(pos, head, member) \
++ for (pos = spa_list_first(head, __typeof__(*pos), member); \
++ !spa_list_is_empty(head); \
++ pos = spa_list_first(head, __typeof__(*pos), member))
++
++#define spa_list_for_each_next(pos, head, curr, member) \
++ for (pos = spa_list_first(curr, __typeof__(*pos), member); \
++ !spa_list_is_end(pos, head, member); \
++ pos = spa_list_next(pos, member))
++
++#define spa_list_for_each_prev(pos, head, curr, member) \
++ for (pos = spa_list_last(curr, __typeof__(*pos), member); \
++ !spa_list_is_end(pos, head, member); \
++ pos = spa_list_prev(pos, member))
++
++#define spa_list_for_each(pos, head, member) \
++ spa_list_for_each_next(pos, head, head, member)
++
++#define spa_list_for_each_reverse(pos, head, member) \
++ spa_list_for_each_prev(pos, head, head, member)
++
++#define spa_list_for_each_safe_next(pos, tmp, head, curr, member) \
++ for (pos = spa_list_first(curr, __typeof__(*pos), member); \
++ tmp = spa_list_next(pos, member), \
++ !spa_list_is_end(pos, head, member); \
++ pos = tmp)
++
++#define spa_list_for_each_safe(pos, tmp, head, member) \
++ spa_list_for_each_safe_next(pos, tmp, head, head, member)
++
++#define spa_list_cursor_start(cursor, head, member) \
++ spa_list_prepend(head, &(cursor).member)
++
++#define spa_list_for_each_cursor(pos, cursor, head, member) \
++ for(pos = spa_list_first(&(cursor).member, __typeof__(*(pos)), member); \
++ spa_list_remove(&(pos)->member), \
++ spa_list_append(&(cursor).member, &(pos)->member), \
++ !spa_list_is_end(pos, head, member); \
++ pos = spa_list_next(&cursor, member))
++
++#define spa_list_cursor_end(cursor, member) \
++ spa_list_remove(&(cursor).member)
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_LIST_H */
+diff --git a/third_party/pipewire/spa/utils/names.h b/third_party/pipewire/spa/utils/names.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/utils/names.h
+@@ -0,0 +1,137 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2019 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_UTILS_NAMES_H
++#define SPA_UTILS_NAMES_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/** for factory names */
++#define SPA_NAME_SUPPORT_CPU "support.cpu" /**< A CPU interface */
++#define SPA_NAME_SUPPORT_DBUS "support.dbus" /**< A DBUS interface */
++#define SPA_NAME_SUPPORT_LOG "support.log" /**< A Log interface */
++#define SPA_NAME_SUPPORT_LOOP "support.loop" /**< A Loop/LoopControl/LoopUtils
++ * interface */
++#define SPA_NAME_SUPPORT_SYSTEM "support.system" /**< A System interface */
++
++#define SPA_NAME_SUPPORT_NODE_DRIVER "support.node.driver" /**< A dummy driver node */
++
++/* control mixer */
++#define SPA_NAME_CONTROL_MIXER "control.mixer" /**< mixes control streams */
++
++/* audio mixer */
++#define SPA_NAME_AUDIO_MIXER "audio.mixer" /**< mixes the raw audio on N input
++ * ports together on the output
++ * port */
++#define SPA_NAME_AUDIO_MIXER_DSP "audio.mixer.dsp" /**< mixes mono audio with fixed input
++ * and output buffer sizes. supported
++ * formats must include f32 and
++ * optionally f64 and s24_32 */
++
++/** audio processing */
++#define SPA_NAME_AUDIO_PROCESS_FORMAT "audio.process.format" /**< processes raw audio from one format
++ * to another */
++#define SPA_NAME_AUDIO_PROCESS_CHANNELMIX \
++ "audio.process.channelmix" /**< mixes raw audio channels and applies
++ * volume change. */
++#define SPA_NAME_AUDIO_PROCESS_RESAMPLE \
++ "audio.process.resample" /**< resamples raw audio */
++#define SPA_NAME_AUDIO_PROCESS_DEINTERLEAVE \
++ "audio.process.deinterleave" /**< deinterleave raw audio channels */
++#define SPA_NAME_AUDIO_PROCESS_INTERLEAVE \
++ "audio.process.interleave" /**< interleave raw audio channels */
++
++
++/** audio convert combines some of the audio processing */
++#define SPA_NAME_AUDIO_CONVERT "audio.convert" /**< converts raw audio from one format
++ * to another. Must include at least
++ * format, channelmix and resample
++ * processing */
++#define SPA_NAME_AUDIO_ADAPT "audio.adapt" /**< combination of a node and an
++ * audio.convert. Does clock slaving */
++
++/** video processing */
++#define SPA_NAME_VIDEO_PROCESS_FORMAT "video.process.format" /**< processes raw video from one format
++ * to another */
++#define SPA_NAME_VIDEO_PROCESS_SCALE "video.process.scale" /**< scales raw video */
++
++/** video convert combines some of the video processing */
++#define SPA_NAME_VIDEO_CONVERT "video.convert" /**< converts raw video from one format
++ * to another. Must include at least
++ * format and scaling */
++#define SPA_NAME_VIDEO_ADAPT "video.adapt" /**< combination of a node and a
++ * video.convert. */
++/** keys for alsa factory names */
++#define SPA_NAME_API_ALSA_ENUM_UDEV "api.alsa.enum.udev" /**< an alsa udev Device interface */
++#define SPA_NAME_API_ALSA_PCM_DEVICE "api.alsa.pcm.device" /**< an alsa Device interface */
++#define SPA_NAME_API_ALSA_PCM_SOURCE "api.alsa.pcm.source" /**< an alsa Node interface for
++ * capturing PCM */
++#define SPA_NAME_API_ALSA_PCM_SINK "api.alsa.pcm.sink" /**< an alsa Node interface for
++ * playback PCM */
++#define SPA_NAME_API_ALSA_SEQ_DEVICE "api.alsa.seq.device" /**< an alsa Midi device */
++#define SPA_NAME_API_ALSA_SEQ_SOURCE "api.alsa.seq.source" /**< an alsa Node interface for
++ * capture of midi */
++#define SPA_NAME_API_ALSA_SEQ_SINK "api.alsa.seq.sink" /**< an alsa Node interface for
++ * playback of midi */
++#define SPA_NAME_API_ALSA_SEQ_BRIDGE "api.alsa.seq.bridge" /**< an alsa Node interface for
++ * bridging midi ports */
++#define SPA_NAME_API_ALSA_ACP_DEVICE "api.alsa.acp.device" /**< an alsa ACP Device interface */
++
++/** keys for bluez5 factory names */
++#define SPA_NAME_API_BLUEZ5_ENUM_DBUS "api.bluez5.enum.dbus" /**< a dbus Device interface */
++#define SPA_NAME_API_BLUEZ5_DEVICE "api.bluez5.device" /**< a Device interface */
++#define SPA_NAME_API_BLUEZ5_A2DP_SINK "api.bluez5.a2dp.sink" /**< a playback Node interface for A2DP profiles */
++#define SPA_NAME_API_BLUEZ5_A2DP_SOURCE "api.bluez5.a2dp.source" /**< a capture Node interface for A2DP profiles */
++#define SPA_NAME_API_BLUEZ5_SCO_SINK "api.bluez5.sco.sink" /**< a playback Node interface for HSP/HFP profiles */
++#define SPA_NAME_API_BLUEZ5_SCO_SOURCE "api.bluez5.sco.source" /**< a capture Node interface for HSP/HFP profiles */
++
++/** keys for v4l2 factory names */
++#define SPA_NAME_API_V4L2_ENUM_UDEV "api.v4l2.enum.udev" /**< a v4l2 udev Device interface */
++#define SPA_NAME_API_V4L2_DEVICE "api.v4l2.device" /**< a v4l2 Device interface */
++#define SPA_NAME_API_V4L2_SOURCE "api.v4l2.source" /**< a v4l2 Node interface for
++ * capturing */
++
++/** keys for libcamera factory names */
++#define SPA_NAME_API_LIBCAMERA_ENUM_CLIENT "api.libcamera.enum.client" /**< a libcamera client Device interface */
++#define SPA_NAME_API_LIBCAMERA_DEVICE "api.libcamera.device" /**< a libcamera Device interface */
++#define SPA_NAME_API_LIBCAMERA_SOURCE "api.libcamera.source" /**< a libcamera Node interface for
++ * capturing */
++
++/** keys for jack factory names */
++#define SPA_NAME_API_JACK_DEVICE "api.jack.device" /**< a jack device. This is a
++ * client connected to a server */
++#define SPA_NAME_API_JACK_SOURCE "api.jack.source" /**< a jack source */
++#define SPA_NAME_API_JACK_SINK "api.jack.sink" /**< a jack sink */
++
++/** keys for vulkan factory names */
++#define SPA_NAME_API_VULKAN_COMPUTE_SOURCE \
++ "api.vulkan.compute.source" /**< a vulkan compute source. */
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_UTILS_NAMES_H */
+diff --git a/third_party/pipewire/spa/utils/result.h b/third_party/pipewire/spa/utils/result.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/utils/result.h
+@@ -0,0 +1,58 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_UTILS_RESULT_H
++#define SPA_UTILS_RESULT_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++#include <spa/utils/list.h>
++
++#define SPA_ASYNC_BIT (1 << 30)
++#define SPA_ASYNC_MASK (3 << 30)
++#define SPA_ASYNC_SEQ_MASK (SPA_ASYNC_BIT - 1)
++
++#define SPA_RESULT_IS_OK(res) ((res) >= 0)
++#define SPA_RESULT_IS_ERROR(res) ((res) < 0)
++#define SPA_RESULT_IS_ASYNC(res) (((res) & SPA_ASYNC_MASK) == SPA_ASYNC_BIT)
++
++#define SPA_RESULT_ASYNC_SEQ(res) ((res) & SPA_ASYNC_SEQ_MASK)
++#define SPA_RESULT_RETURN_ASYNC(seq) (SPA_ASYNC_BIT | SPA_RESULT_ASYNC_SEQ(seq))
++
++#define spa_strerror(err) \
++({ \
++ int _err = -err; \
++ if (SPA_RESULT_IS_ASYNC(err)) \
++ _err = EINPROGRESS; \
++ strerror(_err); \
++})
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_UTILS_RESULT_H */
+diff --git a/third_party/pipewire/spa/utils/ringbuffer.h b/third_party/pipewire/spa/utils/ringbuffer.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/utils/ringbuffer.h
+@@ -0,0 +1,174 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_RINGBUFFER_H
++#define SPA_RINGBUFFER_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++struct spa_ringbuffer;
++
++#include <string.h>
++
++#include <spa/utils/defs.h>
++
++/**
++ * A ringbuffer type.
++ */
++struct spa_ringbuffer {
++ uint32_t readindex; /*< the current read index */
++ uint32_t writeindex; /*< the current write index */
++};
++
++#define SPA_RINGBUFFER_INIT() (struct spa_ringbuffer) { 0, 0 }
++
++/**
++ * Initialize a spa_ringbuffer with \a size.
++ *
++ * \param rbuf a spa_ringbuffer
++ * \param size the number of elements in the ringbuffer
++ */
++static inline void spa_ringbuffer_init(struct spa_ringbuffer *rbuf)
++{
++ *rbuf = SPA_RINGBUFFER_INIT();
++}
++
++/**
++ * Sets the pointers so that the ringbuffer contains \a size bytes.
++ *
++ * \param rbuf a spa_ringbuffer
++ */
++static inline void spa_ringbuffer_set_avail(struct spa_ringbuffer *rbuf, uint32_t size)
++{
++ rbuf->readindex = 0;
++ rbuf->writeindex = size;
++}
++
++/**
++ * Get the read index and available bytes for reading.
++ *
++ * \param rbuf a spa_ringbuffer
++ * \param index the value of readindex, should be taken modulo the size of the
++ * ringbuffer memory to get the offset in the ringbuffer memory
++ * \return number of available bytes to read. values < 0 mean
++ * there was an underrun. values > rbuf->size means there
++ * was an overrun.
++ */
++static inline int32_t spa_ringbuffer_get_read_index(struct spa_ringbuffer *rbuf, uint32_t *index)
++{
++ *index = __atomic_load_n(&rbuf->readindex, __ATOMIC_RELAXED);
++ return (int32_t) (__atomic_load_n(&rbuf->writeindex, __ATOMIC_ACQUIRE) - *index);
++}
++
++/**
++ * Read \a len bytes from \a rbuf starting \a offset. \a offset must be taken
++ * modulo \a size and len should be smaller than \a size.
++ *
++ * \param rbuf a #struct spa_ringbuffer
++ * \param buffer memory to read from
++ * \param size the size of \a buffer
++ * \param offset offset in \a buffer to read from
++ * \param data destination memory
++ * \param len number of bytes to read
++ */
++static inline void
++spa_ringbuffer_read_data(struct spa_ringbuffer *rbuf,
++ const void *buffer, uint32_t size,
++ uint32_t offset, void *data, uint32_t len)
++{
++ uint32_t l0 = SPA_MIN(len, size - offset), l1 = len - l0;
++ spa_memcpy(data, SPA_MEMBER(buffer, offset, void), l0);
++ if (SPA_UNLIKELY(l1 > 0))
++ spa_memcpy(SPA_MEMBER(data, l0, void), buffer, l1);
++}
++
++/**
++ * Update the read pointer to \a index.
++ *
++ * \param rbuf a spa_ringbuffer
++ * \param index new index
++ */
++static inline void spa_ringbuffer_read_update(struct spa_ringbuffer *rbuf, int32_t index)
++{
++ __atomic_store_n(&rbuf->readindex, index, __ATOMIC_RELEASE);
++}
++
++/**
++ * Get the write index and the number of bytes inside the ringbuffer.
++ *
++ * \param rbuf a spa_ringbuffer
++ * \param index the value of writeindex, should be taken modulo the size of the
++ * ringbuffer memory to get the offset in the ringbuffer memory
++ * \return the fill level of \a rbuf. values < 0 mean
++ * there was an underrun. values > rbuf->size means there
++ * was an overrun. Subtract from the buffer size to get
++ * the number of bytes available for writing.
++ */
++static inline int32_t spa_ringbuffer_get_write_index(struct spa_ringbuffer *rbuf, uint32_t *index)
++{
++ *index = __atomic_load_n(&rbuf->writeindex, __ATOMIC_RELAXED);
++ return (int32_t) (*index - __atomic_load_n(&rbuf->readindex, __ATOMIC_ACQUIRE));
++}
++
++/**
++ * Write \a len bytes to \a buffer starting \a offset. \a offset must be taken
++ * modulo \a size and len should be smaller than \a size.
++ *
++ * \param rbuf a spa_ringbuffer
++ * \param buffer memory to write to
++ * \param size the size of \a buffer
++ * \param offset offset in \a buffer to write to
++ * \param data source memory
++ * \param len number of bytes to write
++ */
++static inline void
++spa_ringbuffer_write_data(struct spa_ringbuffer *rbuf,
++ void *buffer, uint32_t size,
++ uint32_t offset, const void *data, uint32_t len)
++{
++ uint32_t l0 = SPA_MIN(len, size - offset), l1 = len - l0;
++ spa_memcpy(SPA_MEMBER(buffer, offset, void), data, l0);
++ if (SPA_UNLIKELY(l1 > 0))
++ spa_memcpy(buffer, SPA_MEMBER(data, l0, void), l1);
++}
++
++/**
++ * Update the write pointer to \a index
++ *
++ * \param rbuf a spa_ringbuffer
++ * \param index new index
++ */
++static inline void spa_ringbuffer_write_update(struct spa_ringbuffer *rbuf, int32_t index)
++{
++ __atomic_store_n(&rbuf->writeindex, index, __ATOMIC_RELEASE);
++}
++
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_RINGBUFFER_H */
+diff --git a/third_party/pipewire/spa/utils/type-info.h b/third_party/pipewire/spa/utils/type-info.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/utils/type-info.h
+@@ -0,0 +1,128 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_TYPE_INFO_H
++#define SPA_TYPE_INFO_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++
++#ifndef SPA_TYPE_ROOT
++#define SPA_TYPE_ROOT spa_types
++#endif
++
++static inline bool spa_type_is_a(const char *type, const char *parent)
++{
++ return type != NULL && parent != NULL && strncmp(type, parent, strlen(parent)) == 0;
++}
++
++#include <spa/utils/type.h>
++
++/* base for parameter object enumerations */
++#define SPA_TYPE_INFO_Direction SPA_TYPE_INFO_ENUM_BASE "Direction"
++#define SPA_TYPE_INFO_DIRECTION_BASE SPA_TYPE_INFO_Direction ":"
++
++static const struct spa_type_info spa_type_direction[] = {
++ { SPA_DIRECTION_INPUT, SPA_TYPE_Int, SPA_TYPE_INFO_DIRECTION_BASE "Input", NULL },
++ { SPA_DIRECTION_OUTPUT, SPA_TYPE_Int, SPA_TYPE_INFO_DIRECTION_BASE "Output", NULL },
++ { 0, 0, NULL, NULL }
++};
++
++#include <spa/node/type-info.h>
++#include <spa/param/type-info.h>
++#include <spa/control/type-info.h>
++
++/* base for parameter object enumerations */
++#define SPA_TYPE_INFO_Choice SPA_TYPE_INFO_ENUM_BASE "Choice"
++#define SPA_TYPE_INFO_CHOICE_BASE SPA_TYPE_INFO_Choice ":"
++
++static const struct spa_type_info spa_type_choice[] = {
++ { SPA_CHOICE_None, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "None", NULL },
++ { SPA_CHOICE_Range, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "Range", NULL },
++ { SPA_CHOICE_Step, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "Step", NULL },
++ { SPA_CHOICE_Enum, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "Enum", NULL },
++ { SPA_CHOICE_Flags, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "Flags", NULL },
++ { 0, 0, NULL, NULL }
++};
++
++static const struct spa_type_info spa_types[] = {
++ /* Basic types */
++ { SPA_TYPE_START, SPA_TYPE_START, SPA_TYPE_INFO_BASE, NULL },
++ { SPA_TYPE_None, SPA_TYPE_None, SPA_TYPE_INFO_BASE "None", NULL },
++ { SPA_TYPE_Bool, SPA_TYPE_Bool, SPA_TYPE_INFO_BASE "Bool", NULL },
++ { SPA_TYPE_Id, SPA_TYPE_Int, SPA_TYPE_INFO_BASE "Id", NULL },
++ { SPA_TYPE_Int, SPA_TYPE_Int, SPA_TYPE_INFO_BASE "Int", NULL },
++ { SPA_TYPE_Long, SPA_TYPE_Long, SPA_TYPE_INFO_BASE "Long", NULL },
++ { SPA_TYPE_Float, SPA_TYPE_Float, SPA_TYPE_INFO_BASE "Float", NULL },
++ { SPA_TYPE_Double, SPA_TYPE_Double, SPA_TYPE_INFO_BASE "Double", NULL },
++ { SPA_TYPE_String, SPA_TYPE_String, SPA_TYPE_INFO_BASE "String", NULL },
++ { SPA_TYPE_Bytes, SPA_TYPE_Bytes, SPA_TYPE_INFO_BASE "Bytes", NULL },
++ { SPA_TYPE_Rectangle, SPA_TYPE_Rectangle, SPA_TYPE_INFO_BASE "Rectangle", NULL },
++ { SPA_TYPE_Fraction, SPA_TYPE_Fraction, SPA_TYPE_INFO_BASE "Fraction", NULL },
++ { SPA_TYPE_Bitmap, SPA_TYPE_Bitmap, SPA_TYPE_INFO_BASE "Bitmap", NULL },
++ { SPA_TYPE_Array, SPA_TYPE_Array, SPA_TYPE_INFO_BASE "Array", NULL },
++ { SPA_TYPE_Pod, SPA_TYPE_Pod, SPA_TYPE_INFO_Pod, NULL },
++ { SPA_TYPE_Struct, SPA_TYPE_Pod, SPA_TYPE_INFO_Struct, NULL },
++ { SPA_TYPE_Object, SPA_TYPE_Pod, SPA_TYPE_INFO_Object, NULL },
++ { SPA_TYPE_Sequence, SPA_TYPE_Pod, SPA_TYPE_INFO_POD_BASE "Sequence", NULL },
++ { SPA_TYPE_Pointer, SPA_TYPE_Pointer, SPA_TYPE_INFO_Pointer, NULL },
++ { SPA_TYPE_Fd, SPA_TYPE_Fd, SPA_TYPE_INFO_BASE "Fd", NULL },
++ { SPA_TYPE_Choice, SPA_TYPE_Pod, SPA_TYPE_INFO_POD_BASE "Choice", NULL },
++
++ { SPA_TYPE_POINTER_START, SPA_TYPE_Pointer, SPA_TYPE_INFO_Pointer, NULL },
++ { SPA_TYPE_POINTER_Buffer, SPA_TYPE_Pointer, SPA_TYPE_INFO_POINTER_BASE "Buffer", NULL },
++ { SPA_TYPE_POINTER_Meta, SPA_TYPE_Pointer, SPA_TYPE_INFO_POINTER_BASE "Meta", NULL },
++ { SPA_TYPE_POINTER_Dict, SPA_TYPE_Pointer, SPA_TYPE_INFO_POINTER_BASE "Dict", NULL },
++
++ { SPA_TYPE_EVENT_START, SPA_TYPE_Object, SPA_TYPE_INFO_Event, NULL },
++ { SPA_TYPE_EVENT_Device, SPA_TYPE_Object, SPA_TYPE_INFO_EVENT_BASE "Device", NULL },
++ { SPA_TYPE_EVENT_Node, SPA_TYPE_Object, SPA_TYPE_INFO_EVENT_BASE "Node", spa_type_node_event },
++
++ { SPA_TYPE_COMMAND_START, SPA_TYPE_Object, SPA_TYPE_INFO_Command, NULL },
++ { SPA_TYPE_COMMAND_Device, SPA_TYPE_Object, SPA_TYPE_INFO_COMMAND_BASE "Device", NULL },
++ { SPA_TYPE_COMMAND_Node, SPA_TYPE_Object, SPA_TYPE_INFO_COMMAND_BASE "Node", spa_type_node_command },
++
++ { SPA_TYPE_OBJECT_START, SPA_TYPE_Object, SPA_TYPE_INFO_Object, NULL },
++ { SPA_TYPE_OBJECT_PropInfo, SPA_TYPE_Object, SPA_TYPE_INFO_PropInfo, spa_type_prop_info, },
++ { SPA_TYPE_OBJECT_Props, SPA_TYPE_Object, SPA_TYPE_INFO_Props, spa_type_props },
++ { SPA_TYPE_OBJECT_Format, SPA_TYPE_Object, SPA_TYPE_INFO_Format, spa_type_format },
++ { SPA_TYPE_OBJECT_ParamBuffers, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Buffers, spa_type_param_buffers, },
++ { SPA_TYPE_OBJECT_ParamMeta, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Meta, spa_type_param_meta },
++ { SPA_TYPE_OBJECT_ParamIO, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_IO, spa_type_param_io },
++ { SPA_TYPE_OBJECT_ParamProfile, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Profile, spa_type_param_profile },
++ { SPA_TYPE_OBJECT_ParamPortConfig, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_PortConfig, spa_type_param_port_config },
++ { SPA_TYPE_OBJECT_ParamRoute, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Route, spa_type_param_route },
++ { SPA_TYPE_OBJECT_Profiler, SPA_TYPE_Object, SPA_TYPE_INFO_Profiler, spa_type_profiler },
++
++ { 0, 0, NULL, NULL }
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_TYPE_INFO_H */
+diff --git a/third_party/pipewire/spa/utils/type.h b/third_party/pipewire/spa/utils/type.h
+new file mode 100644
+--- /dev/null
++++ b/third_party/pipewire/spa/utils/type.h
+@@ -0,0 +1,138 @@
++/* Simple Plugin API
++ *
++ * Copyright © 2018 Wim Taymans
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef SPA_TYPE_H
++#define SPA_TYPE_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <spa/utils/defs.h>
++
++enum {
++ /* Basic types */
++ SPA_TYPE_START = 0x00000,
++ SPA_TYPE_None,
++ SPA_TYPE_Bool,
++ SPA_TYPE_Id,
++ SPA_TYPE_Int,
++ SPA_TYPE_Long,
++ SPA_TYPE_Float,
++ SPA_TYPE_Double,
++ SPA_TYPE_String,
++ SPA_TYPE_Bytes,
++ SPA_TYPE_Rectangle,
++ SPA_TYPE_Fraction,
++ SPA_TYPE_Bitmap,
++ SPA_TYPE_Array,
++ SPA_TYPE_Struct,
++ SPA_TYPE_Object,
++ SPA_TYPE_Sequence,
++ SPA_TYPE_Pointer,
++ SPA_TYPE_Fd,
++ SPA_TYPE_Choice,
++ SPA_TYPE_Pod,
++ SPA_TYPE_LAST, /**< not part of ABI */
++
++ /* Pointers */
++ SPA_TYPE_POINTER_START = 0x10000,
++ SPA_TYPE_POINTER_Buffer,
++ SPA_TYPE_POINTER_Meta,
++ SPA_TYPE_POINTER_Dict,
++ SPA_TYPE_POINTER_LAST, /**< not part of ABI */
++
++ /* Events */
++ SPA_TYPE_EVENT_START = 0x20000,
++ SPA_TYPE_EVENT_Device,
++ SPA_TYPE_EVENT_Node,
++ SPA_TYPE_EVENT_LAST, /**< not part of ABI */
++
++ /* Commands */
++ SPA_TYPE_COMMAND_START = 0x30000,
++ SPA_TYPE_COMMAND_Device,
++ SPA_TYPE_COMMAND_Node,
++ SPA_TYPE_COMMAND_LAST, /**< not part of ABI */
++
++ /* Objects */
++ SPA_TYPE_OBJECT_START = 0x40000,
++ SPA_TYPE_OBJECT_PropInfo,
++ SPA_TYPE_OBJECT_Props,
++ SPA_TYPE_OBJECT_Format,
++ SPA_TYPE_OBJECT_ParamBuffers,
++ SPA_TYPE_OBJECT_ParamMeta,
++ SPA_TYPE_OBJECT_ParamIO,
++ SPA_TYPE_OBJECT_ParamProfile,
++ SPA_TYPE_OBJECT_ParamPortConfig,
++ SPA_TYPE_OBJECT_ParamRoute,
++ SPA_TYPE_OBJECT_Profiler,
++ SPA_TYPE_OBJECT_LAST, /**< not part of ABI */
++
++ /* vendor extensions */
++ SPA_TYPE_VENDOR_PipeWire = 0x02000000,
++
++ SPA_TYPE_VENDOR_Other = 0x7f000000,
++};
++
++#define SPA_TYPE_INFO_BASE "Spa:"
++
++#define SPA_TYPE_INFO_Flags SPA_TYPE_INFO_BASE "Flags"
++#define SPA_TYPE_INFO_FLAGS_BASE SPA_TYPE_INFO_Flags ":"
++
++#define SPA_TYPE_INFO_Enum SPA_TYPE_INFO_BASE "Enum"
++#define SPA_TYPE_INFO_ENUM_BASE SPA_TYPE_INFO_Enum ":"
++
++#define SPA_TYPE_INFO_Pod SPA_TYPE_INFO_BASE "Pod"
++#define SPA_TYPE_INFO_POD_BASE SPA_TYPE_INFO_Pod ":"
++
++#define SPA_TYPE_INFO_Struct SPA_TYPE_INFO_POD_BASE "Struct"
++#define SPA_TYPE_INFO_STRUCT_BASE SPA_TYPE_INFO_Struct ":"
++
++#define SPA_TYPE_INFO_Object SPA_TYPE_INFO_POD_BASE "Object"
++#define SPA_TYPE_INFO_OBJECT_BASE SPA_TYPE_INFO_Object ":"
++
++#define SPA_TYPE_INFO_Pointer SPA_TYPE_INFO_BASE "Pointer"
++#define SPA_TYPE_INFO_POINTER_BASE SPA_TYPE_INFO_Pointer ":"
++
++#define SPA_TYPE_INFO_Interface SPA_TYPE_INFO_POINTER_BASE "Interface"
++#define SPA_TYPE_INFO_INTERFACE_BASE SPA_TYPE_INFO_Interface ":"
++
++#define SPA_TYPE_INFO_Event SPA_TYPE_INFO_OBJECT_BASE "Event"
++#define SPA_TYPE_INFO_EVENT_BASE SPA_TYPE_INFO_Event ":"
++
++#define SPA_TYPE_INFO_Command SPA_TYPE_INFO_OBJECT_BASE "Command"
++#define SPA_TYPE_INFO_COMMAND_BASE SPA_TYPE_INFO_Command ":"
++
++struct spa_type_info {
++ uint32_t type;
++ uint32_t parent;
++ const char *name;
++ const struct spa_type_info *values;
++};
++
++#ifdef __cplusplus
++} /* extern "C" */
++#endif
++
++#endif /* SPA_TYPE_H */
+
diff --git a/pw5.patch b/pw5.patch
new file mode 100644
index 0000000..bcb004e
--- /dev/null
+++ b/pw5.patch
@@ -0,0 +1,53 @@
+
+# HG changeset patch
+# User stransky <stransky@redhat.com>
+# Date 1605025841 0
+# Node ID e04be7688dfb4fbbe8dee73e366df8bc9a5da580
+# Parent 41d3c1292480de14d05b34aa0cf2d56015994878
+Bug 1675767 [Linux] Use PipeWire on Wayland desktop, r=dminor
+
+Differential Revision: https://phabricator.services.mozilla.com/D96587
+
+diff --git a/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capturer.cc b/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capturer.cc
+--- a/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capturer.cc
++++ b/third_party/libwebrtc/webrtc/modules/desktop_capture/desktop_capturer.cc
+@@ -72,37 +72,21 @@ std::unique_ptr<DesktopCapturer> Desktop
+ if (capturer && options.detect_updated_region()) {
+ capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
+ }
+
+ return capturer;
+ }
+
+ #if defined(WEBRTC_USE_PIPEWIRE) || defined(USE_X11)
+-// Return true if Firefox is actually running with Wayland backend.
+-static bool IsWaylandDisplayUsed() {
+- const auto display = gdk_display_get_default();
+- if (display == nullptr) {
+- // We're running in headless mode.
+- return false;
+- }
+- return !GDK_IS_X11_DISPLAY(display);
+-}
+-
+-// Return true if Firefox is actually running on Wayland enabled session.
+-// It means some screensharing capabilities may be limited.
+-static bool IsWaylandSessionUsed() {
++bool DesktopCapturer::IsRunningUnderWayland() {
+ const char* xdg_session_type = getenv("XDG_SESSION_TYPE");
+ if (!xdg_session_type || strncmp(xdg_session_type, "wayland", 7) != 0)
+ return false;
+
+ if (!(getenv("WAYLAND_DISPLAY")))
+ return false;
+
+ return true;
+ }
+-
+-bool DesktopCapturer::IsRunningUnderWayland() {
+- return IsWaylandSessionUsed() ? IsWaylandDisplayUsed() : false;
+-}
+ #endif // defined(WEBRTC_USE_PIPEWIRE) || defined(USE_X11)
+
+ } // namespace webrtc
+
diff --git a/pw6.patch b/pw6.patch
new file mode 100644
index 0000000..cc0cd7e
--- /dev/null
+++ b/pw6.patch
@@ -0,0 +1,75 @@
+diff --git a/browser/actors/WebRTCParent.jsm b/browser/actors/WebRTCParent.jsm
+--- a/browser/actors/WebRTCParent.jsm
++++ b/browser/actors/WebRTCParent.jsm
+@@ -45,6 +45,9 @@
+ "nsIOSPermissionRequest"
+ );
+
++const PIPEWIRE_PORTAL_NAME = "####_PIPEWIRE_PORTAL_####";
++const PIPEWIRE_ID = 0xaffffff;
++
+ class WebRTCParent extends JSWindowActorParent {
+ didDestroy() {
+ webrtcUI.forgetStreamsFromBrowserContext(this.browsingContext);
+@@ -774,6 +777,23 @@
+ }
+ } else {
+ name = device.name;
++ // When we share content by PipeWire add only one item to the device
++ // list. When it's selected PipeWire portal dialog is opened and
++ // user confirms actual window/screen sharing there.
++ // Don't mark it as scary as there's an extra confirmation step by
++ // PipeWire portal dialog.
++ if (name == PIPEWIRE_PORTAL_NAME && device.id == PIPEWIRE_ID) {
++ let sawcStringId = "getUserMedia.sharePipeWirePortal.label";
++ let item = addDeviceToList(
++ menupopup,
++ stringBundle.getString(sawcStringId),
++ i,
++ type
++ );
++ item.deviceId = device.id;
++ item.mediaSource = type;
++ break;
++ }
+ if (type == "application") {
+ // The application names returned by the platform are of the form:
+ // <window count>\x1e<application name>
+diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties
+--- a/browser/locales/en-US/chrome/browser/browser.properties
++++ b/browser/locales/en-US/chrome/browser/browser.properties
+@@ -767,6 +767,7 @@
+ getUserMedia.selectWindowOrScreen.accesskey=W
+ getUserMedia.pickWindowOrScreen.label = Select Window or Screen
+ getUserMedia.shareEntireScreen.label = Entire screen
++getUserMedia.sharePipeWirePortal.label = Use operating system settings
+ # LOCALIZATION NOTE (getUserMedia.shareMonitor.label):
+ # %S is screen number (digits 1, 2, etc)
+ # Example: Screen 1, Screen 2,..
+diff --git a/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc b/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc
+--- a/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc
++++ b/third_party/libwebrtc/webrtc/modules/desktop_capture/linux/base_capturer_pipewire.cc
+@@ -898,17 +898,17 @@
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(result));
+ }
+
++#define PIPEWIRE_ID 0xaffffff
++#define PIPEWIRE_NAME "####_PIPEWIRE_PORTAL_####"
++
+ bool BaseCapturerPipeWire::GetSourceList(SourceList* sources) {
+- RTC_DCHECK(sources->size() == 0);
+- // List of available screens is already presented by the xdg-desktop-portal.
+- // But we have to add an empty source as the code expects it.
+- sources->push_back({0});
++ sources->push_back({PIPEWIRE_ID, 0, PIPEWIRE_NAME});
+ return true;
+ }
+
+ bool BaseCapturerPipeWire::SelectSource(SourceId id) {
+ // Screen selection is handled by the xdg-desktop-portal.
+- return true;
++ return id == PIPEWIRE_ID;
+ }
+
+ // static
+
diff --git a/pw7.patch b/pw7.patch
new file mode 100644
index 0000000..db75dea
--- /dev/null
+++ b/pw7.patch
@@ -0,0 +1,95 @@
+diff --git a/browser/actors/WebRTCParent.jsm b/browser/actors/WebRTCParent.jsm
+--- a/browser/actors/WebRTCParent.jsm
++++ b/browser/actors/WebRTCParent.jsm
+@@ -756,6 +756,8 @@
+ );
+ menupopup.appendChild(doc.createXULElement("menuseparator"));
+
++ let isPipeWire = false;
++
+ // Build the list of 'devices'.
+ let monitorIndex = 1;
+ for (let i = 0; i < devices.length; ++i) {
+@@ -783,6 +785,7 @@
+ // Don't mark it as scary as there's an extra confirmation step by
+ // PipeWire portal dialog.
+ if (name == PIPEWIRE_PORTAL_NAME && device.id == PIPEWIRE_ID) {
++ isPipeWire = true;
+ let sawcStringId = "getUserMedia.sharePipeWirePortal.label";
+ let item = addDeviceToList(
+ menupopup,
+@@ -908,39 +911,41 @@
+ perms.EXPIRE_SESSION
+ );
+
+- video.deviceId = deviceId;
+- let constraints = {
+- video: { mediaSource: type, deviceId: { exact: deviceId } },
+- };
+- chromeWin.navigator.mediaDevices.getUserMedia(constraints).then(
+- stream => {
+- if (video.deviceId != deviceId) {
+- // The user has selected a different device or closed the panel
+- // before getUserMedia finished.
+- stream.getTracks().forEach(t => t.stop());
+- return;
++ if (!isPipeWire) {
++ video.deviceId = deviceId;
++ let constraints = {
++ video: { mediaSource: type, deviceId: { exact: deviceId } },
++ };
++ chromeWin.navigator.mediaDevices.getUserMedia(constraints).then(
++ stream => {
++ if (video.deviceId != deviceId) {
++ // The user has selected a different device or closed the panel
++ // before getUserMedia finished.
++ stream.getTracks().forEach(t => t.stop());
++ return;
++ }
++ video.srcObject = stream;
++ video.stream = stream;
++ doc.getElementById("webRTC-preview").hidden = false;
++ video.onloadedmetadata = function(e) {
++ video.play();
++ };
++ },
++ err => {
++ if (
++ err.name == "OverconstrainedError" &&
++ err.constraint == "deviceId"
++ ) {
++ // Window has disappeared since enumeration, which can happen.
++ // No preview for you.
++ return;
++ }
++ Cu.reportError(
++ `error in preview: ${err.message} ${err.constraint}`
++ );
+ }
+- video.srcObject = stream;
+- video.stream = stream;
+- doc.getElementById("webRTC-preview").hidden = false;
+- video.onloadedmetadata = function(e) {
+- video.play();
+- };
+- },
+- err => {
+- if (
+- err.name == "OverconstrainedError" &&
+- err.constraint == "deviceId"
+- ) {
+- // Window has disappeared since enumeration, which can happen.
+- // No preview for you.
+- return;
+- }
+- Cu.reportError(
+- `error in preview: ${err.message} ${err.constraint}`
+- );
+- }
+- );
++ );
++ }
+ };
+ menupopup.addEventListener("command", menupopup._commandEventListener);
+ }
+
diff --git a/sources b/sources
index f9e22f5..84d4cfd 100644
--- a/sources
+++ b/sources
@@ -1,3 +1,3 @@
SHA512 (cbindgen-vendor.tar.xz) = f0425020e2d43a3d28b03f82bdb9719728112a2c94b1d595da384d0674ca21d0940a6f729a690434d670e598fbc6bb5193c89da0a4633a734c70dd786222e711
-SHA512 (firefox-82.0.3.source.tar.xz) = b12c35cd1aa223e481be8b79ddb6aa7949531f9dc519bb1caa492ea32c7cbf495c1dd7382692a3428c75955f911f3b8905906e77d246d9f4a0ba12bcd3155d24
-SHA512 (firefox-langpacks-82.0.3-20201109.tar.xz) = fb7a352761e860a0d754161e97c46ef7d34b30d404121042fd495d36c715c318c250eec406e26214f2816b8975772389a54d6cbeb9d98676017f9f1455139c59
+SHA512 (firefox-83.0.source.tar.xz) = 2d0cbc1b778bec6981586ea18e9558356e58678bda05efda0f4ea32f85942bfca35bfa00a28ca7be83a9ade679184394209abb53c38df3ef912d14d000df8fc8
+SHA512 (firefox-langpacks-83.0-20201112.tar.xz) = 7c8999bcdfcd1b23a7593233fec39ea617fd591ff887226feb87c5b3557749745cf6f2aa411a75a6f674423cd511effd8266e8fe76122b21536a4133325dee5c
bgstack15