summaryrefslogtreecommitdiffstats
path: root/third_party/pipewire/spa/utils
diff options
context:
space:
mode:
Diffstat (limited to '')
-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
15 files changed, 3190 insertions, 0 deletions
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 */