summaryrefslogtreecommitdiffstats
path: root/third_party/pipewire/spa
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /third_party/pipewire/spa
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/pipewire/spa')
-rw-r--r--third_party/pipewire/spa/buffer/alloc.h346
-rw-r--r--third_party/pipewire/spa/buffer/buffer.h131
-rw-r--r--third_party/pipewire/spa/buffer/meta.h163
-rw-r--r--third_party/pipewire/spa/buffer/type-info.h93
-rw-r--r--third_party/pipewire/spa/control/control.h62
-rw-r--r--third_party/pipewire/spa/control/type-info.h61
-rw-r--r--third_party/pipewire/spa/debug/buffer.h129
-rw-r--r--third_party/pipewire/spa/debug/dict.h58
-rw-r--r--third_party/pipewire/spa/debug/format.h212
-rw-r--r--third_party/pipewire/spa/debug/log.h53
-rw-r--r--third_party/pipewire/spa/debug/mem.h67
-rw-r--r--third_party/pipewire/spa/debug/node.h63
-rw-r--r--third_party/pipewire/spa/debug/pod.h215
-rw-r--r--third_party/pipewire/spa/debug/types.h127
-rw-r--r--third_party/pipewire/spa/graph/graph.h365
-rw-r--r--third_party/pipewire/spa/interfaces/audio/aec.h95
-rw-r--r--third_party/pipewire/spa/monitor/device.h307
-rw-r--r--third_party/pipewire/spa/monitor/event.h63
-rw-r--r--third_party/pipewire/spa/monitor/type-info.h67
-rw-r--r--third_party/pipewire/spa/monitor/utils.h106
-rw-r--r--third_party/pipewire/spa/node/command.h73
-rw-r--r--third_party/pipewire/spa/node/event.h64
-rw-r--r--third_party/pipewire/spa/node/io.h304
-rw-r--r--third_party/pipewire/spa/node/keys.h64
-rw-r--r--third_party/pipewire/spa/node/node.h674
-rw-r--r--third_party/pipewire/spa/node/type-info.h107
-rw-r--r--third_party/pipewire/spa/node/utils.h158
-rw-r--r--third_party/pipewire/spa/param/audio/dsd.h81
-rw-r--r--third_party/pipewire/spa/param/audio/format-utils.h201
-rw-r--r--third_party/pipewire/spa/param/audio/format.h61
-rw-r--r--third_party/pipewire/spa/param/audio/iec958.h69
-rw-r--r--third_party/pipewire/spa/param/audio/layout.h192
-rw-r--r--third_party/pipewire/spa/param/audio/raw.h324
-rw-r--r--third_party/pipewire/spa/param/audio/type-info.h296
-rw-r--r--third_party/pipewire/spa/param/bluetooth/audio.h66
-rw-r--r--third_party/pipewire/spa/param/bluetooth/type-info.h71
-rw-r--r--third_party/pipewire/spa/param/format-utils.h58
-rw-r--r--third_party/pipewire/spa/param/format.h164
-rw-r--r--third_party/pipewire/spa/param/latency-utils.h197
-rw-r--r--third_party/pipewire/spa/param/param.h212
-rw-r--r--third_party/pipewire/spa/param/profiler.h97
-rw-r--r--third_party/pipewire/spa/param/props.h131
-rw-r--r--third_party/pipewire/spa/param/type-info.h448
-rw-r--r--third_party/pipewire/spa/param/video/chroma.h64
-rw-r--r--third_party/pipewire/spa/param/video/color.h125
-rw-r--r--third_party/pipewire/spa/param/video/encoded.h74
-rw-r--r--third_party/pipewire/spa/param/video/format-utils.h233
-rw-r--r--third_party/pipewire/spa/param/video/format.h59
-rw-r--r--third_party/pipewire/spa/param/video/multiview.h134
-rw-r--r--third_party/pipewire/spa/param/video/raw.h226
-rw-r--r--third_party/pipewire/spa/param/video/type-info.h140
-rw-r--r--third_party/pipewire/spa/pod/builder.h696
-rw-r--r--third_party/pipewire/spa/pod/command.h69
-rw-r--r--third_party/pipewire/spa/pod/compare.h190
-rw-r--r--third_party/pipewire/spa/pod/dynamic.h81
-rw-r--r--third_party/pipewire/spa/pod/event.h68
-rw-r--r--third_party/pipewire/spa/pod/filter.h422
-rw-r--r--third_party/pipewire/spa/pod/iter.h475
-rw-r--r--third_party/pipewire/spa/pod/parser.h583
-rw-r--r--third_party/pipewire/spa/pod/pod.h246
-rw-r--r--third_party/pipewire/spa/pod/vararg.h113
-rw-r--r--third_party/pipewire/spa/support/cpu.h168
-rw-r--r--third_party/pipewire/spa/support/dbus.h167
-rw-r--r--third_party/pipewire/spa/support/i18n.h107
-rw-r--r--third_party/pipewire/spa/support/log-impl.h143
-rw-r--r--third_party/pipewire/spa/support/log.h314
-rw-r--r--third_party/pipewire/spa/support/loop.h327
-rw-r--r--third_party/pipewire/spa/support/plugin-loader.h101
-rw-r--r--third_party/pipewire/spa/support/plugin.h229
-rw-r--r--third_party/pipewire/spa/support/system.h165
-rw-r--r--third_party/pipewire/spa/support/thread.h149
-rw-r--r--third_party/pipewire/spa/utils/ansi.h114
-rw-r--r--third_party/pipewire/spa/utils/defs.h351
-rw-r--r--third_party/pipewire/spa/utils/dict.h120
-rw-r--r--third_party/pipewire/spa/utils/dll.h71
-rw-r--r--third_party/pipewire/spa/utils/hook.h471
-rw-r--r--third_party/pipewire/spa/utils/json-pod.h177
-rw-r--r--third_party/pipewire/spa/utils/json.h483
-rw-r--r--third_party/pipewire/spa/utils/keys.h144
-rw-r--r--third_party/pipewire/spa/utils/list.h161
-rw-r--r--third_party/pipewire/spa/utils/names.h158
-rw-r--r--third_party/pipewire/spa/utils/result.h72
-rw-r--r--third_party/pipewire/spa/utils/ringbuffer.h188
-rw-r--r--third_party/pipewire/spa/utils/string.h387
-rw-r--r--third_party/pipewire/spa/utils/type-info.h140
-rw-r--r--third_party/pipewire/spa/utils/type.h153
86 files changed, 15953 insertions, 0 deletions
diff --git a/third_party/pipewire/spa/buffer/alloc.h b/third_party/pipewire/spa/buffer/alloc.h
new file mode 100644
index 0000000000..6417073838
--- /dev/null
+++ b/third_party/pipewire/spa/buffer/alloc.h
@@ -0,0 +1,346 @@
+/* Simple Plugin API
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#ifndef SPA_BUFFER_ALLOC_H
+#define SPA_BUFFER_ALLOC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/buffer/buffer.h>
+
+/**
+ * \addtogroup spa_buffer
+ * \{
+ */
+
+/** information about the buffer layout */
+struct spa_buffer_alloc_info {
+#define SPA_BUFFER_ALLOC_FLAG_INLINE_META (1<<0) /**< add metadata data in the skeleton */
+#define SPA_BUFFER_ALLOC_FLAG_INLINE_CHUNK (1<<1) /**< add chunk data in the skeleton */
+#define SPA_BUFFER_ALLOC_FLAG_INLINE_DATA (1<<2) /**< add buffer data to the skeleton */
+#define SPA_BUFFER_ALLOC_FLAG_INLINE_ALL 0b111
+#define SPA_BUFFER_ALLOC_FLAG_NO_DATA (1<<3) /**< don't set data pointers */
+ uint32_t flags;
+ uint32_t max_align; /**< max of all alignments */
+ uint32_t n_metas;
+ uint32_t n_datas;
+ struct spa_meta *metas;
+ struct spa_data *datas;
+ uint32_t *data_aligns;
+ size_t skel_size; /**< size of the struct spa_buffer and inlined meta/chunk/data */
+ size_t meta_size; /**< size of the meta if not inlined */
+ size_t chunk_size; /**< size of the chunk if not inlined */
+ size_t data_size; /**< size of the data if not inlined */
+ size_t mem_size; /**< size of the total memory if not inlined */
+};
+
+/**
+ * Fill buffer allocation information
+ *
+ * Fill \a info with allocation information needed to allocate buffers
+ * with the given number of metadata and data members.
+ *
+ * The required size of the skeleton (the struct spa_buffer) information
+ * and the memory (for the metadata, chunk and buffer memory) will be
+ * calculated.
+ *
+ * The flags member in \a info should be configured before calling this
+ * functions.
+ *
+ * \param info the information to fill
+ * \param n_metas the number of metadatas for the buffer
+ * \param metas an array of metadata items
+ * \param n_datas the number of datas for the buffer
+ * \param datas an array of \a n_datas items
+ * \param data_aligns \a n_datas alignments
+ * \return 0 on success.
+ * */
+static inline int spa_buffer_alloc_fill_info(struct spa_buffer_alloc_info *info,
+ uint32_t n_metas, struct spa_meta metas[],
+ uint32_t n_datas, struct spa_data datas[],
+ uint32_t data_aligns[])
+{
+ size_t size, *target;
+ uint32_t i;
+
+ info->n_metas = n_metas;
+ info->metas = metas;
+ info->n_datas = n_datas;
+ info->datas = datas;
+ info->data_aligns = data_aligns;
+ info->max_align = 16;
+ info->mem_size = 0;
+ /*
+ * The buffer skeleton is placed in memory like below and can
+ * be accessed as a regular structure.
+ *
+ * +==============================+
+ * | struct spa_buffer |
+ * | uint32_t n_metas | number of metas
+ * | uint32_t n_datas | number of datas
+ * +-| struct spa_meta *metas | pointer to array of metas
+ * +|-| struct spa_data *datas | pointer to array of datas
+ * || +------------------------------+
+ * |+>| struct spa_meta |
+ * | | uint32_t type | metadata
+ * | | uint32_t size | size of metadata
+ * +|--| void *data | pointer to metadata
+ * || | ... <n_metas> | more spa_meta follow
+ * || +------------------------------+
+ * |+->| struct spa_data |
+ * | | uint32_t type | memory type
+ * | | uint32_t flags |
+ * | | int fd | fd of shared memory block
+ * | | uint32_t mapoffset | offset in shared memory of data
+ * | | uint32_t maxsize | size of data block
+ * | +-| void *data | pointer to data
+ * |+|-| struct spa_chunk *chunk | pointer to chunk
+ * ||| | ... <n_datas> | more spa_data follow
+ * ||| +==============================+
+ * VVV
+ *
+ * metadata, chunk and memory can either be placed right
+ * after the skeleton (inlined) or in a separate piece of memory.
+ *
+ * vvv
+ * ||| +==============================+
+ * +-->| meta data memory | metadata memory, 8 byte aligned
+ * || | ... <n_metas> |
+ * || +------------------------------+
+ * +->| struct spa_chunk | memory for n_datas chunks
+ * | | uint32_t offset |
+ * | | uint32_t size |
+ * | | int32_t stride |
+ * | | int32_t dummy |
+ * | | ... <n_datas> chunks |
+ * | +------------------------------+
+ * +>| data | memory for n_datas data, aligned
+ * | ... <n_datas> blocks | according to alignments
+ * +==============================+
+ */
+ info->skel_size = sizeof(struct spa_buffer);
+ info->skel_size += n_metas * sizeof(struct spa_meta);
+ info->skel_size += n_datas * sizeof(struct spa_data);
+
+ for (i = 0, size = 0; i < n_metas; i++)
+ size += SPA_ROUND_UP_N(metas[i].size, 8);
+ info->meta_size = size;
+
+ if (SPA_FLAG_IS_SET(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_META))
+ target = &info->skel_size;
+ else
+ target = &info->mem_size;
+ *target += info->meta_size;
+
+ info->chunk_size = n_datas * sizeof(struct spa_chunk);
+ if (SPA_FLAG_IS_SET(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_CHUNK))
+ target = &info->skel_size;
+ else
+ target = &info->mem_size;
+ *target += info->chunk_size;
+
+ for (i = 0, size = 0; i < n_datas; i++) {
+ info->max_align = SPA_MAX(info->max_align, data_aligns[i]);
+ size = SPA_ROUND_UP_N(size, data_aligns[i]);
+ size += datas[i].maxsize;
+ }
+ info->data_size = size;
+
+ if (!SPA_FLAG_IS_SET(info->flags, SPA_BUFFER_ALLOC_FLAG_NO_DATA) &&
+ SPA_FLAG_IS_SET(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_DATA))
+ target = &info->skel_size;
+ else
+ target = &info->mem_size;
+
+ *target = SPA_ROUND_UP_N(*target, n_datas ? data_aligns[0] : 1);
+ *target += info->data_size;
+ *target = SPA_ROUND_UP_N(*target, info->max_align);
+
+ return 0;
+}
+
+/**
+ * Fill skeleton and data according to the allocation info
+ *
+ * Use the allocation info to create a struct \ref 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 struct \ref spa_buffer and the
+ * pointers to meta, chunk and memory.
+ * \param data_mem memory to hold the meta, chunk and memory
+ * \return a struct \ref 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_PTROFF(b, sizeof(struct spa_buffer), struct spa_meta);
+ b->n_datas = info->n_datas;
+ b->datas = SPA_PTROFF(b->metas, info->n_metas * sizeof(struct spa_meta), struct spa_data);
+
+ skel = SPA_PTROFF(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_PTROFF(*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_PTROFF(skel, size, void);
+ }
+ else {
+ cp = (struct spa_chunk*)data;
+ data = SPA_PTROFF(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_PTROFF(*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 buffers a array with space to hold \a n_buffers pointers to buffers
+ * \param skel_mem memory for the struct \ref 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_PTROFF(skel_mem, info->skel_size, void);
+ data_mem = SPA_PTROFF(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 struct \ref 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_PTROFF(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
index 0000000000..47204acbcf
--- /dev/null
+++ b/third_party/pipewire/spa/buffer/buffer.h
@@ -0,0 +1,131 @@
+/* Simple Plugin API
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_BUFFER_H
+#define SPA_BUFFER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/defs.h>
+#include <spa/buffer/meta.h>
+
+/** \defgroup spa_buffer Buffers
+ *
+ * Buffers describe the data and metadata that is exchanged between
+ * ports of a node.
+ */
+
+/**
+ * \addtogroup spa_buffer
+ * \{
+ */
+
+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 */
+#define SPA_CHUNK_FLAG_EMPTY (1u<<1) /**< chunk data is empty with media specific
+ * neutral data such as silence or black. This
+ * could be used to optimize processing. */
+ 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. SPA_ID_INVALID is a special
+ * value for the allocator to indicate that the
+ * other side did not explicitly specify any
+ * supported data types. It should probably use
+ * a memory type that does not require special
+ * handling in addition to simple mmap/munmap. */
+#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
index 0000000000..ae72ef970e
--- /dev/null
+++ b/third_party/pipewire/spa/buffer/meta.h
@@ -0,0 +1,163 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_META_H
+#define SPA_META_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/defs.h>
+#include <spa/pod/pod.h>
+
+/**
+ * \addtogroup spa_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, where an invalid entry or end-of-array marks the end. */
+ 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_Busy, /**< don't write to buffer when count > 0 */
+
+ _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_PTROFF((m)->data,(m)->size,void)
+#define spa_meta_check(p,m) (SPA_PTROFF(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 in nanoseconds */
+ 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;
+};
+
+/** a busy counter for the buffer */
+struct spa_meta_busy {
+ uint32_t flags;
+ uint32_t count; /**< number of users busy with the buffer */
+};
+
+/**
+ * \}
+ */
+
+#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
index 0000000000..50f436b864
--- /dev/null
+++ b/third_party/pipewire/spa/buffer/type-info.h
@@ -0,0 +1,93 @@
+/* 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
+
+/**
+ * \addtogroup spa_buffer
+ * \{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/buffer/buffer.h>
+#include <spa/buffer/meta.h>
+#include <spa/utils/type.h>
+
+#define SPA_TYPE_INFO_Buffer SPA_TYPE_INFO_POINTER_BASE "Buffer"
+#define SPA_TYPE_INFO_BUFFER_BASE SPA_TYPE_INFO_Buffer ":"
+
+/** Buffers contain data of a certain type */
+#define SPA_TYPE_INFO_Data SPA_TYPE_INFO_ENUM_BASE "Data"
+#define SPA_TYPE_INFO_DATA_BASE SPA_TYPE_INFO_Data ":"
+
+/** base type for fd based memory */
+#define SPA_TYPE_INFO_DATA_Fd SPA_TYPE_INFO_DATA_BASE "Fd"
+#define SPA_TYPE_INFO_DATA_FD_BASE SPA_TYPE_INFO_DATA_Fd ":"
+
+static const struct spa_type_info spa_type_data_type[] = {
+ { SPA_DATA_Invalid, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "Invalid", NULL },
+ { SPA_DATA_MemPtr, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "MemPtr", NULL },
+ { SPA_DATA_MemFd, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_FD_BASE "MemFd", NULL },
+ { SPA_DATA_DmaBuf, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_FD_BASE "DmaBuf", NULL },
+ { SPA_DATA_MemId, SPA_TYPE_Int, SPA_TYPE_INFO_DATA_BASE "MemId", NULL },
+ { 0, 0, NULL, NULL },
+};
+
+#define SPA_TYPE_INFO_Meta SPA_TYPE_INFO_POINTER_BASE "Meta"
+#define SPA_TYPE_INFO_META_BASE SPA_TYPE_INFO_Meta ":"
+
+#define SPA_TYPE_INFO_META_Array SPA_TYPE_INFO_META_BASE "Array"
+#define SPA_TYPE_INFO_META_ARRAY_BASE SPA_TYPE_INFO_META_Array ":"
+
+#define SPA_TYPE_INFO_META_Region SPA_TYPE_INFO_META_BASE "Region"
+#define SPA_TYPE_INFO_META_REGION_BASE SPA_TYPE_INFO_META_Region ":"
+
+#define SPA_TYPE_INFO_META_ARRAY_Region SPA_TYPE_INFO_META_ARRAY_BASE "Region"
+#define SPA_TYPE_INFO_META_ARRAY_REGION_BASE SPA_TYPE_INFO_META_ARRAY_Region ":"
+
+static const struct spa_type_info spa_type_meta_type[] = {
+ { SPA_META_Invalid, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Invalid", NULL },
+ { SPA_META_Header, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Header", NULL },
+ { SPA_META_VideoCrop, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_REGION_BASE "VideoCrop", NULL },
+ { SPA_META_VideoDamage, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_ARRAY_REGION_BASE "VideoDamage", NULL },
+ { SPA_META_Bitmap, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Bitmap", NULL },
+ { SPA_META_Cursor, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Cursor", NULL },
+ { SPA_META_Control, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Control", NULL },
+ { SPA_META_Busy, SPA_TYPE_Pointer, SPA_TYPE_INFO_META_BASE "Busy", 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
index 0000000000..931ae01a7f
--- /dev/null
+++ b/third_party/pipewire/spa/control/control.h
@@ -0,0 +1,62 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_CONTROL_H
+#define SPA_CONTROL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/defs.h>
+#include <spa/pod/pod.h>
+
+/** \defgroup spa_control Control
+ * Control type declarations
+ */
+
+/**
+ * \addtogroup spa_control
+ * \{
+ */
+
+/** 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
index 0000000000..d703783714
--- /dev/null
+++ b/third_party/pipewire/spa/control/type-info.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_CONTROL_TYPES_H
+#define SPA_CONTROL_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup spa_control
+ * \{
+ */
+
+#include <spa/utils/defs.h>
+#include <spa/utils/type-info.h>
+#include <spa/control/control.h>
+
+/* base for parameter object enumerations */
+#define SPA_TYPE_INFO_Control SPA_TYPE_INFO_ENUM_BASE "Control"
+#define SPA_TYPE_INFO_CONTROL_BASE SPA_TYPE_INFO_Control ":"
+
+static const struct spa_type_info spa_type_control[] = {
+ { SPA_CONTROL_Invalid, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "Invalid", NULL },
+ { SPA_CONTROL_Properties, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "Properties", NULL },
+ { SPA_CONTROL_Midi, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "Midi", NULL },
+ { SPA_CONTROL_OSC, SPA_TYPE_Int, SPA_TYPE_INFO_CONTROL_BASE "OSC", NULL },
+ { 0, 0, NULL, NULL },
+};
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_CONTROL_TYPES_H */
diff --git a/third_party/pipewire/spa/debug/buffer.h b/third_party/pipewire/spa/debug/buffer.h
new file mode 100644
index 0000000000..df6beaad43
--- /dev/null
+++ b/third_party/pipewire/spa/debug/buffer.h
@@ -0,0 +1,129 @@
+/* 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
+
+/** \defgroup spa_debug Debug
+ * Debugging utilities
+ */
+
+/**
+ * \addtogroup spa_debug
+ * \{
+ */
+
+#include <spa/debug/log.h>
+#include <spa/debug/mem.h>
+#include <spa/debug/types.h>
+#include <spa/buffer/type-info.h>
+
+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
index 0000000000..10498f27c9
--- /dev/null
+++ b/third_party/pipewire/spa/debug/dict.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_DEBUG_DICT_H
+#define SPA_DEBUG_DICT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup spa_debug
+ * \{
+ */
+
+#include <spa/debug/log.h>
+#include <spa/utils/dict.h>
+
+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
index 0000000000..6f9e0a3be0
--- /dev/null
+++ b/third_party/pipewire/spa/debug/format.h
@@ -0,0 +1,212 @@
+/* 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
+
+/**
+ * \addtogroup spa_debug
+ * \{
+ */
+
+#include <spa/pod/parser.h>
+#include <spa/debug/log.h>
+#include <spa/debug/types.h>
+#include <spa/param/type-info.h>
+#include <spa/param/format-utils.h>
+
+static inline int
+spa_debug_format_value(const struct spa_type_info *info,
+ uint32_t type, void *body, uint32_t size)
+{
+ switch (type) {
+ case SPA_TYPE_Bool:
+ spa_debugn("%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;
+ }
+ spa_debugn("%s", str);
+ break;
+ }
+ case SPA_TYPE_Int:
+ spa_debugn("%d", *(int32_t *) body);
+ break;
+ case SPA_TYPE_Long:
+ spa_debugn("%" PRIi64, *(int64_t *) body);
+ break;
+ case SPA_TYPE_Float:
+ spa_debugn("%f", *(float *) body);
+ break;
+ case SPA_TYPE_Double:
+ spa_debugn("%f", *(double *) body);
+ break;
+ case SPA_TYPE_String:
+ spa_debugn("%s", (char *) body);
+ break;
+ case SPA_TYPE_Rectangle:
+ {
+ struct spa_rectangle *r = (struct spa_rectangle *)body;
+ spa_debugn("%" PRIu32 "x%" PRIu32, r->width, r->height);
+ break;
+ }
+ case SPA_TYPE_Fraction:
+ {
+ struct spa_fraction *f = (struct spa_fraction *)body;
+ spa_debugn("%" PRIu32 "/%" PRIu32, f->num, f->denom);
+ break;
+ }
+ case SPA_TYPE_Bitmap:
+ spa_debugn("Bitmap");
+ break;
+ case SPA_TYPE_Bytes:
+ spa_debugn("Bytes");
+ break;
+ case SPA_TYPE_Array:
+ {
+ void *p;
+ struct spa_pod_array_body *b = (struct spa_pod_array_body *)body;
+ int i = 0;
+ info = info && info->values ? info->values : info;
+ spa_debugn("< ");
+ SPA_POD_ARRAY_BODY_FOREACH(b, size, p) {
+ if (i++ > 0)
+ spa_debugn(", ");
+ spa_debug_format_value(info, b->child.type, p, b->child.size);
+ }
+ spa_debugn(" >");
+ break;
+ }
+ default:
+ spa_debugn("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);
+
+ spa_debug("%*s %s/%s", 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;
+
+ spa_debugn("%*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;
+ }
+
+ spa_debugn("%s", ssep);
+
+ for (i = 1; i < n_vals; i++) {
+ vals = SPA_PTROFF(vals, size, void);
+ if (i > 1)
+ spa_debugn("%s", sep);
+ spa_debug_format_value(ti ? ti->values : NULL, type, vals, size);
+ }
+ spa_debugn("%s", esep);
+ }
+ spa_debugn("\n");
+ }
+ return 0;
+}
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_DEBUG_FORMAT_H */
diff --git a/third_party/pipewire/spa/debug/log.h b/third_party/pipewire/spa/debug/log.h
new file mode 100644
index 0000000000..8311e95460
--- /dev/null
+++ b/third_party/pipewire/spa/debug/log.h
@@ -0,0 +1,53 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2022 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_LOG_H
+#define SPA_DEBUG_LOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+/**
+ * \addtogroup spa_debug
+ * \{
+ */
+
+#ifndef spa_debug
+#define spa_debug(fmt,...) ({ printf(fmt"\n", ## __VA_ARGS__); })
+#endif
+#ifndef spa_debugn
+#define spa_debugn(fmt,...) ({ printf(fmt, ## __VA_ARGS__); })
+#endif
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_DEBUG_LOGH */
diff --git a/third_party/pipewire/spa/debug/mem.h b/third_party/pipewire/spa/debug/mem.h
new file mode 100644
index 0000000000..4999c00bd4
--- /dev/null
+++ b/third_party/pipewire/spa/debug/mem.h
@@ -0,0 +1,67 @@
+/* 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 <inttypes.h>
+
+/**
+ * \addtogroup spa_debug
+ * \{
+ */
+
+#include <spa/debug/log.h>
+
+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
index 0000000000..6ec14b4fa3
--- /dev/null
+++ b/third_party/pipewire/spa/debug/node.h
@@ -0,0 +1,63 @@
+/* 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
+
+/**
+ * \addtogroup spa_debug
+ * \{
+ */
+
+#include <spa/node/node.h>
+#include <spa/debug/log.h>
+#include <spa/debug/dict.h>
+
+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
index 0000000000..1468aab041
--- /dev/null
+++ b/third_party/pipewire/spa/debug/pod.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_DEBUG_POD_H
+#define SPA_DEBUG_POD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup spa_debug
+ * \{
+ */
+
+#include <spa/debug/log.h>
+#include <spa/debug/mem.h>
+#include <spa/debug/types.h>
+#include <spa/pod/pod.h>
+#include <spa/pod/iter.h>
+
+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");
+
+ info = info && info->values ? info->values : info;
+ 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
index 0000000000..55fb379aa7
--- /dev/null
+++ b/third_party/pipewire/spa/debug/types.h
@@ -0,0 +1,127 @@
+/* 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
+
+/**
+ * \addtogroup spa_debug
+ * \{
+ */
+
+#include <spa/utils/type-info.h>
+
+#include <string.h>
+
+static inline const struct spa_type_info *spa_debug_type_find(const struct spa_type_info *info, uint32_t type)
+{
+ const struct spa_type_info *res;
+
+ if (info == NULL)
+ info = SPA_TYPE_ROOT;
+
+ while (info && info->name) {
+ if (info->type == SPA_ID_INVALID) {
+ if (info->values && (res = spa_debug_type_find(info->values, type)))
+ return res;
+ }
+ else if (info->type == type)
+ return info;
+ info++;
+ }
+ return NULL;
+}
+
+static inline const char *spa_debug_type_short_name(const char *name)
+{
+ const char *h;
+ if ((h = strrchr(name, ':')) != NULL)
+ name = h + 1;
+ return name;
+}
+
+static inline const char *spa_debug_type_find_name(const struct spa_type_info *info, uint32_t type)
+{
+ if ((info = spa_debug_type_find(info, type)) == NULL)
+ return NULL;
+ return info->name;
+}
+
+static inline const char *spa_debug_type_find_short_name(const struct spa_type_info *info, uint32_t type)
+{
+ const char *str;
+ if ((str = spa_debug_type_find_name(info, type)) == NULL)
+ return NULL;
+ return spa_debug_type_short_name(str);
+}
+
+static inline uint32_t spa_debug_type_find_type(const struct spa_type_info *info, const char *name)
+{
+ if (info == NULL)
+ info = SPA_TYPE_ROOT;
+
+ while (info && info->name) {
+ uint32_t res;
+ if (strcmp(info->name, name) == 0)
+ return info->type;
+ if (info->values && (res = spa_debug_type_find_type(info->values, name)) != SPA_ID_INVALID)
+ return res;
+ info++;
+ }
+ return SPA_ID_INVALID;
+}
+
+static inline const struct spa_type_info *spa_debug_type_find_short(const struct spa_type_info *info, const char *name)
+{
+ while (info && info->name) {
+ if (strcmp(spa_debug_type_short_name(info->name), name) == 0)
+ return info;
+ if (strcmp(info->name, name) == 0)
+ return info;
+ if (info->type != 0 && info->type == (uint32_t)atoi(name))
+ return info;
+ info++;
+ }
+ return NULL;
+}
+
+static inline uint32_t spa_debug_type_find_type_short(const struct spa_type_info *info, const char *name)
+{
+ if ((info = spa_debug_type_find_short(info, name)) == NULL)
+ return SPA_ID_INVALID;
+ return info->type;
+}
+/**
+ * \}
+ */
+
+#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
index 0000000000..ba402481c9
--- /dev/null
+++ b/third_party/pipewire/spa/graph/graph.h
@@ -0,0 +1,365 @@
+/* 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
+
+/** \defgroup spa_graph Graph
+ * Node graph
+ */
+
+/**
+ * \addtogroup spa_graph
+ * \{
+ */
+
+#include <spa/utils/defs.h>
+#include <spa/utils/list.h>
+#include <spa/utils/hook.h>
+#include <spa/node/node.h>
+#include <spa/node/io.h>
+
+#ifndef spa_debug
+#define spa_debug(...)
+#endif
+
+struct spa_graph;
+struct spa_graph_node;
+struct spa_graph_link;
+struct spa_graph_port;
+
+struct spa_graph_state {
+ int status; /**< current status */
+ int32_t required; /**< required number of signals */
+ int32_t pending; /**< number of pending signals */
+};
+
+static inline void spa_graph_state_reset(struct spa_graph_state *state)
+{
+ state->pending = state->required;
+}
+
+struct spa_graph_link {
+ struct spa_list link;
+ struct spa_graph_state *state;
+ int (*signal) (void *data);
+ void *signal_data;
+};
+
+#define spa_graph_link_signal(l) ((l)->signal((l)->signal_data))
+
+#define spa_graph_state_dec(s,c) (__atomic_sub_fetch(&(s)->pending, c, __ATOMIC_SEQ_CST) == 0)
+
+static inline int spa_graph_link_trigger(struct spa_graph_link *link)
+{
+ struct spa_graph_state *state = link->state;
+
+ spa_debug("link %p: state %p: pending %d/%d", link, state,
+ state->pending, state->required);
+
+ if (spa_graph_state_dec(state, 1))
+ spa_graph_link_signal(link);
+
+ return state->status;
+}
+struct spa_graph {
+ uint32_t flags; /* flags */
+ struct spa_graph_node *parent; /* parent node or NULL when driver */
+ struct spa_graph_state *state; /* state of graph */
+ struct spa_list nodes; /* list of nodes of this graph */
+};
+
+struct spa_graph_node_callbacks {
+#define SPA_VERSION_GRAPH_NODE_CALLBACKS 0
+ uint32_t version;
+
+ int (*process) (void *data, struct spa_graph_node *node);
+ int (*reuse_buffer) (void *data, struct spa_graph_node *node,
+ uint32_t port_id, uint32_t buffer_id);
+};
+
+struct spa_graph_node {
+ struct spa_list link; /**< link in graph nodes list */
+ struct spa_graph *graph; /**< owner graph */
+ struct spa_list ports[2]; /**< list of input and output ports */
+ struct spa_list links; /**< list of links to next nodes */
+ uint32_t flags; /**< node flags */
+ struct spa_graph_state *state; /**< state of the node */
+ struct spa_graph_link graph_link; /**< link in graph */
+ struct spa_graph *subgraph; /**< subgraph or NULL */
+ struct spa_callbacks callbacks;
+ struct spa_list sched_link; /**< link for scheduler */
+};
+
+#define spa_graph_node_call(n,method,version,...) \
+({ \
+ int __res = 0; \
+ spa_callbacks_call_res(&(n)->callbacks, \
+ struct spa_graph_node_callbacks, __res, \
+ method, version, ##__VA_ARGS__); \
+ __res; \
+})
+
+#define spa_graph_node_process(n) spa_graph_node_call(n, process, 0, n)
+#define spa_graph_node_reuse_buffer(n,p,i) spa_graph_node_call(n, reuse_buffer, 0, n, p, i)
+
+struct spa_graph_port {
+ struct spa_list link; /**< link in node port list */
+ struct spa_graph_node *node; /**< owner node */
+ enum spa_direction direction; /**< port direction */
+ uint32_t port_id; /**< port id */
+ uint32_t flags; /**< port flags */
+ struct spa_graph_port *peer; /**< peer */
+};
+
+static inline int spa_graph_node_trigger(struct spa_graph_node *node)
+{
+ struct spa_graph_link *l;
+ spa_debug("node %p trigger", node);
+ spa_list_for_each(l, &node->links, link)
+ spa_graph_link_trigger(l);
+ return 0;
+}
+
+static inline int spa_graph_run(struct spa_graph *graph)
+{
+ struct spa_graph_node *n, *t;
+ struct spa_list pending;
+
+ spa_graph_state_reset(graph->state);
+ spa_debug("graph %p run with state %p pending %d/%d", graph, graph->state,
+ graph->state->pending, graph->state->required);
+
+ spa_list_init(&pending);
+
+ spa_list_for_each(n, &graph->nodes, link) {
+ struct spa_graph_state *s = n->state;
+ spa_graph_state_reset(s);
+ spa_debug("graph %p node %p: state %p pending %d/%d status %d", graph, n,
+ s, s->pending, s->required, s->status);
+ if (--s->pending == 0)
+ spa_list_append(&pending, &n->sched_link);
+ }
+ spa_list_for_each_safe(n, t, &pending, sched_link)
+ spa_graph_node_process(n);
+
+ return 0;
+}
+
+static inline int spa_graph_finish(struct spa_graph *graph)
+{
+ spa_debug("graph %p finish", graph);
+ if (graph->parent)
+ return spa_graph_node_trigger(graph->parent);
+ return 0;
+}
+static inline int spa_graph_link_signal_node(void *data)
+{
+ struct spa_graph_node *node = (struct spa_graph_node *)data;
+ spa_debug("node %p call process", node);
+ return spa_graph_node_process(node);
+}
+
+static inline int spa_graph_link_signal_graph(void *data)
+{
+ struct spa_graph_node *node = (struct spa_graph_node *)data;
+ return spa_graph_finish(node->graph);
+}
+
+static inline void spa_graph_init(struct spa_graph *graph, struct spa_graph_state *state)
+{
+ spa_list_init(&graph->nodes);
+ graph->flags = 0;
+ graph->state = state;
+ spa_debug("graph %p init state %p", graph, state);
+}
+
+static inline void
+spa_graph_link_add(struct spa_graph_node *out,
+ struct spa_graph_state *state,
+ struct spa_graph_link *link)
+{
+ link->state = state;
+ state->required++;
+ spa_debug("node %p add link %p to state %p %d", out, link, state, state->required);
+ spa_list_append(&out->links, &link->link);
+}
+
+static inline void spa_graph_link_remove(struct spa_graph_link *link)
+{
+ link->state->required--;
+ spa_debug("link %p state %p remove %d", link, link->state, link->state->required);
+ spa_list_remove(&link->link);
+}
+
+static inline void
+spa_graph_node_init(struct spa_graph_node *node, struct spa_graph_state *state)
+{
+ spa_list_init(&node->ports[SPA_DIRECTION_INPUT]);
+ spa_list_init(&node->ports[SPA_DIRECTION_OUTPUT]);
+ spa_list_init(&node->links);
+ node->flags = 0;
+ node->subgraph = NULL;
+ node->state = state;
+ node->state->required = node->state->pending = 0;
+ node->state->status = SPA_STATUS_OK;
+ node->graph_link.signal = spa_graph_link_signal_graph;
+ node->graph_link.signal_data = node;
+ spa_debug("node %p init state %p", node, state);
+}
+
+
+static inline int spa_graph_node_impl_sub_process(void *data, struct spa_graph_node *node)
+{
+ struct spa_graph *graph = node->subgraph;
+ spa_debug("node %p: sub process %p", node, graph);
+ return spa_graph_run(graph);
+}
+
+static const struct spa_graph_node_callbacks spa_graph_node_sub_impl_default = {
+ SPA_VERSION_GRAPH_NODE_CALLBACKS,
+ .process = spa_graph_node_impl_sub_process,
+};
+
+static inline void spa_graph_node_set_subgraph(struct spa_graph_node *node,
+ struct spa_graph *subgraph)
+{
+ node->subgraph = subgraph;
+ subgraph->parent = node;
+ spa_debug("node %p set subgraph %p", node, subgraph);
+}
+
+static inline void
+spa_graph_node_set_callbacks(struct spa_graph_node *node,
+ const struct spa_graph_node_callbacks *callbacks,
+ void *data)
+{
+ node->callbacks = SPA_CALLBACKS_INIT(callbacks, data);
+}
+
+static inline void
+spa_graph_node_add(struct spa_graph *graph,
+ struct spa_graph_node *node)
+{
+ node->graph = graph;
+ spa_list_append(&graph->nodes, &node->link);
+ node->state->required++;
+ spa_debug("node %p add to graph %p, state %p required %d",
+ node, graph, node->state, node->state->required);
+ spa_graph_link_add(node, graph->state, &node->graph_link);
+}
+
+static inline void spa_graph_node_remove(struct spa_graph_node *node)
+{
+ spa_debug("node %p remove from graph %p, state %p required %d",
+ node, node->graph, node->state, node->state->required);
+ spa_graph_link_remove(&node->graph_link);
+ node->state->required--;
+ spa_list_remove(&node->link);
+}
+
+
+static inline void
+spa_graph_port_init(struct spa_graph_port *port,
+ enum spa_direction direction,
+ uint32_t port_id,
+ uint32_t flags)
+{
+ spa_debug("port %p init type %d id %d", port, direction, port_id);
+ port->direction = direction;
+ port->port_id = port_id;
+ port->flags = flags;
+}
+
+static inline void
+spa_graph_port_add(struct spa_graph_node *node,
+ struct spa_graph_port *port)
+{
+ spa_debug("port %p add to node %p", port, node);
+ port->node = node;
+ spa_list_append(&node->ports[port->direction], &port->link);
+}
+
+static inline void spa_graph_port_remove(struct spa_graph_port *port)
+{
+ spa_debug("port %p remove", port);
+ spa_list_remove(&port->link);
+}
+
+static inline void
+spa_graph_port_link(struct spa_graph_port *out, struct spa_graph_port *in)
+{
+ spa_debug("port %p link to %p %p %p", out, in, in->node, in->node->state);
+ out->peer = in;
+ in->peer = out;
+}
+
+static inline void
+spa_graph_port_unlink(struct spa_graph_port *port)
+{
+ spa_debug("port %p unlink from %p", port, port->peer);
+ if (port->peer) {
+ port->peer->peer = NULL;
+ port->peer = NULL;
+ }
+}
+
+static inline int spa_graph_node_impl_process(void *data, struct spa_graph_node *node)
+{
+ struct spa_node *n = (struct spa_node *)data;
+ struct spa_graph_state *state = node->state;
+
+ spa_debug("node %p: process state %p: %d, node %p", node, state, state->status, n);
+ if ((state->status = spa_node_process(n)) != SPA_STATUS_OK)
+ spa_graph_node_trigger(node);
+
+ return state->status;
+}
+
+static inline int spa_graph_node_impl_reuse_buffer(void *data, struct spa_graph_node *node,
+ uint32_t port_id, uint32_t buffer_id)
+{
+ struct spa_node *n = (struct spa_node *)data;
+ return spa_node_port_reuse_buffer(n, port_id, buffer_id);
+}
+
+static const struct spa_graph_node_callbacks spa_graph_node_impl_default = {
+ SPA_VERSION_GRAPH_NODE_CALLBACKS,
+ .process = spa_graph_node_impl_process,
+ .reuse_buffer = spa_graph_node_impl_reuse_buffer,
+};
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_GRAPH_H */
diff --git a/third_party/pipewire/spa/interfaces/audio/aec.h b/third_party/pipewire/spa/interfaces/audio/aec.h
new file mode 100644
index 0000000000..17e4e4e463
--- /dev/null
+++ b/third_party/pipewire/spa/interfaces/audio/aec.h
@@ -0,0 +1,95 @@
+/* PipeWire
+ *
+ * Copyright © 2021 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * 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 <spa/utils/dict.h>
+#include <spa/utils/hook.h>
+#include <spa/param/audio/raw.h>
+
+#ifndef SPA_AUDIO_AEC_H
+#define SPA_AUDIO_AEC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SPA_TYPE_INTERFACE_AUDIO_AEC SPA_TYPE_INFO_INTERFACE_BASE "Audio:AEC"
+
+#define SPA_VERSION_AUDIO_AEC 0
+struct spa_audio_aec {
+ struct spa_interface iface;
+ const char *name;
+ const struct spa_dict *info;
+ const char *latency;
+};
+
+struct spa_audio_aec_info {
+#define SPA_AUDIO_AEC_CHANGE_MASK_PROPS (1u<<0)
+ uint64_t change_mask;
+
+ const struct spa_dict *props;
+};
+
+struct spa_audio_aec_events {
+#define SPA_VERSION_AUDIO_AEC_EVENTS 0
+ uint32_t version; /**< version of this structure */
+
+ /** Emitted when info changes */
+ void (*info) (void *data, const struct spa_audio_aec_info *info);
+};
+
+struct spa_audio_aec_methods {
+#define SPA_VERSION_AUDIO_AEC_METHODS 0
+ uint32_t version;
+
+ int (*add_listener) (void *object,
+ struct spa_hook *listener,
+ const struct spa_audio_aec_events *events,
+ void *data);
+
+ int (*init) (void *data, const struct spa_dict *args, const struct spa_audio_info_raw *info);
+ int (*run) (void *data, const float *rec[], const float *play[], float *out[], uint32_t n_samples);
+ int (*set_props) (void *data, const struct spa_dict *args);
+};
+
+#define spa_audio_aec_method(o,method,version,...) \
+({ \
+ int _res = -ENOTSUP; \
+ struct spa_audio_aec *_o = o; \
+ spa_interface_call_res(&_o->iface, \
+ struct spa_audio_aec_methods, _res, \
+ method, version, ##__VA_ARGS__); \
+ _res; \
+})
+
+#define spa_audio_aec_add_listener(o,...) spa_audio_aec_method(o, add_listener, 0, __VA_ARGS__)
+#define spa_audio_aec_init(o,...) spa_audio_aec_method(o, init, 0, __VA_ARGS__)
+#define spa_audio_aec_run(o,...) spa_audio_aec_method(o, run, 0, __VA_ARGS__)
+#define spa_audio_aec_set_props(o,...) spa_audio_aec_method(o, set_props, 0, __VA_ARGS__)
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_AUDIO_AEC_H */
diff --git a/third_party/pipewire/spa/monitor/device.h b/third_party/pipewire/spa/monitor/device.h
new file mode 100644
index 0000000000..481874d184
--- /dev/null
+++ b/third_party/pipewire/spa/monitor/device.h
@@ -0,0 +1,307 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_DEVICE_H
+#define SPA_DEVICE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/defs.h>
+#include <spa/utils/hook.h>
+#include <spa/utils/dict.h>
+#include <spa/pod/event.h>
+
+/**
+ * \defgroup spa_device 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.
+ *
+ */
+
+/**
+ * \addtogroup spa_device
+ * \{
+ */
+#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 object a \ref spa_device
+ * \param listener a listener
+ * \param events a struct \ref 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 \a 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 object \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 */
+#define SPA_KEY_DEVICE_STRING "device.string" /**< device string in the underlying
+ * layer's format. E.g. "surround51:0" */
+
+/**
+ * \}
+ */
+
+#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
index 0000000000..7b8a256008
--- /dev/null
+++ b/third_party/pipewire/spa/monitor/event.h
@@ -0,0 +1,63 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2020 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_EVENT_DEVICE_H
+#define SPA_EVENT_DEVICE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/pod/event.h>
+
+/**
+ * \addtogroup spa_device
+ * \{
+ */
+
+/* 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/type-info.h b/third_party/pipewire/spa/monitor/type-info.h
new file mode 100644
index 0000000000..6bf781afa8
--- /dev/null
+++ b/third_party/pipewire/spa/monitor/type-info.h
@@ -0,0 +1,67 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2021 Collabora Ltd.
+ *
+ * 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_TYPE_INFO_H
+#define SPA_DEVICE_TYPE_INFO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/type-info.h>
+
+#include <spa/monitor/event.h>
+
+/**
+ * \addtogroup spa_device
+ * \{
+ */
+
+#define SPA_TYPE_INFO_DeviceEvent SPA_TYPE_INFO_EVENT_BASE "Device"
+#define SPA_TYPE_INFO_DEVICE_EVENT_BASE SPA_TYPE_INFO_DeviceEvent ":"
+
+#define SPA_TYPE_INFO_DeviceEventId SPA_TYPE_INFO_ENUM_BASE "DeviceEventId"
+#define SPA_TYPE_INFO_DEVICE_EVENT_ID_BASE SPA_TYPE_INFO_DeviceEventId ":"
+
+static const struct spa_type_info spa_type_device_event_id[] = {
+ { SPA_DEVICE_EVENT_ObjectConfig, SPA_TYPE_EVENT_Device, SPA_TYPE_INFO_DEVICE_EVENT_ID_BASE "ObjectConfig", NULL },
+ { 0, 0, NULL, NULL },
+};
+
+static const struct spa_type_info spa_type_device_event[] = {
+ { SPA_EVENT_DEVICE_START, SPA_TYPE_Id, SPA_TYPE_INFO_DEVICE_EVENT_BASE, spa_type_device_event_id },
+ { SPA_EVENT_DEVICE_Object, SPA_TYPE_Int, SPA_TYPE_INFO_DEVICE_EVENT_BASE "Object", NULL },
+ { SPA_EVENT_DEVICE_Props, SPA_TYPE_OBJECT_Props, SPA_TYPE_INFO_DEVICE_EVENT_BASE "Props", NULL },
+ { 0, 0, NULL, NULL },
+};
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_DEVICE_TYPE_INFO_H */
diff --git a/third_party/pipewire/spa/monitor/utils.h b/third_party/pipewire/spa/monitor/utils.h
new file mode 100644
index 0000000000..169fe4700c
--- /dev/null
+++ b/third_party/pipewire/spa/monitor/utils.h
@@ -0,0 +1,106 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2019 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_DEVICE_UTILS_H
+#define SPA_DEVICE_UTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/pod/builder.h>
+#include <spa/monitor/device.h>
+
+/**
+ * \addtogroup spa_device
+ * \{
+ */
+
+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;
+ if (spa_pod_builder_raw_padded(d->builder, r->param, SPA_POD_SIZE(r->param)) < 0)
+ return;
+ d->data.next = r->next;
+ d->data.param = spa_pod_builder_deref(d->builder, offset);
+}
+
+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 = {
+ .version = SPA_VERSION_DEVICE_EVENTS,
+ .info = NULL,
+ .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
index 0000000000..9bf50fb5e4
--- /dev/null
+++ b/third_party/pipewire/spa/node/command.h
@@ -0,0 +1,73 @@
+/* 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
+
+/**
+ * \addtogroup spa_node
+ * \{
+ */
+
+#include <spa/pod/command.h>
+
+/* object id of SPA_TYPE_COMMAND_Node */
+enum spa_node_command {
+ SPA_NODE_COMMAND_Suspend, /**< suspend a node, this removes all configured
+ * formats and closes any devices */
+ SPA_NODE_COMMAND_Pause, /**< pause a node. this makes it stop emitting
+ * scheduling events */
+ SPA_NODE_COMMAND_Start, /**< start a node, this makes it start emitting
+ * scheduling events */
+ SPA_NODE_COMMAND_Enable,
+ SPA_NODE_COMMAND_Disable,
+ SPA_NODE_COMMAND_Flush,
+ SPA_NODE_COMMAND_Drain,
+ SPA_NODE_COMMAND_Marker,
+ SPA_NODE_COMMAND_ParamBegin, /**< begin a set of parameter enumerations or
+ * configuration that require the device to
+ * remain opened, like query formats and then
+ * set a format */
+ SPA_NODE_COMMAND_ParamEnd, /**< end a transaction */
+ SPA_NODE_COMMAND_RequestProcess,/**< Sent to a driver when some other node emitted
+ * the RequestProcess event. */
+};
+
+#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
index 0000000000..ceb6d60164
--- /dev/null
+++ b/third_party/pipewire/spa/node/event.h
@@ -0,0 +1,64 @@
+/* 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
+
+/**
+ * \addtogroup spa_node
+ * \{
+ */
+
+#include <spa/pod/event.h>
+
+/* object id of SPA_TYPE_EVENT_Node */
+enum spa_node_event {
+ SPA_NODE_EVENT_Error,
+ SPA_NODE_EVENT_Buffering,
+ SPA_NODE_EVENT_RequestRefresh,
+ SPA_NODE_EVENT_RequestProcess, /*< Ask the driver to start processing
+ * the graph */
+};
+
+#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)
+
+/* properties for SPA_TYPE_EVENT_Node */
+enum spa_event_node {
+ SPA_EVENT_NODE_START,
+};
+
+/**
+ * \}
+ */
+
+#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
index 0000000000..74635c79a5
--- /dev/null
+++ b/third_party/pipewire/spa/node/io.h
@@ -0,0 +1,304 @@
+/* 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
+
+/**
+ * \addtogroup spa_node
+ * \{
+ */
+
+#include <spa/utils/defs.h>
+#include <spa/pod/pod.h>
+
+/** IO areas
+ *
+ * IO information for a port on a node. This is allocated
+ * by the host and configured on a node or all ports for which
+ * IO is requested.
+ *
+ * The plugin will communicate with the host through the IO
+ * areas.
+ */
+
+/** Different IO area types */
+enum spa_io_type {
+ SPA_IO_Invalid,
+ SPA_IO_Buffers, /**< area to exchange buffers, struct spa_io_buffers */
+ SPA_IO_Range, /**< expected byte range, struct spa_io_range */
+ SPA_IO_Clock, /**< area to update clock information, struct spa_io_clock */
+ SPA_IO_Latency, /**< latency reporting, struct spa_io_latency */
+ SPA_IO_Control, /**< area for control messages, struct spa_io_sequence */
+ SPA_IO_Notify, /**< area for notify messages, struct spa_io_sequence */
+ SPA_IO_Position, /**< position information in the graph, struct spa_io_position */
+ SPA_IO_RateMatch, /**< rate matching between nodes, struct spa_io_rate_match */
+ SPA_IO_Memory, /**< memory pointer, struct spa_io_memory */
+};
+
+/**
+ * IO area to exchange buffers.
+ *
+ * A set of buffers should first be configured on the node/port.
+ * Further references to those buffers will be made by using the
+ * id of the buffer.
+ *
+ * If status is SPA_STATUS_OK, the host should ignore
+ * the io area.
+ *
+ * If status is SPA_STATUS_NEED_DATA, the host should:
+ * 1) recycle the buffer in buffer_id, if possible
+ * 2) prepare a new buffer and place the id in buffer_id.
+ *
+ * If status is SPA_STATUS_HAVE_DATA, the host should consume
+ * the buffer in buffer_id and set the state to
+ * SPA_STATUS_NEED_DATA when new data is requested.
+ *
+ * If status is SPA_STATUS_STOPPED, some error occurred on the
+ * port.
+ *
+ * If status is SPA_STATUS_DRAINED, data from the io area was
+ * used to drain.
+ *
+ * Status can also be a negative errno value to indicate errors.
+ * such as:
+ * -EINVAL: buffer_id is invalid
+ * -EPIPE: no more buffers available
+ */
+struct spa_io_buffers {
+#define SPA_STATUS_OK 0
+#define SPA_STATUS_NEED_DATA (1<<0)
+#define SPA_STATUS_HAVE_DATA (1<<1)
+#define SPA_STATUS_STOPPED (1<<2)
+#define SPA_STATUS_DRAINED (1<<3)
+ int32_t status; /**< the status code */
+ uint32_t buffer_id; /**< a buffer id */
+};
+
+#define SPA_IO_BUFFERS_INIT (struct spa_io_buffers) { SPA_STATUS_OK, SPA_ID_INVALID, }
+
+/**
+ * IO area to exchange a memory region
+ */
+struct spa_io_memory {
+ int32_t status; /**< the status code */
+ uint32_t size; /**< the size of \a data */
+ void *data; /**< a memory pointer */
+};
+#define SPA_IO_MEMORY_INIT (struct spa_io_memory) { SPA_STATUS_OK, 0, NULL, }
+
+/** A range, suitable for input ports that can suggest a range to output ports */
+struct spa_io_range {
+ uint64_t offset; /**< offset in range */
+ uint32_t min_size; /**< minimum size of data */
+ uint32_t max_size; /**< maximum size of data */
+};
+
+/**
+ * Absolute time reporting.
+ *
+ * Nodes that can report clocking information will receive this io block.
+ * The application sets the id. This is usually set as part of the
+ * position information but can also be set separately.
+ *
+ * The clock counts the elapsed time according to the clock provider
+ * since the provider was last started.
+ */
+struct spa_io_clock {
+#define SPA_IO_CLOCK_FLAG_FREEWHEEL (1u<<0)
+ 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; /**< estimated 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; /**< overall 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
index 0000000000..94a02852de
--- /dev/null
+++ b/third_party/pipewire/spa/node/keys.h
@@ -0,0 +1,64 @@
+/* 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
+
+/**
+ * \addtogroup spa_node
+ * \{
+ */
+
+/** 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_MAX_LATENCY "node.max-latency" /**< maximum supported 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
index 0000000000..f32adec16c
--- /dev/null
+++ b/third_party/pipewire/spa/node/node.h
@@ -0,0 +1,674 @@
+/* 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
+
+/** \defgroup spa_node Node
+ *
+ * A spa_node is a component that can consume and produce buffers.
+ */
+
+/**
+ * \addtogroup spa_node
+ * \{
+ */
+
+#include <errno.h>
+#include <spa/utils/defs.h>
+#include <spa/utils/type.h>
+#include <spa/utils/hook.h>
+#include <spa/buffer/buffer.h>
+#include <spa/node/event.h>
+#include <spa/node/command.h>
+
+
+#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
+ * immediately 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 \ref 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 \ref 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 \ref 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 \ref spa_node
+ * \param direction a enum \ref spa_direction
+ * \param port_id the port to configure
+ * \param id the parameter id to set
+ * \param flags optional flags
+ * \param param a struct \ref 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
index 0000000000..7c481fd663
--- /dev/null
+++ b/third_party/pipewire/spa/node/type-info.h
@@ -0,0 +1,107 @@
+/* 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
+
+/**
+ * \addtogroup spa_node
+ * \{
+ */
+
+#include <spa/utils/type-info.h>
+
+#include <spa/node/command.h>
+#include <spa/node/event.h>
+#include <spa/node/io.h>
+
+#define SPA_TYPE_INFO_IO SPA_TYPE_INFO_ENUM_BASE "IO"
+#define SPA_TYPE_INFO_IO_BASE SPA_TYPE_INFO_IO ":"
+
+static const struct spa_type_info spa_type_io[] = {
+ { SPA_IO_Invalid, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Invalid", NULL },
+ { SPA_IO_Buffers, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Buffers", NULL },
+ { SPA_IO_Range, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Range", NULL },
+ { SPA_IO_Clock, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Clock", NULL },
+ { SPA_IO_Latency, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Latency", NULL },
+ { SPA_IO_Control, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Control", NULL },
+ { SPA_IO_Notify, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Notify", NULL },
+ { SPA_IO_Position, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Position", NULL },
+ { SPA_IO_RateMatch, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "RateMatch", NULL },
+ { SPA_IO_Memory, SPA_TYPE_Int, SPA_TYPE_INFO_IO_BASE "Memory", NULL },
+ { 0, 0, NULL, NULL },
+};
+
+#define SPA_TYPE_INFO_NodeEvent SPA_TYPE_INFO_EVENT_BASE "Node"
+#define SPA_TYPE_INFO_NODE_EVENT_BASE SPA_TYPE_INFO_NodeEvent ":"
+
+static const struct spa_type_info spa_type_node_event_id[] = {
+ { SPA_NODE_EVENT_Error, SPA_TYPE_EVENT_Node, SPA_TYPE_INFO_NODE_EVENT_BASE "Error", NULL },
+ { SPA_NODE_EVENT_Buffering, SPA_TYPE_EVENT_Node, SPA_TYPE_INFO_NODE_EVENT_BASE "Buffering", NULL },
+ { SPA_NODE_EVENT_RequestRefresh, SPA_TYPE_EVENT_Node, SPA_TYPE_INFO_NODE_EVENT_BASE "RequestRefresh", NULL },
+ { SPA_NODE_EVENT_RequestProcess, SPA_TYPE_EVENT_Node, SPA_TYPE_INFO_NODE_EVENT_BASE "RequestProcess", NULL },
+ { 0, 0, NULL, NULL },
+};
+
+static const struct spa_type_info spa_type_node_event[] = {
+ { SPA_EVENT_NODE_START, 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_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Suspend", NULL },
+ { SPA_NODE_COMMAND_Pause, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Pause", NULL },
+ { SPA_NODE_COMMAND_Start, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Start", NULL },
+ { SPA_NODE_COMMAND_Enable, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Enable", NULL },
+ { SPA_NODE_COMMAND_Disable, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Disable", NULL },
+ { SPA_NODE_COMMAND_Flush, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Flush", NULL },
+ { SPA_NODE_COMMAND_Drain, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Drain", NULL },
+ { SPA_NODE_COMMAND_Marker, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "Marker", NULL },
+ { SPA_NODE_COMMAND_ParamBegin, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "ParamBegin", NULL },
+ { SPA_NODE_COMMAND_ParamEnd, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "ParamEnd", NULL },
+ { SPA_NODE_COMMAND_RequestProcess, SPA_TYPE_COMMAND_Node, SPA_TYPE_INFO_NODE_COMMAND_BASE "RequestProcess", 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
index 0000000000..9503d8bbf4
--- /dev/null
+++ b/third_party/pipewire/spa/node/utils.h
@@ -0,0 +1,158 @@
+/* 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
+
+/**
+ * \addtogroup spa_node
+ * \{
+ */
+
+#include <spa/pod/builder.h>
+
+#include <spa/node/node.h>
+
+struct spa_result_node_params_data {
+ struct spa_pod_builder *builder;
+ struct spa_result_node_params data;
+};
+
+static inline void spa_result_func_node_params(void *data,
+ int seq, int res, uint32_t type, const void *result)
+{
+ struct spa_result_node_params_data *d =
+ (struct spa_result_node_params_data *) data;
+ const struct spa_result_node_params *r =
+ (const struct spa_result_node_params *) result;
+ uint32_t offset = d->builder->state.offset;
+ if (spa_pod_builder_raw_padded(d->builder, r->param, SPA_POD_SIZE(r->param)) < 0)
+ return;
+ d->data.next = r->next;
+ d->data.param = spa_pod_builder_deref(d->builder, offset);
+}
+
+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 = {
+ .version = SPA_VERSION_NODE_EVENTS,
+ .info = NULL,
+ .port_info = NULL,
+ .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 = {
+ .version = SPA_VERSION_NODE_EVENTS,
+ .info = NULL,
+ .port_info = NULL,
+ .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/dsd.h b/third_party/pipewire/spa/param/audio/dsd.h
new file mode 100644
index 0000000000..3228f2565d
--- /dev/null
+++ b/third_party/pipewire/spa/param/audio/dsd.h
@@ -0,0 +1,81 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2021 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_DSD_H
+#define SPA_AUDIO_DSD_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/param/param.h>
+#include <spa/param/audio/raw.h>
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+/** Extra DSD audio flags */
+#define SPA_AUDIO_DSD_FLAG_NONE (0) /*< no valid flag */
+
+/* DSD bits are transferred in a buffer grouped in bytes with the bitorder
+ * defined by \a bitorder.
+ *
+ * Channels are placed in separate planes (interleave = 0) or interleaved
+ * using the interleave value. A negative interleave value means that the
+ * bytes need to be reversed in the group.
+ *
+ * Planar (interleave = 0):
+ * plane1: l1 l2 l3 l4 l5 ...
+ * plane2: r1 r2 r3 r4 r5 ...
+ *
+ * Interleaved 4:
+ * plane1: l1 l2 l3 l4 r1 r2 r3 r4 l5 l6 l7 l8 r5 r6 r7 r8 l9 ...
+ *
+ * Interleaved 2:
+ * plane1: l1 l2 r1 r2 l3 l4 r3 r4 ...
+ */
+struct spa_audio_info_dsd {
+ enum spa_param_bitorder bitorder; /*< the order of the bits */
+ uint32_t flags; /*< extra flags */
+ int32_t interleave; /*< interleave bytes */
+ uint32_t rate; /*< sample rate (in bytes per second) */
+ uint32_t channels; /*< channels */
+ uint32_t position[SPA_AUDIO_MAX_CHANNELS]; /*< channel position from enum spa_audio_channel */
+};
+
+#define SPA_AUDIO_INFO_DSD_INIT(...) (struct spa_audio_info_dsd) { __VA_ARGS__ }
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_AUDIO_DSD_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
index 0000000000..6ee8f93359
--- /dev/null
+++ b/third_party/pipewire/spa/param/audio/format-utils.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_PARAM_AUDIO_FORMAT_UTILS_H
+#define SPA_PARAM_AUDIO_FORMAT_UTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/pod/parser.h>
+#include <spa/pod/builder.h>
+#include <spa/param/audio/format.h>
+#include <spa/param/format-utils.h>
+
+static inline int
+spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info)
+{
+ struct spa_pod *position = NULL;
+ int res;
+ info->flags = 0;
+ res = spa_pod_parse_object(format,
+ SPA_TYPE_OBJECT_Format, NULL,
+ SPA_FORMAT_AUDIO_format, SPA_POD_Id(&info->format),
+ SPA_FORMAT_AUDIO_rate, SPA_POD_Int(&info->rate),
+ SPA_FORMAT_AUDIO_channels, SPA_POD_Int(&info->channels),
+ SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
+ if (position == NULL ||
+ !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS))
+ SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
+
+ return res;
+}
+
+static inline int
+spa_format_audio_dsp_parse(const struct spa_pod *format, struct spa_audio_info_dsp *info)
+{
+ int res;
+ res = spa_pod_parse_object(format,
+ SPA_TYPE_OBJECT_Format, NULL,
+ SPA_FORMAT_AUDIO_format, SPA_POD_Id(&info->format));
+ return res;
+}
+
+static inline int
+spa_format_audio_iec958_parse(const struct spa_pod *format, struct spa_audio_info_iec958 *info)
+{
+ int res;
+ res = spa_pod_parse_object(format,
+ SPA_TYPE_OBJECT_Format, NULL,
+ SPA_FORMAT_AUDIO_iec958Codec, SPA_POD_Id(&info->codec),
+ SPA_FORMAT_AUDIO_rate, SPA_POD_Int(&info->rate));
+ return res;
+}
+
+static inline int
+spa_format_audio_dsd_parse(const struct spa_pod *format, struct spa_audio_info_dsd *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_bitorder, SPA_POD_Id(&info->bitorder),
+ SPA_FORMAT_AUDIO_interleave, SPA_POD_Int(&info->interleave),
+ 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 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),
+ 0);
+ if (info->format != SPA_AUDIO_FORMAT_UNKNOWN)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_AUDIO_format, SPA_POD_Id(info->format), 0);
+ if (info->rate != 0)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_AUDIO_rate, SPA_POD_Int(info->rate), 0);
+ if (info->channels != 0) {
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info->channels), 0);
+ if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) {
+ spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_position,
+ SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id,
+ info->channels, info->position), 0);
+ }
+ }
+ 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),
+ 0);
+ if (info->format != SPA_AUDIO_FORMAT_UNKNOWN)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_AUDIO_format, SPA_POD_Id(info->format), 0);
+ return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
+}
+
+
+static inline struct spa_pod *
+spa_format_audio_iec958_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_iec958 *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_iec958),
+ 0);
+ if (info->codec != SPA_AUDIO_IEC958_CODEC_UNKNOWN)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_AUDIO_iec958Codec, SPA_POD_Id(info->codec), 0);
+ if (info->rate != 0)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_AUDIO_rate, SPA_POD_Int(info->rate), 0);
+ return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
+}
+
+static inline struct spa_pod *
+spa_format_audio_dsd_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_dsd *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_dsd),
+ 0);
+ if (info->bitorder != SPA_PARAM_BITORDER_unknown)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_AUDIO_bitorder, SPA_POD_Id(info->bitorder), 0);
+ if (info->interleave != 0)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_AUDIO_interleave, SPA_POD_Int(info->interleave), 0);
+ if (info->rate != 0)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_AUDIO_rate, SPA_POD_Int(info->rate), 0);
+ if (info->channels != 0) {
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info->channels), 0);
+ if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) {
+ spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_position,
+ SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id,
+ info->channels, info->position), 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
index 0000000000..ed3f13cbbe
--- /dev/null
+++ b/third_party/pipewire/spa/param/audio/format.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_PARAM_AUDIO_FORMAT_H
+#define SPA_PARAM_AUDIO_FORMAT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/param/format.h>
+#include <spa/param/audio/raw.h>
+#include <spa/param/audio/iec958.h>
+#include <spa/param/audio/dsd.h>
+
+struct spa_audio_info {
+ uint32_t media_type;
+ uint32_t media_subtype;
+ union {
+ struct spa_audio_info_raw raw;
+ struct spa_audio_info_dsp dsp;
+ struct spa_audio_info_iec958 iec958;
+ struct spa_audio_info_dsd dsd;
+ } info;
+};
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_PARAM_AUDIO_FORMAT_H */
diff --git a/third_party/pipewire/spa/param/audio/iec958.h b/third_party/pipewire/spa/param/audio/iec958.h
new file mode 100644
index 0000000000..fb46c561e1
--- /dev/null
+++ b/third_party/pipewire/spa/param/audio/iec958.h
@@ -0,0 +1,69 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2021 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_IEC958_H
+#define SPA_AUDIO_IEC958_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+enum spa_audio_iec958_codec {
+ SPA_AUDIO_IEC958_CODEC_UNKNOWN,
+
+ SPA_AUDIO_IEC958_CODEC_PCM,
+ SPA_AUDIO_IEC958_CODEC_DTS,
+ SPA_AUDIO_IEC958_CODEC_AC3,
+ SPA_AUDIO_IEC958_CODEC_MPEG, /**< MPEG-1 or MPEG-2 (Part 3, not AAC) */
+ SPA_AUDIO_IEC958_CODEC_MPEG2_AAC, /**< MPEG-2 AAC */
+
+ SPA_AUDIO_IEC958_CODEC_EAC3,
+
+ SPA_AUDIO_IEC958_CODEC_TRUEHD, /**< Dolby TrueHD */
+ SPA_AUDIO_IEC958_CODEC_DTSHD, /**< DTS-HD Master Audio */
+};
+
+struct spa_audio_info_iec958 {
+ enum spa_audio_iec958_codec codec; /*< format, one of the DSP formats in enum spa_audio_format_dsp */
+ uint32_t flags; /*< extra flags */
+ uint32_t rate; /*< sample rate */
+};
+
+#define SPA_AUDIO_INFO_IEC958_INIT(...) (struct spa_audio_info_iec958) { __VA_ARGS__ }
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_AUDIO_IEC958_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
index 0000000000..66154bf62e
--- /dev/null
+++ b/third_party/pipewire/spa/param/audio/layout.h
@@ -0,0 +1,192 @@
+/* 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
+
+#if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
+#include <endian.h>
+#endif
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+#include <spa/param/audio/raw.h>
+
+struct spa_audio_layout_info {
+ uint32_t n_channels;
+ uint32_t position[SPA_AUDIO_MAX_CHANNELS];
+};
+
+#define SPA_AUDIO_LAYOUT_Mono 1, { SPA_AUDIO_CHANNEL_MONO, }
+#define SPA_AUDIO_LAYOUT_Stereo 2, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, }
+#define SPA_AUDIO_LAYOUT_Quad 4, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, }
+#define SPA_AUDIO_LAYOUT_Pentagonal 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
+ SPA_AUDIO_CHANNEL_FC, }
+#define SPA_AUDIO_LAYOUT_Hexagonal 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RC, }
+#define SPA_AUDIO_LAYOUT_Octagonal 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RC, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_Cube 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR }, \
+ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
+ SPA_AUDIO_CHANNEL_TFL, SPA_AUDIO_CHANNEL_TFR, \
+ SPA_AUDIO_CHANNEL_TRL, SPA_AUDIO_CHANNEL_TRR, }
+
+
+#define SPA_AUDIO_LAYOUT_MPEG_1_0 SPA_AUDIO_LAYOUT_Mono
+#define SPA_AUDIO_LAYOUT_MPEG_2_0 SPA_AUDIO_LAYOUT_Stereo
+#define SPA_AUDIO_LAYOUT_MPEG_3_0A 3, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, }
+#define SPA_AUDIO_LAYOUT_MPEG_3_0B 3, { SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_FL, \
+ SPA_AUDIO_CHANNEL_FR, }
+#define SPA_AUDIO_LAYOUT_MPEG_4_0A 4, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RC, }
+#define SPA_AUDIO_LAYOUT_MPEG_4_0B 4, { SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_FL, \
+ SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_RC, }
+#define SPA_AUDIO_LAYOUT_MPEG_5_0A 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_SL, \
+ SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_MPEG_5_0B 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, \
+ SPA_AUDIO_CHANNEL_FC, }
+#define SPA_AUDIO_LAYOUT_MPEG_5_0C 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FC, \
+ SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_SL, \
+ SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_MPEG_5_0D 5, { SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_FL, \
+ SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_SL, \
+ SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_MPEG_5_1A 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_MPEG_5_1B 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, }
+#define SPA_AUDIO_LAYOUT_MPEG_5_1C 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FC, \
+ SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_SL, \
+ SPA_AUDIO_CHANNEL_SR, SPA_AUDIO_CHANNEL_LFE, }
+#define SPA_AUDIO_LAYOUT_MPEG_5_1D 6, { SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_FL, \
+ SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_SL, \
+ SPA_AUDIO_CHANNEL_SR, SPA_AUDIO_CHANNEL_LFE, }
+#define SPA_AUDIO_LAYOUT_MPEG_6_1A 7, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, \
+ SPA_AUDIO_CHANNEL_RC, }
+#define SPA_AUDIO_LAYOUT_MPEG_7_1A 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
+ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_MPEG_7_1B 8, { SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_SL, \
+ SPA_AUDIO_CHANNEL_SR, SPA_AUDIO_CHANNEL_FL, \
+ SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_RL, \
+ SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_LFE, }
+#define SPA_AUDIO_LAYOUT_MPEG_7_1C 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, \
+ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, }
+
+
+#define SPA_AUDIO_LAYOUT_2_1 3, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_LFE, }
+
+#define SPA_AUDIO_LAYOUT_2RC 3, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_RC, }
+#define SPA_AUDIO_LAYOUT_2FC 3, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, }
+
+#define SPA_AUDIO_LAYOUT_3_1 4, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, }
+#define SPA_AUDIO_LAYOUT_4_0 4, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RC, }
+#define SPA_AUDIO_LAYOUT_2_2 4, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
+
+#define SPA_AUDIO_LAYOUT_4_1 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
+ SPA_AUDIO_CHANNEL_RC, }
+#define SPA_AUDIO_LAYOUT_5_0 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_SL, \
+ SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_5_0R 5, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RL, \
+ SPA_AUDIO_CHANNEL_RR, }
+#define SPA_AUDIO_LAYOUT_5_1 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_5_1R 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
+ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, }
+#define SPA_AUDIO_LAYOUT_6_0 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RC, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_6_0F 6, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FLC, SPA_AUDIO_CHANNEL_FRC, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_6_1 7, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
+ SPA_AUDIO_CHANNEL_RC, SPA_AUDIO_CHANNEL_SL, \
+ SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_6_1F 7, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
+ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
+ SPA_AUDIO_CHANNEL_RC, }
+#define SPA_AUDIO_LAYOUT_7_0 7, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_RL, \
+ SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_SL, \
+ SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_7_0F 7, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_FLC, \
+ SPA_AUDIO_CHANNEL_FRC, SPA_AUDIO_CHANNEL_SL, \
+ SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_7_1 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
+ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_7_1W 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
+ SPA_AUDIO_CHANNEL_FLC, SPA_AUDIO_CHANNEL_FRC, \
+ SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, }
+#define SPA_AUDIO_LAYOUT_7_1WR 8, { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, \
+ SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, \
+ SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, \
+ SPA_AUDIO_CHANNEL_FLC, SPA_AUDIO_CHANNEL_FRC, }
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_AUDIO_LAYOUT_H */
diff --git a/third_party/pipewire/spa/param/audio/raw.h b/third_party/pipewire/spa/param/audio/raw.h
new file mode 100644
index 0000000000..a34915c422
--- /dev/null
+++ b/third_party/pipewire/spa/param/audio/raw.h
@@ -0,0 +1,324 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_AUDIO_RAW_H
+#define SPA_AUDIO_RAW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
+#include <endian.h>
+#endif
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#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,
+
+ SPA_AUDIO_FORMAT_ULAW,
+ SPA_AUDIO_FORMAT_ALAW,
+
+ /* 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,
+ SPA_AUDIO_FORMAT_S8P,
+
+ /* 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_START_Aux = 0x1000, /**< aux channels */
+ SPA_AUDIO_CHANNEL_AUX0 = SPA_AUDIO_CHANNEL_START_Aux,
+ SPA_AUDIO_CHANNEL_AUX1,
+ SPA_AUDIO_CHANNEL_AUX2,
+ SPA_AUDIO_CHANNEL_AUX3,
+ SPA_AUDIO_CHANNEL_AUX4,
+ SPA_AUDIO_CHANNEL_AUX5,
+ SPA_AUDIO_CHANNEL_AUX6,
+ SPA_AUDIO_CHANNEL_AUX7,
+ SPA_AUDIO_CHANNEL_AUX8,
+ SPA_AUDIO_CHANNEL_AUX9,
+ SPA_AUDIO_CHANNEL_AUX10,
+ SPA_AUDIO_CHANNEL_AUX11,
+ SPA_AUDIO_CHANNEL_AUX12,
+ SPA_AUDIO_CHANNEL_AUX13,
+ SPA_AUDIO_CHANNEL_AUX14,
+ SPA_AUDIO_CHANNEL_AUX15,
+ SPA_AUDIO_CHANNEL_AUX16,
+ SPA_AUDIO_CHANNEL_AUX17,
+ SPA_AUDIO_CHANNEL_AUX18,
+ SPA_AUDIO_CHANNEL_AUX19,
+ SPA_AUDIO_CHANNEL_AUX20,
+ SPA_AUDIO_CHANNEL_AUX21,
+ SPA_AUDIO_CHANNEL_AUX22,
+ SPA_AUDIO_CHANNEL_AUX23,
+ SPA_AUDIO_CHANNEL_AUX24,
+ SPA_AUDIO_CHANNEL_AUX25,
+ SPA_AUDIO_CHANNEL_AUX26,
+ SPA_AUDIO_CHANNEL_AUX27,
+ SPA_AUDIO_CHANNEL_AUX28,
+ SPA_AUDIO_CHANNEL_AUX29,
+ SPA_AUDIO_CHANNEL_AUX30,
+ SPA_AUDIO_CHANNEL_AUX31,
+ SPA_AUDIO_CHANNEL_AUX32,
+ SPA_AUDIO_CHANNEL_AUX33,
+ SPA_AUDIO_CHANNEL_AUX34,
+ SPA_AUDIO_CHANNEL_AUX35,
+ SPA_AUDIO_CHANNEL_AUX36,
+ SPA_AUDIO_CHANNEL_AUX37,
+ SPA_AUDIO_CHANNEL_AUX38,
+ SPA_AUDIO_CHANNEL_AUX39,
+ SPA_AUDIO_CHANNEL_AUX40,
+ SPA_AUDIO_CHANNEL_AUX41,
+ SPA_AUDIO_CHANNEL_AUX42,
+ SPA_AUDIO_CHANNEL_AUX43,
+ SPA_AUDIO_CHANNEL_AUX44,
+ SPA_AUDIO_CHANNEL_AUX45,
+ SPA_AUDIO_CHANNEL_AUX46,
+ SPA_AUDIO_CHANNEL_AUX47,
+ SPA_AUDIO_CHANNEL_AUX48,
+ SPA_AUDIO_CHANNEL_AUX49,
+ SPA_AUDIO_CHANNEL_AUX50,
+ SPA_AUDIO_CHANNEL_AUX51,
+ SPA_AUDIO_CHANNEL_AUX52,
+ SPA_AUDIO_CHANNEL_AUX53,
+ SPA_AUDIO_CHANNEL_AUX54,
+ SPA_AUDIO_CHANNEL_AUX55,
+ SPA_AUDIO_CHANNEL_AUX56,
+ SPA_AUDIO_CHANNEL_AUX57,
+ SPA_AUDIO_CHANNEL_AUX58,
+ SPA_AUDIO_CHANNEL_AUX59,
+ SPA_AUDIO_CHANNEL_AUX60,
+ SPA_AUDIO_CHANNEL_AUX61,
+ SPA_AUDIO_CHANNEL_AUX62,
+ SPA_AUDIO_CHANNEL_AUX63,
+
+ SPA_AUDIO_CHANNEL_LAST_Aux = 0x1fff, /**< aux channels */
+
+ SPA_AUDIO_CHANNEL_START_Custom = 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_FORMAT "audio.format" /**< an audio format as string,
+ * Ex. "S16LE" */
+#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 */
+#define SPA_KEY_AUDIO_POSITION "audio.position" /**< channel positions as comma separated list
+ * of channels ex. "FL,FR" */
+#define SPA_KEY_AUDIO_ALLOWED_RATES "audio.allowed-rates" /**< a list of allowed samplerates
+ * ex. "[ 44100 48000 ]" */
+
+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
index 0000000000..f8a29d9df4
--- /dev/null
+++ b/third_party/pipewire/spa/param/audio/type-info.h
@@ -0,0 +1,296 @@
+/* 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
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/param/audio/raw.h>
+
+#define SPA_TYPE_INFO_AudioFormat SPA_TYPE_INFO_ENUM_BASE "AudioFormat"
+#define SPA_TYPE_INFO_AUDIO_FORMAT_BASE SPA_TYPE_INFO_AudioFormat ":"
+
+static const struct spa_type_info spa_type_audio_format[] = {
+ { SPA_AUDIO_FORMAT_UNKNOWN, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "UNKNOWN", NULL },
+ { SPA_AUDIO_FORMAT_ENCODED, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "ENCODED", NULL },
+ { SPA_AUDIO_FORMAT_S8, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S8", NULL },
+ { SPA_AUDIO_FORMAT_U8, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U8", NULL },
+ { SPA_AUDIO_FORMAT_S16_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16LE", NULL },
+ { SPA_AUDIO_FORMAT_S16_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16BE", NULL },
+ { SPA_AUDIO_FORMAT_U16_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16LE", NULL },
+ { SPA_AUDIO_FORMAT_U16_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16BE", NULL },
+ { SPA_AUDIO_FORMAT_S24_32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32LE", NULL },
+ { SPA_AUDIO_FORMAT_S24_32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32BE", NULL },
+ { SPA_AUDIO_FORMAT_U24_32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32LE", NULL },
+ { SPA_AUDIO_FORMAT_U24_32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32BE", NULL },
+ { SPA_AUDIO_FORMAT_S32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32LE", NULL },
+ { SPA_AUDIO_FORMAT_S32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32BE", NULL },
+ { SPA_AUDIO_FORMAT_U32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32LE", NULL },
+ { SPA_AUDIO_FORMAT_U32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32BE", NULL },
+ { SPA_AUDIO_FORMAT_S24_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24LE", NULL },
+ { SPA_AUDIO_FORMAT_S24_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24BE", NULL },
+ { SPA_AUDIO_FORMAT_U24_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24LE", NULL },
+ { SPA_AUDIO_FORMAT_U24_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24BE", NULL },
+ { SPA_AUDIO_FORMAT_S20_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20LE", NULL },
+ { SPA_AUDIO_FORMAT_S20_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20BE", NULL },
+ { SPA_AUDIO_FORMAT_U20_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20LE", NULL },
+ { SPA_AUDIO_FORMAT_U20_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20BE", NULL },
+ { SPA_AUDIO_FORMAT_S18_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18LE", NULL },
+ { SPA_AUDIO_FORMAT_S18_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18BE", NULL },
+ { SPA_AUDIO_FORMAT_U18_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18LE", NULL },
+ { SPA_AUDIO_FORMAT_U18_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18BE", NULL },
+ { SPA_AUDIO_FORMAT_F32_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32LE", NULL },
+ { SPA_AUDIO_FORMAT_F32_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32BE", NULL },
+ { SPA_AUDIO_FORMAT_F64_LE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64LE", NULL },
+ { SPA_AUDIO_FORMAT_F64_BE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64BE", NULL },
+
+ { SPA_AUDIO_FORMAT_ULAW, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "ULAW", NULL },
+ { SPA_AUDIO_FORMAT_ALAW, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "ALAW", 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 },
+ { SPA_AUDIO_FORMAT_S8P, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S8P", NULL },
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+ { SPA_AUDIO_FORMAT_S16_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16OE", NULL },
+ { SPA_AUDIO_FORMAT_S16, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16", NULL },
+ { SPA_AUDIO_FORMAT_U16_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16OE", NULL },
+ { SPA_AUDIO_FORMAT_U16, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16", NULL },
+ { SPA_AUDIO_FORMAT_S24_32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32OE", NULL },
+ { SPA_AUDIO_FORMAT_S24_32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32", NULL },
+ { SPA_AUDIO_FORMAT_U24_32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32OE", NULL },
+ { SPA_AUDIO_FORMAT_U24_32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32", NULL },
+ { SPA_AUDIO_FORMAT_S32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32OE", NULL },
+ { SPA_AUDIO_FORMAT_S32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32", NULL },
+ { SPA_AUDIO_FORMAT_U32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32OE", NULL },
+ { SPA_AUDIO_FORMAT_U32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32", NULL },
+ { SPA_AUDIO_FORMAT_S24_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24OE", NULL },
+ { SPA_AUDIO_FORMAT_S24, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24", NULL },
+ { SPA_AUDIO_FORMAT_U24_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24OE", NULL },
+ { SPA_AUDIO_FORMAT_U24, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24", NULL },
+ { SPA_AUDIO_FORMAT_S20_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20OE", NULL },
+ { SPA_AUDIO_FORMAT_S20, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20", NULL },
+ { SPA_AUDIO_FORMAT_U20_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20OE", NULL },
+ { SPA_AUDIO_FORMAT_U20, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20", NULL },
+ { SPA_AUDIO_FORMAT_S18_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18OE", NULL },
+ { SPA_AUDIO_FORMAT_S18, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18", NULL },
+ { SPA_AUDIO_FORMAT_U18_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18OE", NULL },
+ { SPA_AUDIO_FORMAT_U18, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18", NULL },
+ { SPA_AUDIO_FORMAT_F32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32OE", NULL },
+ { SPA_AUDIO_FORMAT_F32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32", NULL },
+ { SPA_AUDIO_FORMAT_F64_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64OE", NULL },
+ { SPA_AUDIO_FORMAT_F64, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64", NULL },
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+ { SPA_AUDIO_FORMAT_S16, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16", NULL },
+ { SPA_AUDIO_FORMAT_S16_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S16OE", NULL },
+ { SPA_AUDIO_FORMAT_U16, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16", NULL },
+ { SPA_AUDIO_FORMAT_U16_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U16OE", NULL },
+ { SPA_AUDIO_FORMAT_S24_32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32", NULL },
+ { SPA_AUDIO_FORMAT_S24_32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24_32OE", NULL },
+ { SPA_AUDIO_FORMAT_U24_32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32", NULL },
+ { SPA_AUDIO_FORMAT_U24_32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24_32OE", NULL },
+ { SPA_AUDIO_FORMAT_S32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32", NULL },
+ { SPA_AUDIO_FORMAT_S32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S32OE", NULL },
+ { SPA_AUDIO_FORMAT_U32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32", NULL },
+ { SPA_AUDIO_FORMAT_U32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U32OE", NULL },
+ { SPA_AUDIO_FORMAT_S24, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24", NULL },
+ { SPA_AUDIO_FORMAT_S24_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S24OE", NULL },
+ { SPA_AUDIO_FORMAT_U24, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24", NULL },
+ { SPA_AUDIO_FORMAT_U24_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U24OE", NULL },
+ { SPA_AUDIO_FORMAT_S20, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20", NULL },
+ { SPA_AUDIO_FORMAT_S20_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S20OE", NULL },
+ { SPA_AUDIO_FORMAT_U20, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20", NULL },
+ { SPA_AUDIO_FORMAT_U20_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U20OE", NULL },
+ { SPA_AUDIO_FORMAT_S18, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18", NULL },
+ { SPA_AUDIO_FORMAT_S18_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "S18OE", NULL },
+ { SPA_AUDIO_FORMAT_U18, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18", NULL },
+ { SPA_AUDIO_FORMAT_U18_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "U18OE", NULL },
+ { SPA_AUDIO_FORMAT_F32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32", NULL },
+ { SPA_AUDIO_FORMAT_F32_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F32OE", NULL },
+ { SPA_AUDIO_FORMAT_F64, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64", NULL },
+ { SPA_AUDIO_FORMAT_F64_OE, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_FORMAT_BASE "F64OE", NULL },
+#endif
+ { 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 },
+
+ { SPA_AUDIO_CHANNEL_AUX0, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX0", NULL },
+ { SPA_AUDIO_CHANNEL_AUX1, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX1", NULL },
+ { SPA_AUDIO_CHANNEL_AUX2, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX2", NULL },
+ { SPA_AUDIO_CHANNEL_AUX3, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX3", NULL },
+ { SPA_AUDIO_CHANNEL_AUX4, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX4", NULL },
+ { SPA_AUDIO_CHANNEL_AUX5, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX5", NULL },
+ { SPA_AUDIO_CHANNEL_AUX6, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX6", NULL },
+ { SPA_AUDIO_CHANNEL_AUX7, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX7", NULL },
+ { SPA_AUDIO_CHANNEL_AUX8, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX8", NULL },
+ { SPA_AUDIO_CHANNEL_AUX9, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX9", NULL },
+ { SPA_AUDIO_CHANNEL_AUX10, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX10", NULL },
+ { SPA_AUDIO_CHANNEL_AUX11, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX11", NULL },
+ { SPA_AUDIO_CHANNEL_AUX12, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX12", NULL },
+ { SPA_AUDIO_CHANNEL_AUX13, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX13", NULL },
+ { SPA_AUDIO_CHANNEL_AUX14, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX14", NULL },
+ { SPA_AUDIO_CHANNEL_AUX15, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX15", NULL },
+ { SPA_AUDIO_CHANNEL_AUX16, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX16", NULL },
+ { SPA_AUDIO_CHANNEL_AUX17, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX17", NULL },
+ { SPA_AUDIO_CHANNEL_AUX18, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX18", NULL },
+ { SPA_AUDIO_CHANNEL_AUX19, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX19", NULL },
+ { SPA_AUDIO_CHANNEL_AUX20, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX20", NULL },
+ { SPA_AUDIO_CHANNEL_AUX21, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX21", NULL },
+ { SPA_AUDIO_CHANNEL_AUX22, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX22", NULL },
+ { SPA_AUDIO_CHANNEL_AUX23, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX23", NULL },
+ { SPA_AUDIO_CHANNEL_AUX24, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX24", NULL },
+ { SPA_AUDIO_CHANNEL_AUX25, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX25", NULL },
+ { SPA_AUDIO_CHANNEL_AUX26, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX26", NULL },
+ { SPA_AUDIO_CHANNEL_AUX27, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX27", NULL },
+ { SPA_AUDIO_CHANNEL_AUX28, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX28", NULL },
+ { SPA_AUDIO_CHANNEL_AUX29, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX29", NULL },
+ { SPA_AUDIO_CHANNEL_AUX30, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX30", NULL },
+ { SPA_AUDIO_CHANNEL_AUX31, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX31", NULL },
+ { SPA_AUDIO_CHANNEL_AUX32, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX32", NULL },
+ { SPA_AUDIO_CHANNEL_AUX33, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX33", NULL },
+ { SPA_AUDIO_CHANNEL_AUX34, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX34", NULL },
+ { SPA_AUDIO_CHANNEL_AUX35, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX35", NULL },
+ { SPA_AUDIO_CHANNEL_AUX36, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX36", NULL },
+ { SPA_AUDIO_CHANNEL_AUX37, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX37", NULL },
+ { SPA_AUDIO_CHANNEL_AUX38, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX38", NULL },
+ { SPA_AUDIO_CHANNEL_AUX39, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX39", NULL },
+ { SPA_AUDIO_CHANNEL_AUX40, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX40", NULL },
+ { SPA_AUDIO_CHANNEL_AUX41, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX41", NULL },
+ { SPA_AUDIO_CHANNEL_AUX42, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX42", NULL },
+ { SPA_AUDIO_CHANNEL_AUX43, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX43", NULL },
+ { SPA_AUDIO_CHANNEL_AUX44, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX44", NULL },
+ { SPA_AUDIO_CHANNEL_AUX45, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX45", NULL },
+ { SPA_AUDIO_CHANNEL_AUX46, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX46", NULL },
+ { SPA_AUDIO_CHANNEL_AUX47, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX47", NULL },
+ { SPA_AUDIO_CHANNEL_AUX48, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX48", NULL },
+ { SPA_AUDIO_CHANNEL_AUX49, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX49", NULL },
+ { SPA_AUDIO_CHANNEL_AUX50, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX50", NULL },
+ { SPA_AUDIO_CHANNEL_AUX51, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX51", NULL },
+ { SPA_AUDIO_CHANNEL_AUX52, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX52", NULL },
+ { SPA_AUDIO_CHANNEL_AUX53, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX53", NULL },
+ { SPA_AUDIO_CHANNEL_AUX54, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX54", NULL },
+ { SPA_AUDIO_CHANNEL_AUX55, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX55", NULL },
+ { SPA_AUDIO_CHANNEL_AUX56, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX56", NULL },
+ { SPA_AUDIO_CHANNEL_AUX57, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX57", NULL },
+ { SPA_AUDIO_CHANNEL_AUX58, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX58", NULL },
+ { SPA_AUDIO_CHANNEL_AUX59, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX59", NULL },
+ { SPA_AUDIO_CHANNEL_AUX60, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX60", NULL },
+ { SPA_AUDIO_CHANNEL_AUX61, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX61", NULL },
+ { SPA_AUDIO_CHANNEL_AUX62, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX62", NULL },
+ { SPA_AUDIO_CHANNEL_AUX63, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_CHANNEL_BASE "AUX63", NULL },
+ { 0, 0, NULL, NULL },
+};
+
+
+#include <spa/param/audio/iec958.h>
+
+#define SPA_TYPE_INFO_AudioIEC958Codec SPA_TYPE_INFO_ENUM_BASE "AudioIEC958Codec"
+#define SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE SPA_TYPE_INFO_AudioIEC958Codec ":"
+
+static const struct spa_type_info spa_type_audio_iec958_codec[] = {
+ { SPA_AUDIO_IEC958_CODEC_UNKNOWN, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "UNKNOWN", NULL },
+ { SPA_AUDIO_IEC958_CODEC_PCM, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "PCM", NULL },
+ { SPA_AUDIO_IEC958_CODEC_DTS, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "DTS", NULL },
+ { SPA_AUDIO_IEC958_CODEC_AC3, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "AC3", NULL },
+ { SPA_AUDIO_IEC958_CODEC_MPEG, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "MPEG", NULL },
+ { SPA_AUDIO_IEC958_CODEC_MPEG2_AAC, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "MPEG2-AAC", NULL },
+ { SPA_AUDIO_IEC958_CODEC_EAC3, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "EAC3", NULL },
+ { SPA_AUDIO_IEC958_CODEC_TRUEHD, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "TrueHD", NULL },
+ { SPA_AUDIO_IEC958_CODEC_DTSHD, SPA_TYPE_Int, SPA_TYPE_INFO_AUDIO_IEC958_CODEC_BASE "DTS-HD", NULL },
+ { 0, 0, NULL, NULL },
+};
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_AUDIO_RAW_TYPES_H */
diff --git a/third_party/pipewire/spa/param/bluetooth/audio.h b/third_party/pipewire/spa/param/bluetooth/audio.h
new file mode 100644
index 0000000000..5c215b411f
--- /dev/null
+++ b/third_party/pipewire/spa/param/bluetooth/audio.h
@@ -0,0 +1,66 @@
+/* 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_BLUETOOTH_AUDIO_H
+#define SPA_BLUETOOTH_AUDIO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+enum spa_bluetooth_audio_codec {
+ SPA_BLUETOOTH_AUDIO_CODEC_START,
+
+ /* A2DP */
+ SPA_BLUETOOTH_AUDIO_CODEC_SBC,
+ SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ,
+ SPA_BLUETOOTH_AUDIO_CODEC_MPEG,
+ SPA_BLUETOOTH_AUDIO_CODEC_AAC,
+ SPA_BLUETOOTH_AUDIO_CODEC_APTX,
+ SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD,
+ SPA_BLUETOOTH_AUDIO_CODEC_LDAC,
+ SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL,
+ SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL_DUPLEX,
+ SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM,
+ SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM_DUPLEX,
+ SPA_BLUETOOTH_AUDIO_CODEC_LC3PLUS_HR,
+
+ /* HFP */
+ SPA_BLUETOOTH_AUDIO_CODEC_CVSD = 0x100,
+ SPA_BLUETOOTH_AUDIO_CODEC_MSBC,
+};
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_BLUETOOTH_AUDIO_H */
diff --git a/third_party/pipewire/spa/param/bluetooth/type-info.h b/third_party/pipewire/spa/param/bluetooth/type-info.h
new file mode 100644
index 0000000000..0471dcce85
--- /dev/null
+++ b/third_party/pipewire/spa/param/bluetooth/type-info.h
@@ -0,0 +1,71 @@
+/* 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_BLUETOOTH_TYPES_H
+#define SPA_BLUETOOTH_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/param/bluetooth/audio.h>
+
+#define SPA_TYPE_INFO_BluetoothAudioCodec SPA_TYPE_INFO_ENUM_BASE "BluetoothAudioCodec"
+#define SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE SPA_TYPE_INFO_BluetoothAudioCodec ":"
+
+static const struct spa_type_info spa_type_bluetooth_audio_codec[] = {
+ /* A2DP */
+ { SPA_BLUETOOTH_AUDIO_CODEC_SBC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "sbc", NULL },
+ { SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "sbc_xq", NULL },
+ { SPA_BLUETOOTH_AUDIO_CODEC_MPEG, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "mpeg", NULL },
+ { SPA_BLUETOOTH_AUDIO_CODEC_AAC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aac", NULL },
+ { SPA_BLUETOOTH_AUDIO_CODEC_APTX, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx", NULL },
+ { SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx_hd", NULL },
+ { SPA_BLUETOOTH_AUDIO_CODEC_LDAC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "ldac", NULL },
+ { SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx_ll", NULL },
+ { SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL_DUPLEX, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "aptx_ll_duplex", NULL },
+ { SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "faststream", NULL },
+ { SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM_DUPLEX, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "faststream_duplex", NULL },
+ { SPA_BLUETOOTH_AUDIO_CODEC_LC3PLUS_HR, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "lc3plus_hr", NULL },
+
+ { SPA_BLUETOOTH_AUDIO_CODEC_CVSD, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "cvsd", NULL },
+ { SPA_BLUETOOTH_AUDIO_CODEC_MSBC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "msbc", NULL },
+
+ { 0, 0, NULL, NULL },
+};
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_BLUETOOTH_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
index 0000000000..e2c83e2af7
--- /dev/null
+++ b/third_party/pipewire/spa/param/format-utils.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_PARAM_FORMAT_UTILS_H
+#define SPA_PARAM_FORMAT_UTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/pod/parser.h>
+#include <spa/param/format.h>
+
+static inline int
+spa_format_parse(const struct spa_pod *format, uint32_t *media_type, uint32_t *media_subtype)
+{
+ return spa_pod_parse_object(format,
+ SPA_TYPE_OBJECT_Format, NULL,
+ SPA_FORMAT_mediaType, SPA_POD_Id(media_type),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(media_subtype));
+}
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_PARAM_FORMAT_UTILS_H */
diff --git a/third_party/pipewire/spa/param/format.h b/third_party/pipewire/spa/param/format.h
new file mode 100644
index 0000000000..fa316076c5
--- /dev/null
+++ b/third_party/pipewire/spa/param/format.h
@@ -0,0 +1,164 @@
+/* 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
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/param/param.h>
+
+/** media type for SPA_TYPE_OBJECT_Format */
+enum spa_media_type {
+ SPA_MEDIA_TYPE_unknown,
+ SPA_MEDIA_TYPE_audio,
+ SPA_MEDIA_TYPE_video,
+ SPA_MEDIA_TYPE_image,
+ SPA_MEDIA_TYPE_binary,
+ SPA_MEDIA_TYPE_stream,
+ SPA_MEDIA_TYPE_application,
+};
+
+/** media subtype for SPA_TYPE_OBJECT_Format */
+enum spa_media_subtype {
+ SPA_MEDIA_SUBTYPE_unknown,
+ SPA_MEDIA_SUBTYPE_raw,
+ SPA_MEDIA_SUBTYPE_dsp,
+ SPA_MEDIA_SUBTYPE_iec958, /** S/PDIF */
+ SPA_MEDIA_SUBTYPE_dsd,
+
+ 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) */
+
+ SPA_FORMAT_AUDIO_iec958Codec, /**< codec used (IEC958) (Id enum spa_audio_iec958_codec) */
+
+ SPA_FORMAT_AUDIO_bitorder, /**< bit order (Id enum spa_param_bitorder) */
+ SPA_FORMAT_AUDIO_interleave, /**< Interleave bytes (Int) */
+
+ /* 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)
+ * use only with DMA-BUF and omit for other buffer types */
+ 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/latency-utils.h b/third_party/pipewire/spa/param/latency-utils.h
new file mode 100644
index 0000000000..57a6828813
--- /dev/null
+++ b/third_party/pipewire/spa/param/latency-utils.h
@@ -0,0 +1,197 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2021 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_LATENCY_UTILS_H
+#define SPA_PARAM_LATENCY_UTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <float.h>
+
+#include <spa/pod/builder.h>
+#include <spa/pod/parser.h>
+#include <spa/param/param.h>
+
+struct spa_latency_info {
+ enum spa_direction direction;
+ float min_quantum;
+ float max_quantum;
+ uint32_t min_rate;
+ uint32_t max_rate;
+ uint64_t min_ns;
+ uint64_t max_ns;
+};
+
+#define SPA_LATENCY_INFO(dir,...) (struct spa_latency_info) { .direction = (dir), ## __VA_ARGS__ }
+
+static inline int
+spa_latency_info_compare(const struct spa_latency_info *a, struct spa_latency_info *b)
+{
+ if (a->min_quantum == b->min_quantum &&
+ a->max_quantum == b->max_quantum &&
+ a->min_rate == b->min_rate &&
+ a->max_rate == b->max_rate &&
+ a->min_ns == b->min_ns &&
+ a->max_ns == b->max_ns)
+ return 0;
+ return 1;
+}
+
+static inline void
+spa_latency_info_combine_start(struct spa_latency_info *info, enum spa_direction direction)
+{
+ *info = SPA_LATENCY_INFO(direction,
+ .min_quantum = FLT_MAX,
+ .max_quantum = 0.0f,
+ .min_rate = UINT32_MAX,
+ .max_rate = 0,
+ .min_ns = UINT64_MAX,
+ .max_ns = 0);
+}
+static inline void
+spa_latency_info_combine_finish(struct spa_latency_info *info)
+{
+ if (info->min_quantum == FLT_MAX)
+ info->min_quantum = 0;
+ if (info->min_rate == UINT32_MAX)
+ info->min_rate = 0;
+ if (info->min_ns == UINT64_MAX)
+ info->min_ns = 0;
+}
+
+static inline int
+spa_latency_info_combine(struct spa_latency_info *info, const struct spa_latency_info *other)
+{
+ if (info->direction != other->direction)
+ return -EINVAL;
+ if (other->min_quantum < info->min_quantum)
+ info->min_quantum = other->min_quantum;
+ if (other->max_quantum > info->max_quantum)
+ info->max_quantum = other->max_quantum;
+ if (other->min_rate < info->min_rate)
+ info->min_rate = other->min_rate;
+ if (other->max_rate > info->max_rate)
+ info->max_rate = other->max_rate;
+ if (other->min_ns < info->min_ns)
+ info->min_ns = other->min_ns;
+ if (other->max_ns > info->max_ns)
+ info->max_ns = other->max_ns;
+ return 0;
+}
+
+static inline int
+spa_latency_parse(const struct spa_pod *latency, struct spa_latency_info *info)
+{
+ int res;
+ spa_zero(*info);
+ if ((res = spa_pod_parse_object(latency,
+ SPA_TYPE_OBJECT_ParamLatency, NULL,
+ SPA_PARAM_LATENCY_direction, SPA_POD_Id(&info->direction),
+ SPA_PARAM_LATENCY_minQuantum, SPA_POD_OPT_Float(&info->min_quantum),
+ SPA_PARAM_LATENCY_maxQuantum, SPA_POD_OPT_Float(&info->max_quantum),
+ SPA_PARAM_LATENCY_minRate, SPA_POD_OPT_Int(&info->min_rate),
+ SPA_PARAM_LATENCY_maxRate, SPA_POD_OPT_Int(&info->max_rate),
+ SPA_PARAM_LATENCY_minNs, SPA_POD_OPT_Long(&info->min_ns),
+ SPA_PARAM_LATENCY_maxNs, SPA_POD_OPT_Long(&info->max_ns))) < 0)
+ return res;
+ info->direction = (enum spa_direction)(info->direction & 1);
+ return 0;
+}
+
+static inline struct spa_pod *
+spa_latency_build(struct spa_pod_builder *builder, uint32_t id, const struct spa_latency_info *info)
+{
+ return (struct spa_pod *)spa_pod_builder_add_object(builder,
+ SPA_TYPE_OBJECT_ParamLatency, id,
+ SPA_PARAM_LATENCY_direction, SPA_POD_Id(info->direction),
+ SPA_PARAM_LATENCY_minQuantum, SPA_POD_Float(info->min_quantum),
+ SPA_PARAM_LATENCY_maxQuantum, SPA_POD_Float(info->max_quantum),
+ SPA_PARAM_LATENCY_minRate, SPA_POD_Int(info->min_rate),
+ SPA_PARAM_LATENCY_maxRate, SPA_POD_Int(info->max_rate),
+ SPA_PARAM_LATENCY_minNs, SPA_POD_Long(info->min_ns),
+ SPA_PARAM_LATENCY_maxNs, SPA_POD_Long(info->max_ns));
+}
+
+struct spa_process_latency_info {
+ float quantum;
+ uint32_t rate;
+ uint64_t ns;
+};
+
+#define SPA_PROCESS_LATENCY_INFO_INIT(...) (struct spa_process_latency_info) { __VA_ARGS__ }
+
+static inline int
+spa_process_latency_parse(const struct spa_pod *latency, struct spa_process_latency_info *info)
+{
+ int res;
+ spa_zero(*info);
+ if ((res = spa_pod_parse_object(latency,
+ SPA_TYPE_OBJECT_ParamProcessLatency, NULL,
+ SPA_PARAM_PROCESS_LATENCY_quantum, SPA_POD_OPT_Float(&info->quantum),
+ SPA_PARAM_PROCESS_LATENCY_rate, SPA_POD_OPT_Int(&info->rate),
+ SPA_PARAM_PROCESS_LATENCY_ns, SPA_POD_OPT_Long(&info->ns))) < 0)
+ return res;
+ return 0;
+}
+
+static inline struct spa_pod *
+spa_process_latency_build(struct spa_pod_builder *builder, uint32_t id,
+ const struct spa_process_latency_info *info)
+{
+ return (struct spa_pod *)spa_pod_builder_add_object(builder,
+ SPA_TYPE_OBJECT_ParamProcessLatency, id,
+ SPA_PARAM_PROCESS_LATENCY_quantum, SPA_POD_Float(info->quantum),
+ SPA_PARAM_PROCESS_LATENCY_rate, SPA_POD_Int(info->rate),
+ SPA_PARAM_PROCESS_LATENCY_ns, SPA_POD_Long(info->ns));
+}
+
+static inline int
+spa_process_latency_info_add(const struct spa_process_latency_info *process,
+ struct spa_latency_info *info)
+{
+ info->min_quantum += process->quantum;
+ info->max_quantum += process->quantum;
+ info->min_rate += process->rate;
+ info->max_rate += process->rate;
+ info->min_ns += process->ns;
+ info->max_ns += process->ns;
+ return 0;
+}
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_PARAM_LATENCY_UTILS_H */
diff --git a/third_party/pipewire/spa/param/param.h b/third_party/pipewire/spa/param/param.h
new file mode 100644
index 0000000000..6059ee3618
--- /dev/null
+++ b/third_party/pipewire/spa/param/param.h
@@ -0,0 +1,212 @@
+/* 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
+
+/** \defgroup spa_param Parameters
+ * Parameter value enumerations and type information
+ */
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/utils/defs.h>
+
+/** different parameter types that can be queried */
+enum spa_param_type {
+ SPA_PARAM_Invalid, /**< invalid */
+ SPA_PARAM_PropInfo, /**< property information as SPA_TYPE_OBJECT_PropInfo */
+ SPA_PARAM_Props, /**< properties as SPA_TYPE_OBJECT_Props */
+ SPA_PARAM_EnumFormat, /**< available formats as SPA_TYPE_OBJECT_Format */
+ SPA_PARAM_Format, /**< configured format as SPA_TYPE_OBJECT_Format */
+ SPA_PARAM_Buffers, /**< buffer configurations as SPA_TYPE_OBJECT_ParamBuffers*/
+ SPA_PARAM_Meta, /**< allowed metadata for buffers as SPA_TYPE_OBJECT_ParamMeta*/
+ SPA_PARAM_IO, /**< configurable IO areas as SPA_TYPE_OBJECT_ParamIO */
+ SPA_PARAM_EnumProfile, /**< profile enumeration as SPA_TYPE_OBJECT_ParamProfile */
+ SPA_PARAM_Profile, /**< profile configuration as SPA_TYPE_OBJECT_ParamProfile */
+ SPA_PARAM_EnumPortConfig, /**< port configuration enumeration as SPA_TYPE_OBJECT_ParamPortConfig */
+ SPA_PARAM_PortConfig, /**< port configuration as SPA_TYPE_OBJECT_ParamPortConfig */
+ SPA_PARAM_EnumRoute, /**< routing enumeration as SPA_TYPE_OBJECT_ParamRoute */
+ SPA_PARAM_Route, /**< routing configuration as SPA_TYPE_OBJECT_ParamRoute */
+ SPA_PARAM_Control, /**< Control parameter, a SPA_TYPE_Sequence */
+ SPA_PARAM_Latency, /**< latency reporting, a SPA_TYPE_OBJECT_ParamLatency */
+ SPA_PARAM_ProcessLatency, /**< processing latency, a SPA_TYPE_OBJECT_ParamProcessLatency */
+};
+
+/** 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
+ * String : property (eg. "card.profile.devices"),
+ * Array of Int: device indexes
+ * )*)) */
+ SPA_PARAM_PROFILE_save, /**< If profile should be saved (Bool) */
+};
+
+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) */
+ SPA_PARAM_ROUTE_save, /**< If route should be saved (Bool) */
+};
+
+
+/** properties for SPA_TYPE_OBJECT_ParamLatency */
+enum spa_param_latency {
+ SPA_PARAM_LATENCY_START,
+ SPA_PARAM_LATENCY_direction, /**< direction, input/output (Id enum spa_direction) */
+ SPA_PARAM_LATENCY_minQuantum, /**< min latency relative to quantum (Float) */
+ SPA_PARAM_LATENCY_maxQuantum, /**< max latency relative to quantum (Float) */
+ SPA_PARAM_LATENCY_minRate, /**< min latency (Int) relative to rate */
+ SPA_PARAM_LATENCY_maxRate, /**< max latency (Int) relative to rate */
+ SPA_PARAM_LATENCY_minNs, /**< min latency (Long) in nanoseconds */
+ SPA_PARAM_LATENCY_maxNs, /**< max latency (Long) in nanoseconds */
+};
+
+/** properties for SPA_TYPE_OBJECT_ParamProcessLatency */
+enum spa_param_process_latency {
+ SPA_PARAM_PROCESS_LATENCY_START,
+ SPA_PARAM_PROCESS_LATENCY_quantum, /**< latency relative to quantum (Float) */
+ SPA_PARAM_PROCESS_LATENCY_rate, /**< latency (Int) relative to rate */
+ SPA_PARAM_PROCESS_LATENCY_ns, /**< latency (Long) in nanoseconds */
+};
+
+enum spa_param_bitorder {
+ SPA_PARAM_BITORDER_unknown, /**< unknown bitorder */
+ SPA_PARAM_BITORDER_msb, /**< most significant bit */
+ SPA_PARAM_BITORDER_lsb, /**< least significant bit */
+};
+
+/**
+ * \}
+ */
+
+#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
index 0000000000..44b5688ac1
--- /dev/null
+++ b/third_party/pipewire/spa/param/profiler.h
@@ -0,0 +1,97 @@
+/* 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
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/param/param.h>
+
+/** properties for SPA_TYPE_OBJECT_Profiler */
+enum spa_profiler {
+ SPA_PROFILER_START,
+
+ SPA_PROFILER_START_Driver = 0x10000, /**< driver related profiler properties */
+ SPA_PROFILER_info, /**< Generic info, counter and CPU load,
+ * (Struct(
+ * Long : counter,
+ * Float : cpu_load fast,
+ * Float : cpu_load medium,
+ * Float : cpu_load slow),
+ * Int : xrun-count)) */
+ 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),
+ * Fraction : latency)) */
+
+ 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,
+ * Fraction : latency)) */
+
+ 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
index 0000000000..3f57bdeb6a
--- /dev/null
+++ b/third_party/pipewire/spa/param/props.h
@@ -0,0 +1,131 @@
+/* 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
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/param/param.h>
+
+/** properties of SPA_TYPE_OBJECT_PropInfo */
+enum spa_prop_info {
+ SPA_PROP_INFO_START,
+ SPA_PROP_INFO_id, /**< associated id of the property */
+ SPA_PROP_INFO_name, /**< name of the property */
+ SPA_PROP_INFO_type, /**< type and range/enums of property */
+ SPA_PROP_INFO_labels, /**< labels of property if any, this is a
+ * struct with pairs of values, the first one
+ * is of the type of the property, the second
+ * one is a string with a user readable label
+ * for the value. */
+ SPA_PROP_INFO_container, /**< type of container if any (Id) */
+ SPA_PROP_INFO_params, /**< is part of params property (Bool) */
+ SPA_PROP_INFO_description, /**< User readable description */
+};
+
+/** 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_bluetoothAudioCodec,
+
+ 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_channelMap, /**< a channelmap array
+ * (Array (Id enum spa_audio_channel)) */
+ SPA_PROP_monitorMute, /**< mute (Bool) */
+ SPA_PROP_monitorVolumes, /**< a volume array, one volume per
+ * channel (Array of Float) */
+ SPA_PROP_latencyOffsetNsec, /**< delay adjustment */
+ SPA_PROP_softMute, /**< mute (Bool) */
+ SPA_PROP_softVolumes, /**< a volume array, one volume per
+ * channel (Array of Float) */
+
+ SPA_PROP_iec958Codecs, /**< enabled IEC958 (S/PDIF) codecs,
+ * (Array (Id enum spa_audio_iec958_codec) */
+
+ 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_Other = 0x80000, /**< other properties */
+ SPA_PROP_params, /**< simple control params
+ * (Struct(
+ * (String : key,
+ * Pod : value)*)) */
+
+
+ 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
index 0000000000..55a03124e2
--- /dev/null
+++ b/third_party/pipewire/spa/param/type-info.h
@@ -0,0 +1,448 @@
+/* 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
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/utils/defs.h>
+#include <spa/param/props.h>
+#include <spa/param/format.h>
+#include <spa/buffer/type-info.h>
+
+/* base for parameter object enumerations */
+#define SPA_TYPE_INFO_ParamId SPA_TYPE_INFO_ENUM_BASE "ParamId"
+#define SPA_TYPE_INFO_PARAM_ID_BASE SPA_TYPE_INFO_ParamId ":"
+
+static const struct spa_type_info spa_type_param[] = {
+ { SPA_PARAM_Invalid, SPA_TYPE_None, SPA_TYPE_INFO_PARAM_ID_BASE "Invalid", NULL },
+ { SPA_PARAM_PropInfo, SPA_TYPE_OBJECT_PropInfo, 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 },
+ { SPA_PARAM_Latency, SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_INFO_PARAM_ID_BASE "Latency", NULL },
+ { SPA_PARAM_ProcessLatency, SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_INFO_PARAM_ID_BASE "ProcessLatency", 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 ":"
+
+#include <spa/param/audio/type-info.h>
+#include <spa/param/video/type-info.h>
+#include <spa/param/bluetooth/type-info.h>
+
+static const struct spa_type_info spa_type_prop_float_array[] = {
+ { SPA_PROP_START, SPA_TYPE_Float, SPA_TYPE_INFO_BASE "floatArray", NULL, },
+ { 0, 0, NULL, NULL },
+};
+
+static const struct spa_type_info spa_type_prop_channel_map[] = {
+ { SPA_PROP_START, SPA_TYPE_Id, SPA_TYPE_INFO_BASE "channelMap", spa_type_audio_channel, },
+ { 0, 0, NULL, NULL },
+};
+
+static const struct spa_type_info spa_type_prop_iec958_codec[] = {
+ { SPA_PROP_START, SPA_TYPE_Id, SPA_TYPE_INFO_BASE "iec958Codec", spa_type_audio_iec958_codec, },
+ { 0, 0, NULL, NULL },
+};
+
+#define SPA_TYPE_INFO_ParamBitorder SPA_TYPE_INFO_ENUM_BASE "ParamBitorder"
+#define SPA_TYPE_INFO_PARAM_BITORDER_BASE SPA_TYPE_INFO_ParamBitorder ":"
+
+static const struct spa_type_info spa_type_param_bitorder[] = {
+ { SPA_PARAM_BITORDER_unknown, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BITORDER_BASE "unknown", NULL },
+ { SPA_PARAM_BITORDER_msb, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BITORDER_BASE "msb", NULL },
+ { SPA_PARAM_BITORDER_lsb, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_BITORDER_BASE "lsb", NULL },
+ { 0, 0, NULL, NULL },
+};
+
+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_bluetoothAudioCodec, SPA_TYPE_Id, SPA_TYPE_INFO_PROPS_BASE "bluetoothAudioCodec", spa_type_bluetooth_audio_codec },
+
+ { 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", spa_type_prop_float_array },
+ { 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_channelMap, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "channelMap", spa_type_prop_channel_map },
+ { SPA_PROP_monitorMute, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "monitorMute", NULL },
+ { SPA_PROP_monitorVolumes, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "monitorVolumes", spa_type_prop_float_array },
+ { SPA_PROP_latencyOffsetNsec, SPA_TYPE_Long, SPA_TYPE_INFO_PROPS_BASE "latencyOffsetNsec", NULL },
+ { SPA_PROP_softMute, SPA_TYPE_Bool, SPA_TYPE_INFO_PROPS_BASE "softMute", NULL },
+ { SPA_PROP_softVolumes, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "softVolumes", spa_type_prop_float_array },
+ { SPA_PROP_iec958Codecs, SPA_TYPE_Array, SPA_TYPE_INFO_PROPS_BASE "iec958Codecs", spa_type_prop_iec958_codec },
+
+ { 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 },
+
+ { SPA_PROP_params, SPA_TYPE_Struct, SPA_TYPE_INFO_PROPS_BASE "params", 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 },
+ { SPA_PROP_INFO_container, SPA_TYPE_Id, SPA_TYPE_INFO_PROP_INFO_BASE "container", NULL },
+ { SPA_PROP_INFO_params, SPA_TYPE_Bool, SPA_TYPE_INFO_PROP_INFO_BASE "params", NULL },
+ { SPA_PROP_INFO_description, SPA_TYPE_String, SPA_TYPE_INFO_PROP_INFO_BASE "description", 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 ":"
+
+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 },
+ { SPA_MEDIA_SUBTYPE_iec958, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "iec958", NULL },
+ { SPA_MEDIA_SUBTYPE_dsd, SPA_TYPE_Int, SPA_TYPE_INFO_MEDIA_SUBTYPE_BASE "dsd", 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_Array, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "position",
+ spa_type_prop_channel_map },
+
+ { SPA_FORMAT_AUDIO_iec958Codec, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "iec958Codec",
+ spa_type_audio_iec958_codec },
+
+ { SPA_FORMAT_AUDIO_bitorder, SPA_TYPE_Id, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "bitorder",
+ spa_type_param_bitorder },
+ { SPA_FORMAT_AUDIO_interleave, SPA_TYPE_Int, SPA_TYPE_INFO_FORMAT_AUDIO_BASE "interleave", NULL },
+
+ { 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, },
+ { SPA_PARAM_PROFILE_save, SPA_TYPE_Bool, SPA_TYPE_INFO_PARAM_PROFILE_BASE "save", 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_Format, 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_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "profiles", NULL, },
+ { SPA_PARAM_ROUTE_props, SPA_TYPE_OBJECT_Props, SPA_TYPE_INFO_PARAM_ROUTE_BASE "props", NULL, },
+ { SPA_PARAM_ROUTE_devices, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "devices", NULL, },
+ { SPA_PARAM_ROUTE_profile, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "profile", NULL, },
+ { SPA_PARAM_ROUTE_save, SPA_TYPE_Bool, SPA_TYPE_INFO_PARAM_ROUTE_BASE "save", NULL, },
+ { 0, 0, NULL, NULL },
+};
+
+#include <spa/param/profiler.h>
+
+#define SPA_TYPE_INFO_Profiler SPA_TYPE_INFO_OBJECT_BASE "Profiler"
+#define SPA_TYPE_INFO_PROFILER_BASE SPA_TYPE_INFO_Profiler ":"
+
+static const struct spa_type_info spa_type_profiler[] = {
+ { SPA_PROFILER_START, SPA_TYPE_Id, SPA_TYPE_INFO_PROFILER_BASE, spa_type_param, },
+ { SPA_PROFILER_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "info", NULL, },
+ { SPA_PROFILER_clock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "clock", NULL, },
+ { SPA_PROFILER_driverBlock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "driverBlock", NULL, },
+ { SPA_PROFILER_followerBlock, SPA_TYPE_Struct, SPA_TYPE_INFO_PROFILER_BASE "followerBlock", NULL, },
+ { 0, 0, NULL, NULL },
+};
+
+#define SPA_TYPE_INFO_PARAM_Latency SPA_TYPE_INFO_PARAM_BASE "Latency"
+#define SPA_TYPE_INFO_PARAM_LATENCY_BASE SPA_TYPE_INFO_PARAM_Latency ":"
+
+static const struct spa_type_info spa_type_param_latency[] = {
+ { SPA_PARAM_LATENCY_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_LATENCY_BASE, spa_type_param, },
+ { SPA_PARAM_LATENCY_direction, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_LATENCY_BASE "direction", spa_type_direction, },
+ { SPA_PARAM_LATENCY_minQuantum, SPA_TYPE_Float, SPA_TYPE_INFO_PARAM_LATENCY_BASE "minQuantum", NULL, },
+ { SPA_PARAM_LATENCY_maxQuantum, SPA_TYPE_Float, SPA_TYPE_INFO_PARAM_LATENCY_BASE "maxQuantum", NULL, },
+ { SPA_PARAM_LATENCY_minRate, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_LATENCY_BASE "minRate", NULL, },
+ { SPA_PARAM_LATENCY_maxRate, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_LATENCY_BASE "maxRate", NULL, },
+ { SPA_PARAM_LATENCY_minNs, SPA_TYPE_Long, SPA_TYPE_INFO_PARAM_LATENCY_BASE "minNs", NULL, },
+ { SPA_PARAM_LATENCY_maxNs, SPA_TYPE_Long, SPA_TYPE_INFO_PARAM_LATENCY_BASE "maxNs", NULL, },
+ { 0, 0, NULL, NULL },
+};
+
+#define SPA_TYPE_INFO_PARAM_ProcessLatency SPA_TYPE_INFO_PARAM_BASE "ProcessLatency"
+#define SPA_TYPE_INFO_PARAM_PROCESS_LATENCY_BASE SPA_TYPE_INFO_PARAM_ProcessLatency ":"
+
+static const struct spa_type_info spa_type_param_process_latency[] = {
+ { SPA_PARAM_PROCESS_LATENCY_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_LATENCY_BASE, spa_type_param, },
+ { SPA_PARAM_PROCESS_LATENCY_quantum, SPA_TYPE_Float, SPA_TYPE_INFO_PARAM_PROCESS_LATENCY_BASE "quantum", NULL, },
+ { SPA_PARAM_PROCESS_LATENCY_rate, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_PROCESS_LATENCY_BASE "rate", NULL, },
+ { SPA_PARAM_PROCESS_LATENCY_ns, SPA_TYPE_Long, SPA_TYPE_INFO_PARAM_PROCESS_LATENCY_BASE "ns", 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
index 0000000000..0ad207218a
--- /dev/null
+++ b/third_party/pipewire/spa/param/video/chroma.h
@@ -0,0 +1,64 @@
+/* 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
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+/** Various Chroma settings.
+ */
+enum spa_video_chroma_site {
+ SPA_VIDEO_CHROMA_SITE_UNKNOWN = 0, /**< unknown cositing */
+ SPA_VIDEO_CHROMA_SITE_NONE = (1 << 0), /**< no cositing */
+ SPA_VIDEO_CHROMA_SITE_H_COSITED = (1 << 1), /**< chroma is horizontally cosited */
+ SPA_VIDEO_CHROMA_SITE_V_COSITED = (1 << 2), /**< chroma is vertically cosited */
+ SPA_VIDEO_CHROMA_SITE_ALT_LINE = (1 << 3), /**< chroma samples are sited on alternate lines */
+ /* some common chroma cositing */
+ /** chroma samples cosited with luma samples */
+ SPA_VIDEO_CHROMA_SITE_COSITED = (SPA_VIDEO_CHROMA_SITE_H_COSITED | SPA_VIDEO_CHROMA_SITE_V_COSITED),
+ /** jpeg style cositing, also for mpeg1 and mjpeg */
+ SPA_VIDEO_CHROMA_SITE_JPEG = (SPA_VIDEO_CHROMA_SITE_NONE),
+ /** mpeg2 style cositing */
+ SPA_VIDEO_CHROMA_SITE_MPEG2 = (SPA_VIDEO_CHROMA_SITE_H_COSITED),
+ /**< DV style cositing */
+ 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
index 0000000000..028239cf89
--- /dev/null
+++ b/third_party/pipewire/spa/param/video/color.h
@@ -0,0 +1,125 @@
+/* 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
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+/**
+ * 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, /**< 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. */
+};
+
+/**
+ * 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, /**< unknown matrix */
+ SPA_VIDEO_COLOR_MATRIX_RGB, /**< identity matrix */
+ SPA_VIDEO_COLOR_MATRIX_FCC, /**< FCC color matrix */
+ SPA_VIDEO_COLOR_MATRIX_BT709, /**< ITU BT.709 color matrix */
+ SPA_VIDEO_COLOR_MATRIX_BT601, /**< ITU BT.601 color matrix */
+ SPA_VIDEO_COLOR_MATRIX_SMPTE240M, /**< SMTPE 240M color matrix */
+ SPA_VIDEO_COLOR_MATRIX_BT2020, /**< ITU-R BT.2020 color matrix. since 1.6. */
+};
+
+/**
+ * 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, /**< 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 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, /**< 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 */
+};
+
+/**
+ * spa_video_colorimetry:
+ *
+ * Structure describing the color info.
+ */
+struct spa_video_colorimetry {
+ enum spa_video_color_range range; /**< The color range. This is the valid range for the
+ * samples. It is used to convert the samples to Y'PbPr
+ * values. */
+ enum spa_video_color_matrix matrix; /**< the color matrix. Used to convert between Y'PbPr and
+ * non-linear RGB (R'G'B') */
+ enum spa_video_transfer_function transfer; /**< The transfer function. Used to convert between
+ * R'G'B' and RGB */
+ enum spa_video_color_primaries primaries; /**< Color primaries. Used to convert between R'G'B'
+ * and CIE XYZ */
+};
+
+/**
+ * \}
+ */
+
+#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
index 0000000000..782f500231
--- /dev/null
+++ b/third_party/pipewire/spa/param/video/encoded.h
@@ -0,0 +1,74 @@
+/* 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
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/param/format.h>
+
+enum spa_h264_stream_format {
+ SPA_H264_STREAM_FORMAT_UNKNOWN = 0,
+ SPA_H264_STREAM_FORMAT_AVC,
+ SPA_H264_STREAM_FORMAT_AVC3,
+ SPA_H264_STREAM_FORMAT_BYTESTREAM
+};
+
+enum spa_h264_alignment {
+ SPA_H264_ALIGNMENT_UNKNOWN = 0,
+ SPA_H264_ALIGNMENT_AU,
+ SPA_H264_ALIGNMENT_NAL
+};
+
+struct spa_video_info_h264 {
+ struct spa_rectangle size;
+ struct spa_fraction framerate;
+ struct spa_fraction max_framerate;
+ enum spa_h264_stream_format stream_format;
+ enum spa_h264_alignment alignment;
+};
+
+struct spa_video_info_mjpg {
+ struct spa_rectangle size;
+ struct spa_fraction framerate;
+ struct spa_fraction max_framerate;
+};
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_VIDEO_ENCODED_H */
diff --git a/third_party/pipewire/spa/param/video/format-utils.h b/third_party/pipewire/spa/param/video/format-utils.h
new file mode 100644
index 0000000000..9abf67021d
--- /dev/null
+++ b/third_party/pipewire/spa/param/video/format-utils.h
@@ -0,0 +1,233 @@
+/* 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
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+#include <spa/pod/parser.h>
+#include <spa/pod/builder.h>
+#include <spa/param/video/format.h>
+#include <spa/param/format-utils.h>
+
+static inline int
+spa_format_video_raw_parse(const struct spa_pod *format,
+ struct spa_video_info_raw *info)
+{
+ return spa_pod_parse_object(format,
+ SPA_TYPE_OBJECT_Format, NULL,
+ SPA_FORMAT_VIDEO_format, SPA_POD_Id(&info->format),
+ SPA_FORMAT_VIDEO_modifier, SPA_POD_OPT_Long(&info->modifier),
+ SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&info->size),
+ SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&info->framerate),
+ SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_OPT_Fraction(&info->max_framerate),
+ SPA_FORMAT_VIDEO_views, SPA_POD_OPT_Int(&info->views),
+ SPA_FORMAT_VIDEO_interlaceMode, SPA_POD_OPT_Id(&info->interlace_mode),
+ SPA_FORMAT_VIDEO_pixelAspectRatio, SPA_POD_OPT_Fraction(&info->pixel_aspect_ratio),
+ SPA_FORMAT_VIDEO_multiviewMode, SPA_POD_OPT_Id(&info->multiview_mode),
+ SPA_FORMAT_VIDEO_multiviewFlags, SPA_POD_OPT_Id(&info->multiview_flags),
+ SPA_FORMAT_VIDEO_chromaSite, SPA_POD_OPT_Id(&info->chroma_site),
+ SPA_FORMAT_VIDEO_colorRange, SPA_POD_OPT_Id(&info->color_range),
+ SPA_FORMAT_VIDEO_colorMatrix, SPA_POD_OPT_Id(&info->color_matrix),
+ SPA_FORMAT_VIDEO_transferFunction, SPA_POD_OPT_Id(&info->transfer_function),
+ SPA_FORMAT_VIDEO_colorPrimaries, SPA_POD_OPT_Id(&info->color_primaries));
+}
+
+static inline int
+spa_format_video_dsp_parse(const struct spa_pod *format,
+ struct spa_video_info_dsp *info)
+{
+ return spa_pod_parse_object(format,
+ SPA_TYPE_OBJECT_Format, NULL,
+ SPA_FORMAT_VIDEO_format, SPA_POD_Id(&info->format),
+ SPA_FORMAT_VIDEO_modifier, SPA_POD_OPT_Long(&info->modifier));
+}
+
+static inline struct spa_pod *
+spa_format_video_raw_build(struct spa_pod_builder *builder, uint32_t id,
+ struct spa_video_info_raw *info)
+{
+ struct spa_pod_frame f;
+ spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
+ 0);
+ if (info->format != SPA_VIDEO_FORMAT_UNKNOWN)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_VIDEO_format, SPA_POD_Id(info->format), 0);
+ if (info->size.width != 0 && info->size.height != 0)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&info->size), 0);
+ if (info->framerate.denom != 0)
+ spa_pod_builder_add(builder,
+ 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),
+ 0);
+ if (info->format != SPA_VIDEO_FORMAT_UNKNOWN)
+ spa_pod_builder_add(builder,
+ 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 struct spa_pod *
+spa_format_video_h264_build(struct spa_pod_builder *builder, uint32_t id,
+ struct spa_video_info_h264 *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_h264),
+ 0);
+ if (info->size.width != 0 && info->size.height != 0)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&info->size), 0);
+ if (info->framerate.denom != 0)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&info->framerate), 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->stream_format != 0)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_VIDEO_H264_streamFormat, SPA_POD_Id(info->stream_format), 0);
+ if (info->alignment != 0)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_VIDEO_H264_alignment, SPA_POD_Id(info->alignment), 0);
+ return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
+}
+
+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));
+}
+
+static inline struct spa_pod *
+spa_format_video_mjpg_build(struct spa_pod_builder *builder, uint32_t id,
+ struct spa_video_info_mjpg *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_mjpg),
+ 0);
+ if (info->size.width != 0 && info->size.height != 0)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&info->size), 0);
+ if (info->framerate.denom != 0)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&info->framerate), 0);
+ if (info->max_framerate.denom != 0)
+ spa_pod_builder_add(builder,
+ SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_Fraction(info->max_framerate), 0);
+ return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
+}
+
+/**
+ * \}
+ */
+
+#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
index 0000000000..d0111fb96a
--- /dev/null
+++ b/third_party/pipewire/spa/param/video/format.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_PARAM_VIDEO_FORMAT_H
+#define SPA_PARAM_VIDEO_FORMAT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/param/video/raw.h>
+#include <spa/param/video/encoded.h>
+
+struct spa_video_info {
+ uint32_t media_type;
+ uint32_t media_subtype;
+ union {
+ struct spa_video_info_raw raw;
+ struct spa_video_info_dsp dsp;
+ struct spa_video_info_h264 h264;
+ struct spa_video_info_mjpg mjpg;
+ } info;
+};
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_PARAM_VIDEO_FORMAT_H */
diff --git a/third_party/pipewire/spa/param/video/multiview.h b/third_party/pipewire/spa/param/video/multiview.h
new file mode 100644
index 0000000000..ea16da8a8a
--- /dev/null
+++ b/third_party/pipewire/spa/param/video/multiview.h
@@ -0,0 +1,134 @@
+/* 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
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+/**
+ * All possible stereoscopic 3D and multiview representations.
+ * In conjunction with \ref spa_video_multiview_flags, describes how
+ * multiview content is being transported in the stream.
+ */
+enum spa_video_multiview_mode {
+ /** 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_NONE = -1,
+ SPA_VIDEO_MULTIVIEW_MODE_MONO = 0, /**< All frames are monoscopic */
+ /* Single view modes */
+ SPA_VIDEO_MULTIVIEW_MODE_LEFT, /**< All frames represent a left-eye view */
+ SPA_VIDEO_MULTIVIEW_MODE_RIGHT, /**< All frames represent a right-eye view */
+ /* Stereo view modes */
+ 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. */
+ /* Padding for new frame packing modes */
+
+ SPA_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME = 32, /**< Left and right eye views are provided
+ * in separate frames alternately. */
+ /* Multiview mode(s) */
+ SPA_VIDEO_MULTIVIEW_MODE_MULTIVIEW_FRAME_BY_FRAME, /**< Multipleindependent views are
+ * provided in separate frames in
+ * sequence. This method only applies to
+ * raw video buffers at the moment.
+ * Specific view identification is via
+ * \ref spa_video_multiview_meta on raw
+ * video buffers. */
+ SPA_VIDEO_MULTIVIEW_MODE_SEPARATED, /**< Multiple views are provided as separate
+ * \ref spa_data framebuffers attached
+ * to each \ref spa_buffer, described
+ * by the \ref spa_video_multiview_meta */
+ /* future expansion for annotated modes */
+};
+
+/**
+ * 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 \ref spa_video_multiview_mode.
+ */
+enum spa_video_multiview_flags {
+ SPA_VIDEO_MULTIVIEW_FLAGS_NONE = 0, /**< No flags */
+ SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST = (1 << 0), /**< For stereo streams, the normal arrangement
+ * of left and right views is reversed */
+ SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED = (1 << 1), /**< The left view is vertically mirrored */
+ SPA_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED = (1 << 2), /**< The left view is horizontally mirrored */
+ SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED = (1 << 3), /**< The right view is vertically mirrored */
+ SPA_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED = (1 << 4), /**< The right view is horizontally mirrored */
+ SPA_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT = (1 << 14), /**< 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 = (1 << 15), /**< The video stream contains both
+ * mono and multiview portions,
+ * signalled on each buffer by the
+ * absence or presence of the
+ * \ref SPA_VIDEO_BUFFER_FLAG_MULTIPLE_VIEW
+ * buffer flag. */
+};
+
+
+/**
+ * \}
+ */
+
+#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
index 0000000000..dae4e738eb
--- /dev/null
+++ b/third_party/pipewire/spa/param/video/raw.h
@@ -0,0 +1,226 @@
+/* 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
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+
+#include <spa/utils/defs.h>
+#include <spa/param/video/chroma.h>
+#include <spa/param/video/color.h>
+#include <spa/param/video/multiview.h>
+
+#define SPA_VIDEO_MAX_PLANES 4
+#define SPA_VIDEO_MAX_COMPONENTS 4
+
+/**
+ * Video formats
+ *
+ * The components are in general described in big-endian order. There are some
+ * exceptions (e.g. RGB15 and RGB16) which use the host endianness.
+ *
+ * Most of the formats are identical to their GStreamer equivalent. See the
+ * GStreamer video formats documentation for more details:
+ *
+ * https://gstreamer.freedesktop.org/documentation/additional/design/mediatype-video-raw.html#formats
+ */
+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,
+
+ SPA_VIDEO_FORMAT_xRGB_210LE, /**< 32-bit x:R:G:B 2:10:10:10 little endian */
+ SPA_VIDEO_FORMAT_xBGR_210LE, /**< 32-bit x:B:G:R 2:10:10:10 little endian */
+ SPA_VIDEO_FORMAT_RGBx_102LE, /**< 32-bit R:G:B:x 10:10:10:2 little endian */
+ SPA_VIDEO_FORMAT_BGRx_102LE, /**< 32-bit B:G:R:x 10:10:10:2 little endian */
+ SPA_VIDEO_FORMAT_ARGB_210LE, /**< 32-bit A:R:G:B 2:10:10:10 little endian */
+ SPA_VIDEO_FORMAT_ABGR_210LE, /**< 32-bit A:B:G:R 2:10:10:10 little endian */
+ SPA_VIDEO_FORMAT_RGBA_102LE, /**< 32-bit R:G:B:A 10:10:10:2 little endian */
+ SPA_VIDEO_FORMAT_BGRA_102LE, /**< 32-bit B:G:R:A 10:10:10:2 little endian */
+
+ /* Aliases */
+ SPA_VIDEO_FORMAT_DSP_F32 = SPA_VIDEO_FORMAT_RGBA_F32,
+};
+
+/**
+ * Extra video flags
+ */
+enum spa_video_flags {
+ SPA_VIDEO_FLAG_NONE = 0, /**< no flags */
+ SPA_VIDEO_FLAG_VARIABLE_FPS = (1 << 0), /**< a variable fps is selected, fps_n and fps_d
+ * denote the maximum fps of the video */
+ SPA_VIDEO_FLAG_PREMULTIPLIED_ALPHA = (1 << 1), /**< Each color has been scaled by the alpha value. */
+};
+
+/**
+ * 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, /**< 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. */
+};
+
+/**
+ */
+struct spa_video_info_raw {
+ enum spa_video_format format; /**< the format */
+ int64_t modifier; /**< format modifier
+ * only used with DMA-BUF */
+ struct spa_rectangle size; /**< the frame size of the video */
+ struct spa_fraction framerate; /**< the framerate of the video, 0/1 means variable rate */
+ struct spa_fraction max_framerate; /**< the maximum framerate of the video. This is only valid when
+ \ref framerate is 0/1 */
+ uint32_t views; /**< the number of views in this video */
+ enum spa_video_interlace_mode interlace_mode; /**< the interlace mode */
+ struct spa_fraction pixel_aspect_ratio; /**< the pixel aspect ratio */
+ enum spa_video_multiview_mode multiview_mode; /**< multiview mode */
+ enum spa_video_multiview_flags multiview_flags; /**< multiview flags */
+ enum spa_video_chroma_site chroma_site; /**< the chroma siting */
+ enum spa_video_color_range color_range; /**< the color range. This is the valid range for the samples.
+ * It is used to convert the samples to Y'PbPr values. */
+ enum spa_video_color_matrix color_matrix; /**< the color matrix. Used to convert between Y'PbPr and
+ * non-linear RGB (R'G'B') */
+ enum spa_video_transfer_function transfer_function; /**< the transfer function. used to convert between R'G'B' and RGB */
+ enum spa_video_color_primaries color_primaries; /**< color primaries. used to convert between R'G'B' and CIE XYZ */
+};
+
+#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
index 0000000000..933ead5917
--- /dev/null
+++ b/third_party/pipewire/spa/param/video/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_VIDEO_TYPES_H
+#define SPA_VIDEO_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup spa_param
+ * \{
+ */
+#include <spa/param/video/raw.h>
+
+#define SPA_TYPE_INFO_VideoFormat SPA_TYPE_INFO_ENUM_BASE "VideoFormat"
+#define SPA_TYPE_INFO_VIDEO_FORMAT_BASE SPA_TYPE_INFO_VideoFormat ":"
+
+static const struct spa_type_info spa_type_video_format[] = {
+ { SPA_VIDEO_FORMAT_ENCODED, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "encoded", NULL },
+ { SPA_VIDEO_FORMAT_I420, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420", NULL },
+ { SPA_VIDEO_FORMAT_YV12, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YV12", NULL },
+ { SPA_VIDEO_FORMAT_YUY2, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YUY2", NULL },
+ { SPA_VIDEO_FORMAT_UYVY, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "UYVY", NULL },
+ { SPA_VIDEO_FORMAT_AYUV, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "AYUV", NULL },
+ { SPA_VIDEO_FORMAT_RGBx, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBx", NULL },
+ { SPA_VIDEO_FORMAT_BGRx, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGRx", NULL },
+ { SPA_VIDEO_FORMAT_xRGB, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "xRGB", NULL },
+ { SPA_VIDEO_FORMAT_xBGR, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "xBGR", NULL },
+ { SPA_VIDEO_FORMAT_RGBA, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBA", NULL },
+ { SPA_VIDEO_FORMAT_BGRA, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGRA", NULL },
+ { SPA_VIDEO_FORMAT_ARGB, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ARGB", NULL },
+ { SPA_VIDEO_FORMAT_ABGR, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ABGR", NULL },
+ { SPA_VIDEO_FORMAT_RGB, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGB", NULL },
+ { SPA_VIDEO_FORMAT_BGR, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGR", NULL },
+ { SPA_VIDEO_FORMAT_Y41B, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y41B", NULL },
+ { SPA_VIDEO_FORMAT_Y42B, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y42B", NULL },
+ { SPA_VIDEO_FORMAT_YVYU, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YVYU", NULL },
+ { SPA_VIDEO_FORMAT_Y444, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444", NULL },
+ { SPA_VIDEO_FORMAT_v210, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "v210", NULL },
+ { SPA_VIDEO_FORMAT_v216, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "v216", NULL },
+ { SPA_VIDEO_FORMAT_NV12, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV12", NULL },
+ { SPA_VIDEO_FORMAT_NV21, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV21", NULL },
+ { SPA_VIDEO_FORMAT_GRAY8, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GRAY8", NULL },
+ { SPA_VIDEO_FORMAT_GRAY16_BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GRAY16_BE", NULL },
+ { SPA_VIDEO_FORMAT_GRAY16_LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GRAY16_LE", NULL },
+ { SPA_VIDEO_FORMAT_v308, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "v308", NULL },
+ { SPA_VIDEO_FORMAT_RGB16, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGB16", NULL },
+ { SPA_VIDEO_FORMAT_BGR16, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGR16", NULL },
+ { SPA_VIDEO_FORMAT_RGB15, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGB15", NULL },
+ { SPA_VIDEO_FORMAT_BGR15, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGR15", NULL },
+ { SPA_VIDEO_FORMAT_UYVP, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "UYVP", NULL },
+ { SPA_VIDEO_FORMAT_A420, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A420", NULL },
+ { SPA_VIDEO_FORMAT_RGB8P, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGB8P", NULL },
+ { SPA_VIDEO_FORMAT_YUV9, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YUV9", NULL },
+ { SPA_VIDEO_FORMAT_YVU9, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "YVU9", NULL },
+ { SPA_VIDEO_FORMAT_IYU1, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "IYU1", NULL },
+ { SPA_VIDEO_FORMAT_ARGB64, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ARGB64", NULL },
+ { SPA_VIDEO_FORMAT_AYUV64, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "AYUV64", NULL },
+ { SPA_VIDEO_FORMAT_r210, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "r210", NULL },
+ { SPA_VIDEO_FORMAT_I420_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420_10BE", NULL },
+ { SPA_VIDEO_FORMAT_I420_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420_10LE", NULL },
+ { SPA_VIDEO_FORMAT_I422_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I422_10BE", NULL },
+ { SPA_VIDEO_FORMAT_I422_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I422_10LE", NULL },
+ { SPA_VIDEO_FORMAT_Y444_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444_10BE", NULL },
+ { SPA_VIDEO_FORMAT_Y444_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444_10LE", NULL },
+ { SPA_VIDEO_FORMAT_GBR, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR", NULL },
+ { SPA_VIDEO_FORMAT_GBR_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR_10BE", NULL },
+ { SPA_VIDEO_FORMAT_GBR_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR_10LE", NULL },
+ { SPA_VIDEO_FORMAT_NV16, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV16", NULL },
+ { SPA_VIDEO_FORMAT_NV24, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV24", NULL },
+ { SPA_VIDEO_FORMAT_NV12_64Z32, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV12_64Z32", NULL },
+ { SPA_VIDEO_FORMAT_A420_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A420_10BE", NULL },
+ { SPA_VIDEO_FORMAT_A420_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A420_10LE", NULL },
+ { SPA_VIDEO_FORMAT_A422_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A422_10BE", NULL },
+ { SPA_VIDEO_FORMAT_A422_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A422_10LE", NULL },
+ { SPA_VIDEO_FORMAT_A444_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A444_10BE", NULL },
+ { SPA_VIDEO_FORMAT_A444_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "A444_10LE", NULL },
+ { SPA_VIDEO_FORMAT_NV61, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "NV61", NULL },
+ { SPA_VIDEO_FORMAT_P010_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "P010_10BE", NULL },
+ { SPA_VIDEO_FORMAT_P010_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "P010_10LE", NULL },
+ { SPA_VIDEO_FORMAT_IYU2, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "IYU2", NULL },
+ { SPA_VIDEO_FORMAT_VYUY, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "VYUY", NULL },
+ { SPA_VIDEO_FORMAT_GBRA, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA", NULL },
+ { SPA_VIDEO_FORMAT_GBRA_10BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA_10BE", NULL },
+ { SPA_VIDEO_FORMAT_GBRA_10LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA_10LE", NULL },
+ { SPA_VIDEO_FORMAT_GBR_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR_12BE", NULL },
+ { SPA_VIDEO_FORMAT_GBR_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBR_12LE", NULL },
+ { SPA_VIDEO_FORMAT_GBRA_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA_12BE", NULL },
+ { SPA_VIDEO_FORMAT_GBRA_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "GBRA_12LE", NULL },
+ { SPA_VIDEO_FORMAT_I420_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420_12BE", NULL },
+ { SPA_VIDEO_FORMAT_I420_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I420_12LE", NULL },
+ { SPA_VIDEO_FORMAT_I422_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I422_12BE", NULL },
+ { SPA_VIDEO_FORMAT_I422_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "I422_12LE", NULL },
+ { SPA_VIDEO_FORMAT_Y444_12BE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444_12BE", NULL },
+ { SPA_VIDEO_FORMAT_Y444_12LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "Y444_12LE", NULL },
+ { SPA_VIDEO_FORMAT_RGBA_F16, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBA_F16", NULL },
+ { SPA_VIDEO_FORMAT_RGBA_F32, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBA_F32", NULL },
+ { SPA_VIDEO_FORMAT_xRGB_210LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "xRGB_210LE", NULL },
+ { SPA_VIDEO_FORMAT_xBGR_210LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "xBGR_210LE", NULL },
+ { SPA_VIDEO_FORMAT_RGBx_102LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBx_102LE", NULL },
+ { SPA_VIDEO_FORMAT_BGRx_102LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGRx_102LE", NULL },
+ { SPA_VIDEO_FORMAT_ARGB_210LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ARGB_210LE", NULL },
+ { SPA_VIDEO_FORMAT_ABGR_210LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "ABGR_210LE", NULL },
+ { SPA_VIDEO_FORMAT_RGBA_102LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "RGBA_102LE", NULL },
+ { SPA_VIDEO_FORMAT_BGRA_102LE, SPA_TYPE_Int, SPA_TYPE_INFO_VIDEO_FORMAT_BASE "BGRA_102LE", 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
index 0000000000..624b543c40
--- /dev/null
+++ b/third_party/pipewire/spa/pod/builder.h
@@ -0,0 +1,696 @@
+/* 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
+
+/** \defgroup spa_pod POD
+ * Binary data serialization format
+ */
+
+/**
+ * \addtogroup spa_pod
+ * \{
+ */
+
+#include <stdarg.h>
+
+#include <spa/utils/hook.h>
+#include <spa/pod/iter.h>
+#include <spa/pod/vararg.h>
+
+struct spa_pod_builder_state {
+ uint32_t offset;
+#define SPA_POD_BUILDER_FLAG_BODY (1<<0)
+#define SPA_POD_BUILDER_FLAG_FIRST (1<<1)
+ uint32_t flags;
+ struct spa_pod_frame *frame;
+};
+
+struct spa_pod_builder;
+
+struct spa_pod_builder_callbacks {
+#define SPA_VERSION_POD_BUILDER_CALLBACKS 0
+ uint32_t version;
+
+ int (*overflow) (void *data, uint32_t size);
+};
+
+struct spa_pod_builder {
+ void *data;
+ uint32_t size;
+ uint32_t _padding;
+ struct spa_pod_builder_state state;
+ struct spa_callbacks callbacks;
+};
+
+#define SPA_POD_BUILDER_INIT(buffer,size) (struct spa_pod_builder){ buffer, size, 0, {}, {} }
+
+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_PTROFF(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_PTROFF(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_PTROFF(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 (SPA_FLAG_IS_SET(builder->state.flags, SPA_POD_BUILDER_FLAG_FIRST)) {
+ const struct spa_pod p = { 0, SPA_TYPE_None };
+ spa_pod_builder_raw(builder, &p, sizeof(p));
+ }
+ 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);
+}
+
+static inline int spa_pod_builder_child(struct spa_pod_builder *builder, uint32_t size, uint32_t type)
+{
+ const struct spa_pod p = SPA_POD_INIT(size,type);
+ SPA_FLAG_CLEAR(builder->state.flags, SPA_POD_BUILDER_FLAG_FIRST);
+ return spa_pod_builder_raw(builder, &p, sizeof(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, 0 }
+
+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 *frame = builder->state.frame;
+ uint32_t ftype = frame ? frame->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);
+ SPA_FALLTHROUGH
+ }
+ 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
index 0000000000..0c292eeae5
--- /dev/null
+++ b/third_party/pipewire/spa/pod/command.h
@@ -0,0 +1,69 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_COMMAND_H
+#define SPA_COMMAND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/defs.h>
+#include <spa/pod/pod.h>
+
+/**
+ * \addtogroup spa_pod
+ * \{
+ */
+
+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
index 0000000000..3fd9d00a5b
--- /dev/null
+++ b/third_party/pipewire/spa/pod/compare.h
@@ -0,0 +1,190 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_POD_COMPARE_H
+#define SPA_POD_COMPARE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <spa/param/props.h>
+#include <spa/pod/iter.h>
+#include <spa/pod/builder.h>
+
+/**
+ * \addtogroup spa_pod
+ * \{
+ */
+
+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/dynamic.h b/third_party/pipewire/spa/pod/dynamic.h
new file mode 100644
index 0000000000..1fe77beecd
--- /dev/null
+++ b/third_party/pipewire/spa/pod/dynamic.h
@@ -0,0 +1,81 @@
+/* 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_DYNAMIC_H
+#define SPA_POD_DYNAMIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/pod/builder.h>
+
+struct spa_pod_dynamic_builder {
+ struct spa_pod_builder b;
+ void *data;
+ uint32_t extend;
+ uint32_t _padding;
+};
+
+static int spa_pod_dynamic_builder_overflow(void *data, uint32_t size)
+{
+ struct spa_pod_dynamic_builder *d = (struct spa_pod_dynamic_builder*)data;
+ int32_t old_size = d->b.size;
+ int32_t new_size = SPA_ROUND_UP_N(size, d->extend);
+ void *old_data = d->b.data;
+
+ if (old_data == d->data)
+ d->b.data = NULL;
+ if ((d->b.data = realloc(d->b.data, new_size)) == NULL)
+ return -errno;
+ if (old_data == d->data && d->b.data != old_data && old_size > 0)
+ memcpy(d->b.data, old_data, old_size);
+ d->b.size = new_size;
+ return 0;
+}
+
+static inline void spa_pod_dynamic_builder_init(struct spa_pod_dynamic_builder *builder,
+ void *data, uint32_t size, uint32_t extend)
+{
+ static const struct spa_pod_builder_callbacks spa_pod_dynamic_builder_callbacks = {
+ SPA_VERSION_POD_BUILDER_CALLBACKS,
+ .overflow = spa_pod_dynamic_builder_overflow
+ };
+ builder->b = SPA_POD_BUILDER_INIT(data, size);
+ spa_pod_builder_set_callbacks(&builder->b, &spa_pod_dynamic_builder_callbacks, builder);
+ builder->extend = extend;
+ builder->data = data;
+}
+
+static inline void spa_pod_dynamic_builder_clean(struct spa_pod_dynamic_builder *builder)
+{
+ if (builder->data != builder->b.data)
+ free(builder->b.data);
+}
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_POD_DYNAMIC_H */
diff --git a/third_party/pipewire/spa/pod/event.h b/third_party/pipewire/spa/pod/event.h
new file mode 100644
index 0000000000..2ecfb0eabb
--- /dev/null
+++ b/third_party/pipewire/spa/pod/event.h
@@ -0,0 +1,68 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_EVENT_H
+#define SPA_EVENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/pod/pod.h>
+
+/**
+ * \addtogroup spa_pod
+ * \{
+ */
+
+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
index 0000000000..944b4cc64f
--- /dev/null
+++ b/third_party/pipewire/spa/pod/filter.h
@@ -0,0 +1,422 @@
+/* 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_FILTER_H
+#define SPA_POD_FILTER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <errno.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <spa/param/props.h>
+#include <spa/pod/iter.h>
+#include <spa/pod/builder.h>
+#include <spa/pod/compare.h>
+
+/**
+ * \addtogroup spa_pod
+ * \{
+ */
+
+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_PTROFF(alt, size, void);
+ if (spa_pod_compare_value(type, val, alt, size) < 0)
+ memcpy(val, alt, size);
+ }
+ if (nvals > 2) {
+ alt = SPA_PTROFF(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_PTROFF(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_PTROFF(alt1, size, void);
+ nalt1--;
+ }
+
+ if (p2c == SPA_CHOICE_None || p2c == SPA_CHOICE_Flags) {
+ nalt2 = 1;
+ } else {
+ alt2 = SPA_PTROFF(alt2, size, void);
+ nalt2--;
+ }
+
+ /* start with copying the property */
+ spa_pod_builder_prop(b, p1->key, p1->flags & p2->flags);
+ 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_PTROFF(a1, size, void)) {
+ for (k = 0, a2 = alt2; k < nalt2; k++, a2 = SPA_PTROFF(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_PTROFF(a1,size,void)) {
+ if (spa_pod_compare_value(type, a1, a2, size) < 0)
+ continue;
+ if (spa_pod_compare_value(type, a1, SPA_PTROFF(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_PTROFF(a2,size,void)) {
+ if (spa_pod_compare_value(type, a2, a1, size) < 0)
+ continue;
+ if (spa_pod_compare_value(type, a2, SPA_PTROFF(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_PTROFF(alt1,size,void);
+ alt2 = SPA_PTROFF(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 if ((p1->flags & SPA_POD_PROP_FLAG_MANDATORY) != 0)
+ res = -EINVAL;
+ 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;
+ if ((p2->flags & SPA_POD_PROP_FLAG_MANDATORY) != 0)
+ res = -EINVAL;
+ if (res < 0)
+ break;
+ 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_PTROFF(pp,filter_offset,const struct spa_pod),
+ SPA_POD_SIZE(pp) - filter_offset,
+ SPA_PTROFF(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;
+}
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_POD_FILTER_H */
diff --git a/third_party/pipewire/spa/pod/iter.h b/third_party/pipewire/spa/pod/iter.h
new file mode 100644
index 0000000000..d3dcf139de
--- /dev/null
+++ b/third_party/pipewire/spa/pod/iter.h
@@ -0,0 +1,475 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_POD_ITER_H
+#define SPA_POD_ITER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <errno.h>
+#include <sys/types.h>
+
+#include <spa/pod/pod.h>
+
+/**
+ * \addtogroup spa_pod
+ * \{
+ */
+
+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_PTROFF(pod, size, void) &&
+ SPA_PTROFF(iter, SPA_POD_SIZE(iter), void) <= SPA_PTROFF(pod, size, void);
+}
+
+static inline void *spa_pod_next(const void *iter)
+{
+ return SPA_PTROFF(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_PTROFF(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_PTROFF(body, size, void) &&
+ SPA_PTROFF(iter, SPA_POD_PROP_SIZE(iter), void) <= SPA_PTROFF(body, size, void);
+}
+
+static inline struct spa_pod_prop *spa_pod_prop_next(const struct spa_pod_prop *iter)
+{
+ return SPA_PTROFF(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_PTROFF(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_PTROFF(body, size, void) &&
+ SPA_PTROFF(iter, SPA_POD_CONTROL_SIZE(iter), void) <= SPA_PTROFF(body, size, void);
+}
+
+static inline struct spa_pod_control *spa_pod_control_next(const struct spa_pod_control *iter)
+{
+ return SPA_PTROFF(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_PTROFF((body), sizeof(struct spa_pod_array_body), void); \
+ (iter) < (__typeof__(iter))SPA_PTROFF((body), (_size), void); \
+ (iter) = (__typeof__(iter))SPA_PTROFF((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_PTROFF((body), sizeof(struct spa_pod_choice_body), void); \
+ (iter) < (__typeof__(iter))SPA_PTROFF((body), (_size), void); \
+ (iter) = (__typeof__(iter))SPA_PTROFF((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_PTROFF(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) {
+ *n_vals = SPA_POD_CHOICE_N_VALUES(pod);
+ if ((*choice = SPA_POD_CHOICE_TYPE(pod)) == SPA_CHOICE_None)
+ *n_vals = SPA_MIN(1u, 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 &&
+ !SPA_FLAG_IS_SET(res->flags, SPA_POD_PROP_FLAG_DONT_FIXATE))
+ ((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);
+}
+
+static inline int spa_pod_object_is_fixated(const 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;
+ }
+ return 1;
+}
+
+static inline int spa_pod_is_fixated(const struct spa_pod *pod)
+{
+ if (!spa_pod_is_object(pod))
+ return -EINVAL;
+ return spa_pod_object_is_fixated((const 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
index 0000000000..c0a3099da6
--- /dev/null
+++ b/third_party/pipewire/spa/pod/parser.h
@@ -0,0 +1,583 @@
+/* Spa
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_POD_PARSER_H
+#define SPA_POD_PARSER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <errno.h>
+#include <stdarg.h>
+
+#include <spa/pod/iter.h>
+#include <spa/pod/vararg.h>
+
+/**
+ * \addtogroup spa_pod
+ * \{
+ */
+
+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, 0, {} }
+
+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_PTROFF(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_PTROFF(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); \
+ dest[maxlen-1] = '\0'; \
+ 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*); \
+ SPA_FALLTHROUGH \
+ case 'p': \
+ case 'y': \
+ va_arg(args, void*); \
+ SPA_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
index 0000000000..2d2eaad6a1
--- /dev/null
+++ b/third_party/pipewire/spa/pod/pod.h
@@ -0,0 +1,246 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_POD_H
+#define SPA_POD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/defs.h>
+#include <spa/utils/type.h>
+
+/**
+ * \addtogroup spa_pod
+ * \{
+ */
+
+#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_PTROFF((pod),sizeof(type),void)
+#define SPA_POD_CONTENTS_CONST(type,pod) SPA_PTROFF((pod),sizeof(type),const void)
+#define SPA_POD_BODY(pod) SPA_PTROFF((pod),sizeof(struct spa_pod),void)
+#define SPA_POD_BODY_CONST(pod) SPA_PTROFF((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_ARRAY_VALUE_SIZE(arr) ? ((SPA_POD_BODY_SIZE(arr) - sizeof(struct spa_pod_array_body)) / SPA_POD_ARRAY_VALUE_SIZE(arr)) : 0)
+#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_CHOICE_VALUE_SIZE(choice) ? ((SPA_POD_BODY_SIZE(choice) - sizeof(struct spa_pod_choice_body)) / SPA_POD_CHOICE_VALUE_SIZE(choice)) : 0)
+#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 */
+#define SPA_POD_PROP_FLAG_HINT_DICT (1u<<2) /**< contains a dictionary struct as
+ * (Struct(
+ * Int : n_items,
+ * (String : key,
+ * String : value)*)) */
+#define SPA_POD_PROP_FLAG_MANDATORY (1u<<3) /**< is mandatory */
+#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u<<4) /**< choices need no fixation */
+ 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
index 0000000000..53dc654047
--- /dev/null
+++ b/third_party/pipewire/spa/pod/vararg.h
@@ -0,0 +1,113 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2019 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_POD_VARARG_H
+#define SPA_POD_VARARG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+
+#include <spa/pod/pod.h>
+
+/**
+ * \addtogroup spa_pod
+ * \{
+ */
+
+#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
index 0000000000..39a82f426e
--- /dev/null
+++ b/third_party/pipewire/spa/support/cpu.h
@@ -0,0 +1,168 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_CPU_H
+#define SPA_CPU_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+
+#include <spa/utils/defs.h>
+#include <spa/utils/hook.h>
+
+/** \defgroup spa_cpu CPU
+ * Querying CPU properties
+ */
+
+/**
+ * \addtogroup spa_cpu
+ * \{
+ */
+
+/**
+ * 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)
+
+#define SPA_CPU_VM_NONE (0)
+#define SPA_CPU_VM_OTHER (1 << 0)
+#define SPA_CPU_VM_KVM (1 << 1)
+#define SPA_CPU_VM_QEMU (1 << 2)
+#define SPA_CPU_VM_BOCHS (1 << 3)
+#define SPA_CPU_VM_XEN (1 << 4)
+#define SPA_CPU_VM_UML (1 << 5)
+#define SPA_CPU_VM_VMWARE (1 << 6)
+#define SPA_CPU_VM_ORACLE (1 << 7)
+#define SPA_CPU_VM_MICROSOFT (1 << 8)
+#define SPA_CPU_VM_ZVM (1 << 9)
+#define SPA_CPU_VM_PARALLELS (1 << 10)
+#define SPA_CPU_VM_BHYVE (1 << 11)
+#define SPA_CPU_VM_QNX (1 << 12)
+#define SPA_CPU_VM_ACRN (1 << 13)
+#define SPA_CPU_VM_POWERVM (1 << 14)
+
+/**
+ * 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 2
+ 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);
+
+ /* check if running in a VM. Since:1 */
+ uint32_t (*get_vm_type) (void *object);
+
+ /* denormals will be handled as zero, either with FTZ or DAZ.
+ * Since:2 */
+ int (*zero_denormals) (void *object, bool enable);
+};
+
+#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)
+#define spa_cpu_get_vm_type(c) spa_cpu_method(c, get_vm_type, 1)
+#define spa_cpu_zero_denormals(c,e) spa_cpu_method(c, zero_denormals, 2, e)
+
+/** keys can be given when initializing the cpu handle */
+#define SPA_KEY_CPU_FORCE "cpu.force" /**< force cpu flags */
+#define SPA_KEY_CPU_VM_TYPE "cpu.vm.type" /**< force a VM type */
+#define SPA_KEY_CPU_ZERO_DENORMALS "cpu.zero.denormals" /**< zero denormals */
+
+/**
+ * \}
+ */
+
+#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
index 0000000000..9ebb7d8355
--- /dev/null
+++ b/third_party/pipewire/spa/support/dbus.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_DBUS_H
+#define SPA_DBUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/support/loop.h>
+
+/** \defgroup spa_dbus DBus
+ * DBus communication
+ */
+
+/**
+ * \addtogroup spa_dbus
+ * \{
+ */
+
+#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 */
+};
+
+#define SPA_DBUS_CONNECTION_EVENT_DESTROY 0
+#define SPA_DBUS_CONNECTION_EVENT_DISCONNECTED 1
+#define SPA_DBUS_CONNECTION_EVENT_NUM 2
+
+struct spa_dbus_connection_events {
+#define SPA_VERSION_DBUS_CONNECTION_EVENTS 0
+ uint32_t version;
+
+ /* a connection is destroyed */
+ void (*destroy) (void *data);
+
+ /* a connection disconnected */
+ void (*disconnected) (void *data);
+};
+
+struct spa_dbus_connection {
+#define SPA_VERSION_DBUS_CONNECTION 1
+ uint32_t version;
+ /**
+ * Get the DBusConnection from a wrapper
+ *
+ * Note that the returned handle is closed and unref'd by spa_dbus
+ * immediately before emitting the asynchronous "disconnected" event.
+ * The caller must either deal with the invalidation, or keep an extra
+ * ref on the handle returned.
+ *
+ * \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);
+
+ /**
+ * Add a listener for events
+ *
+ * Since version 1
+ */
+ void (*add_listener) (struct spa_dbus_connection *conn,
+ struct spa_hook *listener,
+ const struct spa_dbus_connection_events *events,
+ void *data);
+};
+
+#define spa_dbus_connection_call(c,method,vers,...) \
+({ \
+ if (SPA_LIKELY(SPA_CALLBACK_CHECK(c,method,vers))) \
+ c->method((c), ## __VA_ARGS__); \
+})
+
+#define spa_dbus_connection_call_vp(c,method,vers,...) \
+({ \
+ void *_res = NULL; \
+ if (SPA_LIKELY(SPA_CALLBACK_CHECK(c,method,vers))) \
+ _res = c->method((c), ## __VA_ARGS__); \
+ _res; \
+})
+
+/** \copydoc spa_dbus_connection.get
+ * \sa spa_dbus_connection.get */
+#define spa_dbus_connection_get(c) spa_dbus_connection_call_vp(c,get,0)
+/** \copydoc spa_dbus_connection.destroy
+ * \sa spa_dbus_connection.destroy */
+#define spa_dbus_connection_destroy(c) spa_dbus_connection_call(c,destroy,0)
+/** \copydoc spa_dbus_connection.add_listener
+ * \sa spa_dbus_connection.add_listener */
+#define spa_dbus_connection_add_listener(c,...) spa_dbus_connection_call(c,add_listener,1,__VA_ARGS__)
+
+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);
+};
+
+/** \copydoc spa_dbus_methods.get_connection
+ * \sa spa_dbus_methods.get_connection
+ */
+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/i18n.h b/third_party/pipewire/spa/support/i18n.h
new file mode 100644
index 0000000000..fc45b0734e
--- /dev/null
+++ b/third_party/pipewire/spa/support/i18n.h
@@ -0,0 +1,107 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2021 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_I18N_H
+#define SPA_I18N_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/hook.h>
+#include <spa/utils/defs.h>
+
+/** \defgroup spa_i18n I18N
+ * Gettext interface
+ */
+
+/**
+ * \addtogroup spa_i18n
+ * \{
+ */
+
+#define SPA_TYPE_INTERFACE_I18N SPA_TYPE_INFO_INTERFACE_BASE "I18N"
+
+#define SPA_VERSION_I18N 0
+struct spa_i18n { struct spa_interface iface; };
+
+struct spa_i18n_methods {
+#define SPA_VERSION_I18N_METHODS 0
+ uint32_t version;
+
+ /**
+ * Translate a message
+ *
+ * \param object the i18n interface
+ * \param msgid the message
+ * \return a translated message
+ */
+ const char *(*text) (void *object, const char *msgid);
+
+ /**
+ * Translate a message for a number
+ *
+ * \param object the i18n interface
+ * \param msgid the message to translate
+ * \param msgid_plural the plural form of \a msgid
+ * \param n a number
+ * \return a translated message for the number \a n
+ */
+ const char *(*ntext) (void *object, const char *msgid,
+ const char *msgid_plural, unsigned long int n);
+};
+
+SPA_FORMAT_ARG_FUNC(2)
+static inline const char *
+spa_i18n_text(struct spa_i18n *i18n, const char *msgid)
+{
+ const char *res = msgid;
+ if (SPA_LIKELY(i18n != NULL))
+ spa_interface_call_res(&i18n->iface,
+ struct spa_i18n_methods, res,
+ text, 0, msgid);
+ return res;
+}
+
+static inline const char *
+spa_i18n_ntext(struct spa_i18n *i18n, const char *msgid,
+ const char *msgid_plural, unsigned long int n)
+{
+ const char *res = n == 1 ? msgid : msgid_plural;
+ if (SPA_LIKELY(i18n != NULL))
+ spa_interface_call_res(&i18n->iface,
+ struct spa_i18n_methods, res,
+ ntext, 0, msgid, msgid_plural, n);
+ return res;
+}
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_I18N_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
index 0000000000..124153d073
--- /dev/null
+++ b/third_party/pipewire/spa/support/log-impl.h
@@ -0,0 +1,143 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_LOG_IMPL_H
+#define SPA_LOG_IMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+
+#include <spa/utils/type.h>
+#include <spa/support/log.h>
+
+/**
+ * \addtogroup spa_log
+ * \{
+ */
+
+static inline SPA_PRINTF_FUNC(7, 0) void spa_log_impl_logtv(void *object,
+ enum spa_log_level level,
+ const struct spa_log_topic *topic,
+ const char *file,
+ int line,
+ const char *func,
+ const char *fmt,
+ va_list args)
+{
+ static const char * const levels[] = { "-", "E", "W", "I", "D", "T" };
+
+ const char *basename = strrchr(file, '/');
+ char text[512], location[1024];
+ char topicstr[32] = {0};
+
+ if (basename)
+ basename += 1; /* skip '/' */
+ else
+ basename = file; /* use whole string if no '/' is found */
+
+ if (topic && topic->topic)
+ snprintf(topicstr, sizeof(topicstr), " %s ", topic->topic);
+ vsnprintf(text, sizeof(text), fmt, args);
+ snprintf(location, sizeof(location), "[%s]%s[%s:%i %s()] %s\n",
+ levels[level],
+ topicstr,
+ basename, line, func, text);
+ fputs(location, stderr);
+}
+
+static inline SPA_PRINTF_FUNC(7,8) void spa_log_impl_logt(void *object,
+ enum spa_log_level level,
+ const struct spa_log_topic *topic,
+ const char *file,
+ int line,
+ const char *func,
+ const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ spa_log_impl_logtv(object, level, topic, file, line, func, fmt, args);
+ va_end(args);
+}
+
+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)
+{
+
+ spa_log_impl_logtv(object, level, NULL, file, line, func, fmt, args);
+}
+
+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);
+}
+
+static inline void spa_log_impl_topic_init(void *object, struct spa_log_topic *topic)
+{
+ /* noop */
+}
+
+#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, \
+ spa_log_impl_logt, \
+ spa_log_impl_logtv, \
+ } }
+
+#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
index 0000000000..e4990d1bb6
--- /dev/null
+++ b/third_party/pipewire/spa/support/log.h
@@ -0,0 +1,314 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_LOG_H
+#define SPA_LOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+
+#include <spa/utils/type.h>
+#include <spa/utils/defs.h>
+#include <spa/utils/hook.h>
+
+/** \defgroup spa_log Log
+ * Logging interface
+ */
+
+/**
+ * \addtogroup spa_log
+ * \{
+ */
+
+/** The default log topic. Redefine this in your code to
+ * allow for the spa_log_* macros to work correctly, e.g:
+ *
+ * \code{.c}
+ * struct spa_log_topic *mylogger;
+ * #undef SPA_LOG_TOPIC_DEFAULT
+ * #define SPA_LOG_TOPIC_DEFAULT mylogger
+ * \endcode
+ */
+#define SPA_LOG_TOPIC_DEFAULT NULL
+
+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"
+
+
+struct spa_log {
+ /** the version of this log. This can be used to expand this
+ * structure in the future */
+#define SPA_VERSION_LOG 0
+ struct spa_interface iface;
+ /**
+ * Logging level, everything above this level is not logged
+ */
+ enum spa_log_level level;
+};
+
+/**
+ * \struct spa_log_topic
+ *
+ * Identifier for a topic. Topics are string-based filters that logically
+ * group messages together. An implementation may decide to filter different
+ * topics on different levels, for example the "protocol" topic may require
+ * debug level TRACE while the "core" topic defaults to debug level INFO.
+ *
+ * spa_log_topics require a spa_log_methods version of 1 or higher.
+ */
+struct spa_log_topic {
+#define SPA_VERSION_LOG_TOPIC 0
+ /** the version of this topic. This can be used to expand this
+ * structure in the future */
+ uint32_t version;
+ /** The string identifier for the topic */
+ const char *topic;
+ /** Logging level set for this topic */
+ enum spa_log_level level;
+ /** False if this topic follows the \ref spa_log level */
+ bool has_custom_level;
+};
+
+struct spa_log_methods {
+#define SPA_VERSION_LOG_METHODS 1
+ uint32_t version;
+ /**
+ * Log a message with the given log level.
+ *
+ * \note If compiled with this header, this function is only called
+ * for implementations of version 0. For versions 1 and above, see
+ * logt() instead.
+ *
+ * \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.
+ *
+ * \note If compiled with this header, this function is only called
+ * for implementations of version 0. For versions 1 and above, see
+ * logtv() instead.
+ *
+ * \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);
+ /**
+ * Log a message with the given log level for the given topic.
+ *
+ * \note Callers that do not use topic-based logging (version 0), the \a
+ * topic is NULL
+ *
+ * \param log a spa_log
+ * \param level a spa_log_level
+ * \param topic the topic for this message, may be NULL
+ * \param file the file name
+ * \param line the line number
+ * \param func the function name
+ * \param fmt printf style format
+ * \param ... format arguments
+ *
+ * \since 1
+ */
+ void (*logt) (void *object,
+ enum spa_log_level level,
+ const struct spa_log_topic *topic,
+ const char *file,
+ int line,
+ const char *func,
+ const char *fmt, ...) SPA_PRINTF_FUNC(7, 8);
+
+ /**
+ * Log a message with the given log level for the given topic.
+ *
+ * \note For callers that do not use topic-based logging (version 0),
+ * the \a topic is NULL
+ *
+ * \param log a spa_log
+ * \param level a spa_log_level
+ * \param topic the topic for this message, may be NULL
+ * \param file the file name
+ * \param line the line number
+ * \param func the function name
+ * \param fmt printf style format
+ * \param args format arguments
+ *
+ * \since 1
+ */
+ void (*logtv) (void *object,
+ enum spa_log_level level,
+ const struct spa_log_topic *topic,
+ const char *file,
+ int line,
+ const char *func,
+ const char *fmt,
+ va_list args) SPA_PRINTF_FUNC(7, 0);
+
+ /**
+ * Initializes a \ref spa_log_topic to the correct logging level.
+ *
+ * \since 1
+ */
+ void (*topic_init) (void *object, struct spa_log_topic *topic);
+};
+
+
+#define SPA_LOG_TOPIC(v, t) \
+ (struct spa_log_topic){ .version = v, .topic = (t)}
+
+#define spa_log_topic_init(l, topic) \
+do { \
+ struct spa_log *_l = l; \
+ if (SPA_LIKELY(_l)) { \
+ struct spa_interface *_if = &_l->iface; \
+ spa_interface_call(_if, struct spa_log_methods, \
+ topic_init, 1, topic); \
+ } \
+} while(0)
+
+/* Unused, left for backwards compat */
+#define spa_log_level_enabled(l,lev) ((l) && (l)->level >= (lev))
+
+#define spa_log_level_topic_enabled(l,topic,lev) \
+({ \
+ struct spa_log *_log = l; \
+ enum spa_log_level _lev = _log ? _log->level : SPA_LOG_LEVEL_NONE; \
+ struct spa_log_topic *_t = (struct spa_log_topic *)topic; \
+ if (_t && _t->has_custom_level) \
+ _lev = _t->level; \
+ _lev >= lev; \
+})
+
+/* Transparently calls to version 0 log if v1 is not supported */
+#define spa_log_logt(l,lev,topic,...) \
+({ \
+ struct spa_log *_l = l; \
+ struct spa_interface *_if = &_l->iface; \
+ if (SPA_UNLIKELY(spa_log_level_topic_enabled(_l, topic, lev))) { \
+ if (!spa_interface_call(_if, \
+ struct spa_log_methods, logt, 1, \
+ lev, topic, \
+ __VA_ARGS__)) \
+ spa_interface_call(_if, \
+ struct spa_log_methods, log, 0, \
+ lev, __VA_ARGS__); \
+ } \
+})
+
+/* Transparently calls to version 0 logv if v1 is not supported */
+#define spa_log_logtv(l,lev,topic,...) \
+({ \
+ struct spa_log *_l = l; \
+ struct spa_interface *_if = &_l->iface; \
+ if (SPA_UNLIKELY(spa_log_level_topic_enabled(_l, topic, lev))) { \
+ if (!spa_interface_call(_if, \
+ struct spa_log_methods, logtv, 1, \
+ lev, topic, \
+ __VA_ARGS__)) \
+ spa_interface_call(_if, \
+ struct spa_log_methods, logv, 0, \
+ lev, __VA_ARGS__); \
+ } \
+})
+
+#define spa_log_log(l,lev,...) \
+ spa_log_logt(l,lev,SPA_LOG_TOPIC_DEFAULT,__VA_ARGS__)
+
+#define spa_log_logv(l,lev,...) \
+ spa_log_logtv(l,lev,SPA_LOG_TOPIC_DEFAULT,__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__)
+
+#define spa_logt_error(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_ERROR,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
+#define spa_logt_warn(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_WARN,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
+#define spa_logt_info(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_INFO,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
+#define spa_logt_debug(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_DEBUG,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
+#define spa_logt_trace(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_TRACE,t,__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
+
+/** \fn spa_log_error */
+
+/** 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 */
+#define SPA_KEY_LOG_PATTERNS "log.patterns" /**< Spa:String:JSON array of [ {"pattern" : level}, ... ] */
+
+/**
+ * \}
+ */
+
+#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
index 0000000000..3c93573c8f
--- /dev/null
+++ b/third_party/pipewire/spa/support/loop.h
@@ -0,0 +1,327 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_LOOP_H
+#define SPA_LOOP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/defs.h>
+#include <spa/utils/hook.h>
+#include <spa/support/system.h>
+
+/** \defgroup spa_loop Loop
+ * Event loop interface
+ */
+
+/**
+ * \addtogroup spa_loop
+ * \{
+ */
+
+#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;
+ /* private data for the loop implementer */
+ void *priv;
+};
+
+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-loader.h b/third_party/pipewire/spa/support/plugin-loader.h
new file mode 100644
index 0000000000..ced58cbce2
--- /dev/null
+++ b/third_party/pipewire/spa/support/plugin-loader.h
@@ -0,0 +1,101 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2021 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_LOADER_H
+#define SPA_PLUGIN_LOADER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/hook.h>
+#include <spa/utils/dict.h>
+
+/** \defgroup spa_plugin_loader Plugin Loader
+ * SPA plugin loader
+ */
+
+/**
+ * \addtogroup spa_plugin_loader
+ * \{
+ */
+
+#define SPA_TYPE_INTERFACE_PluginLoader SPA_TYPE_INFO_INTERFACE_BASE "PluginLoader"
+
+#define SPA_VERSION_PLUGIN_LOADER 0
+struct spa_plugin_loader { struct spa_interface iface; };
+
+struct spa_plugin_loader_methods {
+#define SPA_VERSION_PLUGIN_LOADER_METHODS 0
+ uint32_t version;
+
+ /**
+ * Load a SPA plugin.
+ *
+ * \param factory_name Plugin factory name
+ * \param info Info dictionary for plugin. NULL if none.
+ * \return plugin handle, or NULL on error
+ */
+ struct spa_handle *(*load) (void *object, const char *factory_name, const struct spa_dict *info);
+
+ /**
+ * Unload a SPA plugin.
+ *
+ * \param handle Plugin handle.
+ * \return 0 on success, < 0 on error
+ */
+ int (*unload)(void *object, struct spa_handle *handle);
+};
+
+static inline struct spa_handle *
+spa_plugin_loader_load(struct spa_plugin_loader *loader, const char *factory_name, const struct spa_dict *info)
+{
+ struct spa_handle *res = NULL;
+ if (SPA_LIKELY(loader != NULL))
+ spa_interface_call_res(&loader->iface,
+ struct spa_plugin_loader_methods, res,
+ load, 0, factory_name, info);
+ return res;
+}
+
+static inline int
+spa_plugin_loader_unload(struct spa_plugin_loader *loader, struct spa_handle *handle)
+{
+ int res = -1;
+ if (SPA_LIKELY(loader != NULL))
+ spa_interface_call_res(&loader->iface,
+ struct spa_plugin_loader_methods, res,
+ unload, 0, handle);
+ return res;
+}
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_PLUGIN_LOADER_H */
diff --git a/third_party/pipewire/spa/support/plugin.h b/third_party/pipewire/spa/support/plugin.h
new file mode 100644
index 0000000000..e66bdc9976
--- /dev/null
+++ b/third_party/pipewire/spa/support/plugin.h
@@ -0,0 +1,229 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_PLUGIN_H
+#define SPA_PLUGIN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/defs.h>
+#include <spa/utils/dict.h>
+
+/**
+ * \defgroup spa_handle Plugin Handle
+ * SPA plugin handle and factory interfaces
+ */
+
+/**
+ * \addtogroup spa_handle
+ * \{
+ */
+
+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
index 0000000000..8076ceb4bb
--- /dev/null
+++ b/third_party/pipewire/spa/support/system.h
@@ -0,0 +1,165 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2019 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_SYSTEM_H
+#define SPA_SYSTEM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct itimerspec;
+
+#include <time.h>
+#include <sys/types.h>
+
+#include <spa/utils/defs.h>
+#include <spa/utils/hook.h>
+
+/** \defgroup spa_system System
+ * I/O, clock, polling, timer, and signal interfaces
+ */
+
+/**
+ * \addtogroup spa_system
+ * \{
+ */
+
+/**
+ * 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/support/thread.h b/third_party/pipewire/spa/support/thread.h
new file mode 100644
index 0000000000..ef3866f014
--- /dev/null
+++ b/third_party/pipewire/spa/support/thread.h
@@ -0,0 +1,149 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2021 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_THREAD_H
+#define SPA_THREAD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+#include <spa/utils/defs.h>
+#include <spa/utils/hook.h>
+#include <spa/utils/dict.h>
+
+/** \defgroup spa_thread Thread
+ * Threading utility interfaces
+ */
+
+/**
+ * \addtogroup spa_thread
+ * \{
+ */
+
+/** a thread object.
+ * This can be cast to a platform native thread, like pthread on posix systems
+ */
+#define SPA_TYPE_INFO_Thread SPA_TYPE_INFO_POINTER_BASE "Thread"
+struct spa_thread;
+
+#define SPA_TYPE_INTERFACE_ThreadUtils SPA_TYPE_INFO_INTERFACE_BASE "ThreadUtils"
+#define SPA_VERSION_THREAD_UTILS 0
+struct spa_thread_utils { struct spa_interface iface; };
+
+/** thread utils */
+struct spa_thread_utils_methods {
+#define SPA_VERSION_THREAD_UTILS_METHODS 0
+ uint32_t version;
+
+ /** create a new thread that runs \a start with \a arg */
+ struct spa_thread * (*create) (void *object, const struct spa_dict *props,
+ void *(*start)(void*), void *arg);
+ /** stop and join a thread */
+ int (*join)(void *object, struct spa_thread *thread, void **retval);
+
+ /** get realtime priority range for threads created with \a props */
+ int (*get_rt_range) (void *object, const struct spa_dict *props, int *min, int *max);
+ /** acquire realtime priority, a priority of -1 refers to the priority
+ * configured in the realtime module
+ */
+ int (*acquire_rt) (void *object, struct spa_thread *thread, int priority);
+ /** drop realtime priority */
+ int (*drop_rt) (void *object, struct spa_thread *thread);
+};
+
+/** \copydoc spa_thread_utils_methods.create
+ * \sa spa_thread_utils_methods.create */
+static inline struct spa_thread *spa_thread_utils_create(struct spa_thread_utils *o,
+ const struct spa_dict *props, void *(*start_routine)(void*), void *arg)
+{
+ struct spa_thread *res = NULL;
+ spa_interface_call_res(&o->iface,
+ struct spa_thread_utils_methods, res, create, 0,
+ props, start_routine, arg);
+ return res;
+}
+
+/** \copydoc spa_thread_utils_methods.join
+ * \sa spa_thread_utils_methods.join */
+static inline int spa_thread_utils_join(struct spa_thread_utils *o,
+ struct spa_thread *thread, void **retval)
+{
+ int res = -ENOTSUP;
+ spa_interface_call_res(&o->iface,
+ struct spa_thread_utils_methods, res, join, 0,
+ thread, retval);
+ return res;
+}
+
+/** \copydoc spa_thread_utils_methods.get_rt_range
+ * \sa spa_thread_utils_methods.get_rt_range */
+static inline int spa_thread_utils_get_rt_range(struct spa_thread_utils *o,
+ const struct spa_dict *props, int *min, int *max)
+{
+ int res = -ENOTSUP;
+ spa_interface_call_res(&o->iface,
+ struct spa_thread_utils_methods, res, get_rt_range, 0,
+ props, min, max);
+ return res;
+}
+
+/** \copydoc spa_thread_utils_methods.acquire_rt
+ * \sa spa_thread_utils_methods.acquire_rt */
+static inline int spa_thread_utils_acquire_rt(struct spa_thread_utils *o,
+ struct spa_thread *thread, int priority)
+{
+ int res = -ENOTSUP;
+ spa_interface_call_res(&o->iface,
+ struct spa_thread_utils_methods, res, acquire_rt, 0,
+ thread, priority);
+ return res;
+}
+
+/** \copydoc spa_thread_utils_methods.drop_rt
+ * \sa spa_thread_utils_methods.drop_rt */
+static inline int spa_thread_utils_drop_rt(struct spa_thread_utils *o,
+ struct spa_thread *thread)
+{
+ int res = -ENOTSUP;
+ spa_interface_call_res(&o->iface,
+ struct spa_thread_utils_methods, res, drop_rt, 0, thread);
+ return res;
+}
+
+#define SPA_KEY_THREAD_NAME "thread.name" /* the thread name */
+#define SPA_KEY_THREAD_STACK_SIZE "thread.stack-size" /* the stack size of the thread */
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_THREAD_H */
diff --git a/third_party/pipewire/spa/utils/ansi.h b/third_party/pipewire/spa/utils/ansi.h
new file mode 100644
index 0000000000..83726f8e06
--- /dev/null
+++ b/third_party/pipewire/spa/utils/ansi.h
@@ -0,0 +1,114 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2021 Red Hat, Inc.
+ *
+ * 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_ANSI_H
+#define SPA_UTILS_ANSI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \defgroup spa_ansi ANSI codes
+ * ANSI color code macros
+ */
+
+/**
+ * \addtogroup spa_ansi
+ * \{
+ */
+
+/**
+ * Ansi escape sequences. Note that the color names are approximate only and
+ * the actual rendering of the color depends on the terminal.
+ */
+
+#define SPA_ANSI_RESET "\x1B[0m"
+#define SPA_ANSI_BOLD "\x1B[1m"
+#define SPA_ANSI_ITALIC "\x1B[3m"
+#define SPA_ANSI_UNDERLINE "\x1B[4m"
+
+#define SPA_ANSI_BLACK "\x1B[0;30m"
+#define SPA_ANSI_RED "\x1B[0;31m"
+#define SPA_ANSI_GREEN "\x1B[0;32m"
+#define SPA_ANSI_YELLOW "\x1B[0;33m"
+#define SPA_ANSI_BLUE "\x1B[0;34m"
+#define SPA_ANSI_MAGENTA "\x1B[0;35m"
+#define SPA_ANSI_CYAN "\x1B[0;36m"
+#define SPA_ANSI_WHITE "\x1B[0;37m"
+#define SPA_ANSI_BRIGHT_BLACK "\x1B[90m"
+#define SPA_ANSI_BRIGHT_RED "\x1B[91m"
+#define SPA_ANSI_BRIGHT_GREEN "\x1B[92m"
+#define SPA_ANSI_BRIGHT_YELLOW "\x1B[93m"
+#define SPA_ANSI_BRIGHT_BLUE "\x1B[94m"
+#define SPA_ANSI_BRIGHT_MAGENTA "\x1B[95m"
+#define SPA_ANSI_BRIGHT_CYAN "\x1B[96m"
+#define SPA_ANSI_BRIGHT_WHITE "\x1B[97m"
+
+/* Shortcut because it's a common use-case and easier than combining both */
+#define SPA_ANSI_BOLD_BLACK "\x1B[1;30m"
+#define SPA_ANSI_BOLD_RED "\x1B[1;31m"
+#define SPA_ANSI_BOLD_GREEN "\x1B[1;32m"
+#define SPA_ANSI_BOLD_YELLOW "\x1B[1;33m"
+#define SPA_ANSI_BOLD_BLUE "\x1B[1;34m"
+#define SPA_ANSI_BOLD_MAGENTA "\x1B[1;35m"
+#define SPA_ANSI_BOLD_CYAN "\x1B[1;36m"
+#define SPA_ANSI_BOLD_WHITE "\x1B[1;37m"
+
+#define SPA_ANSI_DARK_BLACK "\x1B[2;30m"
+#define SPA_ANSI_DARK_RED "\x1B[2;31m"
+#define SPA_ANSI_DARK_GREEN "\x1B[2;32m"
+#define SPA_ANSI_DARK_YELLOW "\x1B[2;33m"
+#define SPA_ANSI_DARK_BLUE "\x1B[2;34m"
+#define SPA_ANSI_DARK_MAGENTA "\x1B[2;35m"
+#define SPA_ANSI_DARK_CYAN "\x1B[2;36m"
+#define SPA_ANSI_DARK_WHITE "\x1B[2;37m"
+
+/* Background colors */
+#define SPA_ANSI_BG_BLACK "\x1B[0;40m"
+#define SPA_ANSI_BG_RED "\x1B[0;41m"
+#define SPA_ANSI_BG_GREEN "\x1B[0;42m"
+#define SPA_ANSI_BG_YELLOW "\x1B[0;43m"
+#define SPA_ANSI_BG_BLUE "\x1B[0;44m"
+#define SPA_ANSI_BG_MAGENTA "\x1B[0;45m"
+#define SPA_ANSI_BG_CYAN "\x1B[0;46m"
+#define SPA_ANSI_BG_WHITE "\x1B[0;47m"
+#define SPA_ANSI_BG_BRIGHT_BLACK "\x1B[100m"
+#define SPA_ANSI_BG_BRIGHT_RED "\x1B[101m"
+#define SPA_ANSI_BG_BRIGHT_GREEN "\x1B[102m"
+#define SPA_ANSI_BG_BRIGHT_YELLOW "\x1B[103m"
+#define SPA_ANSI_BG_BRIGHT_BLUE "\x1B[104m"
+#define SPA_ANSI_BG_BRIGHT_MAGENTA "\x1B[105m"
+#define SPA_ANSI_BG_BRIGHT_CYAN "\x1B[106m"
+#define SPA_ANSI_BG_BRIGHT_WHITE "\x1B[107m"
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_UTILS_ANSI_H */
diff --git a/third_party/pipewire/spa/utils/defs.h b/third_party/pipewire/spa/utils/defs.h
new file mode 100644
index 0000000000..874fab410f
--- /dev/null
+++ b/third_party/pipewire/spa/utils/defs.h
@@ -0,0 +1,351 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_UTILS_DEFS_H
+#define SPA_UTILS_DEFS_H
+
+#ifdef __cplusplus
+extern "C" {
+#else
+#include <stdbool.h>
+#endif
+#include <inttypes.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdio.h>
+
+/**
+ * \defgroup spa_utils_defs Miscellaneous
+ * Helper macros and functions
+ */
+
+/**
+ * \addtogroup spa_utils_defs
+ * \{
+ */
+
+/**
+ * SPA_FALLTHROUGH is an annotation to suppress compiler warnings about switch
+ * cases that fall through without a break or return statement. SPA_FALLTHROUGH
+ * is only needed on cases that have code:
+ *
+ * switch (foo) {
+ * case 1: // These cases have no code. No fallthrough annotations are needed.
+ * case 2:
+ * case 3:
+ * foo = 4; // This case has code, so a fallthrough annotation is needed:
+ * SPA_FALLTHROUGH;
+ * default:
+ * return foo;
+ * }
+ */
+#if defined(__clang__) && defined(__cplusplus) && __cplusplus >= 201103L
+ /* clang's fallthrough annotations are only available starting in C++11. */
+# define SPA_FALLTHROUGH [[clang::fallthrough]];
+#elif __GNUC__ >= 7 || __clang_major__ >= 10
+# define SPA_FALLTHROUGH __attribute__ ((fallthrough));
+#else
+# define SPA_FALLTHROUGH /* FALLTHROUGH */
+#endif
+
+#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]))
+/**
+ * Array iterator macro. Usage:
+ * ```c
+ * struct foo array[16];
+ * struct foo *f;
+ * SPA_FOR_EACH_ELEMENT(array, f) {
+ * f->bar = baz;
+ * }
+ * ```
+ */
+#define SPA_FOR_EACH_ELEMENT(arr, ptr) \
+ for (ptr = arr; (void*)ptr < SPA_PTROFF(arr, sizeof(arr), void); ptr++)
+
+#define SPA_ABS(a) \
+({ \
+ __typeof__(a) _a = (a); \
+ SPA_LIKELY(_a >= 0) ? _a : -_a; \
+})
+#define SPA_MIN(a,b) \
+({ \
+ __typeof__(a) _min_a = (a); \
+ __typeof__(b) _min_b = (b); \
+ SPA_LIKELY(_min_a < _min_b) ? _min_a : _min_b; \
+})
+#define SPA_MAX(a,b) \
+({ \
+ __typeof__(a) _max_a = (a); \
+ __typeof__(b) _max_b = (b); \
+ SPA_LIKELY(_max_a > _max_b) ? _max_a : _max_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; \
+})
+
+/**
+ * Return the address (buffer + offset) as pointer of \a type
+ */
+#define SPA_PTROFF(ptr_,offset_,type_) ((type_*)((uintptr_t)(ptr_) + (ptrdiff_t)(offset_)))
+#define SPA_PTROFF_ALIGN(ptr_,offset_,alignment_,type_) \
+ SPA_PTR_ALIGN(SPA_PTROFF(ptr_,offset_,type_),alignment_,type_)
+
+
+/**
+ * Deprecated, use SPA_PTROFF and SPA_PTROFF_ALIGN instead
+ */
+#define SPA_MEMBER(b,o,t) SPA_PTROFF(b,o,t)
+#define SPA_MEMBER_ALIGN(b,o,a,t) SPA_PTROFF_ALIGN(b,o,a,t)
+
+#define SPA_CONTAINER_OF(p,t,m) ((t*)((uintptr_t)p - offsetof(t,m)))
+
+#define SPA_PTRDIFF(p1,p2) ((intptr_t)(p1) - (intptr_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_FORMAT_ARG_FUNC(arg1) __attribute__((format_arg(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))
+#define SPA_NORETURN __attribute__ ((noreturn))
+#else
+#define SPA_PRINTF_FUNC(fmt, arg1)
+#define SPA_FORMAT_ARG_FUNC(arg1)
+#define SPA_ALIGNED(align)
+#define SPA_DEPRECATED
+#define SPA_EXPORT
+#define SPA_SENTINEL
+#define SPA_UNUSED
+#define SPA_NORETURN
+#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(num,value) ((num) - ((num) % (value)))
+#define SPA_ROUND_UP(num,value) ((((num) + (value) - 1) / (value)) * (value))
+
+#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. */
+#ifndef __COVERITY__
+#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)
+#else
+#define spa_assert_se(expr) \
+ do { \
+ int _unique_var = (expr); \
+ if (!_unique_var) \
+ abort(); \
+ } while (false)
+#endif
+
+/* Does exactly nothing */
+#define spa_nop() do {} while (false)
+
+#ifdef NDEBUG
+#define spa_assert(expr) spa_nop()
+#elif defined (FASTPATH)
+#define spa_assert(expr) spa_assert_se(expr)
+#else
+#define spa_assert(expr) spa_assert_se(expr)
+#endif
+
+#ifdef NDEBUG
+#define spa_assert_not_reached() abort()
+#else
+#define spa_assert_not_reached() \
+ do { \
+ fprintf(stderr, "Code should not be reached at %s:%u %s()\n", \
+ __FILE__, __LINE__, __func__); \
+ abort(); \
+ } while (false)
+#endif
+
+#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
index 0000000000..558c6fd016
--- /dev/null
+++ b/third_party/pipewire/spa/utils/dict.h
@@ -0,0 +1,120 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_DICT_H
+#define SPA_DICT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <string.h>
+
+#include <spa/utils/defs.h>
+
+/**
+ * \defgroup spa_dict Dictionary
+ * Dictionary data structure
+ */
+
+/**
+ * \addtogroup spa_dict
+ * \{
+ */
+
+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)
+{
+ if (dict->n_items > 0)
+ 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) &&
+ dict->n_items > 0) {
+ 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/dll.h b/third_party/pipewire/spa/utils/dll.h
new file mode 100644
index 0000000000..65d8cd61de
--- /dev/null
+++ b/third_party/pipewire/spa/utils/dll.h
@@ -0,0 +1,71 @@
+/* Simple DLL
+ *
+ * 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_DLL_H
+#define SPA_DLL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <math.h>
+
+#define SPA_DLL_BW_MAX 0.128
+#define SPA_DLL_BW_MIN 0.016
+
+struct spa_dll {
+ double bw;
+ double z1, z2, z3;
+ double w0, w1, w2;
+};
+
+static inline void spa_dll_init(struct spa_dll *dll)
+{
+ dll->bw = 0.0;
+ dll->z1 = dll->z2 = dll->z3 = 0.0;
+}
+
+static inline void spa_dll_set_bw(struct spa_dll *dll, double bw, unsigned period, unsigned rate)
+{
+ double w = 2 * M_PI * bw * period / rate;
+ dll->w0 = 1.0 - exp (-20.0 * w);
+ dll->w1 = w * 1.5 / period;
+ dll->w2 = w / 1.5;
+ dll->bw = bw;
+}
+
+static inline double spa_dll_update(struct spa_dll *dll, double err)
+{
+ dll->z1 += dll->w0 * (dll->w1 * err - dll->z1);
+ dll->z2 += dll->w0 * (dll->z1 - dll->z2);
+ dll->z3 += dll->w2 * dll->z2;
+ return 1.0 - (dll->z2 + dll->z3);
+}
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_DLL_H */
diff --git a/third_party/pipewire/spa/utils/hook.h b/third_party/pipewire/spa/utils/hook.h
new file mode 100644
index 0000000000..9b1a50b63b
--- /dev/null
+++ b/third_party/pipewire/spa/utils/hook.h
@@ -0,0 +1,471 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_HOOK_H
+#define SPA_HOOK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/defs.h>
+#include <spa/utils/list.h>
+
+/** \defgroup spa_interfaces Interfaces
+ *
+ * \brief Generic implementation of implementation-independent interfaces
+ *
+ * A SPA Interface is a generic struct that, together with a few macros,
+ * provides a generic way of invoking methods on objects without knowing the
+ * details of the implementation.
+ *
+ * The primary interaction with interfaces is through macros that expand into
+ * the right method call. For the implementation of an interface, we need two
+ * structs and a macro to invoke the `bar` method:
+ *
+ * \code{.c}
+ * // this struct must be public and defines the interface to a
+ * // struct foo
+ * struct foo_methods {
+ * uint32_t version;
+ * void (*bar)(void *object, const char *msg);
+ * };
+ *
+ * // this struct does not need to be public
+ * struct foo {
+ * struct spa_interface iface; // must be first element, see foo_bar()
+ * int some_other_field;
+ * ...
+ * };
+ *
+ * // if struct foo is private, we need to cast to a
+ * // generic spa_interface object
+ * #define foo_bar(obj, ...) ({ \
+ * struct foo *f = obj;
+ * spa_interface_call((struct spa_interface *)f, // pointer to spa_interface in foo
+ * struct foo_methods, // type of callbacks
+ * bar, // name of methods
+ * 0, // hardcoded version to match foo_methods->version
+ * __VA_ARGS__ // pass rest of args through
+ * );/
+ * })
+ * \endcode
+ *
+ * The `struct foo_methods` and the invocation macro `foo_bar()` must be
+ * available to the caller. The implementation of `struct foo` can be private.
+ *
+ * \code{.c}
+ * void main(void) {
+ * struct foo *myfoo = get_foo_from_somewhere();
+ * foo_bar(myfoo, "Invoking bar() on myfoo");
+ * }
+ * \endcode
+ * The expansion of `foo_bar()` resolves roughly into this code:
+ * \code{.c}
+ * void main(void) {
+ * struct foo *myfoo = get_foo_from_somewhere();
+ * // foo_bar(myfoo, "Invoking bar() on myfoo");
+ * const struct foo_methods *methods = ((struct spa_interface*)myfoo)->cb;
+ * if (0 >= methods->version && // version check
+ * methods->bar) // compile error if this function does not exist,
+ * methods->bar(myfoo, "Invoking bar() on myfoo");
+ * }
+ * \endcode
+ *
+ * The typecast used in `foo_bar()` allows `struct foo` to be opaque to the
+ * caller. The implementation may assign the callback methods at object
+ * instantiation, and the caller will transparently invoke the method on the
+ * given object. For example, the following code assigns a different `bar()` method on
+ * Mondays - the caller does not need to know this.
+ * \code{.c}
+ *
+ * static void bar_stdout(struct foo *f, const char *msg) {
+ * printf(msg);
+ * }
+ * static void bar_stderr(struct foo *f, const char *msg) {
+ * fprintf(stderr, msg);
+ * }
+ *
+ * struct foo* get_foo_from_somewhere() {
+ * struct foo *f = calloc(sizeof struct foo);
+ * // illustrative only, use SPA_INTERFACE_INIT()
+ * f->iface->cb = (struct foo_methods*) { .bar = bar_stdout };
+ * if (today_is_monday)
+ * f->iface->cb = (struct foo_methods*) { .bar = bar_stderr };
+ * return f;
+ * }
+ * \endcode
+ */
+
+/**
+ * \addtogroup spa_interfaces
+ * \{
+ */
+
+/** \struct spa_callbacks
+ * 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 \a c is of at least version \a v */
+#define SPA_CALLBACK_VERSION_MIN(c,v) ((c) && ((v) == 0 || (c)->version > (v)-1))
+
+/** Check if a callback \a c has method \a m of version \a v */
+#define SPA_CALLBACK_CHECK(c,m,v) (SPA_CALLBACK_VERSION_MIN(c,v) && (c)->m)
+
+/**
+ * Initialize the set of functions \a funcs as a \ref spa_callbacks, together
+ * with \a _data.
+ */
+#define SPA_CALLBACKS_INIT(_funcs,_data) (struct spa_callbacks){ _funcs, _data, }
+
+/** \struct spa_interface
+ */
+struct spa_interface {
+ const char *type;
+ uint32_t version;
+ struct spa_callbacks cb;
+};
+
+/**
+ * Initialize a \ref spa_interface.
+ *
+ * \code{.c}
+ * const static struct foo_methods foo_funcs = {
+ * .bar = some_bar_implementation,
+ * };
+ *
+ * struct foo *f = malloc(...);
+ * f->iface = SPA_INTERFACE_INIT("foo type", 0, foo_funcs, NULL);
+ * \endcode
+ *
+ */
+#define SPA_INTERFACE_INIT(_type,_version,_funcs,_data) \
+ (struct spa_interface){ _type, _version, SPA_CALLBACKS_INIT(_funcs,_data), }
+
+/**
+ * Invoke method named \a method in the \a callbacks.
+ * The \a method_type defines the type of the method struct.
+ * Returns true if the method could be called, false otherwise.
+ */
+#define spa_callbacks_call(callbacks,type,method,vers,...) \
+({ \
+ const type *_f = (const type *) (callbacks)->funcs; \
+ bool _res = SPA_CALLBACK_CHECK(_f,method,vers); \
+ if (SPA_LIKELY(_res)) \
+ _f->method((callbacks)->data, ## __VA_ARGS__); \
+ _res; \
+})
+
+/**
+ * True if the \a callbacks are of version \a vers, false otherwise
+ */
+#define spa_callback_version_min(callbacks,type,vers) \
+({ \
+ const type *_f = (const type *) (callbacks)->funcs; \
+ SPA_CALLBACK_VERSION_MIN(_f,vers); \
+})
+
+/**
+ * True if the \a callbacks contains \a method of version
+ * \a vers, false otherwise
+ */
+#define spa_callback_check(callbacks,type,method,vers) \
+({ \
+ const type *_f = (const type *) (callbacks)->funcs; \
+ SPA_CALLBACK_CHECK(_f,method,vers); \
+})
+
+/**
+ * Invoke method named \a method in the \a callbacks.
+ * The \a method_type defines the type of the method struct.
+ *
+ * The return value is stored in \a res.
+ */
+#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; \
+})
+
+/**
+ * True if the \a iface's callbacks are of version \a vers, false otherwise
+ */
+#define spa_interface_callback_version_min(iface,method_type,vers) \
+ spa_callback_version_min(&(iface)->cb, method_type, vers)
+
+/**
+ * True if the \a iface's callback \a method is of version \a vers
+ * and exists, false otherwise
+ */
+#define spa_interface_callback_check(iface,method_type,method,vers) \
+ spa_callback_check(&(iface)->cb, method_type, method, vers)
+
+/**
+ * Invoke method named \a method in the callbacks on the given interface object.
+ * The \a method_type defines the type of the method struct, not the interface
+ * itself.
+ */
+#define spa_interface_call(iface,method_type,method,vers,...) \
+ spa_callbacks_call(&(iface)->cb,method_type,method,vers,##__VA_ARGS__)
+
+/**
+ * Invoke method named \a method in the callbacks on the given interface object.
+ * The \a method_type defines the type of the method struct, not the interface
+ * itself.
+ *
+ * The return value is stored in \a res.
+ */
+#define spa_interface_call_res(iface,method_type,res,method,vers,...) \
+ spa_callbacks_call_res(&(iface)->cb,method_type,res,method,vers,##__VA_ARGS__)
+
+/**
+ * \}
+ */
+
+/** \defgroup spa_hooks Hooks
+ *
+ * A SPA Hook is a data structure to keep track of callbacks. It is similar to
+ * the \ref spa_interfaces and typically used where an implementation allows
+ * for multiple external callback functions. For example, an implementation may
+ * use a hook list to implement signals with each caller using a hook to
+ * register callbacks to be invoked on those signals.
+ *
+ * The below (pseudo)code is a minimal example outlining the use of hooks:
+ * \code{.c}
+ * // the public interface
+ * #define VERSION_BAR_EVENTS 0 // version of the vtable
+ * struct bar_events {
+ * uint32_t version; // NOTE: an integral member named `version`
+ * // must be present in the vtable
+ * void (*boom)(void *data, const char *msg);
+ * };
+ *
+ * // private implementation
+ * struct party {
+ * struct spa_hook_list bar_list;
+ * };
+ *
+ * void party_add_event_listener(struct party *p, struct spa_hook *listener,
+ * const struct bar_events *events, void *data)
+ * {
+ * spa_hook_list_append(&p->bar_list, listener, events, data);
+ * }
+ *
+ * static void party_on(struct party *p)
+ * {
+ * // NOTE: this is a macro, it evaluates to an integer,
+ * // which is the number of hooks called
+ * spa_hook_list_call(&p->list,
+ * struct bar_events, // vtable type
+ * boom, // function name
+ * 0, // hardcoded version,
+ * // usually the version in which `boom`
+ * // has been added to the vtable
+ * "party on, wayne" // function argument(s)
+ * );
+ * }
+ * \endcode
+ *
+ * In the caller, the hooks can be used like this:
+ * \code{.c}
+ * static void boom_cb(void *data, const char *msg) {
+ * // data is userdata from main()
+ * printf("%s", msg);
+ * }
+ *
+ * static const struct bar_events events = {
+ * .version = VERSION_BAR_EVENTS, // version of the implemented interface
+ * .boom = boom_cb,
+ * };
+ *
+ * void main(void) {
+ * void *userdata = whatever;
+ * struct spa_hook hook;
+ * struct party *p = start_the_party();
+ *
+ * party_add_event_listener(p, &hook, &events, userdata);
+ *
+ * mainloop();
+ * return 0;
+ * }
+ *
+ * \endcode
+ */
+
+/**
+ * \addtogroup spa_hooks
+ * \{
+ */
+
+/** \struct spa_hook_list
+ * A list of hooks. This struct is primarily used by
+ * implementation that use multiple caller-provided \ref spa_hook. */
+struct spa_hook_list {
+ struct spa_list list;
+};
+
+
+/** \struct spa_hook
+ * A hook, contains the structure with functions and the data passed
+ * to the functions.
+ *
+ * A hook should be treated as opaque by the caller.
+ */
+struct spa_hook {
+ struct spa_list link;
+ struct spa_callbacks cb;
+ /** callback and data for the hook list, private to the
+ * hook_list implementor */
+ void (*removed) (struct spa_hook *hook);
+ void *priv;
+};
+
+/** Initialize a hook list to the empty 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. */
+static inline void spa_hook_list_append(struct spa_hook_list *list,
+ struct spa_hook *hook,
+ const void *funcs, void *data)
+{
+ spa_zero(*hook);
+ hook->cb = SPA_CALLBACKS_INIT(funcs, data);
+ spa_list_append(&list->list, &hook->link);
+}
+
+/** Prepend a hook */
+static inline void spa_hook_list_prepend(struct spa_hook_list *list,
+ struct spa_hook *hook,
+ const void *funcs, void *data)
+{
+ spa_zero(*hook);
+ hook->cb = SPA_CALLBACKS_INIT(funcs, data);
+ spa_list_prepend(&list->list, &hook->link);
+}
+
+/** Remove a hook */
+static inline void spa_hook_remove(struct spa_hook *hook)
+{
+ spa_list_remove(&hook->link);
+ if (hook->removed)
+ hook->removed(hook);
+}
+
+/** Remove all hooks from the list */
+static inline void spa_hook_list_clean(struct spa_hook_list *list)
+{
+ struct spa_hook *h;
+ spa_list_consume(h, &list->list, link)
+ spa_hook_remove(h);
+}
+
+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_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) { \
+ if (spa_callbacks_call(&_ci->cb,type,method,vers, ## __VA_ARGS__)) { \
+ _count++; \
+ if (once) \
+ break; \
+ } \
+ } \
+ spa_list_cursor_end(_cursor, link); \
+ _count; \
+})
+
+/**
+ * Call the method named \a m for each element in list \a l.
+ * \a t specifies the type of the callback struct.
+ */
+#define spa_hook_list_call(l,t,m,v,...) spa_hook_list_do_call(l,NULL,t,m,v,false,##__VA_ARGS__)
+/**
+ * Call the method named \a m for each element in list \a l, stopping after
+ * the first invocation.
+ * \a t specifies the type of the callback struct.
+ */
+#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/json-pod.h b/third_party/pipewire/spa/utils/json-pod.h
new file mode 100644
index 0000000000..34c7e08f6d
--- /dev/null
+++ b/third_party/pipewire/spa/utils/json-pod.h
@@ -0,0 +1,177 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2022 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_JSON_POD_H
+#define SPA_UTILS_JSON_POD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/string.h>
+#include <spa/utils/json.h>
+#include <spa/pod/pod.h>
+#include <spa/pod/builder.h>
+#include <spa/debug/types.h>
+
+/** \defgroup spa_json_pod JSON to POD
+ * JSON to POD conversion
+ */
+
+/**
+ * \addtogroup spa_json_pod
+ * \{
+ */
+
+static inline int spa_json_to_pod_part(struct spa_pod_builder *b, uint32_t flags, uint32_t id,
+ const struct spa_type_info *info, struct spa_json *iter, const char *value, int len)
+{
+ const struct spa_type_info *ti;
+ char key[256];
+ struct spa_pod_frame f[1];
+ struct spa_json it[1];
+ int l, res;
+ const char *v;
+ uint32_t type;
+
+ if (spa_json_is_object(value, len) && info != NULL) {
+ if ((ti = spa_debug_type_find(NULL, info->parent)) == NULL)
+ return -EINVAL;
+
+ spa_pod_builder_push_object(b, &f[0], info->parent, id);
+
+ spa_json_enter(iter, &it[0]);
+ while (spa_json_get_string(&it[0], key, sizeof(key)) > 0) {
+ const struct spa_type_info *pi;
+ if ((l = spa_json_next(&it[0], &v)) <= 0)
+ break;
+ if ((pi = spa_debug_type_find_short(ti->values, key)) != NULL)
+ type = pi->type;
+ else if (!spa_atou32(key, &type, 0))
+ continue;
+ spa_pod_builder_prop(b, type, 0);
+ if ((res = spa_json_to_pod_part(b, flags, id, pi, &it[0], v, l)) < 0)
+ return res;
+ }
+ spa_pod_builder_pop(b, &f[0]);
+ }
+ else if (spa_json_is_array(value, len)) {
+ if (info == NULL || info->parent == SPA_TYPE_Struct) {
+ spa_pod_builder_push_struct(b, &f[0]);
+ } else {
+ spa_pod_builder_push_array(b, &f[0]);
+ info = info->values;
+ }
+ spa_json_enter(iter, &it[0]);
+ while ((l = spa_json_next(&it[0], &v)) > 0)
+ if ((res = spa_json_to_pod_part(b, flags, id, info, &it[0], v, l)) < 0)
+ return res;
+ spa_pod_builder_pop(b, &f[0]);
+ }
+ else if (spa_json_is_float(value, len)) {
+ float val = 0.0f;
+ spa_json_parse_float(value, len, &val);
+ switch (info ? info->parent : (uint32_t)SPA_TYPE_Struct) {
+ case SPA_TYPE_Bool:
+ spa_pod_builder_bool(b, val >= 0.5f);
+ break;
+ case SPA_TYPE_Id:
+ spa_pod_builder_id(b, val);
+ break;
+ case SPA_TYPE_Int:
+ spa_pod_builder_int(b, val);
+ break;
+ case SPA_TYPE_Long:
+ spa_pod_builder_long(b, val);
+ break;
+ case SPA_TYPE_Struct:
+ if (spa_json_is_int(value, len))
+ spa_pod_builder_int(b, val);
+ else
+ spa_pod_builder_float(b, val);
+ break;
+ case SPA_TYPE_Float:
+ spa_pod_builder_float(b, val);
+ break;
+ case SPA_TYPE_Double:
+ spa_pod_builder_double(b, val);
+ break;
+ default:
+ spa_pod_builder_none(b);
+ break;
+ }
+ }
+ else if (spa_json_is_bool(value, len)) {
+ bool val = false;
+ spa_json_parse_bool(value, len, &val);
+ spa_pod_builder_bool(b, val);
+ }
+ else if (spa_json_is_null(value, len)) {
+ spa_pod_builder_none(b);
+ }
+ else {
+ char *val = (char*)alloca(len+1);
+ spa_json_parse_stringn(value, len, val, len+1);
+ switch (info ? info->parent : (uint32_t)SPA_TYPE_Struct) {
+ case SPA_TYPE_Id:
+ if ((ti = spa_debug_type_find_short(info->values, val)) != NULL)
+ type = ti->type;
+ else if (!spa_atou32(val, &type, 0))
+ return -EINVAL;
+ spa_pod_builder_id(b, type);
+ break;
+ case SPA_TYPE_Struct:
+ case SPA_TYPE_String:
+ spa_pod_builder_string(b, val);
+ break;
+ default:
+ spa_pod_builder_none(b);
+ break;
+ }
+ }
+ return 0;
+}
+
+static inline int spa_json_to_pod(struct spa_pod_builder *b, uint32_t flags,
+ const struct spa_type_info *info, const char *value, int len)
+{
+ struct spa_json iter;
+ const char *val;
+
+ spa_json_init(&iter, value, len);
+ if ((len = spa_json_next(&iter, &val)) <= 0)
+ return -EINVAL;
+
+ return spa_json_to_pod_part(b, flags, info->type, info, &iter, val, len);
+}
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_UTILS_JSON_POD_H */
diff --git a/third_party/pipewire/spa/utils/json.h b/third_party/pipewire/spa/utils/json.h
new file mode 100644
index 0000000000..73cad7f596
--- /dev/null
+++ b/third_party/pipewire/spa/utils/json.h
@@ -0,0 +1,483 @@
+/* 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_UTILS_JSON_H
+#define SPA_UTILS_JSON_H
+
+#ifdef __cplusplus
+extern "C" {
+#else
+#include <stdbool.h>
+#endif
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+
+#include <spa/utils/defs.h>
+#include <spa/utils/string.h>
+
+/** \defgroup spa_json JSON
+ * Relaxed JSON variant parsing
+ */
+
+/**
+ * \addtogroup spa_json
+ * \{
+ */
+
+/* a simple JSON compatible tokenizer */
+struct spa_json {
+ const char *cur;
+ const char *end;
+ struct spa_json *parent;
+ uint32_t state;
+ uint32_t depth;
+};
+
+#define SPA_JSON_INIT(data,size) (struct spa_json) { (data), (data)+(size), }
+
+static inline void spa_json_init(struct spa_json * iter, const char *data, size_t size)
+{
+ *iter = SPA_JSON_INIT(data, size);
+}
+#define SPA_JSON_ENTER(iter) (struct spa_json) { (iter)->cur, (iter)->end, (iter), }
+
+static inline void spa_json_enter(struct spa_json * iter, struct spa_json * sub)
+{
+ *sub = SPA_JSON_ENTER(iter);
+}
+
+#define SPA_JSON_SAVE(iter) (struct spa_json) { (iter)->cur, (iter)->end, }
+
+/** Get the next token. \a value points to the token and the return value
+ * is the length. */
+static inline int spa_json_next(struct spa_json * iter, const char **value)
+{
+ int utf8_remain = 0;
+ enum { __NONE, __STRUCT, __BARE, __STRING, __UTF8, __ESC, __COMMENT };
+
+ *value = iter->cur;
+ for (; iter->cur < iter->end; iter->cur++) {
+ unsigned char cur = (unsigned char)*iter->cur;
+ again:
+ switch (iter->state) {
+ case __NONE:
+ iter->state = __STRUCT;
+ iter->depth = 0;
+ goto again;
+ case __STRUCT:
+ switch (cur) {
+ case '\0': case '\t': case ' ': case '\r': case '\n': case ':': case '=': case ',':
+ continue;
+ case '#':
+ iter->state = __COMMENT;
+ continue;
+ case '"':
+ *value = iter->cur;
+ iter->state = __STRING;
+ continue;
+ case '[': case '{':
+ *value = iter->cur;
+ if (++iter->depth > 1)
+ continue;
+ iter->cur++;
+ return 1;
+ case '}': case ']':
+ if (iter->depth == 0) {
+ if (iter->parent)
+ iter->parent->cur = iter->cur;
+ return 0;
+ }
+ --iter->depth;
+ continue;
+ default:
+ *value = iter->cur;
+ iter->state = __BARE;
+ }
+ continue;
+ case __BARE:
+ switch (cur) {
+ case '\t': case ' ': case '\r': case '\n':
+ case ':': case ',': case '=': case ']': case '}':
+ iter->state = __STRUCT;
+ if (iter->depth > 0)
+ goto again;
+ return iter->cur - *value;
+ }
+ continue;
+ case __STRING:
+ switch (cur) {
+ case '\\':
+ iter->state = __ESC;
+ continue;
+ case '"':
+ iter->state = __STRUCT;
+ if (iter->depth > 0)
+ continue;
+ return ++iter->cur - *value;
+ case 240 ... 247:
+ utf8_remain++;
+ SPA_FALLTHROUGH;
+ case 224 ... 239:
+ utf8_remain++;
+ SPA_FALLTHROUGH;
+ case 192 ... 223:
+ utf8_remain++;
+ iter->state = __UTF8;
+ continue;
+ default:
+ if (cur >= 32 && cur <= 126)
+ continue;
+ }
+ return -1;
+ case __UTF8:
+ switch (cur) {
+ case 128 ... 191:
+ if (--utf8_remain == 0)
+ iter->state = __STRING;
+ continue;
+ }
+ return -1;
+ case __ESC:
+ switch (cur) {
+ case '"': case '\\': case '/': case 'b': case 'f':
+ case 'n': case 'r': case 't': case 'u':
+ iter->state = __STRING;
+ continue;
+ }
+ return -1;
+ case __COMMENT:
+ switch (cur) {
+ case '\n': case '\r':
+ iter->state = __STRUCT;
+ }
+ }
+
+ }
+ if (iter->depth != 0)
+ return -1;
+ if (iter->state != __STRUCT) {
+ iter->state = __STRUCT;
+ return iter->cur - *value;
+ }
+ return 0;
+}
+
+static inline int spa_json_enter_container(struct spa_json *iter, struct spa_json *sub, char type)
+{
+ const char *value;
+ if (spa_json_next(iter, &value) <= 0 || *value != type)
+ return -1;
+ spa_json_enter(iter, sub);
+ return 1;
+}
+
+static inline int spa_json_is_container(const char *val, int len)
+{
+ return len > 0 && (*val == '{' || *val == '[');
+}
+
+static inline int spa_json_container_len(struct spa_json *iter, const char *value, int len)
+{
+ const char *val;
+ struct spa_json sub;
+ spa_json_enter(iter, &sub);
+ while (spa_json_next(&sub, &val) > 0);
+ return sub.cur + 1 - value;
+}
+
+/* object */
+static inline int spa_json_is_object(const char *val, int len)
+{
+ return len > 0 && *val == '{';
+}
+static inline int spa_json_enter_object(struct spa_json *iter, struct spa_json *sub)
+{
+ return spa_json_enter_container(iter, sub, '{');
+}
+
+/* array */
+static inline bool spa_json_is_array(const char *val, int len)
+{
+ return len > 0 && *val == '[';
+}
+static inline int spa_json_enter_array(struct spa_json *iter, struct spa_json *sub)
+{
+ return spa_json_enter_container(iter, sub, '[');
+}
+
+/* null */
+static inline bool spa_json_is_null(const char *val, int len)
+{
+ return len == 4 && strncmp(val, "null", 4) == 0;
+}
+
+/* float */
+static inline int spa_json_parse_float(const char *val, int len, float *result)
+{
+ char *end;
+ *result = spa_strtof(val, &end);
+ return len > 0 && end == val + len;
+}
+
+static inline bool spa_json_is_float(const char *val, int len)
+{
+ float dummy;
+ return spa_json_parse_float(val, len, &dummy);
+}
+static inline int spa_json_get_float(struct spa_json *iter, float *res)
+{
+ const char *value;
+ int len;
+ if ((len = spa_json_next(iter, &value)) <= 0)
+ return -1;
+ return spa_json_parse_float(value, len, res);
+}
+
+static inline char *spa_json_format_float(char *str, int size, float val)
+{
+ if (SPA_UNLIKELY(!isnormal(val))) {
+ if (val == INFINITY)
+ val = FLT_MAX;
+ else if (val == -INFINITY)
+ val = FLT_MIN;
+ else
+ val = 0.0f;
+ }
+ return spa_dtoa(str, size, val);
+}
+
+/* int */
+static inline int spa_json_parse_int(const char *val, int len, int *result)
+{
+ char *end;
+ *result = strtol(val, &end, 0);
+ return len > 0 && end == val + len;
+}
+static inline bool spa_json_is_int(const char *val, int len)
+{
+ int dummy;
+ return spa_json_parse_int(val, len, &dummy);
+}
+static inline int spa_json_get_int(struct spa_json *iter, int *res)
+{
+ const char *value;
+ int len;
+ if ((len = spa_json_next(iter, &value)) <= 0)
+ return -1;
+ return spa_json_parse_int(value, len, res);
+}
+
+/* bool */
+static inline bool spa_json_is_true(const char *val, int len)
+{
+ return len == 4 && strncmp(val, "true", 4) == 0;
+}
+
+static inline bool spa_json_is_false(const char *val, int len)
+{
+ return len == 5 && strncmp(val, "false", 5) == 0;
+}
+
+static inline bool spa_json_is_bool(const char *val, int len)
+{
+ return spa_json_is_true(val, len) || spa_json_is_false(val, len);
+}
+
+static inline int spa_json_parse_bool(const char *val, int len, bool *result)
+{
+ if ((*result = spa_json_is_true(val, len)))
+ return 1;
+ if (!(*result = !spa_json_is_false(val, len)))
+ return 1;
+ return -1;
+}
+static inline int spa_json_get_bool(struct spa_json *iter, bool *res)
+{
+ const char *value;
+ int len;
+ if ((len = spa_json_next(iter, &value)) <= 0)
+ return -1;
+ return spa_json_parse_bool(value, len, res);
+}
+
+/* string */
+static inline bool spa_json_is_string(const char *val, int len)
+{
+ return len > 1 && *val == '"';
+}
+
+static inline int spa_json_parse_hex(const char *p, int num, uint32_t *res)
+{
+ int i;
+ *res = 0;
+ for (i = 0; i < num; i++) {
+ char v = p[i];
+ if (v >= '0' && v <= '9')
+ v = v - '0';
+ else if (v >= 'a' && v <= 'f')
+ v = v - 'a' + 10;
+ else if (v >= 'A' && v <= 'F')
+ v = v - 'A' + 10;
+ else
+ return -1;
+ *res = (*res << 4) | v;
+ }
+ return 1;
+}
+
+static inline int spa_json_parse_stringn(const char *val, int len, char *result, int maxlen)
+{
+ const char *p;
+ if (maxlen <= len)
+ return -1;
+ if (!spa_json_is_string(val, len)) {
+ if (result != val)
+ strncpy(result, val, len);
+ result += len;
+ } else {
+ for (p = val+1; p < val + len; p++) {
+ if (*p == '\\') {
+ p++;
+ if (*p == 'n')
+ *result++ = '\n';
+ else if (*p == 'r')
+ *result++ = '\r';
+ else if (*p == 'b')
+ *result++ = '\b';
+ else if (*p == 't')
+ *result++ = '\t';
+ else if (*p == 'f')
+ *result++ = '\f';
+ else if (*p == 'u') {
+ uint8_t prefix[] = { 0, 0xc0, 0xe0, 0xf0 };
+ uint32_t idx, n, v, cp, enc[] = { 0x80, 0x800, 0x10000 };
+ if (val + len - p < 5 ||
+ spa_json_parse_hex(p+1, 4, &cp) < 0) {
+ *result++ = *p;
+ continue;
+ }
+ p += 4;
+
+ if (cp >= 0xd800 && cp <= 0xdbff) {
+ if (val + len - p < 7 ||
+ p[1] != '\\' || p[2] != 'u' ||
+ spa_json_parse_hex(p+3, 4, &v) < 0 ||
+ v < 0xdc00 || v > 0xdfff)
+ continue;
+ p += 6;
+ cp = 0x010000 | ((cp & 0x3ff) << 10) | (v & 0x3ff);
+ } else if (cp >= 0xdc00 && cp <= 0xdfff)
+ continue;
+
+ for (idx = 0; idx < 3; idx++)
+ if (cp < enc[idx])
+ break;
+ for (n = idx; n > 0; n--, cp >>= 6)
+ result[n] = (cp | 0x80) & 0xbf;
+ *result++ = (cp | prefix[idx]) & 0xff;
+ result += idx;
+ } else
+ *result++ = *p;
+ } else if (*p == '\"') {
+ break;
+ } else
+ *result++ = *p;
+ }
+ }
+ *result = '\0';
+ return 1;
+}
+
+static inline int spa_json_parse_string(const char *val, int len, char *result)
+{
+ return spa_json_parse_stringn(val, len, result, len+1);
+}
+
+static inline int spa_json_get_string(struct spa_json *iter, char *res, int maxlen)
+{
+ const char *value;
+ int len;
+ if ((len = spa_json_next(iter, &value)) <= 0)
+ return -1;
+ return spa_json_parse_stringn(value, len, res, maxlen);
+}
+
+static inline int spa_json_encode_string(char *str, int size, const char *val)
+{
+ int len = 0;
+ static const char hex[] = { "0123456789abcdef" };
+#define __PUT(c) { if (len < size) *str++ = c; len++; }
+ __PUT('"');
+ while (*val) {
+ switch (*val) {
+ case '\n':
+ __PUT('\\'); __PUT('n');
+ break;
+ case '\r':
+ __PUT('\\'); __PUT('r');
+ break;
+ case '\b':
+ __PUT('\\'); __PUT('b');
+ break;
+ case '\t':
+ __PUT('\\'); __PUT('t');
+ break;
+ case '\f':
+ __PUT('\\'); __PUT('f');
+ break;
+ case '\\':
+ case '"':
+ __PUT('\\'); __PUT(*val);
+ break;
+ default:
+ if (*val > 0 && *val < 0x20) {
+ __PUT('\\'); __PUT('u');
+ __PUT('0'); __PUT('0');
+ __PUT(hex[((*val)>>4)&0xf]); __PUT(hex[(*val)&0xf]);
+ } else {
+ __PUT(*val);
+ }
+ break;
+ }
+ val++;
+ }
+ __PUT('"');
+ __PUT('\0');
+#undef __PUT
+ return len-1;
+}
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_UTILS_JSON_H */
diff --git a/third_party/pipewire/spa/utils/keys.h b/third_party/pipewire/spa/utils/keys.h
new file mode 100644
index 0000000000..80d578fc0e
--- /dev/null
+++ b/third_party/pipewire/spa/utils/keys.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_UTILS_KEYS_H
+#define SPA_UTILS_KEYS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \defgroup spa_keys Key Names
+ * Key names used by SPA plugins
+ */
+
+/**
+ * \addtogroup spa_keys
+ * \{
+ */
+
+/** 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 */
+#define SPA_KEY_API_ALSA_OPEN_UCM "api.alsa.open.ucm" /**< if UCM should be opened card */
+
+/** 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() */
+#define SPA_KEY_API_LIBCAMERA_LOCATION "api.libcamera.location" /**< location of the camera:
+ * "front", "back" or "external" */
+
+/** 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_CONNECTION "api.bluez5.connection" /**< bluez5 device connection status */
+#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 */
+#define SPA_KEY_API_BLUEZ5_CODEC "api.bluez5.codec" /**< a bluetooth codec */
+#define SPA_KEY_API_BLUEZ5_CLASS "api.bluez5.class" /**< a bluetooth class */
+#define SPA_KEY_API_BLUEZ5_ICON "api.bluez5.icon" /**< a bluetooth icon */
+
+/** 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
index 0000000000..62aa9a3df7
--- /dev/null
+++ b/third_party/pipewire/spa/utils/list.h
@@ -0,0 +1,161 @@
+/* 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
+
+/**
+ * \defgroup spa_list List
+ * Doubly linked list data structure
+ */
+
+/**
+ * \addtogroup spa_list List
+ * \{
+ */
+
+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_prev(pos, tmp, head, curr, member) \
+ for (pos = spa_list_last(curr, __typeof__(*pos), member); \
+ tmp = spa_list_prev(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_for_each_safe_reverse(pos, tmp, head, member) \
+ spa_list_for_each_safe_prev(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
index 0000000000..7d60b07ed8
--- /dev/null
+++ b/third_party/pipewire/spa/utils/names.h
@@ -0,0 +1,158 @@
+/* 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
+
+/** \defgroup spa_names Factory Names
+ * SPA plugin factory names
+ */
+
+/**
+ * \addtogroup spa_names
+ * \{
+ */
+
+/** 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 */
+
+#define SPA_NAME_AEC "audio.aec" /**< Echo canceling */
+
+/** 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 codec factory names */
+#define SPA_NAME_API_CODEC_BLUEZ5_A2DP "api.codec.bluez5.a2dp" /**< Bluez5 A2DP codec plugin */
+
+/** 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_ENUM_MANAGER "api.libcamera.enum.manager" /**< a libcamera manager 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. */
+#define SPA_NAME_API_VULKAN_COMPUTE_FILTER \
+ "api.vulkan.compute.filter" /**< a vulkan compute filter. */
+
+/**
+ * \}
+ */
+
+#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
index 0000000000..67ee401a14
--- /dev/null
+++ b/third_party/pipewire/spa/utils/result.h
@@ -0,0 +1,72 @@
+/* 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
+
+/**
+ * \defgroup spa_result Result handling
+ * Asynchronous result utilities
+ */
+
+/**
+ * \addtogroup spa_result
+ * \{
+ */
+
+#include <spa/utils/defs.h>
+#include <spa/utils/list.h>
+
+#define SPA_ASYNC_BIT (1 << 30)
+#define SPA_ASYNC_SEQ_MASK (SPA_ASYNC_BIT - 1)
+#define SPA_ASYNC_MASK (~SPA_ASYNC_SEQ_MASK)
+
+#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
index 0000000000..19bfb86755
--- /dev/null
+++ b/third_party/pipewire/spa/utils/ringbuffer.h
@@ -0,0 +1,188 @@
+/* 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
+
+/**
+ * \defgroup spa_ringbuffer Ringbuffer
+ * Ring buffer implementation
+ */
+
+/**
+ * \addtogroup spa_ringbuffer
+ * \{
+ */
+
+struct spa_ringbuffer;
+
+#include <string.h>
+
+#include <spa/utils/defs.h>
+
+/**
+ * A ringbuffer type.
+ */
+struct spa_ringbuffer {
+ uint32_t readindex; /*< the current read index */
+ uint32_t writeindex; /*< the current write index */
+};
+
+#define SPA_RINGBUFFER_INIT() (struct spa_ringbuffer) { 0, 0 }
+
+/**
+ * Initialize a spa_ringbuffer with \a size.
+ *
+ * \param rbuf a spa_ringbuffer
+ */
+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
+ * \param size the target size of \a rbuf
+ */
+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 \ref 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_PTROFF(buffer, offset, void), l0);
+ if (SPA_UNLIKELY(l1 > 0))
+ spa_memcpy(SPA_PTROFF(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_PTROFF(buffer, offset, void), data, l0);
+ if (SPA_UNLIKELY(l1 > 0))
+ spa_memcpy(buffer, SPA_PTROFF(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/string.h b/third_party/pipewire/spa/utils/string.h
new file mode 100644
index 0000000000..edf4e954f8
--- /dev/null
+++ b/third_party/pipewire/spa/utils/string.h
@@ -0,0 +1,387 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2021 Red Hat, Inc.
+ *
+ * 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_STRING_H
+#define SPA_UTILS_STRING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include <spa/utils/defs.h>
+
+/**
+ * \defgroup spa_string String handling
+ * String handling utilities
+ */
+
+/**
+ * \addtogroup spa_string
+ * \{
+ */
+
+/**
+ * \return true if the two strings are equal, false otherwise
+ *
+ * If both \a a and \a b are NULL, the two are considered equal.
+ *
+ */
+static inline bool spa_streq(const char *s1, const char *s2)
+{
+ return SPA_LIKELY(s1 && s2) ? strcmp(s1, s2) == 0 : s1 == s2;
+}
+
+/**
+ * \return true if the two strings are equal, false otherwise
+ *
+ * If both \a a and \a b are NULL, the two are considered equal.
+ */
+static inline bool spa_strneq(const char *s1, const char *s2, size_t len)
+{
+ return SPA_LIKELY(s1 && s2) ? strncmp(s1, s2, len) == 0 : s1 == s2;
+}
+
+
+/**
+ * \return true if \a s starts with the \a prefix or false otherwise.
+ * A \a s is NULL, it never starts with the given \a prefix. A \a prefix of
+ * NULL is a bug in the caller.
+ */
+static inline bool spa_strstartswith(const char *s, const char *prefix)
+{
+ if (SPA_UNLIKELY(s == NULL))
+ return false;
+
+ spa_assert_se(prefix);
+
+ return strncmp(s, prefix, strlen(prefix)) == 0;
+}
+
+
+/**
+ * \return true if \a s ends with the \a suffix or false otherwise.
+ * A \a s is NULL, it never ends with the given \a suffix. A \a suffix of
+ * NULL is a bug in the caller.
+ */
+static inline bool spa_strendswith(const char *s, const char *suffix)
+{
+ size_t l1, l2;
+
+ if (SPA_UNLIKELY(s == NULL))
+ return false;
+
+ spa_assert_se(suffix);
+
+ l1 = strlen(s);
+ l2 = strlen(suffix);
+ return l1 >= l2 && spa_streq(s + l1 - l2, suffix);
+}
+
+/**
+ * Convert \a str to an int32_t with the given \a base and store the
+ * result in \a val.
+ *
+ * On failure, the value of \a val is unmodified.
+ *
+ * \return true on success, false otherwise
+ */
+static inline bool spa_atoi32(const char *str, int32_t *val, int base)
+{
+ char *endptr;
+ long v;
+
+ if (!str || *str =='\0')
+ return false;
+
+ errno = 0;
+ v = strtol(str, &endptr, base);
+ if (errno != 0 || *endptr != '\0')
+ return false;
+
+ if (v != (int32_t)v)
+ return false;
+
+ *val = v;
+ return true;
+}
+
+/**
+ * Convert \a str to an uint32_t with the given \a base and store the
+ * result in \a val.
+ *
+ * On failure, the value of \a val is unmodified.
+ *
+ * \return true on success, false otherwise
+ */
+static inline bool spa_atou32(const char *str, uint32_t *val, int base)
+{
+ char *endptr;
+ unsigned long long v;
+
+ if (!str || *str =='\0')
+ return false;
+
+ errno = 0;
+ v = strtoull(str, &endptr, base);
+ if (errno != 0 || *endptr != '\0')
+ return false;
+
+ if (v != (uint32_t)v)
+ return false;
+
+ *val = v;
+ return true;
+}
+
+/**
+ * Convert \a str to an int64_t with the given \a base and store the
+ * result in \a val.
+ *
+ * On failure, the value of \a val is unmodified.
+ *
+ * \return true on success, false otherwise
+ */
+static inline bool spa_atoi64(const char *str, int64_t *val, int base)
+{
+ char *endptr;
+ long long v;
+
+ if (!str || *str =='\0')
+ return false;
+
+ errno = 0;
+ v = strtoll(str, &endptr, base);
+ if (errno != 0 || *endptr != '\0')
+ return false;
+
+ *val = v;
+ return true;
+}
+
+/**
+ * Convert \a str to an uint64_t with the given \a base and store the
+ * result in \a val.
+ *
+ * On failure, the value of \a val is unmodified.
+ *
+ * \return true on success, false otherwise
+ */
+static inline bool spa_atou64(const char *str, uint64_t *val, int base)
+{
+ char *endptr;
+ unsigned long long v;
+
+ if (!str || *str =='\0')
+ return false;
+
+ errno = 0;
+ v = strtoull(str, &endptr, base);
+ if (errno != 0 || *endptr != '\0')
+ return false;
+
+ *val = v;
+ return true;
+}
+
+/**
+ * Convert \a str to a boolean. Allowed boolean values are "true" and a
+ * literal "1", anything else is false.
+ *
+ * \return true on success, false otherwise
+ */
+static inline bool spa_atob(const char *str)
+{
+ return spa_streq(str, "true") || spa_streq(str, "1");
+}
+
+/**
+ * "Safe" version of vsnprintf. Exactly the same as vsnprintf but the
+ * returned value is clipped to `size - 1` and a negative or zero size
+ * will abort() the program.
+ *
+ * \return The number of bytes printed, capped to `size-1`, or a negative
+ * number on error.
+ */
+SPA_PRINTF_FUNC(3, 0)
+static inline int spa_vscnprintf(char *buffer, size_t size, const char *format, va_list args)
+{
+ int r;
+
+ spa_assert_se((ssize_t)size > 0);
+
+ r = vsnprintf(buffer, size, format, args);
+ if (SPA_UNLIKELY(r < 0))
+ buffer[0] = '\0';
+ if (SPA_LIKELY(r < (ssize_t)size))
+ return r;
+ return size - 1;
+}
+
+/**
+ * "Safe" version of snprintf. Exactly the same as snprintf but the
+ * returned value is clipped to `size - 1` and a negative or zero size
+ * will abort() the program.
+ *
+ * \return The number of bytes printed, capped to `size-1`, or a negative
+ * number on error.
+ */
+SPA_PRINTF_FUNC(3, 4)
+static inline int spa_scnprintf(char *buffer, size_t size, const char *format, ...)
+{
+ int r;
+ va_list args;
+
+ va_start(args, format);
+ r = spa_vscnprintf(buffer, size, format, args);
+ va_end(args);
+
+ return r;
+}
+
+/**
+ * Convert \a str to a float in the C locale.
+ *
+ * If \a endptr is not NULL, a pointer to the character after the last character
+ * used in the conversion is stored in the location referenced by endptr.
+ *
+ * \return the result float.
+ */
+static inline float spa_strtof(const char *str, char **endptr)
+{
+#ifndef __LOCALE_C_ONLY
+ static locale_t locale = NULL;
+ locale_t prev;
+#endif
+ float v;
+#ifndef __LOCALE_C_ONLY
+ if (SPA_UNLIKELY(locale == NULL))
+ locale = newlocale(LC_ALL_MASK, "C", NULL);
+ prev = uselocale(locale);
+#endif
+ v = strtof(str, endptr);
+#ifndef __LOCALE_C_ONLY
+ uselocale(prev);
+#endif
+ return v;
+}
+
+/**
+ * Convert \a str to a float and store the result in \a val.
+ *
+ * On failure, the value of \a val is unmodified.
+ *
+ * \return true on success, false otherwise
+ */
+static inline bool spa_atof(const char *str, float *val)
+{
+ char *endptr;
+ float v;
+
+ if (!str || *str =='\0')
+ return false;
+ errno = 0;
+ v = spa_strtof(str, &endptr);
+ if (errno != 0 || *endptr != '\0')
+ return false;
+
+ *val = v;
+ return true;
+}
+
+/**
+ * Convert \a str to a double in the C locale.
+ *
+ * If \a endptr is not NULL, a pointer to the character after the last character
+ * used in the conversion is stored in the location referenced by endptr.
+ *
+ * \return the result float.
+ */
+static inline double spa_strtod(const char *str, char **endptr)
+{
+#ifndef __LOCALE_C_ONLY
+ static locale_t locale = NULL;
+ locale_t prev;
+#endif
+ double v;
+#ifndef __LOCALE_C_ONLY
+ if (SPA_UNLIKELY(locale == NULL))
+ locale = newlocale(LC_ALL_MASK, "C", NULL);
+ prev = uselocale(locale);
+#endif
+ v = strtod(str, endptr);
+#ifndef __LOCALE_C_ONLY
+ uselocale(prev);
+#endif
+ return v;
+}
+
+/**
+ * Convert \a str to a double and store the result in \a val.
+ *
+ * On failure, the value of \a val is unmodified.
+ *
+ * \return true on success, false otherwise
+ */
+static inline bool spa_atod(const char *str, double *val)
+{
+ char *endptr;
+ double v;
+
+ if (!str || *str =='\0')
+ return false;
+
+ errno = 0;
+ v = spa_strtod(str, &endptr);
+ if (errno != 0 || *endptr != '\0')
+ return false;
+
+ *val = v;
+ return true;
+}
+
+static inline char *spa_dtoa(char *str, size_t size, double val)
+{
+ int i, l;
+ l = spa_scnprintf(str, size, "%f", val);
+ for (i = 0; i < l; i++)
+ if (str[i] == ',')
+ str[i] = '.';
+ return str;
+}
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SPA_UTILS_STRING_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
index 0000000000..0293278713
--- /dev/null
+++ b/third_party/pipewire/spa/utils/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_TYPE_INFO_H
+#define SPA_TYPE_INFO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/defs.h>
+
+/**
+ * \addtogroup spa_types
+ * \{
+ */
+
+#ifndef SPA_TYPE_ROOT
+#define SPA_TYPE_ROOT spa_types
+#endif
+
+static inline bool spa_type_is_a(const char *type, const char *parent)
+{
+ return type != NULL && parent != NULL && strncmp(type, parent, strlen(parent)) == 0;
+}
+
+#include <spa/utils/type.h>
+
+/* base for parameter object enumerations */
+#define SPA_TYPE_INFO_Direction SPA_TYPE_INFO_ENUM_BASE "Direction"
+#define SPA_TYPE_INFO_DIRECTION_BASE SPA_TYPE_INFO_Direction ":"
+
+static const struct spa_type_info spa_type_direction[] = {
+ { SPA_DIRECTION_INPUT, SPA_TYPE_Int, SPA_TYPE_INFO_DIRECTION_BASE "Input", NULL },
+ { SPA_DIRECTION_OUTPUT, SPA_TYPE_Int, SPA_TYPE_INFO_DIRECTION_BASE "Output", NULL },
+ { 0, 0, NULL, NULL }
+};
+
+#include <spa/monitor/type-info.h>
+#include <spa/node/type-info.h>
+#include <spa/param/type-info.h>
+#include <spa/control/type-info.h>
+
+/* base for parameter object enumerations */
+#define SPA_TYPE_INFO_Choice SPA_TYPE_INFO_ENUM_BASE "Choice"
+#define SPA_TYPE_INFO_CHOICE_BASE SPA_TYPE_INFO_Choice ":"
+
+static const struct spa_type_info spa_type_choice[] = {
+ { SPA_CHOICE_None, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "None", NULL },
+ { SPA_CHOICE_Range, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "Range", NULL },
+ { SPA_CHOICE_Step, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "Step", NULL },
+ { SPA_CHOICE_Enum, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "Enum", NULL },
+ { SPA_CHOICE_Flags, SPA_TYPE_Int, SPA_TYPE_INFO_CHOICE_BASE "Flags", NULL },
+ { 0, 0, NULL, NULL }
+};
+
+static const struct spa_type_info spa_types[] = {
+ /* Basic types */
+ { SPA_TYPE_START, SPA_TYPE_START, SPA_TYPE_INFO_BASE, NULL },
+ { SPA_TYPE_None, SPA_TYPE_None, SPA_TYPE_INFO_BASE "None", NULL },
+ { SPA_TYPE_Bool, SPA_TYPE_Bool, SPA_TYPE_INFO_BASE "Bool", NULL },
+ { SPA_TYPE_Id, SPA_TYPE_Int, SPA_TYPE_INFO_BASE "Id", NULL },
+ { SPA_TYPE_Int, SPA_TYPE_Int, SPA_TYPE_INFO_BASE "Int", NULL },
+ { SPA_TYPE_Long, SPA_TYPE_Long, SPA_TYPE_INFO_BASE "Long", NULL },
+ { SPA_TYPE_Float, SPA_TYPE_Float, SPA_TYPE_INFO_BASE "Float", NULL },
+ { SPA_TYPE_Double, SPA_TYPE_Double, SPA_TYPE_INFO_BASE "Double", NULL },
+ { SPA_TYPE_String, SPA_TYPE_String, SPA_TYPE_INFO_BASE "String", NULL },
+ { SPA_TYPE_Bytes, SPA_TYPE_Bytes, SPA_TYPE_INFO_BASE "Bytes", NULL },
+ { SPA_TYPE_Rectangle, SPA_TYPE_Rectangle, SPA_TYPE_INFO_BASE "Rectangle", NULL },
+ { SPA_TYPE_Fraction, SPA_TYPE_Fraction, SPA_TYPE_INFO_BASE "Fraction", NULL },
+ { SPA_TYPE_Bitmap, SPA_TYPE_Bitmap, SPA_TYPE_INFO_BASE "Bitmap", NULL },
+ { SPA_TYPE_Array, SPA_TYPE_Array, SPA_TYPE_INFO_BASE "Array", NULL },
+ { SPA_TYPE_Pod, SPA_TYPE_Pod, SPA_TYPE_INFO_Pod, NULL },
+ { SPA_TYPE_Struct, SPA_TYPE_Pod, SPA_TYPE_INFO_Struct, NULL },
+ { SPA_TYPE_Object, SPA_TYPE_Pod, SPA_TYPE_INFO_Object, NULL },
+ { SPA_TYPE_Sequence, SPA_TYPE_Pod, SPA_TYPE_INFO_POD_BASE "Sequence", NULL },
+ { SPA_TYPE_Pointer, SPA_TYPE_Pointer, SPA_TYPE_INFO_Pointer, NULL },
+ { SPA_TYPE_Fd, SPA_TYPE_Fd, SPA_TYPE_INFO_BASE "Fd", NULL },
+ { SPA_TYPE_Choice, SPA_TYPE_Pod, SPA_TYPE_INFO_POD_BASE "Choice", NULL },
+
+ { SPA_TYPE_POINTER_START, SPA_TYPE_Pointer, SPA_TYPE_INFO_Pointer, NULL },
+ { SPA_TYPE_POINTER_Buffer, SPA_TYPE_Pointer, SPA_TYPE_INFO_POINTER_BASE "Buffer", NULL },
+ { SPA_TYPE_POINTER_Meta, SPA_TYPE_Pointer, SPA_TYPE_INFO_POINTER_BASE "Meta", NULL },
+ { SPA_TYPE_POINTER_Dict, SPA_TYPE_Pointer, SPA_TYPE_INFO_POINTER_BASE "Dict", NULL },
+
+ { SPA_TYPE_EVENT_START, SPA_TYPE_Object, SPA_TYPE_INFO_Event, NULL },
+ { SPA_TYPE_EVENT_Device, SPA_TYPE_Object, SPA_TYPE_INFO_EVENT_BASE "Device", spa_type_device_event },
+ { 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 },
+ { SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Latency, spa_type_param_latency },
+ { SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_ProcessLatency, spa_type_param_process_latency },
+
+ { 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
index 0000000000..31623c37a1
--- /dev/null
+++ b/third_party/pipewire/spa/utils/type.h
@@ -0,0 +1,153 @@
+/* Simple Plugin API
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef SPA_TYPE_H
+#define SPA_TYPE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <spa/utils/defs.h>
+
+/** \defgroup spa_types Types
+ * Data type information enumerations
+ */
+
+/**
+ * \addtogroup spa_types
+ * \{
+ */
+
+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_ParamLatency,
+ SPA_TYPE_OBJECT_ParamProcessLatency,
+ _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 */