Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c38ef745a3 | ||
|
|
bee1c7726a | ||
|
|
14ad11db98 | ||
|
|
ca42eeb386 | ||
|
|
ab7e2797a9 | ||
|
|
b230e6829a | ||
|
|
3dbe5c4ddd | ||
|
|
63d9650c55 | ||
|
|
1cd207180f | ||
|
|
87e40a2afa |
@@ -30,14 +30,40 @@ jobs:
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Cache cargo registry
|
||||
- name: Cache cargo + build artifacts
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-${{ matrix.arch }}-cargo-${{ hashFiles('Cargo.lock') }}
|
||||
# Reuse a prior build's compiled deps (pipewire-sys/bindgen is slow,
|
||||
# especially on the aarch64 host) even when Cargo.lock changed.
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.arch }}-cargo-
|
||||
|
||||
- name: Ensure audio build deps (libpipewire + clang for native capture)
|
||||
run: |
|
||||
if command -v apt-get >/dev/null; then
|
||||
# Ephemeral container runner: safe to install into it.
|
||||
SUDO=""; command -v sudo >/dev/null && SUDO=sudo
|
||||
$SUDO apt-get update
|
||||
$SUDO apt-get install -y libpipewire-0.3-dev libspa-0.2-dev clang libclang-dev pkg-config
|
||||
else
|
||||
# Bare/host runner: never mutate it - require deps preinstalled.
|
||||
missing=""
|
||||
pkg-config --exists libpipewire-0.3 2>/dev/null || missing="$missing libpipewire(+dev headers)"
|
||||
command -v clang >/dev/null || missing="$missing clang"
|
||||
if [ -n "$missing" ]; then
|
||||
echo "::error::bare runner missing build deps:$missing — install once on the host; CI does not modify bare runners"
|
||||
exit 1
|
||||
fi
|
||||
echo "bare runner already has libpipewire + clang"
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: cargo build --release --target ${{ matrix.target }}
|
||||
run: cargo build --release --locked --target ${{ matrix.target }}
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
@@ -53,7 +79,7 @@ jobs:
|
||||
- name: Create release
|
||||
run: |
|
||||
curl -sX POST \
|
||||
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases" \
|
||||
-d "{\"tag_name\":\"${{ gitea.ref_name }}\",\"name\":\"${{ gitea.ref_name }}\"}" \
|
||||
@@ -62,23 +88,23 @@ jobs:
|
||||
- name: Upload assets
|
||||
run: |
|
||||
RELEASE_ID=$(curl -s \
|
||||
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases/tags/${{ gitea.ref_name }}" \
|
||||
| jq -r '.id')
|
||||
|
||||
for FILE in "${{ env.TARBALL }}" "${{ env.TARBALL }}.sha256"; do
|
||||
EXISTING=$(curl -s \
|
||||
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases/${RELEASE_ID}/assets" \
|
||||
| jq -r ".[] | select(.name == \"${FILE}\") | .id")
|
||||
for AID in $EXISTING; do
|
||||
curl -sX DELETE \
|
||||
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases/${RELEASE_ID}/assets/${AID}"
|
||||
done
|
||||
|
||||
curl -sX POST \
|
||||
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases/${RELEASE_ID}/assets?name=${FILE}" \
|
||||
--data-binary "@${FILE}" --fail-with-body
|
||||
@@ -102,6 +128,35 @@ jobs:
|
||||
sed -i "s/sha256sums_x86_64=('.*')/sha256sums_x86_64=('${SUM_X86}')/" aur/PKGBUILD
|
||||
sed -i "s/sha256sums_aarch64=('.*')/sha256sums_aarch64=('${SUM_AARCH}')/" aur/PKGBUILD
|
||||
|
||||
# Also publish the built packages to this instance's Arch registry
|
||||
# (docs.gitea.com/usage/packages/arch). The PKGBUILD only repacks the
|
||||
# release tarballs, so CARCH can produce both architectures from this
|
||||
# one host. Consumers: see the infrastructure README.
|
||||
# Best-effort mirror to the instance Arch registry. The ephemeral
|
||||
# GITHUB_TOKEN is not accepted as a package-write credential, so this
|
||||
# uses a dedicated REGISTRY_TOKEN secret (a write:package token for bl);
|
||||
# if it is unset the step is skipped, and continue-on-error keeps a
|
||||
# registry hiccup from failing the release or the AUR push.
|
||||
- name: Publish to the Arch package registry
|
||||
continue-on-error: true
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${{ secrets.REGISTRY_TOKEN }}" ]; then
|
||||
echo "::warning::REGISTRY_TOKEN not set — skipping Arch registry publish"
|
||||
exit 0
|
||||
fi
|
||||
cd aur
|
||||
# --packagelist gives the exact filename (honours the runner's
|
||||
# PKGEXT); !strip (in the PKGBUILD) lets the foreign-arch binary be
|
||||
# packaged on this host without stripping.
|
||||
for carch in aarch64 x86_64; do
|
||||
pkgfile=$(CARCH="$carch" makepkg --packagelist | tail -1)
|
||||
CARCH="$carch" makepkg -f --nodeps --noconfirm --skipinteg
|
||||
curl --fail-with-body --user "bl:${{ secrets.REGISTRY_TOKEN }}" \
|
||||
--upload-file "$pkgfile" \
|
||||
"${{ gitea.server_url }}/api/packages/${{ gitea.repository_owner }}/arch/uhhm"
|
||||
done
|
||||
|
||||
- name: Push to AUR
|
||||
env:
|
||||
AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }}
|
||||
|
||||
Generated
+403
-7
@@ -2,6 +2,25 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "annotate-snippets"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
@@ -57,6 +76,25 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.72.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
|
||||
dependencies = [
|
||||
"annotate-snippets",
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex 1.3.0",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.13.0"
|
||||
@@ -69,12 +107,52 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex 2.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom 7.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-expr"
|
||||
version = "0.20.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe4ece8474b5f766c63426647e7b4b316b67431ade1036a8313cee24a03ae917"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.21"
|
||||
@@ -106,7 +184,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -130,6 +208,12 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie-factory"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.29.0"
|
||||
@@ -176,7 +260,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -188,6 +272,18 @@ dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
@@ -195,27 +291,101 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libspa"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "882f7427e7989dcc9d388b7f05c4630390a1d7696f9ffa469cd4a7a48f0b4c40"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cookie-factory",
|
||||
"libc",
|
||||
"libspa-sys",
|
||||
"nom 8.0.0",
|
||||
"rustix",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libspa-sys"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b6e17bdaf63ed0d5e4144022624032b41fd9733112e8c74ac26fc9bf1291924"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.12.1"
|
||||
@@ -243,6 +413,18 @@ version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.2.1"
|
||||
@@ -255,6 +437,25 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.6"
|
||||
@@ -305,6 +506,37 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pipewire"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bde71084c4e25959d68f1ea54daa75e5ecdb338e5caf0b5510143b79baa32d5c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"libspa",
|
||||
"libspa-sys",
|
||||
"pipewire-sys",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pipewire-sys"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce653f53e63e5b93853218092ee9a8906a5d082c92f3f1db26316955dd63ce0"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"libspa-sys",
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
|
||||
|
||||
[[package]]
|
||||
name = "primal-check"
|
||||
version = "0.3.4"
|
||||
@@ -316,9 +548,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.89"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -341,6 +573,41 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
@@ -375,7 +642,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -390,6 +657,47 @@ version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.18"
|
||||
@@ -429,11 +737,12 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
||||
|
||||
[[package]]
|
||||
name = "spectrust"
|
||||
version = "0.2.6"
|
||||
version = "0.2.9"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"clap",
|
||||
"crossterm",
|
||||
"pipewire",
|
||||
"rustfft",
|
||||
]
|
||||
|
||||
@@ -460,6 +769,75 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "7.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7"
|
||||
dependencies = [
|
||||
"cfg-expr",
|
||||
"heck",
|
||||
"pkg-config",
|
||||
"toml",
|
||||
"version-compare",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.1.6+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||
|
||||
[[package]]
|
||||
name = "transpose"
|
||||
version = "0.2.3"
|
||||
@@ -482,12 +860,24 @@ version = "1.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
@@ -609,3 +999,9 @@ name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "spectrust"
|
||||
version = "0.2.6"
|
||||
version = "0.2.9"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
@@ -12,3 +12,4 @@ byteorder = "1.5.0"
|
||||
clap = { version = "4.5.21", features = ["derive"] }
|
||||
crossterm = "0.29.0"
|
||||
rustfft = "6.2.0"
|
||||
pipewire = "0.10"
|
||||
|
||||
@@ -10,7 +10,7 @@ A terminal audio spectrum analyzer written in Rust. Visualizes PCM audio in real
|
||||
|
||||
**Arch Linux (AUR)**
|
||||
```
|
||||
yay -S spectrust
|
||||
paru -S spectrust
|
||||
```
|
||||
|
||||
**From source**
|
||||
|
||||
+6
-5
@@ -1,16 +1,17 @@
|
||||
# Maintainer: Bendik Aagaard Lynghaug <bendik.lynghaug@gmail.com>
|
||||
pkgname=spectrust
|
||||
pkgver=0.2.6
|
||||
pkgver=0.2.9
|
||||
pkgrel=1
|
||||
pkgdesc="Terminal audio spectrum analyzer"
|
||||
arch=('x86_64' 'aarch64')
|
||||
url="https://prosjekt.klingenbergbygg.no/bl/spectrust"
|
||||
options=('!strip')
|
||||
url="https://project.uhhm.no/bl/spectrust"
|
||||
license=('MIT')
|
||||
depends=()
|
||||
depends=('libpipewire')
|
||||
provides=('spectrust')
|
||||
conflicts=('spectrust-git' 'spectrust-bin')
|
||||
source_x86_64=("spectrust-v${pkgver}-x86_64.tar.gz::https://prosjekt.klingenbergbygg.no/bl/spectrust/releases/download/v${pkgver}/spectrust-v${pkgver}-x86_64.tar.gz")
|
||||
source_aarch64=("spectrust-v${pkgver}-aarch64.tar.gz::https://prosjekt.klingenbergbygg.no/bl/spectrust/releases/download/v${pkgver}/spectrust-v${pkgver}-aarch64.tar.gz")
|
||||
source_x86_64=("spectrust-v${pkgver}-x86_64.tar.gz::https://project.uhhm.no/bl/spectrust/releases/download/v${pkgver}/spectrust-v${pkgver}-x86_64.tar.gz")
|
||||
source_aarch64=("spectrust-v${pkgver}-aarch64.tar.gz::https://project.uhhm.no/bl/spectrust/releases/download/v${pkgver}/spectrust-v${pkgver}-aarch64.tar.gz")
|
||||
sha256sums_x86_64=('SKIP')
|
||||
sha256sums_aarch64=('SKIP')
|
||||
|
||||
|
||||
+52
-65
@@ -17,7 +17,8 @@ use std::sync::mpsc;
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
mod pipewire_capture;
|
||||
|
||||
const EMPTY_CELL: Cell = Cell { character: ' ', filled: false };
|
||||
const BLOCK_CELL: Cell = Cell { character: '█', filled: true };
|
||||
@@ -121,7 +122,7 @@ fn handle_terminal_resize(
|
||||
long_about = "Spectrust is a PCM spectrum analyzer that visualizes audio in your terminal.\n\
|
||||
It can read from stdin, a file, or capture directly from PipeWire.\n\n\
|
||||
Examples:\n\
|
||||
- Direct PipeWire capture: spectrust -p\n\
|
||||
- Direct PipeWire capture (native, low-latency): spectrust -p\n\
|
||||
- From audio file: spectrust -i audio.pcm\n\
|
||||
- From stdin: pw-record --raw - | spectrust\n\n\
|
||||
Runtime controls:\n\
|
||||
@@ -133,9 +134,12 @@ struct Cli {
|
||||
#[arg(short, long, help = "Target frames per second", default_value_t = 60)]
|
||||
fps: u16,
|
||||
|
||||
#[arg(short, long, help = "Use PipeWire to capture audio directly")]
|
||||
#[arg(short, long, help = "Capture audio directly via native low-latency PipeWire (default sink monitor)")]
|
||||
pipewire: bool,
|
||||
|
||||
#[arg(long, help = "PipeWire capture quantum in frames — lower is lower latency (more CPU/xrun risk)", default_value_t = 256)]
|
||||
latency: u32,
|
||||
|
||||
#[arg(short, long, help = "Input file (reads from stdin if not provided)")]
|
||||
input: Option<String>,
|
||||
|
||||
@@ -184,77 +188,60 @@ fn main() {
|
||||
|
||||
let _ = out.flush();
|
||||
|
||||
let reader: Box<dyn Read + Send + 'static> = if cli.pipewire {
|
||||
let spawn_result = Command::new("pw-record")
|
||||
.args([
|
||||
"--format=s16",
|
||||
&format!("--rate={}", cli.rate),
|
||||
&format!("--channels={}", cli.channels),
|
||||
"--raw",
|
||||
"-",
|
||||
])
|
||||
.stdout(Stdio::piped())
|
||||
.spawn();
|
||||
match spawn_result {
|
||||
Ok(mut process) => {
|
||||
Box::new(process.stdout.take().expect("failed to capture pw-record stdout"))
|
||||
}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||
let _ = execute!(out, LeaveAlternateScreen);
|
||||
let _ = disable_raw_mode();
|
||||
eprintln!("error: pw-record not found — install PipeWire or pipe audio via stdin.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = execute!(out, LeaveAlternateScreen);
|
||||
let _ = disable_raw_mode();
|
||||
eprintln!("error: failed to start pw-record: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
} else if let Some(filename) = &cli.input {
|
||||
Box::new(File::open(filename).expect("failed to open input file"))
|
||||
} else {
|
||||
Box::new(std::io::stdin())
|
||||
};
|
||||
|
||||
let (tx, rx) = mpsc::channel::<Vec<f32>>();
|
||||
let _audio_thread = thread::spawn(move || {
|
||||
let mut reader = reader;
|
||||
let mut input_buffer = vec![0u8; samples_per_frame * num_channels * (bit_depth / 8)];
|
||||
loop {
|
||||
match reader.read(&mut input_buffer) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => {
|
||||
let mut samples = Vec::with_capacity(n / (num_channels * (bit_depth / 8)));
|
||||
let mut cursor = std::io::Cursor::new(&input_buffer[..n]);
|
||||
while cursor.position() < (n as u64 - (num_channels * (bit_depth / 8)) as u64 + 1) {
|
||||
let mut frame_sum = 0.0f32;
|
||||
let mut valid_channels = 0u32;
|
||||
for _ in 0..num_channels {
|
||||
match cursor.read_i16::<LittleEndian>() {
|
||||
Ok(sample) => {
|
||||
frame_sum += sample as f32;
|
||||
valid_channels += 1;
|
||||
|
||||
if cli.pipewire {
|
||||
// Native low-latency capture: pushes mono f32 straight into `tx`.
|
||||
if let Err(e) = pipewire_capture::spawn(tx, cli.rate, cli.channels, cli.latency) {
|
||||
let _ = execute!(out, LeaveAlternateScreen);
|
||||
let _ = disable_raw_mode();
|
||||
eprintln!("error: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
let reader: Box<dyn Read + Send + 'static> = if let Some(filename) = &cli.input {
|
||||
Box::new(File::open(filename).expect("failed to open input file"))
|
||||
} else {
|
||||
Box::new(std::io::stdin())
|
||||
};
|
||||
|
||||
let _audio_thread = thread::spawn(move || {
|
||||
let mut reader = reader;
|
||||
let mut input_buffer = vec![0u8; samples_per_frame * num_channels * (bit_depth / 8)];
|
||||
loop {
|
||||
match reader.read(&mut input_buffer) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => {
|
||||
let mut samples = Vec::with_capacity(n / (num_channels * (bit_depth / 8)));
|
||||
let mut cursor = std::io::Cursor::new(&input_buffer[..n]);
|
||||
while cursor.position() < (n as u64 - (num_channels * (bit_depth / 8)) as u64 + 1) {
|
||||
let mut frame_sum = 0.0f32;
|
||||
let mut valid_channels = 0u32;
|
||||
for _ in 0..num_channels {
|
||||
match cursor.read_i16::<LittleEndian>() {
|
||||
Ok(sample) => {
|
||||
frame_sum += sample as f32;
|
||||
valid_channels += 1;
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
if valid_channels > 0 {
|
||||
samples.push((frame_sum / valid_channels as f32) / 32768.0);
|
||||
}
|
||||
}
|
||||
if valid_channels > 0 {
|
||||
samples.push((frame_sum / valid_channels as f32) / 32768.0);
|
||||
if !samples.is_empty() && tx.send(samples).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !samples.is_empty() && tx.send(samples).is_err() {
|
||||
break;
|
||||
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let max_width: u16 = 1000;
|
||||
let max_height: u16 = 1000;
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
//! Native, low-latency PipeWire capture.
|
||||
//!
|
||||
//! Instead of shelling out to `pw-record` (an extra process, its own
|
||||
//! buffering, and an s16 round-trip), this connects a real capture stream to
|
||||
//! the graph, negotiates F32 at a small quantum, and pushes mono f32 samples
|
||||
//! straight into the render channel from the stream's realtime `process`
|
||||
//! callback. By default it captures the monitor of the default sink (what you
|
||||
//! hear); `latency` is a frames/rate hint the server honours by shrinking the
|
||||
//! quantum. Rate and channel count follow the native graph format.
|
||||
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::thread;
|
||||
|
||||
use pipewire as pw;
|
||||
use pw::spa;
|
||||
use pw::{properties::properties, spa::pod::Pod};
|
||||
use spa::param::audio::AudioInfoRaw;
|
||||
use spa::param::format::{MediaSubtype, MediaType};
|
||||
use spa::param::format_utils;
|
||||
|
||||
/// Start capturing on a dedicated thread. Returns once the stream is connected
|
||||
/// (or with an error describing why it could not start), so the caller can
|
||||
/// report cleanly before taking over the terminal.
|
||||
pub fn spawn(tx: Sender<Vec<f32>>, rate: u32, channels: u8, latency: u32) -> Result<(), String> {
|
||||
let (ready_tx, ready_rx) = std::sync::mpsc::channel::<Result<(), String>>();
|
||||
thread::Builder::new()
|
||||
.name("pipewire-capture".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = run(tx, rate, channels, latency, ready_tx.clone()) {
|
||||
let _ = ready_tx.send(Err(e));
|
||||
}
|
||||
})
|
||||
.map_err(|e| format!("failed to spawn capture thread: {e}"))?;
|
||||
ready_rx
|
||||
.recv()
|
||||
.map_err(|_| "pipewire capture thread exited before connecting".to_string())?
|
||||
}
|
||||
|
||||
fn run(
|
||||
tx: Sender<Vec<f32>>,
|
||||
rate: u32,
|
||||
_channels: u8,
|
||||
latency: u32,
|
||||
ready: Sender<Result<(), String>>,
|
||||
) -> Result<(), String> {
|
||||
pw::init();
|
||||
|
||||
let mainloop = pw::main_loop::MainLoopRc::new(None).map_err(|e| format!("mainloop: {e}"))?;
|
||||
let context =
|
||||
pw::context::ContextRc::new(&mainloop, None).map_err(|e| format!("context: {e}"))?;
|
||||
let core = context
|
||||
.connect_rc(None)
|
||||
.map_err(|e| format!("connect to PipeWire (is it running?): {e}"))?;
|
||||
|
||||
let props = properties! {
|
||||
*pw::keys::MEDIA_TYPE => "Audio",
|
||||
*pw::keys::MEDIA_CATEGORY => "Capture",
|
||||
*pw::keys::MEDIA_ROLE => "Music",
|
||||
// Capture the monitor of the default sink - visualize what's playing.
|
||||
*pw::keys::STREAM_CAPTURE_SINK => "true",
|
||||
// Ask the server for a small quantum: lower = lower latency.
|
||||
*pw::keys::NODE_LATENCY => format!("{latency}/{rate}"),
|
||||
*pw::keys::NODE_NAME => "spectrust",
|
||||
};
|
||||
|
||||
let stream = pw::stream::StreamBox::new(&core, "spectrust", props)
|
||||
.map_err(|e| format!("stream: {e}"))?;
|
||||
|
||||
// Channel count is learned from the negotiated format (param_changed) and
|
||||
// read back in the realtime process callback; 0 until negotiated.
|
||||
let _listener = stream
|
||||
.add_local_listener_with_user_data(AudioInfoRaw::default())
|
||||
.param_changed(|_, info, id, param| {
|
||||
let Some(param) = param else { return };
|
||||
if id != pw::spa::param::ParamType::Format.as_raw() {
|
||||
return;
|
||||
}
|
||||
let Ok((mt, ms)) = format_utils::parse_format(param) else {
|
||||
return;
|
||||
};
|
||||
if mt != MediaType::Audio || ms != MediaSubtype::Raw {
|
||||
return;
|
||||
}
|
||||
let _ = info.parse(param);
|
||||
})
|
||||
.process(move |stream, info| {
|
||||
let Some(mut buffer) = stream.dequeue_buffer() else {
|
||||
return;
|
||||
};
|
||||
let ch = info.channels() as usize;
|
||||
if ch == 0 {
|
||||
return;
|
||||
}
|
||||
let datas = buffer.datas_mut();
|
||||
let Some(data) = datas.first_mut() else {
|
||||
return;
|
||||
};
|
||||
let n_bytes = data.chunk().size() as usize;
|
||||
let Some(bytes) = data.data() else { return };
|
||||
let bytes = &bytes[..n_bytes.min(bytes.len())];
|
||||
let frame_bytes = ch * 4;
|
||||
let frames = bytes.len() / frame_bytes;
|
||||
if frames == 0 {
|
||||
return;
|
||||
}
|
||||
let mut mono = Vec::with_capacity(frames);
|
||||
for f in 0..frames {
|
||||
let base = f * frame_bytes;
|
||||
let mut sum = 0.0f32;
|
||||
for c in 0..ch {
|
||||
let o = base + c * 4;
|
||||
sum += f32::from_le_bytes([bytes[o], bytes[o + 1], bytes[o + 2], bytes[o + 3]]);
|
||||
}
|
||||
mono.push(sum / ch as f32);
|
||||
}
|
||||
let _ = tx.send(mono);
|
||||
})
|
||||
.register()
|
||||
.map_err(|e| format!("listener: {e}"))?;
|
||||
|
||||
// EnumFormat pod: force F32; leave rate/channels empty to accept the
|
||||
// native graph format (the server converts as needed).
|
||||
let mut audio_info = AudioInfoRaw::new();
|
||||
audio_info.set_format(spa::param::audio::AudioFormat::F32LE);
|
||||
let obj = spa::pod::Object {
|
||||
type_: spa::utils::SpaTypes::ObjectParamFormat.as_raw(),
|
||||
id: spa::param::ParamType::EnumFormat.as_raw(),
|
||||
properties: audio_info.into(),
|
||||
};
|
||||
let values: Vec<u8> = spa::pod::serialize::PodSerializer::serialize(
|
||||
std::io::Cursor::new(Vec::new()),
|
||||
&spa::pod::Value::Object(obj),
|
||||
)
|
||||
.map_err(|e| format!("pod serialize: {e}"))?
|
||||
.0
|
||||
.into_inner();
|
||||
let mut params = [Pod::from_bytes(&values).ok_or("invalid format pod")?];
|
||||
|
||||
stream
|
||||
.connect(
|
||||
spa::utils::Direction::Input,
|
||||
None,
|
||||
pw::stream::StreamFlags::AUTOCONNECT
|
||||
| pw::stream::StreamFlags::MAP_BUFFERS
|
||||
| pw::stream::StreamFlags::RT_PROCESS,
|
||||
&mut params,
|
||||
)
|
||||
.map_err(|e| format!("connect stream: {e}"))?;
|
||||
|
||||
let _ = ready.send(Ok(()));
|
||||
mainloop.run();
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user