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 - #include - -+#include -+#include -+#include -+#include -+ - #include - #include - -@@ -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_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_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(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(desktop_size_.width()), -- static_cast(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_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(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(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(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( -+ 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(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(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(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 result(new BasicDesktopFrame(desktop_size_)); -+ DesktopSize frame_size = desktop_size_; -+ if (video_crop_size_initialized_) { -+ frame_size = video_crop_size_; -+ } -+ -+ std::unique_ptr 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 -+BaseCapturerPipeWire::CreateRawScreenCapturer( -+ const DesktopCaptureOptions& options) { -+ std::unique_ptr capturer = -+ std::make_unique(BaseCapturerPipeWire::CaptureSourceType::kAny); -+ return std::move(capturer);} -+ -+// static -+std::unique_ptr -+BaseCapturerPipeWire::CreateRawWindowCapturer( -+ const DesktopCaptureOptions& options) { -+ -+ std::unique_ptr capturer = -+ std::make_unique(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 CreateRawScreenCapturer( -+ const DesktopCaptureOptions& options); -+ -+ static std::unique_ptr 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 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 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 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-pipewire-0-3.patch b/firefox-pipewire-0-3.patch deleted file mode 100644 index 8dc7eed..0000000 --- a/firefox-pipewire-0-3.patch +++ /dev/null @@ -1,834 +0,0 @@ -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 @@ - - #include - #include --#include --#include -+ -+#include -+#include -+#include -+#include - - #include - #include -@@ -36,32 +39,37 @@ const char kSessionInterfaceName[] = "or - 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, -- pw_remote_state state, -- const char* error_message) { -- BaseCapturerPipeWire* that = static_cast(data); -- RTC_DCHECK(that); -+void BaseCapturerPipeWire::SyncDmaBuf(int fd, uint64_t start_or_end) { -+ struct dma_buf_sync sync = { 0 }; - -- 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."; -+ 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; -- case PW_REMOTE_STATE_UNCONNECTED: -- RTC_LOG(LS_INFO) << "PipeWire remote state: unconnected."; -+ } else { - break; -+ } - } - } - - // static -+void BaseCapturerPipeWire::OnCoreError(void *data, -+ uint32_t id, -+ int seq, -+ int res, -+ const char *message) { -+ RTC_LOG(LS_ERROR) << "core error: " << message; -+} -+ -+// static - void BaseCapturerPipeWire::OnStreamStateChanged(void* data, - pw_stream_state old_state, - pw_stream_state state, -@@ -73,76 +81,54 @@ void BaseCapturerPipeWire::OnStreamState - 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_READY: - case PW_STREAM_STATE_PAUSED: - case PW_STREAM_STATE_STREAMING: -+ case PW_STREAM_STATE_UNCONNECTED: -+ case PW_STREAM_STATE_CONNECTING: - break; - } - } - - // static --void BaseCapturerPipeWire::OnStreamFormatChanged(void* data, -- const struct spa_pod* format) { -+void BaseCapturerPipeWire::OnStreamParamChanged(void *data, uint32_t id, -+ const struct spa_pod *format) { - BaseCapturerPipeWire* that = static_cast(data); - RTC_DCHECK(that); - -- RTC_LOG(LS_INFO) << "PipeWire stream format changed."; -+ RTC_LOG(LS_INFO) << "PipeWire stream param changed."; - -- if (!format) { -- pw_stream_finish_format(that->pw_stream_, /*res=*/0, /*params=*/nullptr, -- /*n_params=*/0); -+ if (!format || id != SPA_PARAM_Format) { - return; - } - -- that->spa_video_format_ = new spa_video_info_raw(); -- spa_format_video_raw_parse(format, that->spa_video_format_, -- &that->pw_type_->format_video); -+ spa_format_video_raw_parse(format, &that->spa_video_format_); - -- auto width = that->spa_video_format_->size.width; -- auto height = that->spa_video_format_->size.height; -+ auto width = that->spa_video_format_.size.width; -+ auto height = that->spa_video_format_.size.height; - 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]; -- params[0] = reinterpret_cast(spa_pod_builder_object( -- &builder, -- // id to enumerate buffer requirements -- that->pw_core_type_->param.idBuffers, -- that->pw_core_type_->param_buffers.Buffers, -- // Size: specified as integer (i) and set to specified size -- ":", that->pw_core_type_->param_buffers.size, "i", size, -- // Stride: specified as integer (i) and set to specified stride -- ":", that->pw_core_type_->param_buffers.stride, "i", stride, -- // Buffers: specifies how many buffers we want to deal with, set as -- // integer (i) where preferred number is 8, then allowed number is defined -- // as range (r) from min and max values and it is undecided (u) to allow -- // negotiation -- ":", that->pw_core_type_->param_buffers.buffers, "iru", 8, -- SPA_POD_PROP_MIN_MAX(1, 32), -- // Align: memory alignment of the buffer, set as integer (i) to specified -- // value -- ":", that->pw_core_type_->param_buffers.align, "i", 16)); -- params[1] = reinterpret_cast(spa_pod_builder_object( -- &builder, -- // id to enumerate supported metadata -- that->pw_core_type_->param.idMeta, that->pw_core_type_->param_meta.Meta, -- // Type: specified as id or enum (I) -- ":", that->pw_core_type_->param_meta.type, "I", -- that->pw_core_type_->meta.Header, -- // 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); -+ const struct spa_pod* params[3]; -+ params[0] = reinterpret_cast(spa_pod_builder_add_object(&builder, -+ SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, -+ SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), -+ SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride), -+ SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 1, 32))); -+ params[1] = reinterpret_cast(spa_pod_builder_add_object(&builder, -+ SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, -+ SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), -+ SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)))); -+ params[2] = reinterpret_cast(spa_pod_builder_add_object(&builder, -+ SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, -+ SPA_PARAM_META_type, SPA_POD_Id (SPA_META_VideoCrop), -+ SPA_PARAM_META_size, SPA_POD_Int (sizeof(struct spa_meta_region)))); -+ pw_stream_update_params(that->pw_stream_, params, 3); - } - - // static -@@ -150,15 +136,25 @@ void BaseCapturerPipeWire::OnStreamProce - BaseCapturerPipeWire* that = static_cast(data); - RTC_DCHECK(that); - -- pw_buffer* buf = nullptr; -+ struct pw_buffer *next_buffer; -+ struct pw_buffer *buffer = nullptr; -+ -+ 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 (!(buf = pw_stream_dequeue_buffer(that->pw_stream_))) { -+ 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) -@@ -169,38 +165,22 @@ BaseCapturerPipeWire::~BaseCapturerPipeW - pw_thread_loop_stop(pw_main_loop_); - } - -- if (pw_type_) { -- delete pw_type_; -- } -- -- if (spa_video_format_) { -- delete spa_video_format_; -- } -- - if (pw_stream_) { - pw_stream_destroy(pw_stream_); - } - -- if (pw_remote_) { -- pw_remote_destroy(pw_remote_); -+ if (pw_core_) { -+ pw_core_disconnect(pw_core_); - } - -- if (pw_core_) { -- pw_core_destroy(pw_core_); -+ if (pw_context_) { -+ pw_context_destroy(pw_context_); - } - - if (pw_main_loop_) { - pw_thread_loop_destroy(pw_main_loop_); - } - -- if (pw_loop_) { -- 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_); - } -@@ -250,27 +230,35 @@ void BaseCapturerPipeWire::InitPortal() - 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_) { -+ RTC_LOG(LS_ERROR) << "Failed to create PipeWire context"; -+ return; -+ } - -- InitPipeWireTypes(); -+ pw_core_ = pw_context_connect(pw_context_, nullptr, 0); -+ if (!pw_core_) { -+ RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context"; -+ return; -+ } - - // Initialize event handlers, remote end and stream-related. -- pw_remote_events_.version = PW_VERSION_REMOTE_EVENTS; -- pw_remote_events_.state_changed = &OnStateChanged; -+ pw_core_events_.version = PW_VERSION_CORE_EVENTS; -+ pw_core_events_.error = &OnCoreError; - - pw_stream_events_.version = PW_VERSION_STREAM_EVENTS; - pw_stream_events_.state_changed = &OnStreamStateChanged; -- pw_stream_events_.format_changed = &OnStreamFormatChanged; -+ pw_stream_events_.param_changed = &OnStreamParamChanged; - pw_stream_events_.process = &OnStreamProcess; - -- pw_remote_add_listener(pw_remote_, &spa_remote_listener_, &pw_remote_events_, -- this); -- pw_remote_connect_fd(pw_remote_, pw_fd_); -+ pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this); -+ -+ pw_stream_ = CreateReceivingStream(); -+ if (!pw_stream_) { -+ RTC_LOG(LS_ERROR) << "Failed to create PipeWire stream"; -+ return; -+ } - - 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( - } - } - --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); - --void BaseCapturerPipeWire::CreateReceivingStream() { -- spa_rectangle pwMinScreenBounds = spa_rectangle{1, 1}; -- spa_rectangle pwScreenBounds = -- spa_rectangle{static_cast(desktop_size_.width()), -- static_cast(desktop_size_.height())}; -- -- spa_fraction pwFrameRateMin = spa_fraction{0, 1}; -- spa_fraction pwFrameRateMax = spa_fraction{60, 1}; -- -- pw_properties* reuseProps = pw_properties_new("pipewire.client.reuse", "1", -- /*end of varargs*/ nullptr); -- pw_stream_ = pw_stream_new(pw_remote_, "webrtc-consume-stream", reuseProps); -+ if (!stream) { -+ RTC_LOG(LS_ERROR) << "Could not create receiving stream."; -+ return nullptr; -+ } - - uint8_t buffer[1024] = {}; -- const spa_pod* params[1]; -- spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)}; -- params[0] = reinterpret_cast(spa_pod_builder_object( -- &builder, -- // id to enumerate formats -- pw_core_type_->param.idEnumFormat, pw_core_type_->spa_format, "I", -- pw_type_->media_type.video, "I", pw_type_->media_subtype.raw, -- // Video format: specified as id or enum (I), preferred format is BGRx, -- // 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), -- // 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)); -+ const spa_pod* params[2]; -+ spa_pod_builder builder = SPA_POD_BUILDER_INIT(buffer, sizeof (buffer)); -+ -+ params[0] = reinterpret_cast(spa_pod_builder_add_object(&builder, -+ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, -+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), -+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), -+ SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(5, SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx, SPA_VIDEO_FORMAT_RGBA, -+ SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_BGRA), -+ SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(&pwMinScreenBounds, -+ &pwMinScreenBounds, -+ &pwMaxScreenBounds), -+ 0)); -+ 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_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) { - RTC_LOG(LS_ERROR) << "Could not connect receiving stream."; - portal_init_failed_ = true; -- return; - } -+ -+ return stream; - } - - 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(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(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(spaBuffer->datas[0].data); -+ } else { - 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( -+ 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 (!current_frame_ || -+ (video_crop_size_initialized_ && !video_crop_size_.equals(prev_crop_size))) { -+ current_frame_ = std::make_unique(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 +400,40 @@ void BaseCapturerPipeWire::HandleBuffer( - return; - } - -- if (!current_frame_) { -- current_frame_ = static_cast(malloc(maxSize)); -+ dst = current_frame_.get(); -+ -+ // 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; -+ } -+ } -+ 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 -+ : 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 == SPA_VIDEO_FORMAT_RGBx || -+ spa_video_format_.format == SPA_VIDEO_FORMAT_RGBA) { -+ 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(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); - } - } - -@@ -725,10 +783,7 @@ 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); -- -+ that->pw_stream_node_id_ = stream_id; - g_variant_unref(options); - g_variant_unref(variant); - } -@@ -813,10 +868,15 @@ void BaseCapturerPipeWire::CaptureFrame( - return; - } - -- std::unique_ptr result(new BasicDesktopFrame(desktop_size_)); -+ DesktopSize frame_size = desktop_size_; -+ if (video_crop_size_initialized_) { -+ frame_size = video_crop_size_; -+ } -+ -+ std::unique_ptr 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 +897,22 @@ bool BaseCapturerPipeWire::SelectSource( - return true; - } - -+// static -+std::unique_ptr -+BaseCapturerPipeWire::CreateRawScreenCapturer( -+ const DesktopCaptureOptions& options) { -+ std::unique_ptr capturer = -+ std::make_unique(BaseCapturerPipeWire::CaptureSourceType::kAny); -+ return std::move(capturer);} -+ -+// static -+std::unique_ptr -+BaseCapturerPipeWire::CreateRawWindowCapturer( -+ const DesktopCaptureOptions& options) { -+ -+ std::unique_ptr capturer = -+ std::make_unique(BaseCapturerPipeWire::CaptureSourceType::kAny); -+ return std::move(capturer); -+} -+ - } // 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 @@ - - namespace webrtc { - --class PipeWireType { -- public: -- spa_type_media_type media_type; -- spa_type_media_subtype media_subtype; -- spa_type_format_video format_video; -- spa_type_video_format video_format; --}; -- - 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,28 +39,32 @@ class BaseCapturerPipeWire : public Desk - bool GetSourceList(SourceList* sources) override; - bool SelectSource(SourceId id) override; - -+ static std::unique_ptr CreateRawScreenCapturer( -+ const DesktopCaptureOptions& options); -+ -+ static std::unique_ptr CreateRawWindowCapturer( -+ const DesktopCaptureOptions& options); -+ - private: - // PipeWire types --> -+ pw_context* pw_context_ = nullptr; - pw_core* pw_core_ = nullptr; -- pw_type* pw_core_type_ = nullptr; - pw_stream* pw_stream_ = nullptr; -- pw_remote* pw_remote_ = nullptr; -- pw_loop* pw_loop_ = nullptr; - pw_thread_loop* pw_main_loop_ = nullptr; -- PipeWireType* pw_type_ = nullptr; - -+ spa_hook spa_core_listener_ = {}; - spa_hook spa_stream_listener_ = {}; -- spa_hook spa_remote_listener_ = {}; - -+ pw_core_events pw_core_events_ = {}; - pw_stream_events pw_stream_events_ = {}; -- pw_remote_events pw_remote_events_ = {}; - -- spa_video_info_raw* spa_video_format_ = nullptr; -+ struct spa_video_info_raw spa_video_format_; - -+ guint32 pw_stream_node_id_ = 0; - gint32 pw_fd_ = -1; - - CaptureSourceType capture_source_type_ = -- BaseCapturerPipeWire::CaptureSourceType::Screen; -+ BaseCapturerPipeWire::CaptureSourceType::kAny; - - // <-- end of PipeWire types - -@@ -78,33 +78,37 @@ 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 current_frame_; - Callback* callback_ = nullptr; - - bool portal_init_failed_ = false; - - void InitPortal(); - void InitPipeWire(); -- void InitPipeWireTypes(); - -- void CreateReceivingStream(); -+ pw_stream* CreateReceivingStream(); - void HandleBuffer(pw_buffer* buffer); - - void ConvertRGBxToBGRx(uint8_t* frame, uint32_t size); - -- static void OnStateChanged(void* data, -- 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, -+ int res, -+ const char *message); -+ static void OnStreamParamChanged(void *data, -+ uint32_t id, -+ const struct spa_pod *format); - static void OnStreamStateChanged(void* data, - pw_stream_state old_state, - pw_stream_state state, - const char* error_message); -- -- static void OnStreamFormatChanged(void* data, const struct spa_pod* format); - static void OnStreamProcess(void* data); - static void OnNewBuffer(void* data, uint32_t id); - -diff -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 @@ - namespace webrtc { - - ScreenCapturerPipeWire::ScreenCapturerPipeWire() -- : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::Screen) {} -+ : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::kScreen) {} - 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 @@ - namespace webrtc { - - WindowCapturerPipeWire::WindowCapturerPipeWire() -- : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::Window) {} -+ : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::kWindow) {} - 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 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-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 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 - 83.0-1 +- Updated to 83.0 +- Updated PipeWire patches from mozbz#1672944 + * Tue Nov 10 2020 Martin Stransky - 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 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( -- 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 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 mImageSurface; - AutoTArray 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 +# 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 ++#include ++#endif ++ + namespace webrtc { + + DesktopCapturer::~DesktopCapturer() = default; + + void DesktopCapturer::SetSharedMemoryFactory( + std::unique_ptr shared_memory_factory) {} + + void DesktopCapturer::SetExcludedWindow(WindowId window) {} +@@ -67,21 +72,37 @@ std::unique_ptr 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/pw2.patch b/pw2.patch new file mode 100644 index 0000000..9390460 --- /dev/null +++ b/pw2.patch @@ -0,0 +1,901 @@ + +# HG changeset patch +# User stransky +# 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 + #include + + #include + #include +-#include +-#include ++ ++#include ++#include ++#include + + #include + #include + + #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, +- pw_remote_state state, +- const char* error_message) { +- BaseCapturerPipeWire* that = static_cast(data); +- RTC_DCHECK(that); ++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; ++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) { ++ 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; +- 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; ++ } + } + } + + // static ++void BaseCapturerPipeWire::OnCoreError(void *data, ++ uint32_t id, ++ int seq, ++ int res, ++ const char *message) { ++ RTC_LOG(LS_ERROR) << "core error: " << message; ++} ++ ++// static + void BaseCapturerPipeWire::OnStreamStateChanged(void* data, + pw_stream_state old_state, + pw_stream_state state, + const char* error_message) { + BaseCapturerPipeWire* that = static_cast(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_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: + break; + } + } + + // static +-void BaseCapturerPipeWire::OnStreamFormatChanged(void* data, +- const struct spa_pod* format) { ++void BaseCapturerPipeWire::OnStreamParamChanged(void *data, uint32_t id, ++ const struct spa_pod *format) { + BaseCapturerPipeWire* that = static_cast(data); + RTC_DCHECK(that); + +- RTC_LOG(LS_INFO) << "PipeWire stream format changed."; ++ RTC_LOG(LS_INFO) << "PipeWire stream param changed."; + +- if (!format) { +- pw_stream_finish_format(that->pw_stream_, /*res=*/0, /*params=*/nullptr, +- /*n_params=*/0); ++ if (!format || id != SPA_PARAM_Format) { + return; + } + +- that->spa_video_format_ = new spa_video_info_raw(); +- spa_format_video_raw_parse(format, that->spa_video_format_, +- &that->pw_type_->format_video); ++ spa_format_video_raw_parse(format, &that->spa_video_format_); + +- auto width = that->spa_video_format_->size.width; +- auto height = that->spa_video_format_->size.height; ++ auto width = that->spa_video_format_.size.width; ++ auto height = that->spa_video_format_.size.height; + 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]; +- params[0] = reinterpret_cast(spa_pod_builder_object( +- &builder, +- // id to enumerate buffer requirements +- that->pw_core_type_->param.idBuffers, +- that->pw_core_type_->param_buffers.Buffers, +- // Size: specified as integer (i) and set to specified size +- ":", that->pw_core_type_->param_buffers.size, "i", size, +- // Stride: specified as integer (i) and set to specified stride +- ":", that->pw_core_type_->param_buffers.stride, "i", stride, +- // Buffers: specifies how many buffers we want to deal with, set as +- // integer (i) where preferred number is 8, then allowed number is defined +- // as range (r) from min and max values and it is undecided (u) to allow +- // negotiation +- ":", that->pw_core_type_->param_buffers.buffers, "iru", 8, +- SPA_POD_PROP_MIN_MAX(1, 32), +- // Align: memory alignment of the buffer, set as integer (i) to specified +- // value +- ":", that->pw_core_type_->param_buffers.align, "i", 16)); +- params[1] = reinterpret_cast(spa_pod_builder_object( +- &builder, +- // id to enumerate supported metadata +- that->pw_core_type_->param.idMeta, that->pw_core_type_->param_meta.Meta, +- // Type: specified as id or enum (I) +- ":", that->pw_core_type_->param_meta.type, "I", +- that->pw_core_type_->meta.Header, +- // 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); ++ const struct spa_pod* params[3]; ++ params[0] = reinterpret_cast(spa_pod_builder_add_object(&builder, ++ SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, ++ SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), ++ SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride), ++ SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 1, 32))); ++ params[1] = reinterpret_cast(spa_pod_builder_add_object(&builder, ++ SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, ++ SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), ++ SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)))); ++ params[2] = reinterpret_cast(spa_pod_builder_add_object(&builder, ++ SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, ++ SPA_PARAM_META_type, SPA_POD_Id (SPA_META_VideoCrop), ++ SPA_PARAM_META_size, SPA_POD_Int (sizeof(struct spa_meta_region)))); ++ pw_stream_update_params(that->pw_stream_, params, 3); + } + + // static + void BaseCapturerPipeWire::OnStreamProcess(void* data) { + BaseCapturerPipeWire* that = static_cast(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) + : capture_source_type_(source_type) {} + + BaseCapturerPipeWire::~BaseCapturerPipeWire() { + if (pw_main_loop_) { + pw_thread_loop_stop(pw_main_loop_); + } + +- if (pw_type_) { +- delete pw_type_; +- } +- +- if (spa_video_format_) { +- delete spa_video_format_; +- } +- + if (pw_stream_) { + pw_stream_destroy(pw_stream_); + } + +- if (pw_remote_) { +- pw_remote_destroy(pw_remote_); ++ if (pw_core_) { ++ pw_core_disconnect(pw_core_); + } + +- if (pw_core_) { +- pw_core_destroy(pw_core_); ++ if (pw_context_) { ++ pw_context_destroy(pw_context_); + } + + if (pw_main_loop_) { + pw_thread_loop_destroy(pw_main_loop_); + } + +- if (pw_loop_) { +- 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_); + } + 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(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_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_) { ++ RTC_LOG(LS_ERROR) << "Failed to create PipeWire context"; ++ 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_) { ++ RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context"; ++ return; ++ } + + // Initialize event handlers, remote end and stream-related. +- pw_remote_events_.version = PW_VERSION_REMOTE_EVENTS; +- pw_remote_events_.state_changed = &OnStateChanged; ++ pw_core_events_.version = PW_VERSION_CORE_EVENTS; ++ pw_core_events_.error = &OnCoreError; + + pw_stream_events_.version = PW_VERSION_STREAM_EVENTS; + pw_stream_events_.state_changed = &OnStreamStateChanged; +- pw_stream_events_.format_changed = &OnStreamFormatChanged; ++ pw_stream_events_.param_changed = &OnStreamParamChanged; + pw_stream_events_.process = &OnStreamProcess; + +- pw_remote_add_listener(pw_remote_, &spa_remote_listener_, &pw_remote_events_, +- this); +- pw_remote_connect_fd(pw_remote_, pw_fd_); ++ pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this); ++ ++ pw_stream_ = CreateReceivingStream(); ++ if (!pw_stream_) { ++ RTC_LOG(LS_ERROR) << "Failed to create PipeWire stream"; ++ return; ++ } + + if (pw_thread_loop_start(pw_main_loop_) < 0) { + RTC_LOG(LS_ERROR) << "Failed to start main PipeWire loop"; + portal_init_failed_ = true; + } + } + +-void BaseCapturerPipeWire::InitPipeWireTypes() { +- spa_type_map* map = pw_core_type_->map; +- pw_type_ = new PipeWireType(); +- +- 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); +-} ++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(desktop_size_.width()), +- static_cast(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}; +- +- pw_properties* reuseProps = pw_properties_new("pipewire.client.reuse", "1", +- /*end of varargs*/ nullptr); +- pw_stream_ = pw_stream_new(pw_remote_, "webrtc-consume-stream", reuseProps); ++ if (!stream) { ++ RTC_LOG(LS_ERROR) << "Could not create receiving stream."; ++ return nullptr; ++ } + + uint8_t buffer[1024] = {}; +- const spa_pod* params[1]; +- spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)}; +- params[0] = reinterpret_cast(spa_pod_builder_object( +- &builder, +- // id to enumerate formats +- pw_core_type_->param.idEnumFormat, pw_core_type_->spa_format, "I", +- pw_type_->media_type.video, "I", pw_type_->media_subtype.raw, +- // Video format: specified as id or enum (I), preferred format is BGRx, +- // 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), +- // 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)); ++ 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_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_builder_add_object(&builder, ++ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, ++ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), ++ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), ++ SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(5, SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx, SPA_VIDEO_FORMAT_RGBA, ++ SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_BGRA), ++ SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(&pwMinScreenBounds, ++ &pwMinScreenBounds, ++ &pwMaxScreenBounds), ++ 0)); ++ pw_stream_add_listener(stream, &spa_stream_listener_, &pw_stream_events_, this); ++ ++ if (pw_stream_connect(stream, PW_DIRECTION_INPUT, pw_stream_node_id_, ++ PW_STREAM_FLAG_AUTOCONNECT, params, 1) != 0) { + 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) { + spa_buffer* spaBuffer = buffer->buffer; +- void* src = nullptr; ++ uint8_t *map = nullptr; ++ uint8_t* src = nullptr; + +- if (!(src = spaBuffer->datas[0].data)) { ++ 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 (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(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(spaBuffer->datas[0].data); ++ break; ++ default: ++ return; ++ } ++ ++ 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; ++ } ++ ++ struct spa_meta_region* video_metadata = ++ static_cast( ++ 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(malloc(maxSize)); ++ // 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); ++ ++ if (!current_frame_ || ++ (video_metadata_use_ && !video_size_.equals(video_size_prev))) { ++ current_frame_ = ++ std::make_unique ++ (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(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_metadata_use_ && ++ (video_metadata->region.position.x + video_size_.width() <= desktop_size_.width()) ++ ? video_metadata->region.position.x * kBytesPerPixel ++ : 0; ++ ++ 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); ++ // 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 == SPA_VIDEO_FORMAT_RGBx || ++ spa_video_format_.format == SPA_VIDEO_FORMAT_RGBA) { ++ ConvertRGBxToBGRx(dst, dstStride); ++ } ++ src += srcStride - xOffset; ++ dst += dstStride; ++ } ++ ++ SpaBufferUnmap(map, ++ spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset, ++ spaBuffer->datas[0].type == SPA_DATA_DmaBuf, spaBuffer->datas[0].fd); + } + + 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 . + 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); + +- g_variant_lookup(options, "size", "(ii)", &width, &height); +- +- that->desktop_size_.set(width, height); +- ++ that->pw_stream_node_id_ = stream_id; + g_variant_unref(options); + g_variant_unref(variant); + } + } + 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 result(new BasicDesktopFrame(desktop_size_)); ++ DesktopSize frame_size = desktop_size_; ++ if (video_metadata_use_) { ++ frame_size = video_size_; ++ } ++ ++ std::unique_ptr 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; + } + 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; + } + ++// static ++std::unique_ptr ++BaseCapturerPipeWire::CreateRawScreenCapturer( ++ const DesktopCaptureOptions& options) { ++ std::unique_ptr capturer = ++ std::make_unique(BaseCapturerPipeWire::CaptureSourceType::kAny); ++ return std::move(capturer);} ++ ++// static ++std::unique_ptr ++BaseCapturerPipeWire::CreateRawWindowCapturer( ++ const DesktopCaptureOptions& options) { ++ ++ std::unique_ptr capturer = ++ std::make_unique(BaseCapturerPipeWire::CaptureSourceType::kAny); ++ return std::move(capturer); ++} ++ + } // namespace webrtc +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 + + #include "modules/desktop_capture/desktop_capture_options.h" + #include "modules/desktop_capture/desktop_capturer.h" + #include "rtc_base/constructormagic.h" + + namespace webrtc { + +-class PipeWireType { +- public: +- spa_type_media_type media_type; +- spa_type_media_subtype media_subtype; +- spa_type_format_video format_video; +- spa_type_video_format video_format; +-}; +- + 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; + + // DesktopCapturer interface. + void Start(Callback* delegate) override; + void CaptureFrame() override; + bool GetSourceList(SourceList* sources) override; + bool SelectSource(SourceId id) override; + ++ static std::unique_ptr CreateRawScreenCapturer( ++ const DesktopCaptureOptions& options); ++ ++ static std::unique_ptr CreateRawWindowCapturer( ++ const DesktopCaptureOptions& options); ++ + private: + // PipeWire types --> ++ pw_context* pw_context_ = nullptr; + pw_core* pw_core_ = nullptr; +- pw_type* pw_core_type_ = nullptr; + pw_stream* pw_stream_ = nullptr; +- pw_remote* pw_remote_ = nullptr; +- pw_loop* pw_loop_ = nullptr; + pw_thread_loop* pw_main_loop_ = nullptr; +- PipeWireType* pw_type_ = nullptr; + ++ spa_hook spa_core_listener_ = {}; + spa_hook spa_stream_listener_ = {}; +- spa_hook spa_remote_listener_ = {}; + ++ pw_core_events pw_core_events_ = {}; + pw_stream_events pw_stream_events_ = {}; +- pw_remote_events pw_remote_events_ = {}; + +- spa_video_info_raw* spa_video_format_ = nullptr; ++ struct spa_video_info_raw spa_video_format_; + ++ guint32 pw_stream_node_id_ = 0; + gint32 pw_fd_ = -1; + + CaptureSourceType capture_source_type_ = +- BaseCapturerPipeWire::CaptureSourceType::Screen; ++ BaseCapturerPipeWire::CaptureSourceType::kAny; + + // <-- end of PipeWire types + + 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_metadata_use_ = false; ++ DesktopSize video_size_; + DesktopSize desktop_size_ = {}; + DesktopCaptureOptions options_ = {}; + +- uint8_t* current_frame_ = nullptr; ++ std::unique_ptr current_frame_; + Callback* callback_ = nullptr; + + bool portal_init_failed_ = false; + + void InitPortal(); + void InitPipeWire(); +- void InitPipeWireTypes(); + +- void CreateReceivingStream(); ++ pw_stream* CreateReceivingStream(); + void HandleBuffer(pw_buffer* buffer); + + void ConvertRGBxToBGRx(uint8_t* frame, uint32_t size); + +- static void OnStateChanged(void* data, +- pw_remote_state old_state, +- pw_remote_state state, +- const char* error); ++ static void OnCoreError(void *data, ++ uint32_t id, ++ int seq, ++ int res, ++ const char *message); ++ static void OnStreamParamChanged(void *data, ++ uint32_t id, ++ const struct spa_pod *format); + static void OnStreamStateChanged(void* data, + pw_stream_state old_state, + pw_stream_state state, + const char* error_message); +- +- static void OnStreamFormatChanged(void* data, const struct spa_pod* format); + static void OnStreamProcess(void* data); + static void OnNewBuffer(void* data, uint32_t id); + + 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 + + namespace webrtc { + + ScreenCapturerPipeWire::ScreenCapturerPipeWire() +- : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::Screen) {} ++ : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::kScreen) {} + ScreenCapturerPipeWire::~ScreenCapturerPipeWire() {} + + // static + std::unique_ptr + ScreenCapturerPipeWire::CreateRawScreenCapturer( + const DesktopCaptureOptions& options) { + return std::make_unique(); + } +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 + + namespace webrtc { + + WindowCapturerPipeWire::WindowCapturerPipeWire() +- : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::Window) {} ++ : BaseCapturerPipeWire(BaseCapturerPipeWire::CaptureSourceType::kWindow) {} + WindowCapturerPipeWire::~WindowCapturerPipeWire() {} + + // static + std::unique_ptr + WindowCapturerPipeWire::CreateRawWindowCapturer( + const DesktopCaptureOptions& options) { + return std::make_unique(); + } +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::CreateRawScreenCapturer( + 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) + + #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::CreateRawWindowCapturer( + 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) + + #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 +# 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 +# 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 ++#include "mozilla/Types.h" ++#include "prlink.h" ++ ++#include ++ ++#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 ++ ++#include ++ ++/** \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 ++ ++#include ++#include ++ ++#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 ++#include ++ ++#include ++#include ++ ++#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 ++#include ++ ++/** \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 ++#include ++#include ++ ++/** \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 ++ ++/** \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 ++ ++/** 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 ++#include ++ ++#include ++ ++#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 ++#include ++#include ++ ++/** 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 ++ ++/** \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 ++#include ++ ++/** 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 ++#include ++ ++#include ++ ++#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 ++#include ++ ++#include ++#include ++ ++#include ++ ++#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 ++#include ++#include ++ ++#include ++#include ++ ++/** \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 ++ ++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 ++ ++/** \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 ++#include ++#include ++#include ++#include ++ ++/** \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 ++#include ++#include ++#include ++ ++/** 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 ++ ++#include ++#include ++#include ++#include ++ ++/** 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 ++#include ++#include ++#include ++#include ++ ++/** 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 ++ ++/** \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 ++ * @author Linus Svensson ++ * 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 ++ ++#include ++ ++#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 ++#include ++ ++#include ++ ++/** 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 ++ ++/** \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 ++ ++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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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--, 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 "x ++#include ++ ++#include ++ ++#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 ++ ++#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 ++#include ++ ++/** \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 ++ ++/** 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 ++#include ++ ++#include ++#include ++ ++/** \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 ++ ++#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 ++#include ++ ++#include ++ ++#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 ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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 ++ ++/** \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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/** \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: ++ * ++ * <level>[:<category>,...] ++ * ++ * - <level>: 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. ++ * ++ * - <category>: 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 ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#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 ++ ++#include ++ ++/** \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 ++ ++struct pw_protocol; ++ ++#include ++#include ++#include ++ ++#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 ++ ++/** \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 ++ ++/** 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 ++ ++/** \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 ++ ++/** 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 ++#include ++ ++/** \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 ++ ++/** 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 ++ ++/** \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 ++ ++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 ++#include ++ ++/** \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 ++ ++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 ++ ++/** 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 ++ * || | ... | 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 ++ * ||| | ... | 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 ++ * || | ... | ++ * || +------------------------------+ ++ * +->| struct spa_chunk | memory for n_datas chunks ++ * | | uint32_t offset | ++ * | | uint32_t size | ++ * | | int32_t stride | ++ * | | int32_t dummy | ++ * | | ... chunks | ++ * | +------------------------------+ ++ * +>| data | memory for n_datas data, aligned ++ * | ... 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 ++#include ++ ++/** \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 ++#include ++ ++/** \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 ++#include ++#include ++ ++#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 ++#include ++ ++/** 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 ++#include ++#include ++ ++/* 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 ++#include ++#include ++ ++#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 ++ ++#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 ++#include ++#include ++#include ++ ++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 ++ ++#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 ++#include ++ ++#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 ++#include ++#include ++#include ++ ++#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 ++ ++#include ++ ++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 ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++ ++/** ++ * 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 ++ ++/* 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 ++#include ++ ++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 ++ ++/* 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 ++ ++/* 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 ++#include ++ ++/** 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 ++#include ++#include ++#include ++#include ++#include ++ ++ ++/** ++ * 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 ++ ++#include ++#include ++#include ++ ++#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 ++ ++#include ++ ++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 ++#include ++#include ++#include ++ ++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 ++#include ++ ++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 ++#endif ++ ++#include ++ ++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 ++ ++#ifndef __FreeBSD__ ++#include ++#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 ++ ++#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 ++#include ++ ++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 ++ ++/** 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 ++ ++/** 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 ++ ++/** 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 ++ ++/** 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 ++#include ++#include ++#include ++ ++/* 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 ++#include ++ ++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 ++ ++#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 ++ ++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 ++#include ++#include ++#include ++ ++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 ++#include ++ ++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 ++#include ++#include ++#include ++ ++#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 ++ ++#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 ++ ++#include ++#include ++#include ++ ++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 ++#include ++ ++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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++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 ++ ++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 ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++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 ++#include ++ ++#include ++ ++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 ++#include ++ ++#include ++#include ++ ++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 ++#include ++ ++#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 ++ ++#include ++ ++#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 ++ ++#include ++#include ++ ++/** ++ * 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 ++ ++#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 ++ ++#include ++#include ++ ++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 ++ ++#include ++#include ++ ++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 ++#include ++#include ++ ++#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 ++#include ++ ++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 ++#include ++ ++#include ++#include ++ ++/** ++ * 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 ++#endif ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++#include ++ ++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 ++#include ++ ++/** \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 ++#include ++ ++#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 ++ ++#include ++ ++/** ++ * 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 ++ ++#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 ++ ++/* 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 ++#include ++#include ++ ++/* 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 ++ ++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 +# 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 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: + // \x1e +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