From d5e23146c00c6e60b50318cc75eb93a4b64e8a31 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 06:18:23 +0200 Subject: [PATCH] =?UTF-8?q?feat(client):=20M8=20=E2=80=94=20the=20software?= =?UTF-8?q?=20rung=20is=20openh264=20and=20rav1d,=20and=20swscale=20is=20g?= =?UTF-8?q?one?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ladder's last rung no longer runs FFmpeg. H.264 decodes through openh264, AV1 through rav1d, and HEVC is refused outright: no permissively licensed software HEVC decoder exists, so an HEVC session that exhausts its hardware rungs now tears down and re-dials advertising HEVC-less caps, and the host picks H.264. The plan calls that a first-class path; it is one. swscale is deleted, and with it the BT.601 default that its correction code existed to undo. Colour on the H.264 lane now comes from the same pf-bitstream planner every hardware rung submits from — openh264 reports no VUI at all — and AV1's comes per-picture from the sequence header. One colour source, one CSC: the old default is unrepresentable rather than merely fixed. Frames reach the presenter as three tightly-packed planes through the planar CSC pass, which had to be un-gated from the pyrowave feature and its device probe, since the last rung must exist on devices that failed that probe. rav1d rather than the dav1d crate, deliberately and against the plan's literal wording: dav1d-sys is system-deps-only, so it would add a system library and a .pc file to every client package — in the milestone family whose excision checklist exists to delete exactly that. rav1d is the same decoder, same licence, statically linked. The cost is honest: no-asm builds on both decoders, and software throughput is still unmeasured. The colour test is the milestone's exit criterion, so it is built to fail. Three fixtures, and a mutation check: hardcoding the swscale default turns the red bar to [255,24,0], and swapping Cb/Cr turns red to blue — a silent error no metadata assertion could catch. Review then disproved the range half of it numerically: with eight saturated bars, decoding the full-range fixture with the wrong range gives max error ZERO, because a mismatch only pushes values outside [0,1] where the shader clamps. A mid-tone was added; the wrong range now costs 11, well past the tolerance. The exit criterion I set was satisfiable by a test that proved nothing. Two blocking defects, both emergent rather than local. Software AV1 on a 10-bit stream never reached its typed refusal: rav1d is built 8-bit-only and returns ENOPROTOOPT, which the send loop turned into a generic error, so the pump's typed downcast missed and every AU failed identically — a permanent freeze on precisely the shipping case, since AV1 is advertised only where hardware AV1 exists and hardware AV1 plus HDR is Main 10. The shape is now read from the sequence header before any byte reaches the decoder, exactly as the H.264 leg reads the active SPS. And the new Reconnecting phase was the first state that is not streaming, not connecting, and still holding a live stream — which opened all three guards that had made a second launch impossible. Pressing A assigned over `stream` where every other site shuts down first, and StreamState has no Drop, so the old pump was detached: a second live session still submitting to a Vulkan device that gets destroyed underneath it. Nothing about the reconnect was wrong in isolation; the defect lived between a new state and three guards nobody re-examined. Start is now defensive and the retry raises the connecting modal, so the UI matches the state and B can cancel. Also closed: retry_caps was computed, tested and never applied, so a shape refusal could end a session reporting no codec available while a working retry existed; the retry inherited force_software sticky-true, landing an HEVC→H.264 fallback on software H.264 with working hardware H.264; it re-dialled with a stale mode; the CPU present arm had no survivable-failure handling where the pyrowave arm — same pass — has it; HEVC is no longer advertised when the decoder is pinned to software; and the software rung now feeds the recovery-point SEI it already had in hand to the re-anchor gate. ⚠ Two host-side gaps found while tracing, neither in scope here: Hello::launch is NOT idempotent (gog:/custom: targets spawn a second copy on a retry; the field is kept verbatim because dropping it orphans the gamescope display whose reuse key includes the command), and a reconnected session can never adopt a game predating its own launch stamp, so it has no game-exit detection. ⚠ OWED: the on-glass software run. ~200 lines of new Vulkan on a path that only runs because the GPU already failed, and no driver has seen it. The review's minimum check is sync validation enabled, a non-multiple-of-16 mode, a mid-session resize and demotion, and both colour matrices. Gates: container clippy -D warnings over four crates, 236 tests, workspace check. pf-vkdecode and pf-bitstream are byte-for-byte untouched, so the hardware rungs' 250/250 stands. --- Cargo.lock | 124 +- THIRD-PARTY-NOTICES.txt | 763 +++++++--- .../src/main/assets/THIRD-PARTY-NOTICES.txt | 763 +++++++--- .../Resources/THIRD-PARTY-NOTICES.txt | 763 +++++++--- clients/session/src/main.rs | 18 +- crates/pf-client-core/Cargo.toml | 60 + crates/pf-client-core/src/session.rs | 290 +++- crates/pf-client-core/src/video.rs | 520 ++++++- crates/pf-client-core/src/video_color.rs | 4 +- crates/pf-client-core/src/video_software.rs | 1233 ++++++++++++++--- .../tests/bars-601-limited.h264 | Bin 0 -> 738 bytes .../tests/bars-601-limited.h265 | Bin 4542 -> 0 bytes .../pf-client-core/tests/bars-709-full.h264 | Bin 0 -> 737 bytes .../pf-client-core/tests/bars-709-full.h265 | Bin 4552 -> 0 bytes .../tests/bars-709-limited.h264 | Bin 0 -> 736 bytes .../tests/bars-709-limited.h265 | Bin 4446 -> 0 bytes crates/pf-client-core/tests/gen-bars.sh | 49 + crates/pf-client-core/tests/pq-frame.h265 | Bin 3759 -> 0 bytes crates/pf-console-ui/src/shell.rs | 38 + crates/pf-console-ui/src/skia_overlay.rs | 9 + crates/pf-presenter/src/csc.rs | 27 +- crates/pf-presenter/src/lib.rs | 4 +- crates/pf-presenter/src/overlay.rs | 9 + crates/pf-presenter/src/run.rs | 217 ++- crates/pf-presenter/src/vk/mod.rs | 72 +- crates/pf-presenter/src/vk/present.rs | 224 +-- crates/pf-presenter/src/vk/reconfig.rs | 13 +- crates/pf-presenter/src/vk/resources.rs | 157 ++- crates/pf-presenter/src/vk/setup.rs | 16 +- 29 files changed, 4318 insertions(+), 1055 deletions(-) create mode 100644 crates/pf-client-core/tests/bars-601-limited.h264 delete mode 100644 crates/pf-client-core/tests/bars-601-limited.h265 create mode 100644 crates/pf-client-core/tests/bars-709-full.h264 delete mode 100644 crates/pf-client-core/tests/bars-709-full.h265 create mode 100644 crates/pf-client-core/tests/bars-709-limited.h264 delete mode 100644 crates/pf-client-core/tests/bars-709-limited.h265 create mode 100755 crates/pf-client-core/tests/gen-bars.sh delete mode 100644 crates/pf-client-core/tests/pq-frame.h265 diff --git a/Cargo.lock b/Cargo.lock index 6b522db9..4055dc29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -204,6 +204,12 @@ dependencies = [ "syn", ] +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + [[package]] name = "async-broadcast" version = "0.7.2" @@ -341,6 +347,26 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atomig" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0f41f4bb89f5c6450325e283fb78c4a3d042181b54f3855ee2f872919f9863" +dependencies = [ + "atomig-macro", +] + +[[package]] +name = "atomig-macro" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49c98dba06b920588de7d63f6acc23f1e6a9fade5fd6198e564506334fb5a4f5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "audiopus_sys" version = "0.2.2" @@ -544,6 +570,12 @@ dependencies = [ "syn", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "byteorder-lite" version = "0.1.0" @@ -1846,7 +1878,7 @@ checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", - "zerocopy", + "zerocopy 0.8.52", ] [[package]] @@ -2591,6 +2623,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "706bf8a5e8c8ddb99128c3291d31bd21f4bcde17f0f4c20ec678d85c74faa149" dependencies = [ + "jobserver", "log", ] @@ -2990,9 +3023,12 @@ dependencies = [ "ash", "async-channel", "ffmpeg-next", + "libc", "libloading", "mdns-sd", + "openh264", "opus", + "pf-bitstream", "pf-dxvadec", "pf-ffvk", "pf-update-check", @@ -3002,6 +3038,7 @@ dependencies = [ "punktfunk-core", "pyrowave-sys", "rand 0.9.4", + "rav1d", "rustls", "sdl3", "serde", @@ -3435,7 +3472,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy", + "zerocopy 0.8.52", ] [[package]] @@ -3594,7 +3631,7 @@ dependencies = [ "tokio", "tracing", "windows-sys 0.59.0", - "zerocopy", + "zerocopy 0.8.52", "zeroize", ] @@ -3889,6 +3926,36 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rav1d" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1932f060d5e7bd49dc9f8b272c1dc5e9ce0ffe141c28be900265d3989b36c9ed" +dependencies = [ + "assert_matches", + "atomig", + "bitflags 2.13.0", + "cc", + "cfg-if", + "libc", + "nasm-rs", + "parking_lot", + "paste", + "raw-cpuid", + "strum", + "to_method", + "zerocopy 0.7.35", +] + +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.13.0", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -4684,6 +4751,28 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "subtle" version = "2.6.1" @@ -4887,6 +4976,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "to_method" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" + [[package]] name = "tokio" version = "1.52.3" @@ -6345,13 +6440,34 @@ dependencies = [ "zvariant", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive 0.7.35", +] + [[package]] name = "zerocopy" version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ - "zerocopy-derive", + "zerocopy-derive 0.8.52", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] diff --git a/THIRD-PARTY-NOTICES.txt b/THIRD-PARTY-NOTICES.txt index 288d1670..d5d9ff56 100644 --- a/THIRD-PARTY-NOTICES.txt +++ b/THIRD-PARTY-NOTICES.txt @@ -7,7 +7,7 @@ below. Each is distributed under its own permissive license; the full license te follow the manifest. This file is generated by scripts/gen-third-party-notices.py (or `cargo about`, see about.toml) — do not edit by hand. -Total third-party crates: 566 +Total third-party crates: 590 ---------------------------------------------------------------------------- VENDORED THIRD-PARTY SOURCE (inside first-party crates) @@ -43,6 +43,7 @@ MANIFEST (crate version — SPDX license — source) asn1-rs 0.6.2 — MIT OR Apache-2.0 — https://github.com/rusticata/asn1-rs.git asn1-rs-derive 0.5.1 — MIT OR Apache-2.0 — https://github.com/rusticata/asn1-rs.git asn1-rs-impl 0.2.0 — MIT/Apache-2.0 — https://github.com/rusticata/asn1-rs.git + assert_matches 1.5.0 — MIT/Apache-2.0 — https://github.com/murarth/assert_matches async-broadcast 0.7.2 — MIT OR Apache-2.0 — https://github.com/smol-rs/async-broadcast async-channel 2.5.0 — Apache-2.0 OR MIT — https://github.com/smol-rs/async-channel async-executor 1.14.0 — Apache-2.0 OR MIT — https://github.com/smol-rs/async-executor @@ -54,6 +55,8 @@ MANIFEST (crate version — SPDX license — source) async-task 4.7.1 — Apache-2.0 OR MIT — https://github.com/smol-rs/async-task async-trait 0.1.89 — MIT OR Apache-2.0 — https://github.com/dtolnay/async-trait atomic-waker 1.1.2 — Apache-2.0 OR MIT — https://github.com/smol-rs/atomic-waker + atomig 0.4.3 — MIT/Apache-2.0 — https://github.com/LukasKalbertodt/atomig/ + atomig-macro 0.4.0 — MIT/Apache-2.0 — https://github.com/LukasKalbertodt/atomig/ audiopus_sys 0.2.2 — ISC — https://github.com/lakelezz/audiopus_sys.git autocfg 1.5.1 — Apache-2.0 OR MIT — https://github.com/cuviper/autocfg axum 0.8.9 — MIT — https://github.com/tokio-rs/axum @@ -64,6 +67,7 @@ MANIFEST (crate version — SPDX license — source) bindgen 0.72.1 — BSD-3-Clause — https://github.com/rust-lang/rust-bindgen bit-set 0.8.0 — Apache-2.0 OR MIT — https://github.com/contain-rs/bit-set bit-vec 0.8.0 — Apache-2.0 OR MIT — https://github.com/contain-rs/bit-vec + bitflags 1.3.2 — MIT/Apache-2.0 — https://github.com/bitflags/bitflags bitflags 2.13.0 — MIT OR Apache-2.0 — https://github.com/bitflags/bitflags block-buffer 0.10.4 — MIT OR Apache-2.0 — https://github.com/RustCrypto/utils block-padding 0.3.3 — MIT OR Apache-2.0 — https://github.com/RustCrypto/utils @@ -71,6 +75,7 @@ MANIFEST (crate version — SPDX license — source) bumpalo 3.20.3 — MIT OR Apache-2.0 — https://github.com/fitzgen/bumpalo bytemuck 1.25.0 — Zlib OR Apache-2.0 OR MIT — https://github.com/Lokathor/bytemuck bytemuck_derive 1.10.2 — Zlib OR Apache-2.0 OR MIT — https://github.com/Lokathor/bytemuck + byteorder 1.5.0 — Unlicense OR MIT — https://github.com/BurntSushi/byteorder byteorder-lite 0.1.0 — Unlicense OR MIT — https://github.com/image-rs/byteorder-lite bytes 1.12.0 — MIT — https://github.com/tokio-rs/bytes cairo-rs 0.22.0 — MIT — https://github.com/gtk-rs/gtk-rs-core @@ -115,6 +120,9 @@ MANIFEST (crate version — SPDX license — source) curve25519-dalek 4.1.3 — BSD-3-Clause — https://github.com/dalek-cryptography/curve25519-dalek/tree/main/curve25519-dalek curve25519-dalek-derive 0.1.1 — MIT/Apache-2.0 — https://github.com/dalek-cryptography/curve25519-dalek data-encoding 2.11.0 — MIT — https://github.com/ia0/data-encoding + defmt 1.1.1 — MIT OR Apache-2.0 — https://github.com/knurling-rs/defmt + defmt-macros 1.1.1 — MIT OR Apache-2.0 — https://github.com/knurling-rs/defmt + defmt-parser 1.0.0 — MIT OR Apache-2.0 — https://github.com/knurling-rs/defmt der 0.7.10 — Apache-2.0 OR MIT — https://github.com/RustCrypto/formats/tree/master/der der-parser 9.0.0 — MIT/Apache-2.0 — https://github.com/rusticata/der-parser.git deranged 0.5.8 — MIT OR Apache-2.0 — https://github.com/jhpratt/deranged @@ -126,6 +134,8 @@ MANIFEST (crate version — SPDX license — source) enumflags2 0.7.12 — MIT OR Apache-2.0 — https://github.com/meithecatte/enumflags2 enumflags2_derive 0.7.12 — MIT OR Apache-2.0 — https://github.com/meithecatte/enumflags2 env_filter 0.1.4 — MIT OR Apache-2.0 — https://github.com/rust-cli/env_logger + env_filter 2.0.0 — MIT OR Apache-2.0 — https://github.com/rust-cli/env_logger + env_logger 0.11.11 — MIT OR Apache-2.0 — https://github.com/rust-cli/env_logger equivalent 1.0.2 — Apache-2.0 OR MIT — https://github.com/indexmap-rs/equivalent errno 0.3.14 — MIT OR Apache-2.0 — https://github.com/lambda-fairy/rust-errno event-listener 5.4.1 — Apache-2.0 OR MIT — https://github.com/smol-rs/event-listener @@ -221,6 +231,9 @@ MANIFEST (crate version — SPDX license — source) itertools 0.10.5 — MIT/Apache-2.0 — https://github.com/rust-itertools/itertools itertools 0.13.0 — MIT OR Apache-2.0 — https://github.com/rust-itertools/itertools itoa 1.0.18 — MIT OR Apache-2.0 — https://github.com/dtolnay/itoa + jiff 0.2.35 — Unlicense OR MIT — https://github.com/BurntSushi/jiff + jiff-core 0.1.0 — Unlicense OR MIT — https://github.com/BurntSushi/jiff + jiff-static 0.2.35 — Unlicense OR MIT — https://github.com/BurntSushi/jiff jni 0.21.1 — MIT/Apache-2.0 — https://github.com/jni-rs/jni-rs jni-sys 0.3.1 — MIT OR Apache-2.0 — https://github.com/jni-rs/jni-sys jni-sys 0.4.1 — MIT OR Apache-2.0 — https://github.com/jni-rs/jni-sys @@ -304,6 +317,8 @@ MANIFEST (crate version — SPDX license — source) polling 3.11.0 — Apache-2.0 OR MIT — https://github.com/smol-rs/polling poly1305 0.8.0 — Apache-2.0 OR MIT — https://github.com/RustCrypto/universal-hashes polyval 0.6.2 — Apache-2.0 OR MIT — https://github.com/RustCrypto/universal-hashes + portable-atomic 1.14.0 — Apache-2.0 OR MIT — https://github.com/taiki-e/portable-atomic + portable-atomic-util 0.2.7 — Apache-2.0 OR MIT — https://github.com/taiki-e/portable-atomic-util potential_utf 0.1.5 — Unicode-3.0 — https://github.com/unicode-org/icu4x powerfmt 0.2.0 — MIT OR Apache-2.0 — https://github.com/jhpratt/powerfmt ppv-lite86 0.2.21 — MIT OR Apache-2.0 — https://github.com/cryptocorrosion/cryptocorrosion @@ -327,6 +342,8 @@ MANIFEST (crate version — SPDX license — source) rand_core 0.6.4 — MIT OR Apache-2.0 — https://github.com/rust-random/rand rand_core 0.9.5 — MIT OR Apache-2.0 — https://github.com/rust-random/rand rand_xorshift 0.4.0 — MIT OR Apache-2.0 — https://github.com/rust-random/rngs + rav1d 1.1.0 — BSD-2-Clause — https://github.com/memorysafety/rav1d + raw-cpuid 11.6.0 — MIT — https://github.com/gz/rust-cpuid raw-window-handle 0.6.2 — MIT OR Apache-2.0 OR Zlib — https://github.com/rust-windowing/raw-window-handle rcgen 0.13.2 — MIT OR Apache-2.0 — https://github.com/rustls/rcgen readme-rustdocifier 0.1.1 — MIT — https://github.com/malaire/readme-rustdocifier @@ -404,6 +421,8 @@ MANIFEST (crate version — SPDX license — source) sqlite-wasm-rs 0.5.5 — MIT — https://github.com/Spxg/sqlite-wasm-rs stable_deref_trait 1.2.1 — MIT OR Apache-2.0 — https://github.com/storyyeller/stable_deref_trait strsim 0.11.1 — MIT — https://github.com/rapidfuzz/strsim-rs + strum 0.26.3 — MIT — https://github.com/Peternator7/strum + strum_macros 0.26.4 — MIT — https://github.com/Peternator7/strum subtle 2.6.1 — BSD-3-Clause — https://github.com/dalek-cryptography/subtle syn 2.0.118 — MIT OR Apache-2.0 — https://github.com/dtolnay/syn sync_wrapper 1.0.2 — Apache-2.0 — https://github.com/Actyx/sync_wrapper @@ -425,6 +444,7 @@ MANIFEST (crate version — SPDX license — source) tinytemplate 1.2.1 — Apache-2.0 OR MIT — https://github.com/bheisler/TinyTemplate tinyvec 1.11.0 — Zlib OR Apache-2.0 OR MIT — https://github.com/Lokathor/tinyvec tinyvec_macros 0.1.1 — MIT OR Apache-2.0 OR Zlib — https://github.com/Soveu/tinyvec_macros + to_method 1.1.0 — CC0-1.0 — https://github.com/whentze/to_method tokio 1.52.3 — MIT — https://github.com/tokio-rs/tokio tokio-macros 2.7.0 — MIT — https://github.com/tokio-rs/tokio tokio-rustls 0.26.4 — MIT OR Apache-2.0 — https://github.com/rustls/tokio-rustls @@ -449,6 +469,7 @@ MANIFEST (crate version — SPDX license — source) tracing-log 0.2.0 — MIT — https://github.com/tokio-rs/tracing tracing-subscriber 0.3.23 — MIT — https://github.com/tokio-rs/tracing typenum 1.20.1 — MIT OR Apache-2.0 — https://github.com/paholg/typenum + uac-host 0.1.0 — MIT OR Apache-2.0 — https://github.com/unom-io/usbfs-iso uds_windows 1.2.1 — MIT — https://github.com/haraldh/rust_uds_windows unarray 0.1.4 — MIT OR Apache-2.0 — https://github.com/cameron1024/unarray unicode-ident 1.0.24 — (MIT OR Apache-2.0) AND Unicode-3.0 — https://github.com/dtolnay/unicode-ident @@ -458,6 +479,7 @@ MANIFEST (crate version — SPDX license — source) untrusted 0.9.0 — ISC — https://github.com/briansmith/untrusted ureq 2.12.1 — MIT OR Apache-2.0 — https://github.com/algesten/ureq url 2.5.8 — MIT OR Apache-2.0 — https://github.com/servo/rust-url + usbfs-iso 0.1.0 — MIT OR Apache-2.0 — https://github.com/unom-io/usbfs-iso utf8_iter 1.0.4 — Apache-2.0 OR MIT — https://github.com/hsivonen/utf8_iter utf8parse 0.2.2 — Apache-2.0 OR MIT — https://github.com/alacritty/vte utoipa 5.5.0 — MIT OR Apache-2.0 — https://github.com/juhaku/utoipa @@ -575,7 +597,9 @@ MANIFEST (crate version — SPDX license — source) zbus 5.16.0 — MIT — https://github.com/z-galaxy/zbus/ zbus_macros 5.16.0 — MIT — https://github.com/z-galaxy/zbus/ zbus_names 4.3.2 — MIT — https://github.com/z-galaxy/zbus/ + zerocopy 0.7.35 — BSD-2-Clause OR Apache-2.0 OR MIT — https://github.com/google/zerocopy zerocopy 0.8.52 — BSD-2-Clause OR Apache-2.0 OR MIT — https://github.com/google/zerocopy + zerocopy-derive 0.7.35 — BSD-2-Clause OR Apache-2.0 OR MIT — https://github.com/google/zerocopy zerocopy-derive 0.8.52 — BSD-2-Clause OR Apache-2.0 OR MIT — https://github.com/google/zerocopy zerofrom 0.1.8 — Unicode-3.0 — https://github.com/unicode-org/icu4x zerofrom-derive 0.1.7 — Unicode-3.0 — https://github.com/unicode-org/icu4x @@ -595,7 +619,9 @@ Crates whose package did not embed a license file (SPDX + source only) ---------------------------------------------------------------------------- anes 0.1.6 — MIT OR Apache-2.0 — https://github.com/zrzka/anes-rs asn1-rs-impl 0.2.0 — MIT/Apache-2.0 — https://github.com/rusticata/asn1-rs.git + atomig-macro 0.4.0 — MIT/Apache-2.0 — https://github.com/LukasKalbertodt/atomig/ cookie-factory 0.3.3 — MIT — https://github.com/rust-bakery/cookie-factory + defmt-parser 1.0.0 — MIT OR Apache-2.0 — https://github.com/knurling-rs/defmt ffmpeg-sys-next 8.1.0 — WTFPL — https://github.com/zmwangx/rust-ffmpeg-sys jni-sys-macros 0.4.1 — MIT OR Apache-2.0 — https://github.com/jni-rs/jni-sys ndk 0.9.0 — MIT OR Apache-2.0 — https://github.com/rust-mobile/ndk @@ -612,6 +638,8 @@ Crates whose package did not embed a license file (SPDX + source only) skia-bindings 0.87.0 — MIT — https://github.com/rust-skia/rust-skia skia-safe 0.87.0 — MIT — https://github.com/rust-skia/rust-skia test_reactor 0.0.0 — UNKNOWN + uac-host 0.1.0 — MIT OR Apache-2.0 — https://github.com/unom-io/usbfs-iso + usbfs-iso 0.1.0 — MIT OR Apache-2.0 — https://github.com/unom-io/usbfs-iso valuable 0.1.1 — MIT — https://github.com/tokio-rs/valuable winapi-i686-pc-windows-gnu 0.4.0 — MIT/Apache-2.0 — https://github.com/retep998/winapi-rs winapi-x86_64-pc-windows-gnu 0.4.0 — MIT/Apache-2.0 — https://github.com/retep998/winapi-rs @@ -845,7 +873,7 @@ limitations under the License. ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: adler2 2.0.1, anyhow 1.0.103, async-channel 2.5.0, async-executor 1.14.0, async-io 2.6.0, async-lock 3.4.2, async-process 2.5.0, async-recursion 1.1.1, async-signal 0.2.14, async-task 4.7.1, async-trait 0.1.89, atomic-waker 1.1.2, blocking 1.6.2, concurrent-queue 2.5.0, curve25519-dalek-derive 0.1.1, displaydoc 0.2.6, endi 1.1.1, event-listener 5.4.1, event-listener-strategy 0.5.4, fastrand 2.4.1, flume 0.12.0, fs-err 3.3.0, futures-lite 2.6.1, hermit-abi 0.5.2, is-terminal 0.4.17, itoa 1.0.18, khronos-egl 6.0.0, linux-raw-sys 0.12.1, minimal-lexical 0.2.1, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, ordered-stream 0.2.0, parking 2.2.1, paste 1.0.15, pastey 0.2.3, pin-project-lite 0.2.17, piper 0.2.5, polling 3.11.0, prettyplease 0.2.37, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.46, reis 0.6.1, relm4 0.11.0, relm4-css 0.11.0, relm4-macros 0.11.0, rsa 0.9.10, rustc-hash 2.1.2, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, serde 1.0.228, serde_core 1.0.228, serde_derive 1.0.228, serde_json 1.0.150, serde_path_to_error 0.1.20, serde_repr 0.1.20, syn 2.0.118, system-deps 7.0.8, thiserror 1.0.69, thiserror 2.0.18, thiserror-impl 1.0.69, thiserror-impl 2.0.18, unicode-ident 1.0.24, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wit-bindgen 0.57.1, zmij 1.0.21, zvariant_utils 3.4.0 +The following license (LICENSE-MIT) applies to: adler2 2.0.1, anyhow 1.0.103, async-channel 2.5.0, async-executor 1.14.0, async-io 2.6.0, async-lock 3.4.2, async-process 2.5.0, async-recursion 1.1.1, async-signal 0.2.14, async-task 4.7.1, async-trait 0.1.89, atomic-waker 1.1.2, blocking 1.6.2, concurrent-queue 2.5.0, curve25519-dalek-derive 0.1.1, displaydoc 0.2.6, endi 1.1.1, event-listener 5.4.1, event-listener-strategy 0.5.4, fastrand 2.4.1, flume 0.12.0, fs-err 3.3.0, futures-lite 2.6.1, hermit-abi 0.5.2, is-terminal 0.4.17, itoa 1.0.18, khronos-egl 6.0.0, linux-raw-sys 0.12.1, minimal-lexical 0.2.1, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, ordered-stream 0.2.0, parking 2.2.1, paste 1.0.15, pastey 0.2.3, pin-project-lite 0.2.17, piper 0.2.5, polling 3.11.0, portable-atomic 1.14.0, portable-atomic-util 0.2.7, prettyplease 0.2.37, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.46, reis 0.6.1, relm4 0.11.0, relm4-css 0.11.0, relm4-macros 0.11.0, rsa 0.9.10, rustc-hash 2.1.2, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, serde 1.0.228, serde_core 1.0.228, serde_derive 1.0.228, serde_json 1.0.150, serde_path_to_error 0.1.20, serde_repr 0.1.20, syn 2.0.118, system-deps 7.0.8, thiserror 1.0.69, thiserror 2.0.18, thiserror-impl 1.0.69, thiserror-impl 2.0.18, unicode-ident 1.0.24, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wit-bindgen 0.57.1, zmij 1.0.21, zvariant_utils 3.4.0 ---------------------------------------------------------------------------- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -1170,7 +1198,7 @@ DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (COPYING) applies to: aho-corasick 1.1.4, memchr 2.8.2, same-file 1.0.6, walkdir 2.5.0, winapi-util 0.1.11 +The following license (COPYING) applies to: aho-corasick 1.1.4, byteorder 1.5.0, jiff 0.2.35, jiff-core 0.1.0, jiff-static 0.2.35, memchr 2.8.2, same-file 1.0.6, walkdir 2.5.0, winapi-util 0.1.11 ---------------------------------------------------------------------------- This project is dual-licensed under the Unlicense and MIT licenses. @@ -1178,7 +1206,7 @@ You may use this code under the terms of either license. ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: aho-corasick 1.1.4, byteorder-lite 0.1.0, memchr 2.8.2, walkdir 2.5.0 +The following license (LICENSE-MIT) applies to: aho-corasick 1.1.4, byteorder 1.5.0, byteorder-lite 0.1.0, jiff 0.2.35, jiff-core 0.1.0, jiff-static 0.2.35, memchr 2.8.2, walkdir 2.5.0 ---------------------------------------------------------------------------- The MIT License (MIT) @@ -1204,7 +1232,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (UNLICENSE) applies to: aho-corasick 1.1.4, byteorder-lite 0.1.0, ksni 0.3.5, memchr 2.8.2, same-file 1.0.6, walkdir 2.5.0, winapi-util 0.1.11 +The following license (UNLICENSE) applies to: aho-corasick 1.1.4, byteorder 1.5.0, byteorder-lite 0.1.0, jiff 0.2.35, jiff-core 0.1.0, jiff-static 0.2.35, ksni 0.3.5, memchr 2.8.2, same-file 1.0.6, walkdir 2.5.0, winapi-util 0.1.11 ---------------------------------------------------------------------------- This is free and unencumbered software released into the public domain. @@ -1693,7 +1721,7 @@ SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: annotate-snippets 0.11.5, anstream 1.0.0, anstyle 1.0.14, anstyle-parse 1.0.0, anstyle-query 1.1.5, anstyle-wincon 3.0.11, clap 4.6.1, clap_builder 4.6.0, clap_lex 1.1.0, colorchoice 1.0.5, crc32fast 1.5.0, env_filter 0.1.4, fallible-iterator 0.3.0, fallible-streaming-iterator 0.1.9, hex 0.4.3, is_terminal_polyfill 1.70.2, jni-sys 0.3.1, jni-sys 0.4.1, once_cell_polyfill 1.70.2, quick-error 1.2.3, serde_spanned 0.6.9, serde_spanned 1.1.1, toml 0.8.23, toml 0.9.12+spec-1.1.0, toml 1.1.2+spec-1.1.0, toml_datetime 0.6.11, toml_datetime 0.7.5+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.22.27, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_write 0.1.2, toml_writer 1.1.1+spec-1.1.0, winapi 0.3.9 +The following license (LICENSE-APACHE) applies to: annotate-snippets 0.11.5, anstream 1.0.0, anstyle 1.0.14, anstyle-parse 1.0.0, anstyle-query 1.1.5, anstyle-wincon 3.0.11, clap 4.6.1, clap_builder 4.6.0, clap_lex 1.1.0, colorchoice 1.0.5, crc32fast 1.5.0, env_filter 0.1.4, env_filter 2.0.0, env_logger 0.11.11, fallible-iterator 0.3.0, fallible-streaming-iterator 0.1.9, hex 0.4.3, is_terminal_polyfill 1.70.2, jni-sys 0.3.1, jni-sys 0.4.1, once_cell_polyfill 1.70.2, quick-error 1.2.3, serde_spanned 0.6.9, serde_spanned 1.1.1, toml 0.8.23, toml 0.9.12+spec-1.1.0, toml 1.1.2+spec-1.1.0, toml_datetime 0.6.11, toml_datetime 0.7.5+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.22.27, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_write 0.1.2, toml_writer 1.1.1+spec-1.1.0, winapi 0.3.9 ---------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -1899,7 +1927,7 @@ Apache License ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: annotate-snippets 0.11.5, anstream 1.0.0, anstyle 1.0.14, anstyle-parse 1.0.0, anstyle-query 1.1.5, anstyle-wincon 3.0.11, clap 4.6.1, clap_builder 4.6.0, clap_lex 1.1.0, colorchoice 1.0.5, env_filter 0.1.4, is_terminal_polyfill 1.70.2, once_cell_polyfill 1.70.2, serde_spanned 0.6.9, serde_spanned 1.1.1, toml 0.8.23, toml 0.9.12+spec-1.1.0, toml 1.1.2+spec-1.1.0, toml_datetime 0.6.11, toml_datetime 0.7.5+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.22.27, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_write 0.1.2, toml_writer 1.1.1+spec-1.1.0 +The following license (LICENSE-MIT) applies to: annotate-snippets 0.11.5, anstream 1.0.0, anstyle 1.0.14, anstyle-parse 1.0.0, anstyle-query 1.1.5, anstyle-wincon 3.0.11, clap 4.6.1, clap_builder 4.6.0, clap_lex 1.1.0, colorchoice 1.0.5, env_filter 0.1.4, env_filter 2.0.0, env_logger 0.11.11, is_terminal_polyfill 1.70.2, once_cell_polyfill 1.70.2, serde_spanned 0.6.9, serde_spanned 1.1.1, toml 0.8.23, toml 0.9.12+spec-1.1.0, toml 1.1.2+spec-1.1.0, toml_datetime 0.6.11, toml_datetime 0.7.5+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.22.27, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_write 0.1.2, toml_writer 1.1.1+spec-1.1.0 ---------------------------------------------------------------------------- Copyright (c) Individual contributors @@ -2232,7 +2260,7 @@ SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: asn1-rs 0.6.2, asn1-rs-derive 0.5.1, async-channel 2.5.0, async-executor 1.14.0, async-io 2.6.0, async-lock 3.4.2, async-process 2.5.0, async-recursion 1.1.1, async-signal 0.2.14, async-task 4.7.1, atomic-waker 1.1.2, autocfg 1.5.1, base64 0.22.1, bitflags 2.13.0, blocking 1.6.2, bumpalo 3.20.3, cast 0.3.0, cc 1.2.65, cexpr 0.6.0, cfg-if 1.0.4, cmake 0.1.58, concurrent-queue 2.5.0, core-foundation 0.10.1, core-foundation-sys 0.8.7, criterion 0.5.1, criterion-plot 0.5.0, crossbeam-utils 0.8.21, curve25519-dalek-derive 0.1.1, der-parser 9.0.0, displaydoc 0.2.6, either 1.16.0, equivalent 1.0.2, errno 0.3.14, event-listener 5.4.1, event-listener-strategy 0.5.4, fastrand 2.4.1, filetime 0.2.29, find-msvc-tools 0.1.9, fixedbitset 0.5.7, flate2 1.1.9, fnv 1.0.7, form_urlencoded 1.2.2, fs-err 3.3.0, futures-lite 2.6.1, gethostname 1.1.0, gif 0.14.2, glob 0.3.3, hashbrown 0.16.1, hashbrown 0.17.1, heck 0.5.0, hermit-abi 0.5.2, httparse 1.10.1, idna 1.1.0, idna_adapter 1.2.2, indexmap 2.14.0, itertools 0.10.5, itertools 0.13.0, jni 0.21.1, jobserver 0.1.34, js-sys 0.3.103, khronos-egl 6.0.0, lazy_static 1.5.0, linux-raw-sys 0.12.1, lock_api 0.4.14, log 0.4.33, mime 0.3.17, nasm-rs 0.3.2, num-bigint 0.4.6, num-bigint-dig 0.8.6, num-derive 0.4.2, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, num_cpus 1.17.0, oid-registry 0.7.1, once_cell 1.21.4, openssl-probe 0.2.1, opus 0.3.1, ordered-stream 0.2.0, parking 2.2.1, parking_lot 0.12.5, parking_lot_core 0.9.12, percent-encoding 2.3.2, piper 0.2.5, pkg-config 0.3.33, png 0.18.1, polling 3.11.0, proptest 1.11.0, regex 1.12.4, regex-automata 0.4.14, regex-syntax 0.8.11, roxmltree 0.21.1, rsa 0.9.10, rustc_version 0.4.1, rusticata-macros 4.1.0, rustix 1.1.4, rustls 0.23.41, rustls-native-certs 0.8.4, rusty-fork 0.3.1, scopeguard 1.2.0, security-framework 3.7.0, security-framework-sys 2.17.0, signal-hook-registry 1.4.8, smallvec 1.15.2, socket2 0.6.4, stable_deref_trait 1.2.1, system-deps 7.0.8, tar 0.4.46, tempfile 3.27.0, thread_local 1.1.9, tinytemplate 1.2.1, unicode-segmentation 1.13.3, unicode-width 0.2.2, url 2.5.8, uuid 1.23.4, vcpkg 0.2.15, version_check 0.9.5, wait-timeout 0.2.1, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wasm-bindgen 0.2.126, wasm-bindgen-macro 0.2.126, wasm-bindgen-macro-support 0.2.126, wasm-bindgen-shared 0.2.126, weezl 0.1.12, wit-bindgen 0.57.1, x509-parser 0.16.0, xattr 1.6.1 +The following license (LICENSE-APACHE) applies to: asn1-rs 0.6.2, asn1-rs-derive 0.5.1, assert_matches 1.5.0, async-channel 2.5.0, async-executor 1.14.0, async-io 2.6.0, async-lock 3.4.2, async-process 2.5.0, async-recursion 1.1.1, async-signal 0.2.14, async-task 4.7.1, atomic-waker 1.1.2, autocfg 1.5.1, base64 0.22.1, bitflags 1.3.2, bitflags 2.13.0, blocking 1.6.2, bumpalo 3.20.3, cast 0.3.0, cc 1.2.65, cexpr 0.6.0, cfg-if 1.0.4, cmake 0.1.58, concurrent-queue 2.5.0, core-foundation 0.10.1, core-foundation-sys 0.8.7, criterion 0.5.1, criterion-plot 0.5.0, crossbeam-utils 0.8.21, curve25519-dalek-derive 0.1.1, der-parser 9.0.0, displaydoc 0.2.6, either 1.16.0, equivalent 1.0.2, errno 0.3.14, event-listener 5.4.1, event-listener-strategy 0.5.4, fastrand 2.4.1, filetime 0.2.29, find-msvc-tools 0.1.9, fixedbitset 0.5.7, flate2 1.1.9, fnv 1.0.7, form_urlencoded 1.2.2, fs-err 3.3.0, futures-lite 2.6.1, gethostname 1.1.0, gif 0.14.2, glob 0.3.3, hashbrown 0.16.1, hashbrown 0.17.1, heck 0.5.0, hermit-abi 0.5.2, httparse 1.10.1, idna 1.1.0, idna_adapter 1.2.2, indexmap 2.14.0, itertools 0.10.5, itertools 0.13.0, jni 0.21.1, jobserver 0.1.34, js-sys 0.3.103, khronos-egl 6.0.0, lazy_static 1.5.0, linux-raw-sys 0.12.1, lock_api 0.4.14, log 0.4.33, mime 0.3.17, nasm-rs 0.3.2, num-bigint 0.4.6, num-bigint-dig 0.8.6, num-derive 0.4.2, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, num_cpus 1.17.0, oid-registry 0.7.1, once_cell 1.21.4, openssl-probe 0.2.1, opus 0.3.1, ordered-stream 0.2.0, parking 2.2.1, parking_lot 0.12.5, parking_lot_core 0.9.12, percent-encoding 2.3.2, piper 0.2.5, pkg-config 0.3.33, png 0.18.1, polling 3.11.0, proptest 1.11.0, regex 1.12.4, regex-automata 0.4.14, regex-syntax 0.8.11, roxmltree 0.21.1, rsa 0.9.10, rustc_version 0.4.1, rusticata-macros 4.1.0, rustix 1.1.4, rustls 0.23.41, rustls-native-certs 0.8.4, rusty-fork 0.3.1, scopeguard 1.2.0, security-framework 3.7.0, security-framework-sys 2.17.0, signal-hook-registry 1.4.8, smallvec 1.15.2, socket2 0.6.4, stable_deref_trait 1.2.1, system-deps 7.0.8, tar 0.4.46, tempfile 3.27.0, thread_local 1.1.9, tinytemplate 1.2.1, unicode-segmentation 1.13.3, unicode-width 0.2.2, url 2.5.8, uuid 1.23.4, vcpkg 0.2.15, version_check 0.9.5, wait-timeout 0.2.1, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wasm-bindgen 0.2.126, wasm-bindgen-macro 0.2.126, wasm-bindgen-macro-support 0.2.126, wasm-bindgen-shared 0.2.126, weezl 0.1.12, wit-bindgen 0.57.1, x509-parser 0.16.0, xattr 1.6.1 ---------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -2467,6 +2495,36 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (LICENSE-MIT) applies to: assert_matches 1.5.0 +---------------------------------------------------------------------------- +Copyright (c) 2016 Murarth + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE-APACHE) applies to: async-broadcast 0.7.2 ---------------------------------------------------------------------------- @@ -2738,6 +2796,242 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (LICENSE-APACHE) applies to: atomig 0.4.3, bit-set 0.8.0, bit-vec 0.8.0, cfg-expr 0.20.8, defmt 1.1.1, defmt-macros 1.1.1, downcast-rs 1.2.1, hashlink 0.12.0, minimal-lexical 0.2.1 +---------------------------------------------------------------------------- +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +---------------------------------------------------------------------------- +The following license (LICENSE-MIT) applies to: atomig 0.4.3 +---------------------------------------------------------------------------- +Copyright (c) 2016 Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE.md) applies to: audiopus_sys 0.2.2 ---------------------------------------------------------------------------- @@ -2984,212 +3278,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: bit-set 0.8.0, bit-vec 0.8.0, cfg-expr 0.20.8, downcast-rs 1.2.1, hashlink 0.12.0, minimal-lexical 0.2.1 ----------------------------------------------------------------------------- -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - - ---------------------------------------------------------------------------- The following license (LICENSE-MIT) applies to: bit-set 0.8.0, bit-vec 0.8.0 ---------------------------------------------------------------------------- @@ -3221,7 +3309,7 @@ DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: bitflags 2.13.0, glob 0.3.3, log 0.4.33, num-bigint 0.4.6, num-bigint-dig 0.8.6, num-derive 0.4.2, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, regex 1.12.4, regex-automata 0.4.14, regex-syntax 0.8.11 +The following license (LICENSE-MIT) applies to: bitflags 1.3.2, bitflags 2.13.0, glob 0.3.3, log 0.4.33, num-bigint 0.4.6, num-bigint-dig 0.8.6, num-derive 0.4.2, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, regex 1.12.4, regex-automata 0.4.14, regex-syntax 0.8.11 ---------------------------------------------------------------------------- Copyright (c) 2014 The Rust Project Developers @@ -5153,6 +5241,36 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (LICENSE-MIT) applies to: defmt 1.1.1, defmt-macros 1.1.1 +---------------------------------------------------------------------------- +Copyright (c) Ferrous Systems + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE-MIT) applies to: der 0.7.10, pkcs8 0.10.2 ---------------------------------------------------------------------------- @@ -5848,7 +5966,7 @@ DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: fdeflate 0.3.7, field-offset 0.3.6, half 2.7.1, image 0.25.10, miniz_oxide 0.8.9, num-conv 0.2.2, pin-project-lite 0.2.17, raw-window-handle 0.6.2, sync_wrapper 1.0.2, time 0.3.51, time-core 0.1.9, time-macros 0.2.30, widestring 1.2.1 +The following license (LICENSE-APACHE) applies to: fdeflate 0.3.7, field-offset 0.3.6, half 2.7.1, image 0.25.10, miniz_oxide 0.8.9, num-conv 0.2.2, pin-project-lite 0.2.17, portable-atomic 1.14.0, portable-atomic-util 0.2.7, raw-window-handle 0.6.2, sync_wrapper 1.0.2, time 0.3.51, time-core 0.1.9, time-macros 0.2.30, widestring 1.2.1 ---------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -11397,6 +11515,61 @@ APPENDIX: How to apply the Apache License to your work. identification within third-party archives. +---------------------------------------------------------------------------- +The following license (COPYING) applies to: rav1d 1.1.0 +---------------------------------------------------------------------------- +Copyright © 2018-2019, VideoLAN and dav1d authors +Copyright © 2023-2024, VideoLAN, dav1d authors, and Internet Security Research Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +---------------------------------------------------------------------------- +The following license (LICENSE.md) applies to: raw-cpuid 11.6.0 +---------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2015 Gerd Zellweger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE-MIT.md) applies to: raw-window-handle 0.6.2 ---------------------------------------------------------------------------- @@ -13264,6 +13437,32 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (LICENSE) applies to: strum 0.26.3, strum_macros 0.26.4 +---------------------------------------------------------------------------- +MIT License + +Copyright (c) 2019 Peter Glotfelty + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE) applies to: subtle 2.6.1 ---------------------------------------------------------------------------- @@ -13690,6 +13889,132 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. +---------------------------------------------------------------------------- +The following license (LICENSE) applies to: to_method 1.1.0 +---------------------------------------------------------------------------- +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. + + ---------------------------------------------------------------------------- The following license (LICENSE) applies to: tokio 1.52.3, tokio-util 0.7.18 ---------------------------------------------------------------------------- @@ -16105,7 +16430,7 @@ DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: zerocopy 0.8.52, zerocopy-derive 0.8.52 +The following license (LICENSE-APACHE) applies to: zerocopy 0.7.35, zerocopy 0.8.52, zerocopy-derive 0.7.35, zerocopy-derive 0.8.52 ---------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -16311,7 +16636,7 @@ Apache License ---------------------------------------------------------------------------- -The following license (LICENSE-BSD) applies to: zerocopy 0.8.52, zerocopy-derive 0.8.52 +The following license (LICENSE-BSD) applies to: zerocopy 0.7.35, zerocopy 0.8.52, zerocopy-derive 0.7.35, zerocopy-derive 0.8.52 ---------------------------------------------------------------------------- Copyright 2019 The Fuchsia Authors. @@ -16340,7 +16665,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: zerocopy 0.8.52, zerocopy-derive 0.8.52 +The following license (LICENSE-MIT) applies to: zerocopy 0.7.35, zerocopy 0.8.52, zerocopy-derive 0.7.35, zerocopy-derive 0.8.52 ---------------------------------------------------------------------------- Copyright 2023 The Fuchsia Authors diff --git a/clients/android/app/src/main/assets/THIRD-PARTY-NOTICES.txt b/clients/android/app/src/main/assets/THIRD-PARTY-NOTICES.txt index 288d1670..d5d9ff56 100644 --- a/clients/android/app/src/main/assets/THIRD-PARTY-NOTICES.txt +++ b/clients/android/app/src/main/assets/THIRD-PARTY-NOTICES.txt @@ -7,7 +7,7 @@ below. Each is distributed under its own permissive license; the full license te follow the manifest. This file is generated by scripts/gen-third-party-notices.py (or `cargo about`, see about.toml) — do not edit by hand. -Total third-party crates: 566 +Total third-party crates: 590 ---------------------------------------------------------------------------- VENDORED THIRD-PARTY SOURCE (inside first-party crates) @@ -43,6 +43,7 @@ MANIFEST (crate version — SPDX license — source) asn1-rs 0.6.2 — MIT OR Apache-2.0 — https://github.com/rusticata/asn1-rs.git asn1-rs-derive 0.5.1 — MIT OR Apache-2.0 — https://github.com/rusticata/asn1-rs.git asn1-rs-impl 0.2.0 — MIT/Apache-2.0 — https://github.com/rusticata/asn1-rs.git + assert_matches 1.5.0 — MIT/Apache-2.0 — https://github.com/murarth/assert_matches async-broadcast 0.7.2 — MIT OR Apache-2.0 — https://github.com/smol-rs/async-broadcast async-channel 2.5.0 — Apache-2.0 OR MIT — https://github.com/smol-rs/async-channel async-executor 1.14.0 — Apache-2.0 OR MIT — https://github.com/smol-rs/async-executor @@ -54,6 +55,8 @@ MANIFEST (crate version — SPDX license — source) async-task 4.7.1 — Apache-2.0 OR MIT — https://github.com/smol-rs/async-task async-trait 0.1.89 — MIT OR Apache-2.0 — https://github.com/dtolnay/async-trait atomic-waker 1.1.2 — Apache-2.0 OR MIT — https://github.com/smol-rs/atomic-waker + atomig 0.4.3 — MIT/Apache-2.0 — https://github.com/LukasKalbertodt/atomig/ + atomig-macro 0.4.0 — MIT/Apache-2.0 — https://github.com/LukasKalbertodt/atomig/ audiopus_sys 0.2.2 — ISC — https://github.com/lakelezz/audiopus_sys.git autocfg 1.5.1 — Apache-2.0 OR MIT — https://github.com/cuviper/autocfg axum 0.8.9 — MIT — https://github.com/tokio-rs/axum @@ -64,6 +67,7 @@ MANIFEST (crate version — SPDX license — source) bindgen 0.72.1 — BSD-3-Clause — https://github.com/rust-lang/rust-bindgen bit-set 0.8.0 — Apache-2.0 OR MIT — https://github.com/contain-rs/bit-set bit-vec 0.8.0 — Apache-2.0 OR MIT — https://github.com/contain-rs/bit-vec + bitflags 1.3.2 — MIT/Apache-2.0 — https://github.com/bitflags/bitflags bitflags 2.13.0 — MIT OR Apache-2.0 — https://github.com/bitflags/bitflags block-buffer 0.10.4 — MIT OR Apache-2.0 — https://github.com/RustCrypto/utils block-padding 0.3.3 — MIT OR Apache-2.0 — https://github.com/RustCrypto/utils @@ -71,6 +75,7 @@ MANIFEST (crate version — SPDX license — source) bumpalo 3.20.3 — MIT OR Apache-2.0 — https://github.com/fitzgen/bumpalo bytemuck 1.25.0 — Zlib OR Apache-2.0 OR MIT — https://github.com/Lokathor/bytemuck bytemuck_derive 1.10.2 — Zlib OR Apache-2.0 OR MIT — https://github.com/Lokathor/bytemuck + byteorder 1.5.0 — Unlicense OR MIT — https://github.com/BurntSushi/byteorder byteorder-lite 0.1.0 — Unlicense OR MIT — https://github.com/image-rs/byteorder-lite bytes 1.12.0 — MIT — https://github.com/tokio-rs/bytes cairo-rs 0.22.0 — MIT — https://github.com/gtk-rs/gtk-rs-core @@ -115,6 +120,9 @@ MANIFEST (crate version — SPDX license — source) curve25519-dalek 4.1.3 — BSD-3-Clause — https://github.com/dalek-cryptography/curve25519-dalek/tree/main/curve25519-dalek curve25519-dalek-derive 0.1.1 — MIT/Apache-2.0 — https://github.com/dalek-cryptography/curve25519-dalek data-encoding 2.11.0 — MIT — https://github.com/ia0/data-encoding + defmt 1.1.1 — MIT OR Apache-2.0 — https://github.com/knurling-rs/defmt + defmt-macros 1.1.1 — MIT OR Apache-2.0 — https://github.com/knurling-rs/defmt + defmt-parser 1.0.0 — MIT OR Apache-2.0 — https://github.com/knurling-rs/defmt der 0.7.10 — Apache-2.0 OR MIT — https://github.com/RustCrypto/formats/tree/master/der der-parser 9.0.0 — MIT/Apache-2.0 — https://github.com/rusticata/der-parser.git deranged 0.5.8 — MIT OR Apache-2.0 — https://github.com/jhpratt/deranged @@ -126,6 +134,8 @@ MANIFEST (crate version — SPDX license — source) enumflags2 0.7.12 — MIT OR Apache-2.0 — https://github.com/meithecatte/enumflags2 enumflags2_derive 0.7.12 — MIT OR Apache-2.0 — https://github.com/meithecatte/enumflags2 env_filter 0.1.4 — MIT OR Apache-2.0 — https://github.com/rust-cli/env_logger + env_filter 2.0.0 — MIT OR Apache-2.0 — https://github.com/rust-cli/env_logger + env_logger 0.11.11 — MIT OR Apache-2.0 — https://github.com/rust-cli/env_logger equivalent 1.0.2 — Apache-2.0 OR MIT — https://github.com/indexmap-rs/equivalent errno 0.3.14 — MIT OR Apache-2.0 — https://github.com/lambda-fairy/rust-errno event-listener 5.4.1 — Apache-2.0 OR MIT — https://github.com/smol-rs/event-listener @@ -221,6 +231,9 @@ MANIFEST (crate version — SPDX license — source) itertools 0.10.5 — MIT/Apache-2.0 — https://github.com/rust-itertools/itertools itertools 0.13.0 — MIT OR Apache-2.0 — https://github.com/rust-itertools/itertools itoa 1.0.18 — MIT OR Apache-2.0 — https://github.com/dtolnay/itoa + jiff 0.2.35 — Unlicense OR MIT — https://github.com/BurntSushi/jiff + jiff-core 0.1.0 — Unlicense OR MIT — https://github.com/BurntSushi/jiff + jiff-static 0.2.35 — Unlicense OR MIT — https://github.com/BurntSushi/jiff jni 0.21.1 — MIT/Apache-2.0 — https://github.com/jni-rs/jni-rs jni-sys 0.3.1 — MIT OR Apache-2.0 — https://github.com/jni-rs/jni-sys jni-sys 0.4.1 — MIT OR Apache-2.0 — https://github.com/jni-rs/jni-sys @@ -304,6 +317,8 @@ MANIFEST (crate version — SPDX license — source) polling 3.11.0 — Apache-2.0 OR MIT — https://github.com/smol-rs/polling poly1305 0.8.0 — Apache-2.0 OR MIT — https://github.com/RustCrypto/universal-hashes polyval 0.6.2 — Apache-2.0 OR MIT — https://github.com/RustCrypto/universal-hashes + portable-atomic 1.14.0 — Apache-2.0 OR MIT — https://github.com/taiki-e/portable-atomic + portable-atomic-util 0.2.7 — Apache-2.0 OR MIT — https://github.com/taiki-e/portable-atomic-util potential_utf 0.1.5 — Unicode-3.0 — https://github.com/unicode-org/icu4x powerfmt 0.2.0 — MIT OR Apache-2.0 — https://github.com/jhpratt/powerfmt ppv-lite86 0.2.21 — MIT OR Apache-2.0 — https://github.com/cryptocorrosion/cryptocorrosion @@ -327,6 +342,8 @@ MANIFEST (crate version — SPDX license — source) rand_core 0.6.4 — MIT OR Apache-2.0 — https://github.com/rust-random/rand rand_core 0.9.5 — MIT OR Apache-2.0 — https://github.com/rust-random/rand rand_xorshift 0.4.0 — MIT OR Apache-2.0 — https://github.com/rust-random/rngs + rav1d 1.1.0 — BSD-2-Clause — https://github.com/memorysafety/rav1d + raw-cpuid 11.6.0 — MIT — https://github.com/gz/rust-cpuid raw-window-handle 0.6.2 — MIT OR Apache-2.0 OR Zlib — https://github.com/rust-windowing/raw-window-handle rcgen 0.13.2 — MIT OR Apache-2.0 — https://github.com/rustls/rcgen readme-rustdocifier 0.1.1 — MIT — https://github.com/malaire/readme-rustdocifier @@ -404,6 +421,8 @@ MANIFEST (crate version — SPDX license — source) sqlite-wasm-rs 0.5.5 — MIT — https://github.com/Spxg/sqlite-wasm-rs stable_deref_trait 1.2.1 — MIT OR Apache-2.0 — https://github.com/storyyeller/stable_deref_trait strsim 0.11.1 — MIT — https://github.com/rapidfuzz/strsim-rs + strum 0.26.3 — MIT — https://github.com/Peternator7/strum + strum_macros 0.26.4 — MIT — https://github.com/Peternator7/strum subtle 2.6.1 — BSD-3-Clause — https://github.com/dalek-cryptography/subtle syn 2.0.118 — MIT OR Apache-2.0 — https://github.com/dtolnay/syn sync_wrapper 1.0.2 — Apache-2.0 — https://github.com/Actyx/sync_wrapper @@ -425,6 +444,7 @@ MANIFEST (crate version — SPDX license — source) tinytemplate 1.2.1 — Apache-2.0 OR MIT — https://github.com/bheisler/TinyTemplate tinyvec 1.11.0 — Zlib OR Apache-2.0 OR MIT — https://github.com/Lokathor/tinyvec tinyvec_macros 0.1.1 — MIT OR Apache-2.0 OR Zlib — https://github.com/Soveu/tinyvec_macros + to_method 1.1.0 — CC0-1.0 — https://github.com/whentze/to_method tokio 1.52.3 — MIT — https://github.com/tokio-rs/tokio tokio-macros 2.7.0 — MIT — https://github.com/tokio-rs/tokio tokio-rustls 0.26.4 — MIT OR Apache-2.0 — https://github.com/rustls/tokio-rustls @@ -449,6 +469,7 @@ MANIFEST (crate version — SPDX license — source) tracing-log 0.2.0 — MIT — https://github.com/tokio-rs/tracing tracing-subscriber 0.3.23 — MIT — https://github.com/tokio-rs/tracing typenum 1.20.1 — MIT OR Apache-2.0 — https://github.com/paholg/typenum + uac-host 0.1.0 — MIT OR Apache-2.0 — https://github.com/unom-io/usbfs-iso uds_windows 1.2.1 — MIT — https://github.com/haraldh/rust_uds_windows unarray 0.1.4 — MIT OR Apache-2.0 — https://github.com/cameron1024/unarray unicode-ident 1.0.24 — (MIT OR Apache-2.0) AND Unicode-3.0 — https://github.com/dtolnay/unicode-ident @@ -458,6 +479,7 @@ MANIFEST (crate version — SPDX license — source) untrusted 0.9.0 — ISC — https://github.com/briansmith/untrusted ureq 2.12.1 — MIT OR Apache-2.0 — https://github.com/algesten/ureq url 2.5.8 — MIT OR Apache-2.0 — https://github.com/servo/rust-url + usbfs-iso 0.1.0 — MIT OR Apache-2.0 — https://github.com/unom-io/usbfs-iso utf8_iter 1.0.4 — Apache-2.0 OR MIT — https://github.com/hsivonen/utf8_iter utf8parse 0.2.2 — Apache-2.0 OR MIT — https://github.com/alacritty/vte utoipa 5.5.0 — MIT OR Apache-2.0 — https://github.com/juhaku/utoipa @@ -575,7 +597,9 @@ MANIFEST (crate version — SPDX license — source) zbus 5.16.0 — MIT — https://github.com/z-galaxy/zbus/ zbus_macros 5.16.0 — MIT — https://github.com/z-galaxy/zbus/ zbus_names 4.3.2 — MIT — https://github.com/z-galaxy/zbus/ + zerocopy 0.7.35 — BSD-2-Clause OR Apache-2.0 OR MIT — https://github.com/google/zerocopy zerocopy 0.8.52 — BSD-2-Clause OR Apache-2.0 OR MIT — https://github.com/google/zerocopy + zerocopy-derive 0.7.35 — BSD-2-Clause OR Apache-2.0 OR MIT — https://github.com/google/zerocopy zerocopy-derive 0.8.52 — BSD-2-Clause OR Apache-2.0 OR MIT — https://github.com/google/zerocopy zerofrom 0.1.8 — Unicode-3.0 — https://github.com/unicode-org/icu4x zerofrom-derive 0.1.7 — Unicode-3.0 — https://github.com/unicode-org/icu4x @@ -595,7 +619,9 @@ Crates whose package did not embed a license file (SPDX + source only) ---------------------------------------------------------------------------- anes 0.1.6 — MIT OR Apache-2.0 — https://github.com/zrzka/anes-rs asn1-rs-impl 0.2.0 — MIT/Apache-2.0 — https://github.com/rusticata/asn1-rs.git + atomig-macro 0.4.0 — MIT/Apache-2.0 — https://github.com/LukasKalbertodt/atomig/ cookie-factory 0.3.3 — MIT — https://github.com/rust-bakery/cookie-factory + defmt-parser 1.0.0 — MIT OR Apache-2.0 — https://github.com/knurling-rs/defmt ffmpeg-sys-next 8.1.0 — WTFPL — https://github.com/zmwangx/rust-ffmpeg-sys jni-sys-macros 0.4.1 — MIT OR Apache-2.0 — https://github.com/jni-rs/jni-sys ndk 0.9.0 — MIT OR Apache-2.0 — https://github.com/rust-mobile/ndk @@ -612,6 +638,8 @@ Crates whose package did not embed a license file (SPDX + source only) skia-bindings 0.87.0 — MIT — https://github.com/rust-skia/rust-skia skia-safe 0.87.0 — MIT — https://github.com/rust-skia/rust-skia test_reactor 0.0.0 — UNKNOWN + uac-host 0.1.0 — MIT OR Apache-2.0 — https://github.com/unom-io/usbfs-iso + usbfs-iso 0.1.0 — MIT OR Apache-2.0 — https://github.com/unom-io/usbfs-iso valuable 0.1.1 — MIT — https://github.com/tokio-rs/valuable winapi-i686-pc-windows-gnu 0.4.0 — MIT/Apache-2.0 — https://github.com/retep998/winapi-rs winapi-x86_64-pc-windows-gnu 0.4.0 — MIT/Apache-2.0 — https://github.com/retep998/winapi-rs @@ -845,7 +873,7 @@ limitations under the License. ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: adler2 2.0.1, anyhow 1.0.103, async-channel 2.5.0, async-executor 1.14.0, async-io 2.6.0, async-lock 3.4.2, async-process 2.5.0, async-recursion 1.1.1, async-signal 0.2.14, async-task 4.7.1, async-trait 0.1.89, atomic-waker 1.1.2, blocking 1.6.2, concurrent-queue 2.5.0, curve25519-dalek-derive 0.1.1, displaydoc 0.2.6, endi 1.1.1, event-listener 5.4.1, event-listener-strategy 0.5.4, fastrand 2.4.1, flume 0.12.0, fs-err 3.3.0, futures-lite 2.6.1, hermit-abi 0.5.2, is-terminal 0.4.17, itoa 1.0.18, khronos-egl 6.0.0, linux-raw-sys 0.12.1, minimal-lexical 0.2.1, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, ordered-stream 0.2.0, parking 2.2.1, paste 1.0.15, pastey 0.2.3, pin-project-lite 0.2.17, piper 0.2.5, polling 3.11.0, prettyplease 0.2.37, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.46, reis 0.6.1, relm4 0.11.0, relm4-css 0.11.0, relm4-macros 0.11.0, rsa 0.9.10, rustc-hash 2.1.2, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, serde 1.0.228, serde_core 1.0.228, serde_derive 1.0.228, serde_json 1.0.150, serde_path_to_error 0.1.20, serde_repr 0.1.20, syn 2.0.118, system-deps 7.0.8, thiserror 1.0.69, thiserror 2.0.18, thiserror-impl 1.0.69, thiserror-impl 2.0.18, unicode-ident 1.0.24, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wit-bindgen 0.57.1, zmij 1.0.21, zvariant_utils 3.4.0 +The following license (LICENSE-MIT) applies to: adler2 2.0.1, anyhow 1.0.103, async-channel 2.5.0, async-executor 1.14.0, async-io 2.6.0, async-lock 3.4.2, async-process 2.5.0, async-recursion 1.1.1, async-signal 0.2.14, async-task 4.7.1, async-trait 0.1.89, atomic-waker 1.1.2, blocking 1.6.2, concurrent-queue 2.5.0, curve25519-dalek-derive 0.1.1, displaydoc 0.2.6, endi 1.1.1, event-listener 5.4.1, event-listener-strategy 0.5.4, fastrand 2.4.1, flume 0.12.0, fs-err 3.3.0, futures-lite 2.6.1, hermit-abi 0.5.2, is-terminal 0.4.17, itoa 1.0.18, khronos-egl 6.0.0, linux-raw-sys 0.12.1, minimal-lexical 0.2.1, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, ordered-stream 0.2.0, parking 2.2.1, paste 1.0.15, pastey 0.2.3, pin-project-lite 0.2.17, piper 0.2.5, polling 3.11.0, portable-atomic 1.14.0, portable-atomic-util 0.2.7, prettyplease 0.2.37, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.46, reis 0.6.1, relm4 0.11.0, relm4-css 0.11.0, relm4-macros 0.11.0, rsa 0.9.10, rustc-hash 2.1.2, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, serde 1.0.228, serde_core 1.0.228, serde_derive 1.0.228, serde_json 1.0.150, serde_path_to_error 0.1.20, serde_repr 0.1.20, syn 2.0.118, system-deps 7.0.8, thiserror 1.0.69, thiserror 2.0.18, thiserror-impl 1.0.69, thiserror-impl 2.0.18, unicode-ident 1.0.24, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wit-bindgen 0.57.1, zmij 1.0.21, zvariant_utils 3.4.0 ---------------------------------------------------------------------------- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -1170,7 +1198,7 @@ DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (COPYING) applies to: aho-corasick 1.1.4, memchr 2.8.2, same-file 1.0.6, walkdir 2.5.0, winapi-util 0.1.11 +The following license (COPYING) applies to: aho-corasick 1.1.4, byteorder 1.5.0, jiff 0.2.35, jiff-core 0.1.0, jiff-static 0.2.35, memchr 2.8.2, same-file 1.0.6, walkdir 2.5.0, winapi-util 0.1.11 ---------------------------------------------------------------------------- This project is dual-licensed under the Unlicense and MIT licenses. @@ -1178,7 +1206,7 @@ You may use this code under the terms of either license. ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: aho-corasick 1.1.4, byteorder-lite 0.1.0, memchr 2.8.2, walkdir 2.5.0 +The following license (LICENSE-MIT) applies to: aho-corasick 1.1.4, byteorder 1.5.0, byteorder-lite 0.1.0, jiff 0.2.35, jiff-core 0.1.0, jiff-static 0.2.35, memchr 2.8.2, walkdir 2.5.0 ---------------------------------------------------------------------------- The MIT License (MIT) @@ -1204,7 +1232,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (UNLICENSE) applies to: aho-corasick 1.1.4, byteorder-lite 0.1.0, ksni 0.3.5, memchr 2.8.2, same-file 1.0.6, walkdir 2.5.0, winapi-util 0.1.11 +The following license (UNLICENSE) applies to: aho-corasick 1.1.4, byteorder 1.5.0, byteorder-lite 0.1.0, jiff 0.2.35, jiff-core 0.1.0, jiff-static 0.2.35, ksni 0.3.5, memchr 2.8.2, same-file 1.0.6, walkdir 2.5.0, winapi-util 0.1.11 ---------------------------------------------------------------------------- This is free and unencumbered software released into the public domain. @@ -1693,7 +1721,7 @@ SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: annotate-snippets 0.11.5, anstream 1.0.0, anstyle 1.0.14, anstyle-parse 1.0.0, anstyle-query 1.1.5, anstyle-wincon 3.0.11, clap 4.6.1, clap_builder 4.6.0, clap_lex 1.1.0, colorchoice 1.0.5, crc32fast 1.5.0, env_filter 0.1.4, fallible-iterator 0.3.0, fallible-streaming-iterator 0.1.9, hex 0.4.3, is_terminal_polyfill 1.70.2, jni-sys 0.3.1, jni-sys 0.4.1, once_cell_polyfill 1.70.2, quick-error 1.2.3, serde_spanned 0.6.9, serde_spanned 1.1.1, toml 0.8.23, toml 0.9.12+spec-1.1.0, toml 1.1.2+spec-1.1.0, toml_datetime 0.6.11, toml_datetime 0.7.5+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.22.27, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_write 0.1.2, toml_writer 1.1.1+spec-1.1.0, winapi 0.3.9 +The following license (LICENSE-APACHE) applies to: annotate-snippets 0.11.5, anstream 1.0.0, anstyle 1.0.14, anstyle-parse 1.0.0, anstyle-query 1.1.5, anstyle-wincon 3.0.11, clap 4.6.1, clap_builder 4.6.0, clap_lex 1.1.0, colorchoice 1.0.5, crc32fast 1.5.0, env_filter 0.1.4, env_filter 2.0.0, env_logger 0.11.11, fallible-iterator 0.3.0, fallible-streaming-iterator 0.1.9, hex 0.4.3, is_terminal_polyfill 1.70.2, jni-sys 0.3.1, jni-sys 0.4.1, once_cell_polyfill 1.70.2, quick-error 1.2.3, serde_spanned 0.6.9, serde_spanned 1.1.1, toml 0.8.23, toml 0.9.12+spec-1.1.0, toml 1.1.2+spec-1.1.0, toml_datetime 0.6.11, toml_datetime 0.7.5+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.22.27, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_write 0.1.2, toml_writer 1.1.1+spec-1.1.0, winapi 0.3.9 ---------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -1899,7 +1927,7 @@ Apache License ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: annotate-snippets 0.11.5, anstream 1.0.0, anstyle 1.0.14, anstyle-parse 1.0.0, anstyle-query 1.1.5, anstyle-wincon 3.0.11, clap 4.6.1, clap_builder 4.6.0, clap_lex 1.1.0, colorchoice 1.0.5, env_filter 0.1.4, is_terminal_polyfill 1.70.2, once_cell_polyfill 1.70.2, serde_spanned 0.6.9, serde_spanned 1.1.1, toml 0.8.23, toml 0.9.12+spec-1.1.0, toml 1.1.2+spec-1.1.0, toml_datetime 0.6.11, toml_datetime 0.7.5+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.22.27, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_write 0.1.2, toml_writer 1.1.1+spec-1.1.0 +The following license (LICENSE-MIT) applies to: annotate-snippets 0.11.5, anstream 1.0.0, anstyle 1.0.14, anstyle-parse 1.0.0, anstyle-query 1.1.5, anstyle-wincon 3.0.11, clap 4.6.1, clap_builder 4.6.0, clap_lex 1.1.0, colorchoice 1.0.5, env_filter 0.1.4, env_filter 2.0.0, env_logger 0.11.11, is_terminal_polyfill 1.70.2, once_cell_polyfill 1.70.2, serde_spanned 0.6.9, serde_spanned 1.1.1, toml 0.8.23, toml 0.9.12+spec-1.1.0, toml 1.1.2+spec-1.1.0, toml_datetime 0.6.11, toml_datetime 0.7.5+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.22.27, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_write 0.1.2, toml_writer 1.1.1+spec-1.1.0 ---------------------------------------------------------------------------- Copyright (c) Individual contributors @@ -2232,7 +2260,7 @@ SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: asn1-rs 0.6.2, asn1-rs-derive 0.5.1, async-channel 2.5.0, async-executor 1.14.0, async-io 2.6.0, async-lock 3.4.2, async-process 2.5.0, async-recursion 1.1.1, async-signal 0.2.14, async-task 4.7.1, atomic-waker 1.1.2, autocfg 1.5.1, base64 0.22.1, bitflags 2.13.0, blocking 1.6.2, bumpalo 3.20.3, cast 0.3.0, cc 1.2.65, cexpr 0.6.0, cfg-if 1.0.4, cmake 0.1.58, concurrent-queue 2.5.0, core-foundation 0.10.1, core-foundation-sys 0.8.7, criterion 0.5.1, criterion-plot 0.5.0, crossbeam-utils 0.8.21, curve25519-dalek-derive 0.1.1, der-parser 9.0.0, displaydoc 0.2.6, either 1.16.0, equivalent 1.0.2, errno 0.3.14, event-listener 5.4.1, event-listener-strategy 0.5.4, fastrand 2.4.1, filetime 0.2.29, find-msvc-tools 0.1.9, fixedbitset 0.5.7, flate2 1.1.9, fnv 1.0.7, form_urlencoded 1.2.2, fs-err 3.3.0, futures-lite 2.6.1, gethostname 1.1.0, gif 0.14.2, glob 0.3.3, hashbrown 0.16.1, hashbrown 0.17.1, heck 0.5.0, hermit-abi 0.5.2, httparse 1.10.1, idna 1.1.0, idna_adapter 1.2.2, indexmap 2.14.0, itertools 0.10.5, itertools 0.13.0, jni 0.21.1, jobserver 0.1.34, js-sys 0.3.103, khronos-egl 6.0.0, lazy_static 1.5.0, linux-raw-sys 0.12.1, lock_api 0.4.14, log 0.4.33, mime 0.3.17, nasm-rs 0.3.2, num-bigint 0.4.6, num-bigint-dig 0.8.6, num-derive 0.4.2, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, num_cpus 1.17.0, oid-registry 0.7.1, once_cell 1.21.4, openssl-probe 0.2.1, opus 0.3.1, ordered-stream 0.2.0, parking 2.2.1, parking_lot 0.12.5, parking_lot_core 0.9.12, percent-encoding 2.3.2, piper 0.2.5, pkg-config 0.3.33, png 0.18.1, polling 3.11.0, proptest 1.11.0, regex 1.12.4, regex-automata 0.4.14, regex-syntax 0.8.11, roxmltree 0.21.1, rsa 0.9.10, rustc_version 0.4.1, rusticata-macros 4.1.0, rustix 1.1.4, rustls 0.23.41, rustls-native-certs 0.8.4, rusty-fork 0.3.1, scopeguard 1.2.0, security-framework 3.7.0, security-framework-sys 2.17.0, signal-hook-registry 1.4.8, smallvec 1.15.2, socket2 0.6.4, stable_deref_trait 1.2.1, system-deps 7.0.8, tar 0.4.46, tempfile 3.27.0, thread_local 1.1.9, tinytemplate 1.2.1, unicode-segmentation 1.13.3, unicode-width 0.2.2, url 2.5.8, uuid 1.23.4, vcpkg 0.2.15, version_check 0.9.5, wait-timeout 0.2.1, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wasm-bindgen 0.2.126, wasm-bindgen-macro 0.2.126, wasm-bindgen-macro-support 0.2.126, wasm-bindgen-shared 0.2.126, weezl 0.1.12, wit-bindgen 0.57.1, x509-parser 0.16.0, xattr 1.6.1 +The following license (LICENSE-APACHE) applies to: asn1-rs 0.6.2, asn1-rs-derive 0.5.1, assert_matches 1.5.0, async-channel 2.5.0, async-executor 1.14.0, async-io 2.6.0, async-lock 3.4.2, async-process 2.5.0, async-recursion 1.1.1, async-signal 0.2.14, async-task 4.7.1, atomic-waker 1.1.2, autocfg 1.5.1, base64 0.22.1, bitflags 1.3.2, bitflags 2.13.0, blocking 1.6.2, bumpalo 3.20.3, cast 0.3.0, cc 1.2.65, cexpr 0.6.0, cfg-if 1.0.4, cmake 0.1.58, concurrent-queue 2.5.0, core-foundation 0.10.1, core-foundation-sys 0.8.7, criterion 0.5.1, criterion-plot 0.5.0, crossbeam-utils 0.8.21, curve25519-dalek-derive 0.1.1, der-parser 9.0.0, displaydoc 0.2.6, either 1.16.0, equivalent 1.0.2, errno 0.3.14, event-listener 5.4.1, event-listener-strategy 0.5.4, fastrand 2.4.1, filetime 0.2.29, find-msvc-tools 0.1.9, fixedbitset 0.5.7, flate2 1.1.9, fnv 1.0.7, form_urlencoded 1.2.2, fs-err 3.3.0, futures-lite 2.6.1, gethostname 1.1.0, gif 0.14.2, glob 0.3.3, hashbrown 0.16.1, hashbrown 0.17.1, heck 0.5.0, hermit-abi 0.5.2, httparse 1.10.1, idna 1.1.0, idna_adapter 1.2.2, indexmap 2.14.0, itertools 0.10.5, itertools 0.13.0, jni 0.21.1, jobserver 0.1.34, js-sys 0.3.103, khronos-egl 6.0.0, lazy_static 1.5.0, linux-raw-sys 0.12.1, lock_api 0.4.14, log 0.4.33, mime 0.3.17, nasm-rs 0.3.2, num-bigint 0.4.6, num-bigint-dig 0.8.6, num-derive 0.4.2, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, num_cpus 1.17.0, oid-registry 0.7.1, once_cell 1.21.4, openssl-probe 0.2.1, opus 0.3.1, ordered-stream 0.2.0, parking 2.2.1, parking_lot 0.12.5, parking_lot_core 0.9.12, percent-encoding 2.3.2, piper 0.2.5, pkg-config 0.3.33, png 0.18.1, polling 3.11.0, proptest 1.11.0, regex 1.12.4, regex-automata 0.4.14, regex-syntax 0.8.11, roxmltree 0.21.1, rsa 0.9.10, rustc_version 0.4.1, rusticata-macros 4.1.0, rustix 1.1.4, rustls 0.23.41, rustls-native-certs 0.8.4, rusty-fork 0.3.1, scopeguard 1.2.0, security-framework 3.7.0, security-framework-sys 2.17.0, signal-hook-registry 1.4.8, smallvec 1.15.2, socket2 0.6.4, stable_deref_trait 1.2.1, system-deps 7.0.8, tar 0.4.46, tempfile 3.27.0, thread_local 1.1.9, tinytemplate 1.2.1, unicode-segmentation 1.13.3, unicode-width 0.2.2, url 2.5.8, uuid 1.23.4, vcpkg 0.2.15, version_check 0.9.5, wait-timeout 0.2.1, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wasm-bindgen 0.2.126, wasm-bindgen-macro 0.2.126, wasm-bindgen-macro-support 0.2.126, wasm-bindgen-shared 0.2.126, weezl 0.1.12, wit-bindgen 0.57.1, x509-parser 0.16.0, xattr 1.6.1 ---------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -2467,6 +2495,36 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (LICENSE-MIT) applies to: assert_matches 1.5.0 +---------------------------------------------------------------------------- +Copyright (c) 2016 Murarth + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE-APACHE) applies to: async-broadcast 0.7.2 ---------------------------------------------------------------------------- @@ -2738,6 +2796,242 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (LICENSE-APACHE) applies to: atomig 0.4.3, bit-set 0.8.0, bit-vec 0.8.0, cfg-expr 0.20.8, defmt 1.1.1, defmt-macros 1.1.1, downcast-rs 1.2.1, hashlink 0.12.0, minimal-lexical 0.2.1 +---------------------------------------------------------------------------- +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +---------------------------------------------------------------------------- +The following license (LICENSE-MIT) applies to: atomig 0.4.3 +---------------------------------------------------------------------------- +Copyright (c) 2016 Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE.md) applies to: audiopus_sys 0.2.2 ---------------------------------------------------------------------------- @@ -2984,212 +3278,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: bit-set 0.8.0, bit-vec 0.8.0, cfg-expr 0.20.8, downcast-rs 1.2.1, hashlink 0.12.0, minimal-lexical 0.2.1 ----------------------------------------------------------------------------- -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - - ---------------------------------------------------------------------------- The following license (LICENSE-MIT) applies to: bit-set 0.8.0, bit-vec 0.8.0 ---------------------------------------------------------------------------- @@ -3221,7 +3309,7 @@ DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: bitflags 2.13.0, glob 0.3.3, log 0.4.33, num-bigint 0.4.6, num-bigint-dig 0.8.6, num-derive 0.4.2, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, regex 1.12.4, regex-automata 0.4.14, regex-syntax 0.8.11 +The following license (LICENSE-MIT) applies to: bitflags 1.3.2, bitflags 2.13.0, glob 0.3.3, log 0.4.33, num-bigint 0.4.6, num-bigint-dig 0.8.6, num-derive 0.4.2, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, regex 1.12.4, regex-automata 0.4.14, regex-syntax 0.8.11 ---------------------------------------------------------------------------- Copyright (c) 2014 The Rust Project Developers @@ -5153,6 +5241,36 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (LICENSE-MIT) applies to: defmt 1.1.1, defmt-macros 1.1.1 +---------------------------------------------------------------------------- +Copyright (c) Ferrous Systems + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE-MIT) applies to: der 0.7.10, pkcs8 0.10.2 ---------------------------------------------------------------------------- @@ -5848,7 +5966,7 @@ DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: fdeflate 0.3.7, field-offset 0.3.6, half 2.7.1, image 0.25.10, miniz_oxide 0.8.9, num-conv 0.2.2, pin-project-lite 0.2.17, raw-window-handle 0.6.2, sync_wrapper 1.0.2, time 0.3.51, time-core 0.1.9, time-macros 0.2.30, widestring 1.2.1 +The following license (LICENSE-APACHE) applies to: fdeflate 0.3.7, field-offset 0.3.6, half 2.7.1, image 0.25.10, miniz_oxide 0.8.9, num-conv 0.2.2, pin-project-lite 0.2.17, portable-atomic 1.14.0, portable-atomic-util 0.2.7, raw-window-handle 0.6.2, sync_wrapper 1.0.2, time 0.3.51, time-core 0.1.9, time-macros 0.2.30, widestring 1.2.1 ---------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -11397,6 +11515,61 @@ APPENDIX: How to apply the Apache License to your work. identification within third-party archives. +---------------------------------------------------------------------------- +The following license (COPYING) applies to: rav1d 1.1.0 +---------------------------------------------------------------------------- +Copyright © 2018-2019, VideoLAN and dav1d authors +Copyright © 2023-2024, VideoLAN, dav1d authors, and Internet Security Research Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +---------------------------------------------------------------------------- +The following license (LICENSE.md) applies to: raw-cpuid 11.6.0 +---------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2015 Gerd Zellweger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE-MIT.md) applies to: raw-window-handle 0.6.2 ---------------------------------------------------------------------------- @@ -13264,6 +13437,32 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (LICENSE) applies to: strum 0.26.3, strum_macros 0.26.4 +---------------------------------------------------------------------------- +MIT License + +Copyright (c) 2019 Peter Glotfelty + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE) applies to: subtle 2.6.1 ---------------------------------------------------------------------------- @@ -13690,6 +13889,132 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. +---------------------------------------------------------------------------- +The following license (LICENSE) applies to: to_method 1.1.0 +---------------------------------------------------------------------------- +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. + + ---------------------------------------------------------------------------- The following license (LICENSE) applies to: tokio 1.52.3, tokio-util 0.7.18 ---------------------------------------------------------------------------- @@ -16105,7 +16430,7 @@ DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: zerocopy 0.8.52, zerocopy-derive 0.8.52 +The following license (LICENSE-APACHE) applies to: zerocopy 0.7.35, zerocopy 0.8.52, zerocopy-derive 0.7.35, zerocopy-derive 0.8.52 ---------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -16311,7 +16636,7 @@ Apache License ---------------------------------------------------------------------------- -The following license (LICENSE-BSD) applies to: zerocopy 0.8.52, zerocopy-derive 0.8.52 +The following license (LICENSE-BSD) applies to: zerocopy 0.7.35, zerocopy 0.8.52, zerocopy-derive 0.7.35, zerocopy-derive 0.8.52 ---------------------------------------------------------------------------- Copyright 2019 The Fuchsia Authors. @@ -16340,7 +16665,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: zerocopy 0.8.52, zerocopy-derive 0.8.52 +The following license (LICENSE-MIT) applies to: zerocopy 0.7.35, zerocopy 0.8.52, zerocopy-derive 0.7.35, zerocopy-derive 0.8.52 ---------------------------------------------------------------------------- Copyright 2023 The Fuchsia Authors diff --git a/clients/apple/Sources/PunktfunkKit/Resources/THIRD-PARTY-NOTICES.txt b/clients/apple/Sources/PunktfunkKit/Resources/THIRD-PARTY-NOTICES.txt index 288d1670..d5d9ff56 100644 --- a/clients/apple/Sources/PunktfunkKit/Resources/THIRD-PARTY-NOTICES.txt +++ b/clients/apple/Sources/PunktfunkKit/Resources/THIRD-PARTY-NOTICES.txt @@ -7,7 +7,7 @@ below. Each is distributed under its own permissive license; the full license te follow the manifest. This file is generated by scripts/gen-third-party-notices.py (or `cargo about`, see about.toml) — do not edit by hand. -Total third-party crates: 566 +Total third-party crates: 590 ---------------------------------------------------------------------------- VENDORED THIRD-PARTY SOURCE (inside first-party crates) @@ -43,6 +43,7 @@ MANIFEST (crate version — SPDX license — source) asn1-rs 0.6.2 — MIT OR Apache-2.0 — https://github.com/rusticata/asn1-rs.git asn1-rs-derive 0.5.1 — MIT OR Apache-2.0 — https://github.com/rusticata/asn1-rs.git asn1-rs-impl 0.2.0 — MIT/Apache-2.0 — https://github.com/rusticata/asn1-rs.git + assert_matches 1.5.0 — MIT/Apache-2.0 — https://github.com/murarth/assert_matches async-broadcast 0.7.2 — MIT OR Apache-2.0 — https://github.com/smol-rs/async-broadcast async-channel 2.5.0 — Apache-2.0 OR MIT — https://github.com/smol-rs/async-channel async-executor 1.14.0 — Apache-2.0 OR MIT — https://github.com/smol-rs/async-executor @@ -54,6 +55,8 @@ MANIFEST (crate version — SPDX license — source) async-task 4.7.1 — Apache-2.0 OR MIT — https://github.com/smol-rs/async-task async-trait 0.1.89 — MIT OR Apache-2.0 — https://github.com/dtolnay/async-trait atomic-waker 1.1.2 — Apache-2.0 OR MIT — https://github.com/smol-rs/atomic-waker + atomig 0.4.3 — MIT/Apache-2.0 — https://github.com/LukasKalbertodt/atomig/ + atomig-macro 0.4.0 — MIT/Apache-2.0 — https://github.com/LukasKalbertodt/atomig/ audiopus_sys 0.2.2 — ISC — https://github.com/lakelezz/audiopus_sys.git autocfg 1.5.1 — Apache-2.0 OR MIT — https://github.com/cuviper/autocfg axum 0.8.9 — MIT — https://github.com/tokio-rs/axum @@ -64,6 +67,7 @@ MANIFEST (crate version — SPDX license — source) bindgen 0.72.1 — BSD-3-Clause — https://github.com/rust-lang/rust-bindgen bit-set 0.8.0 — Apache-2.0 OR MIT — https://github.com/contain-rs/bit-set bit-vec 0.8.0 — Apache-2.0 OR MIT — https://github.com/contain-rs/bit-vec + bitflags 1.3.2 — MIT/Apache-2.0 — https://github.com/bitflags/bitflags bitflags 2.13.0 — MIT OR Apache-2.0 — https://github.com/bitflags/bitflags block-buffer 0.10.4 — MIT OR Apache-2.0 — https://github.com/RustCrypto/utils block-padding 0.3.3 — MIT OR Apache-2.0 — https://github.com/RustCrypto/utils @@ -71,6 +75,7 @@ MANIFEST (crate version — SPDX license — source) bumpalo 3.20.3 — MIT OR Apache-2.0 — https://github.com/fitzgen/bumpalo bytemuck 1.25.0 — Zlib OR Apache-2.0 OR MIT — https://github.com/Lokathor/bytemuck bytemuck_derive 1.10.2 — Zlib OR Apache-2.0 OR MIT — https://github.com/Lokathor/bytemuck + byteorder 1.5.0 — Unlicense OR MIT — https://github.com/BurntSushi/byteorder byteorder-lite 0.1.0 — Unlicense OR MIT — https://github.com/image-rs/byteorder-lite bytes 1.12.0 — MIT — https://github.com/tokio-rs/bytes cairo-rs 0.22.0 — MIT — https://github.com/gtk-rs/gtk-rs-core @@ -115,6 +120,9 @@ MANIFEST (crate version — SPDX license — source) curve25519-dalek 4.1.3 — BSD-3-Clause — https://github.com/dalek-cryptography/curve25519-dalek/tree/main/curve25519-dalek curve25519-dalek-derive 0.1.1 — MIT/Apache-2.0 — https://github.com/dalek-cryptography/curve25519-dalek data-encoding 2.11.0 — MIT — https://github.com/ia0/data-encoding + defmt 1.1.1 — MIT OR Apache-2.0 — https://github.com/knurling-rs/defmt + defmt-macros 1.1.1 — MIT OR Apache-2.0 — https://github.com/knurling-rs/defmt + defmt-parser 1.0.0 — MIT OR Apache-2.0 — https://github.com/knurling-rs/defmt der 0.7.10 — Apache-2.0 OR MIT — https://github.com/RustCrypto/formats/tree/master/der der-parser 9.0.0 — MIT/Apache-2.0 — https://github.com/rusticata/der-parser.git deranged 0.5.8 — MIT OR Apache-2.0 — https://github.com/jhpratt/deranged @@ -126,6 +134,8 @@ MANIFEST (crate version — SPDX license — source) enumflags2 0.7.12 — MIT OR Apache-2.0 — https://github.com/meithecatte/enumflags2 enumflags2_derive 0.7.12 — MIT OR Apache-2.0 — https://github.com/meithecatte/enumflags2 env_filter 0.1.4 — MIT OR Apache-2.0 — https://github.com/rust-cli/env_logger + env_filter 2.0.0 — MIT OR Apache-2.0 — https://github.com/rust-cli/env_logger + env_logger 0.11.11 — MIT OR Apache-2.0 — https://github.com/rust-cli/env_logger equivalent 1.0.2 — Apache-2.0 OR MIT — https://github.com/indexmap-rs/equivalent errno 0.3.14 — MIT OR Apache-2.0 — https://github.com/lambda-fairy/rust-errno event-listener 5.4.1 — Apache-2.0 OR MIT — https://github.com/smol-rs/event-listener @@ -221,6 +231,9 @@ MANIFEST (crate version — SPDX license — source) itertools 0.10.5 — MIT/Apache-2.0 — https://github.com/rust-itertools/itertools itertools 0.13.0 — MIT OR Apache-2.0 — https://github.com/rust-itertools/itertools itoa 1.0.18 — MIT OR Apache-2.0 — https://github.com/dtolnay/itoa + jiff 0.2.35 — Unlicense OR MIT — https://github.com/BurntSushi/jiff + jiff-core 0.1.0 — Unlicense OR MIT — https://github.com/BurntSushi/jiff + jiff-static 0.2.35 — Unlicense OR MIT — https://github.com/BurntSushi/jiff jni 0.21.1 — MIT/Apache-2.0 — https://github.com/jni-rs/jni-rs jni-sys 0.3.1 — MIT OR Apache-2.0 — https://github.com/jni-rs/jni-sys jni-sys 0.4.1 — MIT OR Apache-2.0 — https://github.com/jni-rs/jni-sys @@ -304,6 +317,8 @@ MANIFEST (crate version — SPDX license — source) polling 3.11.0 — Apache-2.0 OR MIT — https://github.com/smol-rs/polling poly1305 0.8.0 — Apache-2.0 OR MIT — https://github.com/RustCrypto/universal-hashes polyval 0.6.2 — Apache-2.0 OR MIT — https://github.com/RustCrypto/universal-hashes + portable-atomic 1.14.0 — Apache-2.0 OR MIT — https://github.com/taiki-e/portable-atomic + portable-atomic-util 0.2.7 — Apache-2.0 OR MIT — https://github.com/taiki-e/portable-atomic-util potential_utf 0.1.5 — Unicode-3.0 — https://github.com/unicode-org/icu4x powerfmt 0.2.0 — MIT OR Apache-2.0 — https://github.com/jhpratt/powerfmt ppv-lite86 0.2.21 — MIT OR Apache-2.0 — https://github.com/cryptocorrosion/cryptocorrosion @@ -327,6 +342,8 @@ MANIFEST (crate version — SPDX license — source) rand_core 0.6.4 — MIT OR Apache-2.0 — https://github.com/rust-random/rand rand_core 0.9.5 — MIT OR Apache-2.0 — https://github.com/rust-random/rand rand_xorshift 0.4.0 — MIT OR Apache-2.0 — https://github.com/rust-random/rngs + rav1d 1.1.0 — BSD-2-Clause — https://github.com/memorysafety/rav1d + raw-cpuid 11.6.0 — MIT — https://github.com/gz/rust-cpuid raw-window-handle 0.6.2 — MIT OR Apache-2.0 OR Zlib — https://github.com/rust-windowing/raw-window-handle rcgen 0.13.2 — MIT OR Apache-2.0 — https://github.com/rustls/rcgen readme-rustdocifier 0.1.1 — MIT — https://github.com/malaire/readme-rustdocifier @@ -404,6 +421,8 @@ MANIFEST (crate version — SPDX license — source) sqlite-wasm-rs 0.5.5 — MIT — https://github.com/Spxg/sqlite-wasm-rs stable_deref_trait 1.2.1 — MIT OR Apache-2.0 — https://github.com/storyyeller/stable_deref_trait strsim 0.11.1 — MIT — https://github.com/rapidfuzz/strsim-rs + strum 0.26.3 — MIT — https://github.com/Peternator7/strum + strum_macros 0.26.4 — MIT — https://github.com/Peternator7/strum subtle 2.6.1 — BSD-3-Clause — https://github.com/dalek-cryptography/subtle syn 2.0.118 — MIT OR Apache-2.0 — https://github.com/dtolnay/syn sync_wrapper 1.0.2 — Apache-2.0 — https://github.com/Actyx/sync_wrapper @@ -425,6 +444,7 @@ MANIFEST (crate version — SPDX license — source) tinytemplate 1.2.1 — Apache-2.0 OR MIT — https://github.com/bheisler/TinyTemplate tinyvec 1.11.0 — Zlib OR Apache-2.0 OR MIT — https://github.com/Lokathor/tinyvec tinyvec_macros 0.1.1 — MIT OR Apache-2.0 OR Zlib — https://github.com/Soveu/tinyvec_macros + to_method 1.1.0 — CC0-1.0 — https://github.com/whentze/to_method tokio 1.52.3 — MIT — https://github.com/tokio-rs/tokio tokio-macros 2.7.0 — MIT — https://github.com/tokio-rs/tokio tokio-rustls 0.26.4 — MIT OR Apache-2.0 — https://github.com/rustls/tokio-rustls @@ -449,6 +469,7 @@ MANIFEST (crate version — SPDX license — source) tracing-log 0.2.0 — MIT — https://github.com/tokio-rs/tracing tracing-subscriber 0.3.23 — MIT — https://github.com/tokio-rs/tracing typenum 1.20.1 — MIT OR Apache-2.0 — https://github.com/paholg/typenum + uac-host 0.1.0 — MIT OR Apache-2.0 — https://github.com/unom-io/usbfs-iso uds_windows 1.2.1 — MIT — https://github.com/haraldh/rust_uds_windows unarray 0.1.4 — MIT OR Apache-2.0 — https://github.com/cameron1024/unarray unicode-ident 1.0.24 — (MIT OR Apache-2.0) AND Unicode-3.0 — https://github.com/dtolnay/unicode-ident @@ -458,6 +479,7 @@ MANIFEST (crate version — SPDX license — source) untrusted 0.9.0 — ISC — https://github.com/briansmith/untrusted ureq 2.12.1 — MIT OR Apache-2.0 — https://github.com/algesten/ureq url 2.5.8 — MIT OR Apache-2.0 — https://github.com/servo/rust-url + usbfs-iso 0.1.0 — MIT OR Apache-2.0 — https://github.com/unom-io/usbfs-iso utf8_iter 1.0.4 — Apache-2.0 OR MIT — https://github.com/hsivonen/utf8_iter utf8parse 0.2.2 — Apache-2.0 OR MIT — https://github.com/alacritty/vte utoipa 5.5.0 — MIT OR Apache-2.0 — https://github.com/juhaku/utoipa @@ -575,7 +597,9 @@ MANIFEST (crate version — SPDX license — source) zbus 5.16.0 — MIT — https://github.com/z-galaxy/zbus/ zbus_macros 5.16.0 — MIT — https://github.com/z-galaxy/zbus/ zbus_names 4.3.2 — MIT — https://github.com/z-galaxy/zbus/ + zerocopy 0.7.35 — BSD-2-Clause OR Apache-2.0 OR MIT — https://github.com/google/zerocopy zerocopy 0.8.52 — BSD-2-Clause OR Apache-2.0 OR MIT — https://github.com/google/zerocopy + zerocopy-derive 0.7.35 — BSD-2-Clause OR Apache-2.0 OR MIT — https://github.com/google/zerocopy zerocopy-derive 0.8.52 — BSD-2-Clause OR Apache-2.0 OR MIT — https://github.com/google/zerocopy zerofrom 0.1.8 — Unicode-3.0 — https://github.com/unicode-org/icu4x zerofrom-derive 0.1.7 — Unicode-3.0 — https://github.com/unicode-org/icu4x @@ -595,7 +619,9 @@ Crates whose package did not embed a license file (SPDX + source only) ---------------------------------------------------------------------------- anes 0.1.6 — MIT OR Apache-2.0 — https://github.com/zrzka/anes-rs asn1-rs-impl 0.2.0 — MIT/Apache-2.0 — https://github.com/rusticata/asn1-rs.git + atomig-macro 0.4.0 — MIT/Apache-2.0 — https://github.com/LukasKalbertodt/atomig/ cookie-factory 0.3.3 — MIT — https://github.com/rust-bakery/cookie-factory + defmt-parser 1.0.0 — MIT OR Apache-2.0 — https://github.com/knurling-rs/defmt ffmpeg-sys-next 8.1.0 — WTFPL — https://github.com/zmwangx/rust-ffmpeg-sys jni-sys-macros 0.4.1 — MIT OR Apache-2.0 — https://github.com/jni-rs/jni-sys ndk 0.9.0 — MIT OR Apache-2.0 — https://github.com/rust-mobile/ndk @@ -612,6 +638,8 @@ Crates whose package did not embed a license file (SPDX + source only) skia-bindings 0.87.0 — MIT — https://github.com/rust-skia/rust-skia skia-safe 0.87.0 — MIT — https://github.com/rust-skia/rust-skia test_reactor 0.0.0 — UNKNOWN + uac-host 0.1.0 — MIT OR Apache-2.0 — https://github.com/unom-io/usbfs-iso + usbfs-iso 0.1.0 — MIT OR Apache-2.0 — https://github.com/unom-io/usbfs-iso valuable 0.1.1 — MIT — https://github.com/tokio-rs/valuable winapi-i686-pc-windows-gnu 0.4.0 — MIT/Apache-2.0 — https://github.com/retep998/winapi-rs winapi-x86_64-pc-windows-gnu 0.4.0 — MIT/Apache-2.0 — https://github.com/retep998/winapi-rs @@ -845,7 +873,7 @@ limitations under the License. ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: adler2 2.0.1, anyhow 1.0.103, async-channel 2.5.0, async-executor 1.14.0, async-io 2.6.0, async-lock 3.4.2, async-process 2.5.0, async-recursion 1.1.1, async-signal 0.2.14, async-task 4.7.1, async-trait 0.1.89, atomic-waker 1.1.2, blocking 1.6.2, concurrent-queue 2.5.0, curve25519-dalek-derive 0.1.1, displaydoc 0.2.6, endi 1.1.1, event-listener 5.4.1, event-listener-strategy 0.5.4, fastrand 2.4.1, flume 0.12.0, fs-err 3.3.0, futures-lite 2.6.1, hermit-abi 0.5.2, is-terminal 0.4.17, itoa 1.0.18, khronos-egl 6.0.0, linux-raw-sys 0.12.1, minimal-lexical 0.2.1, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, ordered-stream 0.2.0, parking 2.2.1, paste 1.0.15, pastey 0.2.3, pin-project-lite 0.2.17, piper 0.2.5, polling 3.11.0, prettyplease 0.2.37, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.46, reis 0.6.1, relm4 0.11.0, relm4-css 0.11.0, relm4-macros 0.11.0, rsa 0.9.10, rustc-hash 2.1.2, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, serde 1.0.228, serde_core 1.0.228, serde_derive 1.0.228, serde_json 1.0.150, serde_path_to_error 0.1.20, serde_repr 0.1.20, syn 2.0.118, system-deps 7.0.8, thiserror 1.0.69, thiserror 2.0.18, thiserror-impl 1.0.69, thiserror-impl 2.0.18, unicode-ident 1.0.24, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wit-bindgen 0.57.1, zmij 1.0.21, zvariant_utils 3.4.0 +The following license (LICENSE-MIT) applies to: adler2 2.0.1, anyhow 1.0.103, async-channel 2.5.0, async-executor 1.14.0, async-io 2.6.0, async-lock 3.4.2, async-process 2.5.0, async-recursion 1.1.1, async-signal 0.2.14, async-task 4.7.1, async-trait 0.1.89, atomic-waker 1.1.2, blocking 1.6.2, concurrent-queue 2.5.0, curve25519-dalek-derive 0.1.1, displaydoc 0.2.6, endi 1.1.1, event-listener 5.4.1, event-listener-strategy 0.5.4, fastrand 2.4.1, flume 0.12.0, fs-err 3.3.0, futures-lite 2.6.1, hermit-abi 0.5.2, is-terminal 0.4.17, itoa 1.0.18, khronos-egl 6.0.0, linux-raw-sys 0.12.1, minimal-lexical 0.2.1, num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, ordered-stream 0.2.0, parking 2.2.1, paste 1.0.15, pastey 0.2.3, pin-project-lite 0.2.17, piper 0.2.5, polling 3.11.0, portable-atomic 1.14.0, portable-atomic-util 0.2.7, prettyplease 0.2.37, proc-macro-crate 3.5.0, proc-macro2 1.0.106, quote 1.0.46, reis 0.6.1, relm4 0.11.0, relm4-css 0.11.0, relm4-macros 0.11.0, rsa 0.9.10, rustc-hash 2.1.2, rustix 1.1.4, rustversion 1.0.22, semver 1.0.28, serde 1.0.228, serde_core 1.0.228, serde_derive 1.0.228, serde_json 1.0.150, serde_path_to_error 0.1.20, serde_repr 0.1.20, syn 2.0.118, system-deps 7.0.8, thiserror 1.0.69, thiserror 2.0.18, thiserror-impl 1.0.69, thiserror-impl 2.0.18, unicode-ident 1.0.24, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wit-bindgen 0.57.1, zmij 1.0.21, zvariant_utils 3.4.0 ---------------------------------------------------------------------------- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -1170,7 +1198,7 @@ DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (COPYING) applies to: aho-corasick 1.1.4, memchr 2.8.2, same-file 1.0.6, walkdir 2.5.0, winapi-util 0.1.11 +The following license (COPYING) applies to: aho-corasick 1.1.4, byteorder 1.5.0, jiff 0.2.35, jiff-core 0.1.0, jiff-static 0.2.35, memchr 2.8.2, same-file 1.0.6, walkdir 2.5.0, winapi-util 0.1.11 ---------------------------------------------------------------------------- This project is dual-licensed under the Unlicense and MIT licenses. @@ -1178,7 +1206,7 @@ You may use this code under the terms of either license. ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: aho-corasick 1.1.4, byteorder-lite 0.1.0, memchr 2.8.2, walkdir 2.5.0 +The following license (LICENSE-MIT) applies to: aho-corasick 1.1.4, byteorder 1.5.0, byteorder-lite 0.1.0, jiff 0.2.35, jiff-core 0.1.0, jiff-static 0.2.35, memchr 2.8.2, walkdir 2.5.0 ---------------------------------------------------------------------------- The MIT License (MIT) @@ -1204,7 +1232,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (UNLICENSE) applies to: aho-corasick 1.1.4, byteorder-lite 0.1.0, ksni 0.3.5, memchr 2.8.2, same-file 1.0.6, walkdir 2.5.0, winapi-util 0.1.11 +The following license (UNLICENSE) applies to: aho-corasick 1.1.4, byteorder 1.5.0, byteorder-lite 0.1.0, jiff 0.2.35, jiff-core 0.1.0, jiff-static 0.2.35, ksni 0.3.5, memchr 2.8.2, same-file 1.0.6, walkdir 2.5.0, winapi-util 0.1.11 ---------------------------------------------------------------------------- This is free and unencumbered software released into the public domain. @@ -1693,7 +1721,7 @@ SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: annotate-snippets 0.11.5, anstream 1.0.0, anstyle 1.0.14, anstyle-parse 1.0.0, anstyle-query 1.1.5, anstyle-wincon 3.0.11, clap 4.6.1, clap_builder 4.6.0, clap_lex 1.1.0, colorchoice 1.0.5, crc32fast 1.5.0, env_filter 0.1.4, fallible-iterator 0.3.0, fallible-streaming-iterator 0.1.9, hex 0.4.3, is_terminal_polyfill 1.70.2, jni-sys 0.3.1, jni-sys 0.4.1, once_cell_polyfill 1.70.2, quick-error 1.2.3, serde_spanned 0.6.9, serde_spanned 1.1.1, toml 0.8.23, toml 0.9.12+spec-1.1.0, toml 1.1.2+spec-1.1.0, toml_datetime 0.6.11, toml_datetime 0.7.5+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.22.27, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_write 0.1.2, toml_writer 1.1.1+spec-1.1.0, winapi 0.3.9 +The following license (LICENSE-APACHE) applies to: annotate-snippets 0.11.5, anstream 1.0.0, anstyle 1.0.14, anstyle-parse 1.0.0, anstyle-query 1.1.5, anstyle-wincon 3.0.11, clap 4.6.1, clap_builder 4.6.0, clap_lex 1.1.0, colorchoice 1.0.5, crc32fast 1.5.0, env_filter 0.1.4, env_filter 2.0.0, env_logger 0.11.11, fallible-iterator 0.3.0, fallible-streaming-iterator 0.1.9, hex 0.4.3, is_terminal_polyfill 1.70.2, jni-sys 0.3.1, jni-sys 0.4.1, once_cell_polyfill 1.70.2, quick-error 1.2.3, serde_spanned 0.6.9, serde_spanned 1.1.1, toml 0.8.23, toml 0.9.12+spec-1.1.0, toml 1.1.2+spec-1.1.0, toml_datetime 0.6.11, toml_datetime 0.7.5+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.22.27, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_write 0.1.2, toml_writer 1.1.1+spec-1.1.0, winapi 0.3.9 ---------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -1899,7 +1927,7 @@ Apache License ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: annotate-snippets 0.11.5, anstream 1.0.0, anstyle 1.0.14, anstyle-parse 1.0.0, anstyle-query 1.1.5, anstyle-wincon 3.0.11, clap 4.6.1, clap_builder 4.6.0, clap_lex 1.1.0, colorchoice 1.0.5, env_filter 0.1.4, is_terminal_polyfill 1.70.2, once_cell_polyfill 1.70.2, serde_spanned 0.6.9, serde_spanned 1.1.1, toml 0.8.23, toml 0.9.12+spec-1.1.0, toml 1.1.2+spec-1.1.0, toml_datetime 0.6.11, toml_datetime 0.7.5+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.22.27, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_write 0.1.2, toml_writer 1.1.1+spec-1.1.0 +The following license (LICENSE-MIT) applies to: annotate-snippets 0.11.5, anstream 1.0.0, anstyle 1.0.14, anstyle-parse 1.0.0, anstyle-query 1.1.5, anstyle-wincon 3.0.11, clap 4.6.1, clap_builder 4.6.0, clap_lex 1.1.0, colorchoice 1.0.5, env_filter 0.1.4, env_filter 2.0.0, env_logger 0.11.11, is_terminal_polyfill 1.70.2, once_cell_polyfill 1.70.2, serde_spanned 0.6.9, serde_spanned 1.1.1, toml 0.8.23, toml 0.9.12+spec-1.1.0, toml 1.1.2+spec-1.1.0, toml_datetime 0.6.11, toml_datetime 0.7.5+spec-1.1.0, toml_datetime 1.1.1+spec-1.1.0, toml_edit 0.22.27, toml_edit 0.25.12+spec-1.1.0, toml_parser 1.1.2+spec-1.1.0, toml_write 0.1.2, toml_writer 1.1.1+spec-1.1.0 ---------------------------------------------------------------------------- Copyright (c) Individual contributors @@ -2232,7 +2260,7 @@ SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: asn1-rs 0.6.2, asn1-rs-derive 0.5.1, async-channel 2.5.0, async-executor 1.14.0, async-io 2.6.0, async-lock 3.4.2, async-process 2.5.0, async-recursion 1.1.1, async-signal 0.2.14, async-task 4.7.1, atomic-waker 1.1.2, autocfg 1.5.1, base64 0.22.1, bitflags 2.13.0, blocking 1.6.2, bumpalo 3.20.3, cast 0.3.0, cc 1.2.65, cexpr 0.6.0, cfg-if 1.0.4, cmake 0.1.58, concurrent-queue 2.5.0, core-foundation 0.10.1, core-foundation-sys 0.8.7, criterion 0.5.1, criterion-plot 0.5.0, crossbeam-utils 0.8.21, curve25519-dalek-derive 0.1.1, der-parser 9.0.0, displaydoc 0.2.6, either 1.16.0, equivalent 1.0.2, errno 0.3.14, event-listener 5.4.1, event-listener-strategy 0.5.4, fastrand 2.4.1, filetime 0.2.29, find-msvc-tools 0.1.9, fixedbitset 0.5.7, flate2 1.1.9, fnv 1.0.7, form_urlencoded 1.2.2, fs-err 3.3.0, futures-lite 2.6.1, gethostname 1.1.0, gif 0.14.2, glob 0.3.3, hashbrown 0.16.1, hashbrown 0.17.1, heck 0.5.0, hermit-abi 0.5.2, httparse 1.10.1, idna 1.1.0, idna_adapter 1.2.2, indexmap 2.14.0, itertools 0.10.5, itertools 0.13.0, jni 0.21.1, jobserver 0.1.34, js-sys 0.3.103, khronos-egl 6.0.0, lazy_static 1.5.0, linux-raw-sys 0.12.1, lock_api 0.4.14, log 0.4.33, mime 0.3.17, nasm-rs 0.3.2, num-bigint 0.4.6, num-bigint-dig 0.8.6, num-derive 0.4.2, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, num_cpus 1.17.0, oid-registry 0.7.1, once_cell 1.21.4, openssl-probe 0.2.1, opus 0.3.1, ordered-stream 0.2.0, parking 2.2.1, parking_lot 0.12.5, parking_lot_core 0.9.12, percent-encoding 2.3.2, piper 0.2.5, pkg-config 0.3.33, png 0.18.1, polling 3.11.0, proptest 1.11.0, regex 1.12.4, regex-automata 0.4.14, regex-syntax 0.8.11, roxmltree 0.21.1, rsa 0.9.10, rustc_version 0.4.1, rusticata-macros 4.1.0, rustix 1.1.4, rustls 0.23.41, rustls-native-certs 0.8.4, rusty-fork 0.3.1, scopeguard 1.2.0, security-framework 3.7.0, security-framework-sys 2.17.0, signal-hook-registry 1.4.8, smallvec 1.15.2, socket2 0.6.4, stable_deref_trait 1.2.1, system-deps 7.0.8, tar 0.4.46, tempfile 3.27.0, thread_local 1.1.9, tinytemplate 1.2.1, unicode-segmentation 1.13.3, unicode-width 0.2.2, url 2.5.8, uuid 1.23.4, vcpkg 0.2.15, version_check 0.9.5, wait-timeout 0.2.1, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wasm-bindgen 0.2.126, wasm-bindgen-macro 0.2.126, wasm-bindgen-macro-support 0.2.126, wasm-bindgen-shared 0.2.126, weezl 0.1.12, wit-bindgen 0.57.1, x509-parser 0.16.0, xattr 1.6.1 +The following license (LICENSE-APACHE) applies to: asn1-rs 0.6.2, asn1-rs-derive 0.5.1, assert_matches 1.5.0, async-channel 2.5.0, async-executor 1.14.0, async-io 2.6.0, async-lock 3.4.2, async-process 2.5.0, async-recursion 1.1.1, async-signal 0.2.14, async-task 4.7.1, atomic-waker 1.1.2, autocfg 1.5.1, base64 0.22.1, bitflags 1.3.2, bitflags 2.13.0, blocking 1.6.2, bumpalo 3.20.3, cast 0.3.0, cc 1.2.65, cexpr 0.6.0, cfg-if 1.0.4, cmake 0.1.58, concurrent-queue 2.5.0, core-foundation 0.10.1, core-foundation-sys 0.8.7, criterion 0.5.1, criterion-plot 0.5.0, crossbeam-utils 0.8.21, curve25519-dalek-derive 0.1.1, der-parser 9.0.0, displaydoc 0.2.6, either 1.16.0, equivalent 1.0.2, errno 0.3.14, event-listener 5.4.1, event-listener-strategy 0.5.4, fastrand 2.4.1, filetime 0.2.29, find-msvc-tools 0.1.9, fixedbitset 0.5.7, flate2 1.1.9, fnv 1.0.7, form_urlencoded 1.2.2, fs-err 3.3.0, futures-lite 2.6.1, gethostname 1.1.0, gif 0.14.2, glob 0.3.3, hashbrown 0.16.1, hashbrown 0.17.1, heck 0.5.0, hermit-abi 0.5.2, httparse 1.10.1, idna 1.1.0, idna_adapter 1.2.2, indexmap 2.14.0, itertools 0.10.5, itertools 0.13.0, jni 0.21.1, jobserver 0.1.34, js-sys 0.3.103, khronos-egl 6.0.0, lazy_static 1.5.0, linux-raw-sys 0.12.1, lock_api 0.4.14, log 0.4.33, mime 0.3.17, nasm-rs 0.3.2, num-bigint 0.4.6, num-bigint-dig 0.8.6, num-derive 0.4.2, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, num_cpus 1.17.0, oid-registry 0.7.1, once_cell 1.21.4, openssl-probe 0.2.1, opus 0.3.1, ordered-stream 0.2.0, parking 2.2.1, parking_lot 0.12.5, parking_lot_core 0.9.12, percent-encoding 2.3.2, piper 0.2.5, pkg-config 0.3.33, png 0.18.1, polling 3.11.0, proptest 1.11.0, regex 1.12.4, regex-automata 0.4.14, regex-syntax 0.8.11, roxmltree 0.21.1, rsa 0.9.10, rustc_version 0.4.1, rusticata-macros 4.1.0, rustix 1.1.4, rustls 0.23.41, rustls-native-certs 0.8.4, rusty-fork 0.3.1, scopeguard 1.2.0, security-framework 3.7.0, security-framework-sys 2.17.0, signal-hook-registry 1.4.8, smallvec 1.15.2, socket2 0.6.4, stable_deref_trait 1.2.1, system-deps 7.0.8, tar 0.4.46, tempfile 3.27.0, thread_local 1.1.9, tinytemplate 1.2.1, unicode-segmentation 1.13.3, unicode-width 0.2.2, url 2.5.8, uuid 1.23.4, vcpkg 0.2.15, version_check 0.9.5, wait-timeout 0.2.1, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wasm-bindgen 0.2.126, wasm-bindgen-macro 0.2.126, wasm-bindgen-macro-support 0.2.126, wasm-bindgen-shared 0.2.126, weezl 0.1.12, wit-bindgen 0.57.1, x509-parser 0.16.0, xattr 1.6.1 ---------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -2467,6 +2495,36 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (LICENSE-MIT) applies to: assert_matches 1.5.0 +---------------------------------------------------------------------------- +Copyright (c) 2016 Murarth + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE-APACHE) applies to: async-broadcast 0.7.2 ---------------------------------------------------------------------------- @@ -2738,6 +2796,242 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (LICENSE-APACHE) applies to: atomig 0.4.3, bit-set 0.8.0, bit-vec 0.8.0, cfg-expr 0.20.8, defmt 1.1.1, defmt-macros 1.1.1, downcast-rs 1.2.1, hashlink 0.12.0, minimal-lexical 0.2.1 +---------------------------------------------------------------------------- +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +---------------------------------------------------------------------------- +The following license (LICENSE-MIT) applies to: atomig 0.4.3 +---------------------------------------------------------------------------- +Copyright (c) 2016 Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE.md) applies to: audiopus_sys 0.2.2 ---------------------------------------------------------------------------- @@ -2984,212 +3278,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: bit-set 0.8.0, bit-vec 0.8.0, cfg-expr 0.20.8, downcast-rs 1.2.1, hashlink 0.12.0, minimal-lexical 0.2.1 ----------------------------------------------------------------------------- -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - - ---------------------------------------------------------------------------- The following license (LICENSE-MIT) applies to: bit-set 0.8.0, bit-vec 0.8.0 ---------------------------------------------------------------------------- @@ -3221,7 +3309,7 @@ DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: bitflags 2.13.0, glob 0.3.3, log 0.4.33, num-bigint 0.4.6, num-bigint-dig 0.8.6, num-derive 0.4.2, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, regex 1.12.4, regex-automata 0.4.14, regex-syntax 0.8.11 +The following license (LICENSE-MIT) applies to: bitflags 1.3.2, bitflags 2.13.0, glob 0.3.3, log 0.4.33, num-bigint 0.4.6, num-bigint-dig 0.8.6, num-derive 0.4.2, num-integer 0.1.46, num-iter 0.1.45, num-traits 0.2.19, regex 1.12.4, regex-automata 0.4.14, regex-syntax 0.8.11 ---------------------------------------------------------------------------- Copyright (c) 2014 The Rust Project Developers @@ -5153,6 +5241,36 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (LICENSE-MIT) applies to: defmt 1.1.1, defmt-macros 1.1.1 +---------------------------------------------------------------------------- +Copyright (c) Ferrous Systems + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE-MIT) applies to: der 0.7.10, pkcs8 0.10.2 ---------------------------------------------------------------------------- @@ -5848,7 +5966,7 @@ DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: fdeflate 0.3.7, field-offset 0.3.6, half 2.7.1, image 0.25.10, miniz_oxide 0.8.9, num-conv 0.2.2, pin-project-lite 0.2.17, raw-window-handle 0.6.2, sync_wrapper 1.0.2, time 0.3.51, time-core 0.1.9, time-macros 0.2.30, widestring 1.2.1 +The following license (LICENSE-APACHE) applies to: fdeflate 0.3.7, field-offset 0.3.6, half 2.7.1, image 0.25.10, miniz_oxide 0.8.9, num-conv 0.2.2, pin-project-lite 0.2.17, portable-atomic 1.14.0, portable-atomic-util 0.2.7, raw-window-handle 0.6.2, sync_wrapper 1.0.2, time 0.3.51, time-core 0.1.9, time-macros 0.2.30, widestring 1.2.1 ---------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -11397,6 +11515,61 @@ APPENDIX: How to apply the Apache License to your work. identification within third-party archives. +---------------------------------------------------------------------------- +The following license (COPYING) applies to: rav1d 1.1.0 +---------------------------------------------------------------------------- +Copyright © 2018-2019, VideoLAN and dav1d authors +Copyright © 2023-2024, VideoLAN, dav1d authors, and Internet Security Research Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +---------------------------------------------------------------------------- +The following license (LICENSE.md) applies to: raw-cpuid 11.6.0 +---------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2015 Gerd Zellweger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE-MIT.md) applies to: raw-window-handle 0.6.2 ---------------------------------------------------------------------------- @@ -13264,6 +13437,32 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------------- +The following license (LICENSE) applies to: strum 0.26.3, strum_macros 0.26.4 +---------------------------------------------------------------------------- +MIT License + +Copyright (c) 2019 Peter Glotfelty + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + ---------------------------------------------------------------------------- The following license (LICENSE) applies to: subtle 2.6.1 ---------------------------------------------------------------------------- @@ -13690,6 +13889,132 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. +---------------------------------------------------------------------------- +The following license (LICENSE) applies to: to_method 1.1.0 +---------------------------------------------------------------------------- +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. + + ---------------------------------------------------------------------------- The following license (LICENSE) applies to: tokio 1.52.3, tokio-util 0.7.18 ---------------------------------------------------------------------------- @@ -16105,7 +16430,7 @@ DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------- -The following license (LICENSE-APACHE) applies to: zerocopy 0.8.52, zerocopy-derive 0.8.52 +The following license (LICENSE-APACHE) applies to: zerocopy 0.7.35, zerocopy 0.8.52, zerocopy-derive 0.7.35, zerocopy-derive 0.8.52 ---------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -16311,7 +16636,7 @@ Apache License ---------------------------------------------------------------------------- -The following license (LICENSE-BSD) applies to: zerocopy 0.8.52, zerocopy-derive 0.8.52 +The following license (LICENSE-BSD) applies to: zerocopy 0.7.35, zerocopy 0.8.52, zerocopy-derive 0.7.35, zerocopy-derive 0.8.52 ---------------------------------------------------------------------------- Copyright 2019 The Fuchsia Authors. @@ -16340,7 +16665,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -The following license (LICENSE-MIT) applies to: zerocopy 0.8.52, zerocopy-derive 0.8.52 +The following license (LICENSE-MIT) applies to: zerocopy 0.7.35, zerocopy 0.8.52, zerocopy-derive 0.7.35, zerocopy-derive 0.8.52 ---------------------------------------------------------------------------- Copyright 2023 The Fuchsia Authors diff --git a/clients/session/src/main.rs b/clients/session/src/main.rs index 5abfa882..1670a278 100644 --- a/clients/session/src/main.rs +++ b/clients/session/src/main.rs @@ -346,6 +346,10 @@ mod session_main { bitrate_kbps: settings.bitrate_kbps, audio_channels: settings.audio_channels, preferred_codec: settings.preferred_codec(), + // Nothing excluded on a fresh dial. Only the run loop's codec-fallback retry + // sets this, and it does so on a CLONE of these params — a Settings-level + // "never use HEVC" would be `preferred_codec`, not this. + exclude_codecs: 0, // HDR off = don't advertise 10-bit/HDR at all; the host then never upgrades. // MULTI_SLICE is decoder truth for THIS embedder: every desktop decode stack // (FFmpeg software, VAAPI, D3D11VA, Vulkan Video) handles AUs carrying several @@ -357,12 +361,14 @@ mod session_main { // HEVC, and a real GPU 4:4:4 encode probe, and answers the resolved chroma in the // Welcome BEFORE we build a decoder. Advertised whenever the user asks because // every path can DISPLAY it: the Vulkan presenter samples the 2-plane 4:4:4 pool - // formats (hardware RExt decode where the driver offers it — NVIDIA today) and - // swscale converts anything else for the software rung, with the decoder ladder - // demoting on its own. No capability probe gates the bit — software decode is the - // guaranteed floor — but the cost is VISIBLE, not silent: the Detailed stats - // overlay prints the resolved chroma ("4:4:4→4:2:0" when the host declined) and - // the decode path frames actually took. + // formats (hardware RExt decode where the driver offers it — NVIDIA today), + // with the decoder ladder demoting on its own. No capability probe gates the + // bit — but note (M8) that the software rung below it is 4:2:0 8-bit ONLY and + // refuses anything else rather than mis-scaling it, so on a box whose hardware + // 4:4:4 decode fails the floor is a codec fallback, not a converted picture. + // The cost stays VISIBLE, not silent: the Detailed stats overlay prints the + // resolved chroma ("4:4:4→4:2:0" when the host declined) and the decode path + // frames actually took. video_caps: punktfunk_core::quic::VIDEO_CAP_MULTI_SLICE | if settings.hdr_enabled { punktfunk_core::quic::VIDEO_CAP_10BIT | punktfunk_core::quic::VIDEO_CAP_HDR diff --git a/crates/pf-client-core/Cargo.toml b/crates/pf-client-core/Cargo.toml index 4c42bc45..76b011ac 100644 --- a/crates/pf-client-core/Cargo.toml +++ b/crates/pf-client-core/Cargo.toml @@ -23,8 +23,68 @@ pf-ffvk = { path = "../pf-ffvk" } # video_vk_native.rs, running pf-vkdecode's VkH264Decoder/VkH265Decoder/VkAv1Decoder on # the presenter's shared device. pf-vkdecode = { path = "../pf-vkdecode" } +# The one bitstream parser (M1): the SOFTWARE rung reads its per-picture colour +# signalling, IDR flag and recovery-point SEI from the same `AuPlan` every hardware rung +# already submits from (`video_software.rs`). That is what makes the swscale BT.601 +# default unrepresentable rather than merely fixed — there is no second colour source +# left to disagree with. +pf-bitstream = { path = "../pf-bitstream" } async-channel = "2" +# M8's software rung, the ladder's last one — no FFmpeg in either half. +# +# H.264: openh264 (BSD-2), already a workspace dependency (the HOST's GPU-less encoder, +# `pf-encode/src/enc/sw.rs`), so the licence posture and the bundled-source build are +# both already settled and already compiled by every `--workspace` leg. +# +# AV1: rav1d (BSD-2) — dav1d itself, ported to Rust by the ISRG/Prossimo memory-safety +# project. The plan of record names "dav1d"; the `dav1d` crate reaches it through +# `dav1d-sys`, which is `system-deps`-only (no vendored build): it needs `dav1d.pc` + +# headers at build time and `libdav1d.so`/`dav1d.dll` at run time on EVERY client +# package. That is a new system codec dependency added by the milestone family whose +# §6 excision checklist exists to delete exactly those. rav1d is the same decoder with +# none of that: pure Rust, no linker, nothing new in any package. +# +# ⚠ Both of these are NEW COMPILE COST on the client packaging legs, which is easy to +# miss because the workspace already built openh264: every client leg is `-p`-scoped and +# excludes pf-encode (flatpak's `cargo build -p punktfunk-client-linux -p +# punktfunk-client-session -p punktfunk-cli`, windows.yml/windows-msix.yml's +# `-p punktfunk-client-windows …`, deb.yml's client job, packaging/nix's +# `punktfunk-client`), so all of them compile the bundled OpenH264 tree for the FIRST +# time here. Only the `--workspace` CI legs and the host packages built it before. +# +# `default-features = false` drops two things deliberately: +# * `asm` — rav1d's hand-written assembly needs `nasm` at build time, and it is NOT the +# same trade openh264 makes next to it: openh264-sys2's `try_compile_nasm` returns +# quietly when nasm is missing ("Failed to compile NASM files, not using any +# assembly") and the C build still succeeds, whereas rav1d's build.rs PANICS ("NASM +# build failed. Make sure you have nasm installed or disable the \"asm\" feature"). +# So turning `asm` on makes nasm a hard build requirement of every client package, +# and `ci/rust-ci.Dockerfile` — the container the client .deb and the workspace CI +# build in — does not have it (arch, rpm, nix and the FFmpeg-building noble image +# all do; the flatpak GNOME SDK and the Windows runner are not provisioned by +# anything in this tree). Making the rung that only ever runs BECAUSE the GPU +# already failed a build-breaker for the legs that ship it is the wrong way round. +# Turn it back on the day every client leg provisions nasm — and expect a large +# speedup when you do; this is dav1d's asm, and the Rust fallbacks are much slower. +# * `bitdepth_16` — the CPU rung is 8-bit by contract (`video_software.rs` refuses +# anything else rather than mis-scaling it), so building the 10/12-bit half would be +# compiling a path the code refuses to take. +# +# One packaging risk this DOESN'T carry: rav1d exports dav1d's C ABI as `#[no_mangle]` +# symbols (`dav1d_open`, `dav1d_send_data`, …), which could in principle interpose on a +# real libdav1d loaded into the same process. It cannot here — these are Rust `staticlib` +# symbols in an executable with no `-rdynamic` and no dynamic export table entry, so the +# loader never offers them to anyone. That changes if pf-client-core ever becomes a +# `cdylib` or a leg adds `-rdynamic`/`--export-dynamic`; re-check it then. +openh264 = "0.9" +rav1d = { version = "1", default-features = false, features = ["bitdepth_8"] } +# errno names for rav1d's negated-`c_int` returns (`video_software.rs`): `ENOPROTOOPT` — +# the code a `bitdepth_8`-only build answers a 10-bit stream with — is 92 on Linux and +# 123 on Windows, and rav1d re-exports only `Dav1dResult`, so the typed enum that would +# otherwise name it is out of reach. Already in the tree (rav1d's own dependency). +libc = "0.2" + # Video decode (same FFmpeg pin as the host) and Opus for the audio planes. ffmpeg-next = "8" opus = "0.3" diff --git a/crates/pf-client-core/src/session.rs b/crates/pf-client-core/src/session.rs index c022aa4f..e7b5da75 100644 --- a/crates/pf-client-core/src/session.rs +++ b/crates/pf-client-core/src/session.rs @@ -16,6 +16,12 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::{Duration, Instant}; +/// `Clone` so an embedder can keep the params a session was started with and re-dial with +/// one field changed — which is what the codec fallback ([`SessionEvent::CodecFallback`]) +/// needs and the only reason the derive exists. Every field is either plain data or an +/// `Arc` the retry deliberately SHARES: the same `force_software` flag and the same +/// presenter-written `latch_grid`, because they belong to the presenter, not the session. +#[derive(Clone)] pub struct SessionParams { pub host: String, pub port: u16, @@ -28,6 +34,15 @@ pub struct SessionParams { /// The user's preferred video codec (a `quic::CODEC_*` bit, `0` = auto). Soft — the host honors /// it when it can emit it, else falls back; the resolved codec drives the decoder. pub preferred_codec: u8, + /// `quic::CODEC_*` bits to REMOVE from this session's advertised decode caps. + /// + /// `0` for every ordinary connect. It is set by a retry after + /// [`SessionEvent::CodecFallback`]: a session whose codec exhausted the decode ladder + /// (in practice HEVC, whose CPU rung M8 removed — no permissively licensed software + /// HEVC decoder exists) comes back advertising the codec set the host must pick + /// from instead. The codec is fixed at Welcome and the control stream renegotiates + /// shard payload only, so a fresh Hello is the ONLY lever; this field is it. + pub exclude_codecs: u8, /// The advertised `quic::VIDEO_CAP_*` bits. Normally 10-bit + HDR (Main10/PQ: the /// Vulkan presenter decodes P010 everywhere and presents PQ on an HDR10 swapchain /// where the desktop offers one, tonemapping in the CSC shader where it doesn't; @@ -254,9 +269,55 @@ pub enum SessionEvent { trust_rejected: bool, }, Ended(Option), + /// The session's negotiated codec ran out of decode rungs and the client can finish + /// this stream only as a DIFFERENT codec — terminal, like [`Self::Ended`], but with + /// the retry already computed. + /// + /// The one case in practice is HEVC on a box whose hardware HEVC decode failed: M8 + /// dropped software HEVC (no permissively licensed decoder exists), so the ladder's + /// last rung refuses instead of limping, and the answer is a reconnect advertising + /// [`Self::CodecFallback::retry_caps`] — which never contains the codec that just + /// failed. The other case is a picture SHAPE the CPU rung cannot decode (10-bit, + /// 4:4:4), which is a different diagnosis with the same available action; the two + /// pick different retry sets, and [`crate::video::last_rung_verdict`] is where that + /// is decided. + /// + /// An embedder that does not implement the retry MUST still show `msg` and stop — + /// treating it as an ordinary end is correct, just worse. It is a separate variant + /// rather than a flag on `Ended` so the compiler asks every embedder the question + /// once, which is how the two D3D11VA rungs' shared `stats:` tag went wrong when it + /// was not asked (`1573a987`). + CodecFallback { + /// What to pass as [`SessionParams::exclude_codecs`] on the retry — DERIVED from + /// [`Self::CodecFallback::retry_caps`], so applying it advertises exactly those + /// caps and nothing wider. + exclude_codecs: u8, + /// The caps the retry will advertise — non-empty by construction, and what + /// `exclude_codecs` above resolves to on the wire. + retry_caps: u8, + /// User-facing one-liner for the toast/status strip. + msg: String, + }, Stats(Stats), } +/// How many times THIS PROCESS has had a session's codec exhaust the decode ladder — the +/// telemetry counter the risk register asks for ("telemetry on frequency") for the +/// software-HEVC drop. +/// +/// Process-scoped and monotonic because the thing being counted is a property of the +/// machine, not of one session: a box whose hardware HEVC decode is broken produces one +/// of these per connect, and it is the RATE across a session history that says whether +/// dropping software HEVC hurt anybody. Read it with [`codec_fallbacks`]. +static CODEC_FALLBACKS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); + +/// See [`CODEC_FALLBACKS`]. Surfaced on the session's Detailed stats block as an +/// additive `codec_fallbacks ` line once it is nonzero — appended last, never +/// removed, never reordered (`run.rs`'s `stats_text`). +pub fn codec_fallbacks() -> u64 { + CODEC_FALLBACKS.load(Ordering::Relaxed) +} + /// The in-stream microphone mute (B4), shared between the embedder's toggle (a keyboard chord /// in the presenter) and the capture callback that reads it every quantum. /// @@ -409,6 +470,23 @@ fn pump( // on their arrivals, so this bit alone changes nothing without a wired DualSense. let pad_speaker_on = crate::pad_audio::speaker_active(¶ms.pad_speaker); let pad_audio_on = params.pad_haptics || pad_speaker_on; + // What this session advertises it can decode, minus anything a previous attempt + // proved it cannot FINISH (see `SessionParams::exclude_codecs`). Held for the whole + // pump because the reconnect rule needs to know what was on the table, not just what + // the host picked. + let advertised_codecs = crate::video::decodable_codecs_for( + params.vulkan.as_ref(), + // The decoder pin is part of the answer: a session pinned to software has no HEVC + // rung at all, so advertising HEVC would promise what this build cannot keep. + ¶ms.decoder, + ) & !params.exclude_codecs; + if params.exclude_codecs != 0 { + tracing::info!( + excluded = params.exclude_codecs, + advertising = advertised_codecs, + "retrying with reduced decode caps" + ); + } let connector = match NativeClient::connect( ¶ms.host, params.port, @@ -418,8 +496,9 @@ fn pump( params.bitrate_kbps, params.video_caps, params.audio_channels, - // FFmpeg's codecs plus CODEC_PYROWAVE when the presenter device passed the probe. - crate::video::decodable_codecs_for(params.vulkan.as_ref()), + // FFmpeg's codecs plus CODEC_PYROWAVE when the presenter device passed the probe, + // minus whatever a previous attempt proved undecodable end to end. + advertised_codecs, preferred, // the user's soft codec preference (0 = auto; see the pyrowave opt-in above) // This display's HDR volume → the host's virtual-display EDID. The env hatch wins so an // A/B run can pin an exact peak (PUNKTFUNK_CLIENT_PEAK_NITS=600). @@ -562,7 +641,29 @@ fn pump( let mut decoder = match built { Ok(d) => d, Err(e) => { - let _ = ev_tx.send_blocking(SessionEvent::Ended(Some(format!("video decoder: {e}")))); + // The ladder had NO rung for this codec at all — on a box with no hardware + // HEVC decode (or one that pinned `PUNKTFUNK_DECODER=software` on an HEVC + // session). Same answer as the mid-stream case below, one code path. + let refusal = e.downcast_ref::().map(|nr| { + codec_fallback_event( + connector.codec, + advertised_codecs, + nr.loss(), + &e.to_string(), + ) + }); + // Nothing has been spawned yet at this point — the audio / pad / clipboard + // threads and the mic uplink are all built BELOW — so "joined its threads" is + // vacuously true here. Set the stop flag and drop the connector anyway, in + // the same order the pump's end path does, so an embedder that reconnects on + // receipt of this event finds the same world whichever refusal site produced + // it (`run.rs` starts the retry the instant it reads one). + stop.store(true, Ordering::SeqCst); + mic.set_live(false); + drop(connector); + let _ = ev_tx.send_blocking( + refusal.unwrap_or_else(|| SessionEvent::Ended(Some(format!("video decoder: {e}")))), + ); return; } }; @@ -714,6 +815,10 @@ fn pump( // `window_dropped`: the decoder's counters are session-cumulative, the OSD shows // the delta. `None` on every lane that cannot answer — see `Stats::decode_integrity`. let mut window_health = decoder.decode_health(); + // Set when the ladder ran out of rungs for this codec (M8): the loop breaks and this + // event replaces the plain `Ended` at the bottom. `Some` is the only way the pump + // ends with a retry attached. + let mut codec_fallback: Option = None; let end: Option = loop { if stop.load(Ordering::SeqCst) { @@ -1046,6 +1151,25 @@ fn pump( tracing::debug!("requested keyframe (decoder produced no output)"); } } + // NOT survivable, and the only decode error that isn't: the ladder + // demoted to its last rung and there is no such rung for this codec. + // Feeding more AUs would freeze the screen forever — the exact + // "limping on software" outcome M8's HEVC drop replaces with an + // action. Break out of the pump; the terminal event below carries the + // retry the embedder reconnects with. + Err(e) if e.downcast_ref::().is_some() => { + let loss = e + .downcast_ref::() + .expect("just matched") + .loss(); + codec_fallback = Some(codec_fallback_event( + connector.codec, + advertised_codecs, + loss, + &e.to_string(), + )); + break None; + } // Survivable (loss until the next IDR/RFI recovery) — keep feeding. Err(e) => { tracing::debug!(error = %e, "decode error (recovering)"); @@ -1339,7 +1463,56 @@ fn pump( if let Some(t) = clipboard_thread { let _ = t.join(); // exits within its next_clip wait once `stop` is set } - let _ = ev_tx.send_blocking(SessionEvent::Ended(end)); + // The codec-exhaustion end has its own terminal event — sent HERE, after the audio / + // pad / clipboard threads have joined, so an embedder that reconnects on receipt + // never has two sessions' worth of threads on the same connector. + let _ = ev_tx.send_blocking(codec_fallback.unwrap_or(SessionEvent::Ended(end))); +} + +/// Build the terminal event for a session whose codec exhausted the decode ladder, and +/// bump the telemetry counter. +/// +/// One place, called from both refusal sites (decoder construction and the mid-stream +/// demotion), because the two must produce the SAME retry — a construction-time refusal +/// that reconnected onto a different codec set than the mid-stream one would make field +/// reports unreadable. +fn codec_fallback_event( + negotiated: u8, + advertised: u8, + loss: crate::video::RungLoss, + detail: &str, +) -> SessionEvent { + use crate::video::{last_rung_verdict, wire_codec_name, LastRungVerdict}; + CODEC_FALLBACKS.fetch_add(1, Ordering::Relaxed); + let codec = wire_codec_name(negotiated); + match last_rung_verdict(negotiated, advertised, loss) { + LastRungVerdict::Retry { caps } => { + tracing::warn!( + codec, + retry_caps = caps, + detail, + "video decode ran out of rungs — reconnecting without this codec" + ); + SessionEvent::CodecFallback { + // DERIVED from the verdict, never from the failed codec alone: the retry + // then advertises exactly `caps` (`decodable_codecs_for & !exclude` + // re-intersects to it), so the wire and the rule cannot disagree. They + // did before the M8 review — the rule dropped PyroWave and the wire + // re-offered it. + exclude_codecs: advertised & !caps, + retry_caps: caps, + msg: format!("{codec} decoding failed on this device — reconnecting"), + } + } + // Nothing left to advertise: reconnecting would negotiate the same dead end. End + // the session and say what actually happened, rather than loop. + LastRungVerdict::Dead => { + tracing::error!(codec, detail, "video decode ran out of rungs and of codecs"); + SessionEvent::Ended(Some(format!( + "{codec} can't be decoded on this device, and no other codec is available" + ))) + } + } } /// The dedicated audio thread: owns the Opus decoder, the PCM scratch, and the PipeWire @@ -1467,4 +1640,113 @@ mod tests { assert!(!mic.muted()); assert_eq!(mic.toggle(), None); } + + /// M8's HEVC reconnect, as the terminal event both refusal sites produce. + /// + /// This is the "reconnect flow tested as a first-class path" the plan's risk register + /// asks for, at the layer where it can be tested without a host: the pump's two + /// call sites (decoder construction and the mid-stream demotion) both go through + /// `codec_fallback_event`, so pinning its output pins the flow — the retry never + /// re-offers the codec that just failed, the message is user-facing, and the + /// telemetry counter moves exactly once per occurrence. + /// `CODEC_FALLBACKS` is process-global and `codec_fallback_event` bumps it, so the + /// test that asserts "counted exactly once" cannot run beside another that calls the + /// same builder. Both take this. + static FALLBACK_COUNTER: std::sync::Mutex<()> = std::sync::Mutex::new(()); + + #[test] + fn an_exhausted_codec_produces_a_retry_event_and_moves_the_counter() { + use crate::video::RungLoss; + use punktfunk_core::quic::{CODEC_AV1, CODEC_H264, CODEC_HEVC}; + let _guard = FALLBACK_COUNTER.lock().unwrap_or_else(|e| e.into_inner()); + let before = codec_fallbacks(); + + // The shipping shape: HEVC negotiated, H.264 also advertised. + let ev = codec_fallback_event( + CODEC_HEVC, + CODEC_H264 | CODEC_HEVC, + RungLoss::Codec, + "no software HEVC", + ); + match ev { + SessionEvent::CodecFallback { + exclude_codecs, + retry_caps, + ref msg, + } => { + assert_eq!(exclude_codecs, CODEC_HEVC, "the retry must drop HEVC"); + assert_eq!(retry_caps, CODEC_H264); + // The toast is for a person: it names the codec and says what happens + // next, and does NOT read as an error the user has to act on. + assert!(msg.contains("HEVC"), "{msg}"); + assert!(msg.contains("reconnect"), "{msg}"); + } + _ => panic!("expected a CodecFallback"), + } + assert_eq!(codec_fallbacks(), before + 1, "counted exactly once"); + + // Hardware AV1 advertised too: both survivors stay on the table. + match codec_fallback_event( + CODEC_HEVC, + CODEC_H264 | CODEC_HEVC | CODEC_AV1, + RungLoss::Codec, + "x", + ) { + SessionEvent::CodecFallback { retry_caps, .. } => { + assert_eq!(retry_caps, CODEC_H264 | CODEC_AV1); + } + _ => panic!("expected a CodecFallback"), + } + + // Nothing left to offer: end honestly instead of a reconnect loop. Still counted + // — the failure happened, and its frequency is exactly what the counter is for. + let before = codec_fallbacks(); + match codec_fallback_event(CODEC_HEVC, CODEC_HEVC, RungLoss::Codec, "x") { + SessionEvent::Ended(Some(msg)) => { + assert!(msg.contains("HEVC"), "{msg}"); + assert!(msg.contains("no other codec"), "{msg}"); + } + _ => panic!("expected a plain Ended"), + } + assert_eq!(codec_fallbacks(), before + 1); + } + + /// `exclude_codecs` and `retry_caps` describe the SAME retry — the review found them + /// disagreeing, and the wire follows `exclude_codecs`, so a mismatch means the tested + /// rule is not the shipped one. + /// + /// The property is exact, not approximate: the retry advertises + /// `decodable_codecs_for(vk) & !exclude_codecs`, and this session already advertised + /// `decodable_codecs_for(vk) & !old_exclude` — so re-intersecting with the derived + /// mask must land on `retry_caps` itself. + #[test] + fn the_retrys_exclusion_resolves_to_exactly_its_advertised_caps() { + use crate::video::RungLoss; + use punktfunk_core::quic::{CODEC_AV1, CODEC_H264, CODEC_HEVC, CODEC_PYROWAVE}; + let _guard = FALLBACK_COUNTER.lock().unwrap_or_else(|e| e.into_inner()); + for advertised in 0u8..16 { + for negotiated in [CODEC_H264, CODEC_HEVC, CODEC_AV1, CODEC_PYROWAVE] { + for loss in [RungLoss::Codec, RungLoss::Shape] { + let SessionEvent::CodecFallback { + exclude_codecs, + retry_caps, + .. + } = codec_fallback_event(negotiated, advertised, loss, "x") + else { + continue; // Dead — nothing is advertised at all + }; + assert_eq!( + advertised & !exclude_codecs, + retry_caps, + "advertised {advertised:#x} negotiated {negotiated:#x} {loss:?}" + ); + assert_eq!(retry_caps & negotiated, 0, "the failed codec came back"); + } + } + } + // Excluding twice is idempotent — a second fallback in the same run widens the + // set rather than resetting it (`run.rs` ORs into the existing value). + let full = CODEC_H264 | CODEC_HEVC | CODEC_AV1; + assert_eq!((full & !CODEC_HEVC) & !CODEC_HEVC, CODEC_H264 | CODEC_AV1); + } } diff --git a/crates/pf-client-core/src/video.rs b/crates/pf-client-core/src/video.rs index c3235489..00baf0e6 100644 --- a/crates/pf-client-core/src/video.rs +++ b/crates/pf-client-core/src/video.rs @@ -32,8 +32,12 @@ //! (nvidia-vaapi-driver is broken for this — Moonlight blacklists it); device //! creation fails there. A mid-session error falls back — the host's IDR/RFI //! recovery resynchronizes. -//! * **Software**: libavcodec on the CPU + swscale to RGBA (staging upload). -//! Slice threading only — frame threading would add a frame of latency per thread. +//! * **Software**: the CPU rung, FFmpeg-free since M8 — openh264 for H.264, rav1d +//! (dav1d) for AV1, planes uploaded straight to the presenter's planar CSC pass. It is +//! the LAST rung, so it never demotes further; and it has no HEVC decoder at all (none +//! exists under a permissive licence), which is a REFUSAL that reconnects the session +//! onto a codec this client can decode — see [`last_rung_verdict`] and +//! [`NoSoftwareRung`]. //! //! Both run `AV_CODEC_FLAG_LOW_DELAY`; the host encodes zero-reorder streams (no //! B-frames, in-band parameter sets on every IDR), so decode is strictly one-in/one-out. @@ -57,6 +61,9 @@ use ffmpeg_next as ffmpeg; use std::os::fd::RawFd; pub use crate::video_color::{csc_rows, ColorDesc}; +/// Re-exported so the SESSION layer (and its tests) can name the refusal by type — the +/// module itself stays private, like every other backend's. +pub use crate::video_software::NoSoftwareRung; use crate::video_software::SoftwareDecoder; #[cfg(target_os = "linux")] use crate::video_vaapi::VaapiDecoder; @@ -81,7 +88,16 @@ pub struct DecodedFrame { pub use crate::video_d3d11::D3d11Frame; pub enum DecodedImage { - Cpu(CpuFrame), + /// The SOFTWARE rung's output (M8): tightly-packed 8-bit I420 planes for the + /// presenter to upload and run its planar CSC pass over. + /// + /// It REPLACES the old `Cpu(CpuFrame)` RGBA variant rather than joining it — there is + /// exactly one CPU rung, and the swscale conversion it used to carry (and its BT.601 + /// default) is what this milestone deletes. Adding a second CPU variant would have + /// bought the [`DecodedImage::NativeDmabuf`] property below for a distinction that + /// does not exist: no `stats:` tag, no presenter path and no consumer would ever have + /// been able to reach the old one. + Cpu(CpuPlanarFrame), #[cfg(target_os = "linux")] Dmabuf(DmabufFrame), /// The NATIVE VAAPI rung's output (`pf-vaadec` + `video_vaapi_native`, M6) — @@ -545,14 +561,23 @@ impl DecodedImage { /// What the decoder's OWN bitstream parser saw about an intra-refresh heal on /// this frame's AU — the recovery point SEI, which no platform decoder exposes. /// - /// Only the native rung can answer: libavcodec parses the SEI internally and - /// surfaces nothing of it (its `AV_FRAME_FLAG_KEY` is IDR-only), MediaCodec and - /// VideoToolbox likewise. Everyone else reports + /// Only the rungs with their OWN parser can answer: libavcodec parses the SEI + /// internally and surfaces nothing of it (its `AV_FRAME_FLAG_KEY` is IDR-only), + /// MediaCodec and VideoToolbox likewise. That is the native Vulkan rung and — since + /// M8 — the CPU rung's H.264 leg, which plans every AU with the same `H264Planner` + /// and folds the SEI with the same `RecoveryWatch`. Everyone else reports /// [`LocalRecovery::NONE`](punktfunk_core::reanchor::LocalRecovery::NONE) and /// the pump's re-anchor behaviour on those lanes is byte-for-byte what it was. + /// + /// ⚠ The CPU rung reports no [`Self::decode_order`], so its mark cannot be dated + /// against the pump's arm the way the native rung's is. It does not need to be: + /// openh264 is one-AU-in, at-most-one-picture-out with no DPB flush that replays + /// pictures decoded before a loss, which is the only thing that ordinal defends + /// against. pub fn local_recovery(&self) -> punktfunk_core::reanchor::LocalRecovery { match self { DecodedImage::NativeVk(f) => f.recovery, + DecodedImage::Cpu(f) => f.recovery, _ => punktfunk_core::reanchor::LocalRecovery::NONE, } } @@ -586,19 +611,123 @@ impl DecodedImage { } } -/// RGBA pixels for `GdkMemoryTexture` (which takes a stride). -pub struct CpuFrame { +/// One software-decoded picture as 8-bit 4:2:0 PLANES (M8) — Y, Cb and Cr back to back +/// in one allocation, every plane tightly packed at its own width. +/// +/// "Tightly packed" is a load-bearing invariant, not a convenience: the presenter uploads +/// the buffer with a single `copy_nonoverlapping` and three `vkCmdCopyBufferToImage` +/// regions with `bufferRowLength = 0`, so a padded row here would shear the picture. The +/// decoders' own strides (openh264 pads for SIMD, dav1d aligns) are undone once, in +/// [`Self::from_i420`], which is also the only copy this rung makes per frame — where the +/// old RGBA path made a full swscale conversion pass and then handed over 4 bytes per +/// pixel instead of 1.5. +/// +/// Colour is NOT applied here. The planes carry the stream's own Y′CbCr and `color` +/// carries what the bitstream said about it; the presenter's planar CSC shader converts +/// with [`csc_rows`], the same coefficients every hardware rung's frames go through. That +/// is the whole point of the milestone: there is no second CSC implementation on this +/// lane to get the matrix or the range wrong. +pub struct CpuPlanarFrame { pub width: u32, pub height: u32, - /// RGBA row stride in bytes (≥ width*4 — swscale pads rows for SIMD). - pub stride: usize, - pub rgba: Vec, - /// Signaling of the source frame. swscale already undid the YUV matrix + range (the - /// pixels are full-range RGB), but a PQ/BT.2020 stream keeps its transfer + primaries - /// baked in — the presenter tags the texture so GTK tone-maps it. + /// Y, then Cb, then Cr — see [`Self::plane`]. + data: Vec, + /// Byte offset of each plane's first row in [`Self::data`]. + offsets: [usize; 3], + /// Signalling of the source frame, read from the bitstream (not from the decoder — + /// see `video_software`'s module docs). Drives the CSC matrix/range AND, for a PQ + /// stream, the presenter's tone-map mode. pub color: ColorDesc, - /// Intra keyframe (IDR/I) — the pump's post-loss re-anchor signal. See [`VkVideoFrame`]. + /// Intra keyframe (IDR) — the pump's post-loss re-anchor signal. See [`VkVideoFrame`]. pub keyframe: bool, + /// What this frame's AU said about intra-refresh RECOVERY — the same + /// `pf-vkdecode` [`RecoveryWatch`](pf_vkdecode::RecoveryWatch) fold the native rung + /// runs, over the same `AuPlan`. [`Self::keyframe`] cannot answer for an + /// intra-refresh session (the wave emits no IDR), so without this the pump freezes + /// until its 500 ms backstop forces the very IDR the wave exists to avoid. + /// + /// H.264 only: AV1 carries no equivalent SEI (see `video_software`'s AV1 leg), so + /// that half reports [`LocalRecovery::NONE`](punktfunk_core::reanchor::LocalRecovery) + /// and behaves exactly as it did. + pub recovery: punktfunk_core::reanchor::LocalRecovery, +} + +impl CpuPlanarFrame { + /// Chroma plane size for 4:2:0, rounding UP — an odd luma dimension still has a + /// chroma sample covering its last row/column, and rounding down would drop it. + pub fn chroma_dims(width: u32, height: u32) -> (u32, u32) { + (width.div_ceil(2), height.div_ceil(2)) + } + + /// Plane `i` (0 = Y, 1 = Cb, 2 = Cr), tightly packed. + pub fn plane(&self, i: usize) -> &[u8] { + let (w, h) = self.plane_dims(i); + let start = self.offsets[i]; + &self.data[start..start + (w * h) as usize] + } + + /// Plane `i`'s size in samples — `(width, height)` for luma, the 4:2:0 halves for + /// chroma. The presenter sizes its plane images from this. + pub fn plane_dims(&self, i: usize) -> (u32, u32) { + if i == 0 { + (self.width, self.height) + } else { + Self::chroma_dims(self.width, self.height) + } + } + + /// Copy a decoder's strided I420 output into one tightly-packed allocation. + /// + /// Refuses rather than truncates: a plane the decoder reported shorter than its own + /// geometry means the decoder and we disagree about the picture, and reading the rows + /// that ARE there would produce a plausible-looking picture over uninitialized + /// memory. + pub(crate) fn from_i420( + width: u32, + height: u32, + planes: [&[u8]; 3], + strides: [usize; 3], + color: ColorDesc, + keyframe: bool, + recovery: punktfunk_core::reanchor::LocalRecovery, + ) -> Result { + anyhow::ensure!(width > 0 && height > 0, "empty picture {width}x{height}"); + let (cw, ch) = Self::chroma_dims(width, height); + let dims = [(width, height), (cw, ch), (cw, ch)]; + let total: usize = dims.iter().map(|(w, h)| *w as usize * *h as usize).sum(); + let mut data = vec![0u8; total]; + let mut offsets = [0usize; 3]; + let mut at = 0usize; + for i in 0..3 { + let (w, h) = (dims[i].0 as usize, dims[i].1 as usize); + anyhow::ensure!( + strides[i] >= w, + "plane {i}: stride {} is narrower than {w} samples", + strides[i] + ); + anyhow::ensure!( + planes[i].len() >= (h - 1) * strides[i] + w, + "plane {i}: decoder reported {} bytes for {w}x{h} at stride {}", + planes[i].len(), + strides[i] + ); + offsets[i] = at; + for row in 0..h { + let src = row * strides[i]; + data[at..at + w].copy_from_slice(&planes[i][src..src + w]); + at += w; + } + } + Ok(CpuPlanarFrame { + width, + height, + data, + offsets, + color, + keyframe, + recovery, + }) + } } /// A decoded frame still on the GPU: dmabuf fds + plane layout for @@ -716,6 +845,9 @@ enum Backend { /// Boxed: the decoder (pinned create-info hold + plane ring) dwarfs the other variants. #[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))] PyroWave(Box), + /// The CPU rung (M8: openh264 / rav1d, no libavcodec). Last in every ladder, so it + /// never demotes — and the only rung that can fail to EXIST for a codec, which is a + /// different answer from failing to decode: see [`last_rung_verdict`]. Software(SoftwareDecoder), } @@ -758,6 +890,12 @@ pub struct Decoder { /// The negotiated codec (from the host's Welcome), so a mid-session VAAPI→software demotion /// rebuilds the software decoder for the SAME codec. codec_id: ffmpeg::codec::Id, + /// The same codec as the WIRE states it — what the software rung is built for and + /// what a refusal names. Derived once from [`Self::codec_id`] rather than carried + /// through a widened `Decoder::new` signature, because the ladder above still speaks + /// FFmpeg ids and will until M10 deletes its last FFmpeg rung; this field is the one + /// place the two vocabularies meet. + wire_codec: u8, /// Consecutive hardware decode errors (Vulkan or VAAPI) — a single transient failure /// (e.g. a reference-missing frame after packet loss) shouldn't cost the whole /// session its hardware decoder. @@ -962,6 +1100,120 @@ fn native_vulkan_gate( chosen && video_decode && decode_video_caps & codec_op != 0 } +/// The `quic::CODEC_*` bit's human name — for logs, errors and the user-visible +/// reconnect toast. `?` for a bit this build does not know, which is honest: an unknown +/// codec must not print as one of the known ones. +pub fn wire_codec_name(wire: u8) -> &'static str { + match wire { + punktfunk_core::quic::CODEC_H264 => "H.264", + punktfunk_core::quic::CODEC_HEVC => "HEVC", + punktfunk_core::quic::CODEC_AV1 => "AV1", + punktfunk_core::quic::CODEC_PYROWAVE => "PyroWave", + _ => "?", + } +} + +/// The `quic` codec bit for an FFmpeg decoder id — the inverse of [`ffmpeg_codec_id`], +/// for the one place that has an id in hand and needs the WIRE truth (the software rung's +/// refusal, which must name a codec the host understands). Dies with the ladder's last +/// FFmpeg id at M10. +fn wire_codec_of(id: ffmpeg::codec::Id) -> u8 { + match id { + ffmpeg::codec::Id::H264 => punktfunk_core::quic::CODEC_H264, + ffmpeg::codec::Id::AV1 => punktfunk_core::quic::CODEC_AV1, + _ => punktfunk_core::quic::CODEC_HEVC, + } +} + +/// The `quic` codec bits this build can decode ON THE CPU — the ladder's last rung, and +/// therefore the set a session is guaranteed to survive to the end of. +/// +/// One function so the answer cannot drift between the rung that refuses (the software +/// backend's own codec map) and the rule that decides what to reconnect as +/// ([`last_rung_verdict`]). +pub fn software_decodable_codecs() -> u8 { + punktfunk_core::quic::CODEC_H264 | punktfunk_core::quic::CODEC_AV1 +} + +/// What to do when the last rung has no decoder for the session's codec. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LastRungVerdict { + /// Reconnect advertising these caps instead — they are non-empty, and they exclude + /// the codec that just ran out of rungs, so the host must pick something else. + Retry { caps: u8 }, + /// Nothing is left to advertise: every codec this client offered has now exhausted + /// its rungs. Reconnecting would negotiate the same dead end, so the session ends + /// and says why. + Dead, +} + +/// WHY the last rung had no answer — the two diagnoses behind a [`NoSoftwareRung`], and +/// the reason [`last_rung_verdict`] needs more than "a codec failed". +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RungLoss { + /// The CODEC has no CPU rung in this build at all (HEVC). Every hardware rung for it + /// has already failed, so the retry may only offer codecs that DO have a CPU rung — + /// anything else is the same bet that just lost, one session later. + Codec, + /// The codec has a CPU rung; this stream's picture SHAPE is outside it (10-bit, + /// 4:4:4). The hardware rungs are not implicated at all — nothing failed, the CPU + /// decoder simply is not built for this picture — so every other advertised codec is + /// a genuine candidate and filtering by [`software_decodable_codecs`] here would end + /// sessions a plain HEVC retry would have finished. + Shape, +} + +/// The reconnect rule, in one pure function: an HEVC session whose hardware rungs are +/// exhausted must come back as a session this client can finish. +/// +/// The codec is fixed at Welcome and the control stream renegotiates shard payload only, +/// so there is no in-session move available — the only lever is what the NEXT Hello +/// advertises. `advertised` is what this session offered; the answer removes `negotiated` +/// from it, plus — for a [`RungLoss::Codec`] — any other codec that would land in the +/// same hole (one whose only remaining rung is a software one that does not exist). Two +/// sessions of the same failure is the shape this rules out. +/// +/// `caps` is what the retry ACTUALLY advertises: the pump derives its `exclude_codecs` +/// from this set rather than from the failed codec alone, so the wire and this verdict +/// cannot disagree (they did until the M8 review — the wire re-offered PyroWave the rule +/// had removed). +/// +/// Pure and total on purpose — this is the piece that gets tested as a first-class path, +/// because the on-glass version of it costs a real host with a real GPU failure. +pub fn last_rung_verdict(negotiated: u8, advertised: u8, loss: RungLoss) -> LastRungVerdict { + let survivors = advertised & !negotiated; + let caps = match loss { + // Everything still on the table that ALSO has a CPU rung underneath it. + RungLoss::Codec => survivors & software_decodable_codecs(), + RungLoss::Shape => survivors, + }; + // A retry the host's precedence ladder cannot PICK is not a retry: `resolve_codec` + // deliberately keeps PyroWave out of that ladder (it is opt-in only), so a Hello + // whose survivors are PyroWave alone resolves to nothing and the host refuses the + // session. Judge liveness on the pickable ones and carry the rest along. + const PICKABLE: u8 = punktfunk_core::quic::CODEC_H264 + | punktfunk_core::quic::CODEC_HEVC + | punktfunk_core::quic::CODEC_AV1; + if caps & PICKABLE == 0 { + LastRungVerdict::Dead + } else { + LastRungVerdict::Retry { caps } + } +} + +/// Is video decode PINNED to the CPU rung — the Settings "Video decoder" value, or the +/// `PUNKTFUNK_DECODER` override that wins over it? +/// +/// Same precedence as [`Decoder::new`] resolves (env first, then the setting), because a +/// second reading of the same two inputs is a second place for them to drift. +pub fn decode_pinned_to_software(pref: &str) -> bool { + std::env::var("PUNKTFUNK_DECODER") + .ok() + .filter(|v| !v.is_empty()) + .unwrap_or_else(|| pref.to_string()) + == "software" +} + /// Map a negotiated `quic` codec bit to the FFmpeg decoder id the client opens. pub fn ffmpeg_codec_id(wire: u8) -> ffmpeg::codec::Id { match wire { @@ -1058,6 +1310,16 @@ pub(crate) unsafe fn codec_name(codec: *const ffmpeg::ffi::AVCodec) -> String { /// /// ⚠ **AV1 here is a decoder EXISTING, not a decoder that can keep up.** Use /// [`decodable_codecs_for`], which gates it on hardware — see [`av1_hardware_decodable`]. +/// +/// ⚠ **HEVC here is a HARDWARE decoder existing**, and since M8 that is the only kind +/// there is: the CPU rung has no HEVC ([`software_decodable_codecs`]). Advertising it +/// anyway is deliberate and is the plan's — hardware HEVC is the path most hosts and most +/// clients actually take, and refusing it up front would cost every one of them the codec +/// to protect the few whose hardware later fails. The exhaustion case is handled where it +/// happens, by [`last_rung_verdict`], and it is the ONE codec whose advertisement is a +/// promise this client cannot keep unconditionally. Where the client can KNOW in advance +/// that it cannot keep it — decode pinned to software — [`decodable_codecs_for`] drops +/// the bit before the first Hello instead. pub fn decodable_codecs() -> u8 { let _ = ffmpeg::init(); let mut bits = 0u8; @@ -1111,10 +1373,11 @@ pub fn av1_hardware_decodable(vk: Option<&VulkanDecodeDevice>) -> bool { } /// [`decodable_codecs`] plus the PyroWave bit when the presenter's device passed the -/// compute-feature probe. Advertisement-only: `resolve_codec` never auto-picks PyroWave — -/// the session must also name it `preferred_codec` (plan §3), which the client does only -/// under its explicit opt-in. -pub fn decodable_codecs_for(vk: Option<&VulkanDecodeDevice>) -> u8 { +/// compute-feature probe, minus the codecs `decoder_pref` makes unreachable. +/// Advertisement-only: `resolve_codec` never auto-picks PyroWave — the session must also +/// name it `preferred_codec` (plan §3), which the client does only under its explicit +/// opt-in. +pub fn decodable_codecs_for(vk: Option<&VulkanDecodeDevice>, decoder_pref: &str) -> u8 { let mut bits = decodable_codecs(); // AV1 is hardware-gated (M7). Without this the bit rides on libdav1d's mere // presence and the host is told to send AV1 to a machine that would decode it on @@ -1126,6 +1389,24 @@ pub fn decodable_codecs_for(vk: Option<&VulkanDecodeDevice>) -> u8 { ); bits &= !punktfunk_core::quic::CODEC_AV1; } + // The one HEVC case the client can answer BEFORE the Hello (M8 review): decode is + // pinned to the CPU rung, and the CPU rung has no HEVC — so the advertisement would + // be a promise this build cannot keep for the whole session, exactly what + // `av1_hardware_decodable` exists to stop for AV1. Every other HEVC failure is a + // per-device fact only the session can learn, and `last_rung_verdict` answers it + // there. Guarded on something remaining: a Hello advertising ZERO codecs reads as + // "HEVC-only" to a host (`resolve_codec`'s pre-negotiation default), which would be + // the precise opposite of this. + if bits & punktfunk_core::quic::CODEC_HEVC != 0 + && bits & !punktfunk_core::quic::CODEC_HEVC != 0 + && decode_pinned_to_software(decoder_pref) + { + tracing::info!( + "HEVC not advertised: decode is pinned to software and there is no software \ + HEVC decoder in this build" + ); + bits &= !punktfunk_core::quic::CODEC_HEVC; + } #[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))] if vk.map(|v| v.pyrowave_decode).unwrap_or(false) { return bits | punktfunk_core::quic::CODEC_PYROWAVE; @@ -1244,6 +1525,7 @@ impl Decoder { Ok(Decoder { backend, codec_id, + wire_codec: wire_codec_of(codec_id), vaapi_fails: 0, first_fail: None, want_keyframe: false, @@ -1577,7 +1859,13 @@ impl Decoder { hardware decode not attempted" ); } - done(Backend::Software(SoftwareDecoder::new(codec_id)?)) + // `?` here can carry a `NoSoftwareRung` (an HEVC session that pinned software, or + // one whose device offered no hardware rung at all). It stays typed all the way + // to the pump, which turns it into the reconnect rather than a dead session — + // see [`last_rung_verdict`]. + done(Backend::Software(SoftwareDecoder::new(wire_codec_of( + codec_id, + ))?)) } /// Wait for a Vulkan-Video frame's GPU decode to complete (timeline semaphore) — @@ -1658,6 +1946,7 @@ impl Decoder { hdr16, )?)), codec_id: ffmpeg::codec::Id::HEVC, + wire_codec: punktfunk_core::quic::CODEC_PYROWAVE, vaapi_fails: 0, first_fail: None, want_keyframe: false, @@ -1689,7 +1978,9 @@ impl Decoder { return Ok(()); } tracing::warn!("presenter can't display hardware frames — demoting to software decode"); - self.backend = Backend::Software(SoftwareDecoder::new(self.codec_id)?); + // Same typed refusal as every other software-rung construction: on an HEVC + // session there is nothing below this and the pump reconnects. + self.backend = Backend::Software(SoftwareDecoder::new(self.wire_codec)?); self.vaapi_fails = 0; self.first_fail = None; self.delivered = false; @@ -1935,7 +2226,13 @@ impl Decoder { } tracing::warn!(error = %e, fails = self.vaapi_fails, "{which} decode failing repeatedly — demoting to software"); - self.backend = Backend::Software(SoftwareDecoder::new(self.codec_id)?); + // The ladder's bottom. On H.264/AV1 this always builds; on HEVC it + // NEVER does, and the `?` carries the typed `NoSoftwareRung` up to + // the pump, which reconnects with HEVC-less caps instead of leaving + // the session on a rung that cannot decode a single AU. That + // substitution — a refusal where a silently useless decoder used to + // sit — is the whole reason the drop of software HEVC is safe. + self.backend = Backend::Software(SoftwareDecoder::new(self.wire_codec)?); self.vaapi_fails = 0; self.first_fail = None; self.delivered = false; @@ -2158,6 +2455,185 @@ pub(crate) fn drm_fourcc_for(sw: ffmpeg_next::ffi::AVPixelFormat) -> Option #[cfg(test)] mod tests { use super::*; + use punktfunk_core::quic::{CODEC_AV1, CODEC_H264, CODEC_HEVC, CODEC_PYROWAVE}; + + /// The reconnect rule, as the invariant it is: an exhausted codec must come back as + /// one this client can decode ALL THE WAY DOWN, and must never come back as itself. + /// + /// This is the "first-class path" the risk register asks for, tested where it can be + /// tested exhaustively — the on-glass half needs a host, a GPU and a decode failure + /// nobody can schedule. + #[test] + fn an_exhausted_codec_reconnects_only_onto_one_with_a_cpu_rung() { + let sw = software_decodable_codecs(); + assert_eq!(sw, CODEC_H264 | CODEC_AV1, "M8's CPU rung set"); + assert_eq!(sw & CODEC_HEVC, 0, "software HEVC is what M8 dropped"); + + // The shipping case: a desktop advertises H.264+HEVC, HEVC runs out of rungs. + assert_eq!( + last_rung_verdict(CODEC_HEVC, CODEC_H264 | CODEC_HEVC, RungLoss::Codec), + LastRungVerdict::Retry { caps: CODEC_H264 } + ); + // With hardware AV1 also advertised, both survivors stay on the table — the host + // picks; we only ever REMOVE. + assert_eq!( + last_rung_verdict( + CODEC_HEVC, + CODEC_H264 | CODEC_HEVC | CODEC_AV1, + RungLoss::Codec + ), + LastRungVerdict::Retry { + caps: CODEC_H264 | CODEC_AV1 + } + ); + // A client that offered HEVC alone has nowhere to go: reconnecting would + // negotiate the same dead end, so say so instead of looping. + assert_eq!( + last_rung_verdict(CODEC_HEVC, CODEC_HEVC, RungLoss::Codec), + LastRungVerdict::Dead + ); + // The retry NEVER re-offers the codec that just failed... + for advertised in 0u8..16 { + for negotiated in [CODEC_H264, CODEC_HEVC, CODEC_AV1] { + if let LastRungVerdict::Retry { caps } = + last_rung_verdict(negotiated, advertised, RungLoss::Codec) + { + assert_eq!(caps & negotiated, 0, "{negotiated:#x} re-offered"); + // ...and, when the CODEC is what has no CPU rung, never offers one + // that would reach the same refusal a session later. + assert_eq!(caps & !software_decodable_codecs(), 0); + assert_ne!(caps, 0, "Retry must carry something to advertise"); + } + } + } + // PyroWave is not in the software set and never reaches this rule (its sessions + // renegotiate the codec on failure instead of demoting) — but if it ever did, the + // answer must be Dead, not a retry that offers a codec with no CPU decoder. + assert_eq!( + last_rung_verdict(CODEC_PYROWAVE, CODEC_PYROWAVE, RungLoss::Codec), + LastRungVerdict::Dead + ); + } + + /// A picture SHAPE the CPU rung cannot decode is not "this codec has no CPU rung", + /// and the review found the rule conflating them: a 4:4:4 H.264 session ended with + /// "no other codec is available" while an HEVC retry — whose hardware rungs never + /// even ran — would have worked. + #[test] + fn a_shape_refusal_may_retry_onto_a_codec_with_no_cpu_rung() { + // The one that used to die. HEVC has no CPU rung, but nothing about HEVC failed: + // this client asked for 4:4:4, the host resolved it, and only the CPU DECODER is + // 4:2:0-only. A reconnect without H.264 re-resolves the shape too. + assert_eq!( + last_rung_verdict(CODEC_H264, CODEC_H264 | CODEC_HEVC, RungLoss::Shape), + LastRungVerdict::Retry { caps: CODEC_HEVC } + ); + // Same inputs, the OTHER diagnosis: hardware H.264 exhausted and the CPU rung + // has no H.264 at all (impossible in this build, but the rule must not depend on + // that) — then HEVC really is the same losing bet and the session ends. + assert_eq!( + last_rung_verdict(CODEC_H264, CODEC_H264 | CODEC_HEVC, RungLoss::Codec), + LastRungVerdict::Dead + ); + // The user's PyroWave opt-in survives a shape refusal — but never ALONE: the + // host's `resolve_codec` keeps PyroWave out of its precedence ladder, so a Hello + // offering nothing else resolves to no codec and the host refuses the session. + assert_eq!( + last_rung_verdict( + CODEC_H264, + CODEC_H264 | CODEC_HEVC | CODEC_PYROWAVE, + RungLoss::Shape + ), + LastRungVerdict::Retry { + caps: CODEC_HEVC | CODEC_PYROWAVE + } + ); + assert_eq!( + last_rung_verdict(CODEC_H264, CODEC_H264 | CODEC_PYROWAVE, RungLoss::Shape), + LastRungVerdict::Dead + ); + // And a shape refusal still never re-offers the codec that raised it — the codec + // is fixed at Welcome, so it is the only lever there is. + for advertised in 0u8..16 { + for negotiated in [CODEC_H264, CODEC_HEVC, CODEC_AV1] { + if let LastRungVerdict::Retry { caps } = + last_rung_verdict(negotiated, advertised, RungLoss::Shape) + { + assert_eq!(caps & negotiated, 0, "{negotiated:#x} re-offered"); + assert_ne!(caps, 0, "Retry must carry something to advertise"); + } + } + } + } + + /// The one HEVC promise the client can refuse to make BEFORE the Hello: decode + /// pinned to software has no HEVC rung at any level, so advertising it guarantees + /// the reconnect flow rather than risking it. + #[test] + fn a_software_pin_takes_hevc_off_the_advertisement() { + // The pin is read the way `Decoder::new` reads it: env first, then the setting — + // so a run with the override actually set has nothing here to assert about. + if std::env::var_os("PUNKTFUNK_DECODER").is_some() { + return; + } + assert!(decode_pinned_to_software("software")); + assert!(!decode_pinned_to_software("auto")); + assert!(!decode_pinned_to_software("vulkan")); + assert!(!decode_pinned_to_software("")); + } + + /// The wire↔FFmpeg codec map must round-trip for every codec the software rung can + /// be built for. A mistake here builds an openh264 decoder for an AV1 session — which + /// then fails per AU rather than at construction, i.e. exactly the mid-stream burn + /// this ladder is written to avoid. + #[test] + fn the_wire_codec_of_an_ffmpeg_id_round_trips() { + for wire in [CODEC_H264, CODEC_HEVC, CODEC_AV1] { + assert_eq!(wire_codec_of(ffmpeg_codec_id(wire)), wire); + } + // PyroWave has no FFmpeg id (`ffmpeg_codec_id` folds it onto HEVC), so the + // inverse cannot round-trip it — the PyroWave decoder sets `wire_codec` itself. + assert_eq!(wire_codec_of(ffmpeg_codec_id(CODEC_PYROWAVE)), CODEC_HEVC); + } + + /// `CpuPlanarFrame` is what the presenter uploads with no stride: prove the copy + /// really does undo the decoder's padding, and that a short plane is REFUSED rather + /// than read past. + #[test] + fn planar_frames_are_tightly_packed_and_short_planes_are_refused() { + let color = ColorDesc { + primaries: 1, + transfer: 1, + matrix: 1, + full_range: false, + }; + // 4x2 luma, 2x1 chroma, all planes padded by 3 bytes per row. + let y: Vec = vec![1, 2, 3, 4, 9, 9, 9, 5, 6, 7, 8, 9, 9, 9]; + let u: Vec = vec![10, 11, 9, 9, 9]; + let v: Vec = vec![20, 21, 9, 9, 9]; + let none = punktfunk_core::reanchor::LocalRecovery::NONE; + let f = + CpuPlanarFrame::from_i420(4, 2, [&y, &u, &v], [7, 5, 5], color, true, none).unwrap(); + assert_eq!(f.plane(0), &[1, 2, 3, 4, 5, 6, 7, 8]); + assert_eq!(f.plane(1), &[10, 11]); + assert_eq!(f.plane(2), &[20, 21]); + assert_eq!(f.plane_dims(0), (4, 2)); + assert_eq!(f.plane_dims(1), (2, 1)); + // Odd dimensions round the chroma plane UP — the last column/row still has a + // chroma sample and dropping it would read past the plane on the next frame. + assert_eq!(CpuPlanarFrame::chroma_dims(5, 3), (3, 2)); + // A plane shorter than its own geometry is a disagreement with the decoder, not + // something to truncate into a plausible picture. + let short: Vec = vec![1, 2, 3]; + assert!( + CpuPlanarFrame::from_i420(4, 2, [&short, &u, &v], [7, 5, 5], color, true, none) + .is_err() + ); + // A stride narrower than the picture is the same class of disagreement. + assert!( + CpuPlanarFrame::from_i420(4, 2, [&y, &u, &v], [2, 5, 5], color, true, none).is_err() + ); + } fn decode_device(vendor_id: u32, device_name: &str) -> VulkanDecodeDevice { VulkanDecodeDevice { diff --git a/crates/pf-client-core/src/video_color.rs b/crates/pf-client-core/src/video_color.rs index 87be7439..fec62941 100644 --- a/crates/pf-client-core/src/video_color.rs +++ b/crates/pf-client-core/src/video_color.rs @@ -55,7 +55,9 @@ impl ColorDesc { /// `65535/65472` recovers exact `code/1023`. pub fn csc_rows(desc: ColorDesc, depth: u8, msb_packed: bool) -> [[f32; 4]; 3] { // BT.601 (5/6), BT.2020 (9/10); everything else — incl. unspecified — is the host's - // BT.709 SDR default (mirrors the software path's swscale coefficient choice). + // BT.709 SDR default. Since M8 this is the ONLY coefficient choice in the client: + // the software rung's swscale (which defaulted to BT.601 and needed correcting) is + // gone, and its planes come through this function like every hardware lane's. let (kr, kb) = match desc.matrix { 5 | 6 => (0.299, 0.114), 9 | 10 => (0.2627, 0.0593), diff --git a/crates/pf-client-core/src/video_software.rs b/crates/pf-client-core/src/video_software.rs index e57bbb4a..ed8856b5 100644 --- a/crates/pf-client-core/src/video_software.rs +++ b/crates/pf-client-core/src/video_software.rs @@ -1,224 +1,818 @@ -//! CPU/libavcodec software decode backend (swscale → RGBA). +//! The CPU rung — the ladder's LAST one, and (M8) the first one with no FFmpeg in it. +//! +//! * **H.264 → openh264** (BSD-2). Already a workspace dependency: the host's GPU-less +//! encoder is the same library ([`pf-encode`'s `enc/sw.rs`]), so the licence posture and +//! the statically-bundled build were settled before this rung existed. +//! * **AV1 → rav1d** (BSD-2) — dav1d, ported to Rust. Picking it over the `dav1d` FFI +//! crate is a packaging decision, argued in `Cargo.toml`; picking it over *nothing* is +//! the plan's ("dav1d SW is the safety net"). Two properties come free with it: +//! there is no `avcodec_find_decoder(AV1)` to hand us libdav1d behind a +//! `hw_device_ctx` it silently ignores, and no C decoder in the process at all. +//! * **HEVC → dropped.** No permissively licensed software HEVC decoder exists (libde265 +//! is LGPL, which defeats the point of the excision). This rung REFUSES an HEVC +//! session with a typed [`NoSoftwareRung`], which is what the session layer turns into +//! a reconnect that advertises HEVC-less decode caps — see +//! [`crate::video::last_rung_verdict`]. Narrowing instead (limping on at 5 fps, or +//! freezing) is the failure mode this whole program exists to end. +//! +//! **Output is PLANES, not RGBA.** The decoder hands the presenter tightly-packed I420 +//! and the presenter's existing planar CSC shader does the colour, which deletes two +//! things at once: swscale's per-frame YUV→RGBA pass, and swscale's BT.601 default — +//! the footgun the old `convert_rgba` carried ~30 lines of correction code for. +//! +//! **Colour comes from pf-bitstream, not from the decoder.** openh264 reports no VUI at +//! all and rav1d reports its own sequence header, so a rung that trusted its decoder +//! would have two colour implementations to keep in step with the four hardware rungs' +//! one. Instead the H.264 leg plans every AU with [`H264Planner`] — the SAME planner +//! `pf-vkdecode`/`pf-dxvadec`/`pf-vaadec` submit from — and reads +//! `plan.picture.colour`. The signalled matrix/range therefore cannot differ between the +//! software rung and the hardware rungs, because it is literally the same code reading +//! the same SPS. The H.264 leg takes the recovery point SEI from the same plan, through +//! `pf-vkdecode`'s own [`RecoveryWatch`], so an intra-refresh session re-anchors here on +//! the same rule the native rung uses. +//! +//! **The picture envelope is checked BEFORE the decoder sees the AU, on both legs.** +//! 8-bit 4:2:0 only: openh264 has no wider support at all and rav1d is compiled +//! `bitdepth_8` here. H.264 reads it off the SPS the planner activated; AV1 reads it off +//! the sequence header with `dav1d_parse_sequence_header`. Both raise the SAME typed +//! [`NoSoftwareRung`] so the session reconnects. Letting the DECODER answer instead is +//! what the M8 review caught: rav1d refuses a 10-bit frame with `ENOPROTOOPT`, the pump +//! reads a generic error as survivable, and a Main 10 HDR stream — which is what hardware +//! AV1 sessions are — freezes forever, one keyframe request per identical AU. +//! +//! Threading: openh264's `num_threads` is documented upstream as "will probably just +//! segfault", so this stays single-threaded — the old FFmpeg rung's slice threading has +//! no equivalent here. rav1d gets the machine's cores: `max_frame_delay = 1` is the knob +//! that removes frame delay (dav1d's `get_num_threads` then computes `n_fc = min(1, n_tc)`, +//! so exactly one frame is ever in flight whatever `n_threads` says), and `n_threads` +//! drives the INTRA-frame tile/row workers, which cost no latency at all. Pinning it to 1 +//! bought nothing and gave the rung reached only because the GPU already failed a single +//! core to decode 4K with. -use crate::video::{averr, CpuFrame}; +use crate::video::{CpuPlanarFrame, RungLoss}; use crate::video_color::ColorDesc; -use anyhow::{anyhow, Context as _, Result}; -use ffmpeg::format::Pixel; -use ffmpeg::software::scaling; -use ffmpeg::util::frame::Video as AvFrame; -use ffmpeg_next as ffmpeg; -use std::ptr; +use anyhow::{anyhow, bail, Context as _, Result}; +use pf_bitstream::h264::{H264Planner, PlanError}; +use pf_vkdecode::RecoveryWatch; + +/// The codecs this rung can decode at all. Deliberately its own enum rather than an +/// `ffmpeg::codec::Id`: the whole point of M8 is that nothing in here speaks FFmpeg, and +/// the ladder above still does only because its other rungs are not swapped yet (M10). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum SwCodec { + H264, + Av1, +} + +impl SwCodec { + /// The wire codec bit this rung can serve, or `None` — the one place the + /// "which codecs does software cover" question is answered. + pub(crate) fn for_wire(codec: u8) -> Option { + match codec { + punktfunk_core::quic::CODEC_H264 => Some(SwCodec::H264), + punktfunk_core::quic::CODEC_AV1 => Some(SwCodec::Av1), + _ => None, + } + } +} + +/// This build has no software decoder for the session's stream — the ladder has run out +/// of rungs. +/// +/// A distinct type, not a formatted string, because the SESSION layer must be able to +/// tell this apart from every other decode failure: everything else is survivable (feed +/// the next AU, ask for an IDR), and this one is not survivable at all — it can only be +/// answered by reconnecting with something this client can actually decode. It rides out +/// through `anyhow` and is recovered with `downcast_ref`, so no signature in the ladder +/// changes shape for it. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NoSoftwareRung { + /// The `quic::CODEC_*` bit of the session that has no CPU rung. + pub codec: u8, + /// `None` — the CODEC itself has no CPU decoder (HEVC). `Some(what)` — the codec + /// does, but not for THIS stream's picture shape (10-bit, 4:4:4). + /// + /// The two are one type on purpose. They are different diagnoses but the SAME + /// available action: the codec is fixed at Welcome, so a shape this rung cannot + /// decode can only be escaped the way a codec it cannot decode is — a reconnect that + /// takes the codec off the table, after which the host resolves a new shape too. A + /// blunt instrument for the shape case, and the only one the wire offers. + /// + /// The shape case cannot be answered at construction alone: a Windows HDR desktop + /// flips to Main 10 IN-BAND with a new parameter set, so the Welcome's + /// [`crate::video::StreamFormat`] can say 8-bit for a session that becomes 10-bit + /// mid-stream. That is why this is raised from the per-AU path, off the bitstream's + /// own headers, rather than from a negotiated field. + pub shape: Option<&'static str>, +} + +impl NoSoftwareRung { + /// Which diagnosis this is, for the reconnect rule + /// ([`last_rung_verdict`](crate::video::last_rung_verdict)). The two answers differ: + /// a missing CODEC means every hardware rung already failed, a missing SHAPE means + /// none of them was even asked. + pub fn loss(&self) -> RungLoss { + match self.shape { + None => RungLoss::Codec, + Some(_) => RungLoss::Shape, + } + } +} + +impl std::fmt::Display for NoSoftwareRung { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let codec = crate::video::wire_codec_name(self.codec); + match self.shape { + None => write!( + f, + "no software decoder for {codec} — this build decodes H.264 and AV1 on \ + the CPU (there is no permissively licensed software HEVC decoder)" + ), + Some(shape) => write!( + f, + "the software {codec} decoder cannot decode this stream: {shape} \ + (the CPU rung is 8-bit 4:2:0 only)" + ), + } + } +} + +impl std::error::Error for NoSoftwareRung {} // --- software backend --------------------------------------------------------------- pub(crate) struct SoftwareDecoder { - decoder: ffmpeg::decoder::Video, - /// Rebuilt whenever the decoded format/size — or the colour signaling (a mid-stream - /// SDR↔HDR flip) — changes. - sws: Option<(scaling::Context, Pixel, u32, u32, ColorDesc)>, + inner: Inner, + /// Last colour signalling the stream actually stated. Held across AUs the metadata + /// parser could not read (see [`H264Software::colour_of`]) so a stream never silently + /// reverts to the SDR default mid-session; seeded with that default, which is what + /// "unspecified" resolves to anyway (`csc_rows`). + color: ColorDesc, +} + +enum Inner { + H264(H264Software), + Av1(Av1Software), } impl SoftwareDecoder { - pub(crate) fn new(codec_id: ffmpeg::codec::Id) -> Result { - let codec = ffmpeg::decoder::find(codec_id) - .ok_or_else(|| anyhow!("no {codec_id:?} decoder in libavcodec"))?; - let mut ctx = ffmpeg::codec::Context::new_with_codec(codec); - // SAFETY: `as_mut_ptr` yields the `AVCodecContext` behind the `ctx` allocated on the line - // above, which outlives these writes; each store is an in-bounds scalar field write on that - // live context, made before the decoder is opened and reads them. - unsafe { - let raw = ctx.as_mut_ptr(); - (*raw).flags |= ffmpeg::ffi::AV_CODEC_FLAG_LOW_DELAY as i32; - // Slice threading adds no frame delay (frame threading adds thread_count-1). - (*raw).thread_type = ffmpeg::ffi::FF_THREAD_SLICE; - (*raw).thread_count = 0; // auto - } - let decoder = ctx.decoder().video().context("open video decoder")?; - // Every construction site (session open, preference, mid-stream demotion) says - // which decoder actually opened: for AV1 the ID lookup means libdav1d here — - // deliberately (fastest CPU path; the native `av1` decoder has no software - // path at all) — and the name in the log is what keeps that distinguishable - // from the hardware lanes' capability-selected decoders. - tracing::info!(?codec_id, decoder = codec.name(), "software decoder opened"); - Ok(SoftwareDecoder { decoder, sws: None }) + /// Build the CPU rung for a WIRE codec bit. + /// + /// `Err` carrying a [`NoSoftwareRung`] means "there is no such rung", not "the rung + /// failed to start" — the two are different questions for the caller and must not + /// collapse into one string. + pub(crate) fn new(codec: u8) -> Result { + let Some(sw) = SwCodec::for_wire(codec) else { + return Err(NoSoftwareRung { codec, shape: None }.into()); + }; + let inner = match sw { + SwCodec::H264 => Inner::H264(H264Software::new()?), + SwCodec::Av1 => Inner::Av1(Av1Software::new()?), + }; + tracing::info!( + codec = crate::video::wire_codec_name(codec), + decoder = match sw { + SwCodec::H264 => "openh264", + SwCodec::Av1 => "rav1d", + }, + "software decoder opened (CPU, planar output)" + ); + Ok(SoftwareDecoder { + inner, + // "Unspecified" everywhere: `csc_rows` resolves that to BT.709 limited, the + // host's SDR default, which is also what E.2.1 inference produces for a + // stream whose VUI is silent. + color: ColorDesc { + primaries: 2, + transfer: 2, + matrix: 2, + full_range: false, + }, + }) } - pub(crate) fn decode(&mut self, au: &[u8]) -> Result> { - let packet = ffmpeg::Packet::copy(au); - self.decoder - .send_packet(&packet) - .map_err(|e| anyhow!("send_packet: {e}"))?; - let mut frame = AvFrame::empty(); - let mut out = None; - while self.decoder.receive_frame(&mut frame).is_ok() { - out = Some(self.convert_rgba(&frame)?); + pub(crate) fn decode(&mut self, au: &[u8]) -> Result> { + match &mut self.inner { + Inner::H264(h) => h.decode(au, &mut self.color), + Inner::Av1(a) => a.decode(au, &mut self.color), + } + } +} + +// --- H.264 (openh264) ---------------------------------------------------------------- + +struct H264Software { + decoder: openh264::decoder::Decoder, + /// The metadata half: colour signalling, the IDR flag and the display crop, from the + /// same planner every hardware rung submits from. It does NOT drive openh264 — + /// openh264 owns its own parsing — so a plan the narrow envelope refuses costs + /// metadata for that AU, never the picture. + /// + /// Boxed: the planner's DPB dwarfs everything else here, and `Backend` (which holds + /// this by value) is an enum whose other variants are pointer-sized — the same reason + /// the native rungs are boxed there. + planner: Box, + /// The recovery point SEI, folded per picture by the SAME rule the native Vulkan rung + /// uses (`pf-vkdecode`'s watch, unchanged and shared): an intra-refresh session never + /// emits an IDR, so without this the pump's post-loss freeze on THIS rung waits out + /// its 500 ms backstop and then forces the very IDR the wave exists to avoid. + recovery: RecoveryWatch, + /// One warn per session for a stream whose AUs will not plan: the picture is fine + /// (openh264 decodes it), but colour is then whatever the last plannable AU said, + /// and a support engineer must be able to see that from the log rather than infer it + /// from a hue. + plan_warned: bool, +} + +/// Everything the planner tells the software rung about the AU it is ABOUT to submit. +struct AuFacts { + is_idr: bool, + /// `None` = the AU did not plan; the caller keeps the last colour it saw. + color: Option, + recovery: punktfunk_core::reanchor::LocalRecovery, +} + +impl H264Software { + fn new() -> Result { + // Default config: error concealment OFF, logging quiet, one thread. Concealment + // is deliberately not enabled — this rung's contract is that its errors SURFACE + // (the pump turns an `Err` into a keyframe request through the same throttle as + // every other rung), and a decoder quietly inventing macroblocks is precisely + // the "looked clean, wasn't" shape M4's telemetry exists to make impossible. + let decoder = + openh264::decoder::Decoder::new().map_err(|e| anyhow!("openh264 decoder: {e}"))?; + Ok(H264Software { + decoder, + planner: Box::new(H264Planner::new()), + recovery: RecoveryWatch::new(), + plan_warned: false, + }) + } + + fn decode(&mut self, au: &[u8], color: &mut ColorDesc) -> Result> { + // Plan FIRST: the plan describes the AU we are about to decode, and reading it + // after would attribute this picture's colour to the next one on a decoder that + // buffers. + let facts = self.plan_facts(au)?; + if let Some(c) = facts.color { + *color = c; + } + let picture = self + .decoder + .decode(au) + .map_err(|e| anyhow!("openh264 decode: {e}"))?; + let Some(yuv) = picture else { + return Ok(None); + }; + use openh264::formats::YUVSource as _; + let (w, h) = yuv.dimensions(); + let (sy, su, sv) = yuv.strides(); + let frame = CpuPlanarFrame::from_i420( + w as u32, + h as u32, + [yuv.y(), yuv.u(), yuv.v()], + [sy, su, sv], + *color, + facts.is_idr, + facts.recovery, + )?; + Ok(Some(frame)) + } + + /// The IDR flag, the colour and the recovery mark for this AU, from the shared + /// planner. + /// + /// `colour` is `None` when the AU could not be planned — which is NORMAL for the + /// first AUs after a mid-session demotion onto this rung: the parameter sets arrive + /// in-band on the next IDR (which the demotion has already requested), so until it + /// lands the planner has no active SPS and says so. Answering `is_idr = false` there + /// is the conservative direction: it costs the re-anchor gate one frame of patience, + /// where a false `true` would lift a post-loss freeze onto a picture that is still + /// concealed — and the recovery mark is empty for the same reason. + /// + /// `Err` is reserved for the ONE thing that is not a metadata problem: a picture + /// shape this rung cannot decode. It travels as [`NoSoftwareRung`] so the session + /// reconnects instead of erroring per AU forever — see the type's `shape` field for + /// why that answer has to come from here rather than from the Welcome. + fn plan_facts(&mut self, au: &[u8]) -> Result { + match self.planner.plan_au(au) { + Ok(plan) => { + // The envelope, read from the SPS the planner activated for THIS picture + // — so an in-band flip to Main 10 (a Windows HDR desktop) is caught on + // the AU that carries it, not left to openh264 to fail on repeatedly. + if let Some(shape) = unsupported_shape( + plan.picture.chroma_format_idc, + plan.picture.bit_depth_luma_minus8, + ) { + return Err(NoSoftwareRung { + codec: punktfunk_core::quic::CODEC_H264, + shape: Some(shape), + } + .into()); + } + let c = plan.picture.colour; + // The wave's own verdict for this picture. Folded even when openh264 + // then produces nothing: the watch counts `frame_num` increments, so + // skipping a picture would leave the count owing forever. Losing the + // MARK of a picture that never came out only makes the lift late, which + // is the safe direction. + let mark = self.recovery.note_h264( + plan.picture.frame_num, + plan.picture.is_idr, + plan.picture.recovery_point, + ); + Ok(AuFacts { + is_idr: plan.picture.is_idr, + color: Some(ColorDesc { + primaries: c.colour_primaries, + transfer: c.transfer_characteristics, + matrix: c.matrix_coefficients, + full_range: c.video_full_range, + }), + recovery: punktfunk_core::reanchor::LocalRecovery { + sei_here: mark.sei_here, + is_recovery_point: mark.is_recovery_point, + }, + }) + } + Err(e) => { + // `NoActiveParamSet` before the first in-band IDR is expected and says + // nothing; anything else means the stream is outside the envelope the + // hardware rungs plan from, which is worth exactly one line. + if !matches!(e, PlanError::NoActiveParamSet { .. }) && !self.plan_warned { + self.plan_warned = true; + tracing::warn!( + error = %e, + "software rung: AU did not plan — colour signalling and the \ + keyframe flag now follow the last AU that did" + ); + } + Ok(AuFacts { + is_idr: false, + color: None, + recovery: punktfunk_core::reanchor::LocalRecovery::NONE, + }) + } + } + } +} + +/// The CPU rung's picture envelope: 8-bit 4:2:0 and nothing else. `Some(what)` names what +/// falls outside it, for [`NoSoftwareRung::shape`]. +/// +/// Stated once and shared by both legs. Neither decoder is BUILT for anything wider — +/// openh264 has no 4:2:2/4:4:4 or high-bit-depth support at all, and rav1d is compiled +/// here with `bitdepth_8` only — so this is a refusal that reflects the build, not a +/// policy that could drift from it. +fn unsupported_shape(chroma_format_idc: u8, bit_depth_minus8: u8) -> Option<&'static str> { + if bit_depth_minus8 != 0 { + return Some("10-bit or deeper"); + } + if chroma_format_idc != punktfunk_core::quic::CHROMA_IDC_420 { + return Some("chroma other than 4:2:0"); + } + None +} + +// --- AV1 (rav1d) ---------------------------------------------------------------------- + +// rav1d ships dav1d's C ABI as `#[no_mangle] extern "C"` Rust functions over `#[repr(C)]` +// types — there is no linker and no `.so` in sight, but the calling contract is still +// dav1d's, so the FFI discipline below is dav1d's too: every context/picture is owned by +// exactly one value here, and `Drop` closes it exactly once. +use rav1d::include::dav1d::data::Dav1dData; +use rav1d::include::dav1d::dav1d::{Dav1dContext, Dav1dSettings}; +use rav1d::include::dav1d::headers::{Dav1dSequenceHeader, DAV1D_PIXEL_LAYOUT_I420}; +use rav1d::include::dav1d::picture::Dav1dPicture; +use rav1d::src::lib::{ + dav1d_close, dav1d_data_create, dav1d_data_unref, dav1d_default_settings, dav1d_get_picture, + dav1d_open, dav1d_parse_sequence_header, dav1d_picture_unref, dav1d_send_data, +}; +use std::ptr::NonNull; + +struct Av1Software { + /// `None` only between `Drop` taking it and the close returning — every other + /// observer sees a live context. + ctx: Option, +} + +/// An owned `Dav1dData`, unref'd exactly once on drop. +/// +/// The same shape (and the same lesson) as the FFmpeg rungs' `AvBuffer`: the send loop +/// below has several fallible exits between allocating the buffer and dav1d taking its +/// reference, and hand-unref'ing on each one is how a leak per failed AU gets written. +/// dav1d zeroes the struct when it takes the reference, so an already-consumed `Dav1dData` +/// drops to a no-op and the double-unref this type prevents cannot happen either. +struct Av1Data(Dav1dData); + +impl Av1Data { + fn create(au: &[u8]) -> Result { + let mut data = Dav1dData::default(); + // SAFETY: `data` is a live local; `dav1d_data_create` either writes an allocated + // buffer of `au.len()` bytes into it and returns its start, or returns null. + let buf = unsafe { dav1d_data_create(NonNull::new(&mut data as *mut Dav1dData), au.len()) }; + if buf.is_null() { + bail!("rav1d: could not allocate {} bytes for an AU", au.len()); + } + // SAFETY: `buf` is the start of the `au.len()`-byte allocation just returned, and + // `au` is a distinct live slice of exactly that length. + unsafe { std::ptr::copy_nonoverlapping(au.as_ptr(), buf, au.len()) }; + Ok(Av1Data(data)) + } +} + +impl Drop for Av1Data { + fn drop(&mut self) { + // SAFETY: `self.0` is a live `Dav1dData` this value solely owns (no `Clone`, and + // `Drop` runs once). `dav1d_data_unref` releases whatever reference is left — none + // when a successful `dav1d_send_data` already took it — and rewrites the struct. + unsafe { dav1d_data_unref(NonNull::new(&mut self.0)) }; + } +} + +// SAFETY: `Dav1dContext` is a refcounted handle dav1d documents as usable from one +// thread at a time; this type owns it exclusively (no `Clone`, no `Sync`) and it lives +// on the pump thread with the rest of the decoder — the same promise every other backend +// in this crate makes for its device handles. +unsafe impl Send for Av1Software {} + +impl Av1Software { + fn new() -> Result { + let mut settings = std::mem::MaybeUninit::::uninit(); + // SAFETY: `dav1d_default_settings` fully initializes the `Dav1dSettings` behind + // the pointer it is given; the storage is a live local that outlives the call. + let mut settings = unsafe { + dav1d_default_settings(NonNull::new_unchecked(settings.as_mut_ptr())); + settings.assume_init() + }; + // No frame delay: a punktfunk stream is zero-reorder and real-time, so the + // throughput a FRAME-threaded decoder buys costs exactly the latency this client + // spends the rest of its budget defending. `max_frame_delay = 1` is the knob that + // says so — dav1d's `get_num_threads` derives `n_fc = min(max_frame_delay, n_tc)`, + // so one frame stays in flight no matter how many threads exist. Same reasoning + // as the old FFmpeg rung's `FF_THREAD_SLICE` + `AV_CODEC_FLAG_LOW_DELAY`, and + // `n_threads` is that rung's SLICE half: intra-frame tile/row workers, which add + // no delay. Capped at 8 — this is the rung reached because the GPU already + // failed, and it should not also take the machine over. + settings.max_frame_delay = 1; + settings.n_threads = std::thread::available_parallelism() + .map(|n| n.get().clamp(1, 8)) + .unwrap_or(1) as i32; + // Film grain synthesis is a post-process the hosts never signal and nobody can + // afford on the rung that exists because the GPU already failed. + settings.apply_grain = 0; + let mut ctx: Option = None; + // SAFETY: both pointers are live locals for the duration of the call, which is + // dav1d_open's whole contract: it reads `settings` and writes the context out. + let r = unsafe { + dav1d_open( + NonNull::new(&mut ctx as *mut Option), + NonNull::new(&mut settings as *mut Dav1dSettings), + ) + }; + if r.0 < 0 || ctx.is_none() { + bail!("rav1d (dav1d) decoder open failed: {}", r.0); + } + Ok(Av1Software { ctx }) + } + + fn decode(&mut self, au: &[u8], color: &mut ColorDesc) -> Result> { + let ctx = self.ctx.context("rav1d context closed")?; + if au.is_empty() { + return Ok(None); + } + // The envelope, off the BITSTREAM's own sequence header, before a byte reaches + // the decoder — exactly what the H.264 leg does with `plan_au`, and for the same + // reason. rav1d is compiled `bitdepth_8` only, so a 10-bit frame makes + // `rav1d_submit_frame` refuse with `ENOPROTOOPT`; that refusal is a per-AU error + // the pump would answer with a keyframe request forever, on a stream where every + // following AU is identically 10-bit. This is the shipping case, not a corner: + // AV1 is advertised only where hardware AV1 exists, hardware AV1 + HDR is Main 10, + // and a mid-session hardware failure demotes here. + if let Some(shape) = self.unsupported_sequence(au) { + return Err(NoSoftwareRung { + codec: punktfunk_core::quic::CODEC_AV1, + shape: Some(shape), + } + .into()); + } + // A `Dav1dData` that owns its own copy: `dav1d_data_create` allocates, we fill + // it, and `dav1d_send_data` takes the reference on success. Deliberately not + // `dav1d_data_wrap` over the caller's `au` — that would hand the decoder a + // borrow of a buffer the pump reuses on the next AU. + let mut data = Av1Data::create(au)?; + // dav1d consumes `data` incrementally: a partial send leaves bytes in it and asks + // to be re-sent. A punktfunk AU is one temporal unit and the decoder is drained + // every call, so the loop is bounded by the AU — but it is a LOOP, because + // `EAGAIN` here means "take pictures out first", not "the AU is bad". + // + // Only the NEWEST picture survives, which matches every other backend's + // `decode -> Option` contract (the old libav rung's `while receive_frame` + // did the same). It matters more here than elsewhere because an AV1 temporal unit + // really can carry several shown frames — but this is the rung reached after the + // hardware already failed, and showing the newest is the same answer the pump's + // newest-wins frame queue would give a moment later anyway. + let mut out: Option = None; + loop { + // SAFETY: `ctx` is the live context from `dav1d_open` (not yet closed) and + // `data.0` is a live local dav1d is allowed to read from and write to. Its + // reference is taken by dav1d on success; the guard's `Drop` releases only + // what is left. + let r = unsafe { dav1d_send_data(Some(ctx), NonNull::new(&mut data.0)) }; + let sent = r.0 >= 0; + if !sent && dav1d_errno(r) != Some(libc::EAGAIN) { + // A shape the build cannot decode reaches here only if it slipped past + // the sequence-header check above (an AU whose OBUs carry no sequence + // header of their own). Still typed rather than generic: the pump's + // survivable branch would ask for a keyframe and get the same refusal on + // every AU for the rest of the session — a permanent freeze with no + // fallback, which is the one outcome this rung exists to end. + if dav1d_errno(r) == Some(libc::ENOPROTOOPT) { + return Err(NoSoftwareRung { + codec: punktfunk_core::quic::CODEC_AV1, + shape: Some("10-bit or deeper"), + } + .into()); + } + bail!("rav1d send_data: {}", r.0); + } + match self.take_picture(ctx, color)? { + Some(f) => out = Some(f), + // Nothing more to take and the AU is fully consumed — done. + None if sent && data.0.sz == 0 => break, + // Nothing to take and the decoder still would not accept the rest: it + // has neither produced nor consumed, which is a wedge, not back-pressure. + None if !sent => bail!("rav1d: decoder accepted no data and produced no picture"), + None => {} + } } Ok(out) } - fn convert_rgba(&mut self, frame: &AvFrame) -> Result { - let (fmt, w, h) = (frame.format(), frame.width(), frame.height()); - // SAFETY: `frame.as_ptr()` is the decoder-owned live AVFrame for this call. - let color = unsafe { ColorDesc::from_raw(frame.as_ptr()) }; - let rebuild = !matches!(&self.sws, - Some((_, f, sw, sh, c)) if *f == fmt && *sw == w && *sh == h && *c == color); - if rebuild { - let mut ctx = - scaling::Context::get(fmt, w, h, Pixel::RGBA, w, h, scaling::Flags::POINT) - .context("swscale context")?; - // swscale defaults to BT.601 coefficients — set them from the FRAME's signaling - // (unspecified → BT.709 limited, the host's SDR default; a Windows HDR desktop - // streams BT.2020 in-band). Without this, YUV→RGB decodes with the wrong matrix - // and colours shift. Destination = full-range RGB; the transfer function stays - // baked in (the presenter tags PQ textures so GTK applies the EOTF). - const SWS_CS_ITU709: i32 = 1; - const SWS_CS_ITU601: i32 = 5; - const SWS_CS_BT2020: i32 = 9; - let cs = match color.matrix { - 9 | 10 => SWS_CS_BT2020, - 5 | 6 => SWS_CS_ITU601, - _ => SWS_CS_ITU709, - }; - // SAFETY: `sws_getCoefficients` returns a pointer into libav's own static coefficient - // tables — valid for the process, read-only — and `sws_setColorspaceDetails` takes it - // plus the live `SwsContext` behind `ctx` and plain scalars. - unsafe { - let coeffs = ffmpeg::ffi::sws_getCoefficients(cs); - ffmpeg::ffi::sws_setColorspaceDetails( - ctx.as_mut_ptr(), - coeffs, // inv_table: source (YUV) coefficients per the VUI - color.full_range as i32, // srcRange: 0 = limited/studio (MPEG) - coeffs, // table: destination coefficients (ignored for RGB output) - 1, // dstRange: 1 = full-range RGB - 0, - 1 << 16, - 1 << 16, // brightness, contrast, saturation (defaults) - ); - } - self.sws = Some((ctx, fmt, w, h, color)); - } - let (sws, ..) = self.sws.as_mut().unwrap(); - // Single-pass conversion: swscale writes straight into the Vec the texture will - // wrap. (The old path scaled into a scratch AVFrame and then copied `data(0)` out - // — a second full-frame pass per frame.) 64-byte row alignment keeps swscale on - // aligned SIMD stores; `GdkMemoryTexture` takes the resulting stride explicitly. - const ALIGN: i32 = 64; - use ffmpeg::ffi; - let dst_fmt = ffi::AVPixelFormat::AV_PIX_FMT_RGBA; - // SAFETY: pure size computation from format/dimensions; no pointers involved. - let size = unsafe { ffi::av_image_get_buffer_size(dst_fmt, w as i32, h as i32, ALIGN) }; - if size < 0 { - return Err(averr("av_image_get_buffer_size", size)); - } - let rgba = vec![0u8; size as usize]; - let mut dst_data: [*mut u8; 4] = [ptr::null_mut(); 4]; - let mut dst_linesize: [i32; 4] = [0; 4]; - // SAFETY: fill_arrays only derives plane pointers/strides into `rgba` (sized by - // av_image_get_buffer_size above, same format/align) — no allocation, no - // ownership transfer; `rgba` outlives the scale below. + /// This AU's sequence header against the build's envelope: `Some(what)` names what + /// falls outside it, `None` means "8-bit 4:2:0, or this AU carries no sequence header + /// of its own". + /// + /// An AU without one is the AV1 twin of the H.264 leg's `NoActiveParamSet`: it says + /// nothing, so it decodes against whatever sequence the decoder already holds — which + /// a previous AU was checked for. Punktfunk hosts re-send the sequence header on every + /// key frame, and the demotion onto this rung asks for one immediately, so the first + /// AU this rung ever decodes carries one. + fn unsupported_sequence(&self, au: &[u8]) -> Option<&'static str> { + let mut seq = std::mem::MaybeUninit::::uninit(); + // SAFETY: `out` is a live local this call either fully writes or leaves untouched + // (it writes only on success), and `au` is a live slice of exactly `au.len()` + // bytes. Nothing is allocated or referenced: dav1d fills the struct by value. let r = unsafe { - ffi::av_image_fill_arrays( - dst_data.as_mut_ptr(), - dst_linesize.as_mut_ptr(), - rgba.as_ptr(), - dst_fmt, - w as i32, - h as i32, - ALIGN, + dav1d_parse_sequence_header( + NonNull::new(seq.as_mut_ptr()), + NonNull::new(au.as_ptr().cast_mut()), + au.len(), ) }; - if r < 0 { - return Err(averr("av_image_fill_arrays", r)); + if r.0 < 0 { + return None; // no sequence header here (ENOENT), or an AU we cannot read } - // SAFETY: src pointers/strides belong to the decoder-owned `frame` (alive for the - // call); dst pointers were just filled over `rgba`, and sws_scale writes rows - // [0, h) only — exactly the buffer fill_arrays sized. - let r = unsafe { - ffi::sws_scale( - sws.as_mut_ptr(), - (*frame.as_ptr()).data.as_ptr() as *const *const u8, - (*frame.as_ptr()).linesize.as_ptr(), - 0, - h as i32, - dst_data.as_ptr(), - dst_linesize.as_ptr(), - ) - }; - if r < 0 { - return Err(averr("sws_scale", r)); + // SAFETY: the call returned success, which is its contract for having written + // the whole `Dav1dSequenceHeader`. + let seq = unsafe { seq.assume_init() }; + if seq.hbd != 0 { + return Some("10-bit or deeper"); } - Ok(CpuFrame { - width: w, - height: h, - stride: dst_linesize[0] as usize, - rgba, - color, - // `is_key()` reads the same intra flag `frame_is_keyframe` derives from pict_type - // for the hardware paths; ffmpeg-next handles the FFmpeg-version binding split. - keyframe: frame.is_key(), - }) + if seq.layout != DAV1D_PIXEL_LAYOUT_I420 { + return Some("chroma other than 4:2:0"); + } + None } + + /// One picture out of the decoder, converted. `None` = nothing ready yet. + fn take_picture( + &self, + ctx: Dav1dContext, + color: &mut ColorDesc, + ) -> Result> { + let mut pic = Dav1dPicture::default(); + // SAFETY: `ctx` is live and `pic` is a live local dav1d writes the picture into. + let r = + unsafe { dav1d_get_picture(Some(ctx), NonNull::new(&mut pic as *mut Dav1dPicture)) }; + if dav1d_errno(r) == Some(libc::EAGAIN) { + return Ok(None); + } + if r.0 < 0 { + bail!("rav1d get_picture: {}", r.0); + } + // From here the picture is OURS and must be unref'd on every exit — including the + // refusals below, which is why the conversion is a closure and the unref is not + // in a branch. + let converted = Self::convert(&pic, color); + // SAFETY: `pic` is the live picture `dav1d_get_picture` just wrote; this releases + // exactly the one reference it handed over, once. + unsafe { dav1d_picture_unref(NonNull::new(&mut pic as *mut Dav1dPicture)) }; + converted.map(Some) + } + + fn convert(pic: &Dav1dPicture, color: &mut ColorDesc) -> Result { + // 8-bit 4:2:0 only, stated as a refusal rather than assumed — and as the SAME + // typed refusal the H.264 leg raises, so a shape this rung cannot decode + // reconnects instead of erroring once per AU for the rest of the session. + // Treating a 4:4:4 picture's planes as 4:2:0 would decode correctly and display + // wrong, which is the class this program exists to refuse. + // + // A BELT, not the gate: `unsupported_sequence` refuses these shapes before the + // AU is submitted, and a `bitdepth_8`-only build cannot produce a 10-bit picture + // anyway (`rav1d_submit_frame` refuses the frame setup). Kept because it costs + // two comparisons and because the day this build gains `bitdepth_16` the layout + // half stops being redundant. + let shape = if pic.p.bpc != 8 { + Some("10-bit or deeper") + } else if pic.p.layout != DAV1D_PIXEL_LAYOUT_I420 { + Some("chroma other than 4:2:0") + } else { + None + }; + if let Some(shape) = shape { + return Err(NoSoftwareRung { + codec: punktfunk_core::quic::CODEC_AV1, + shape: Some(shape), + } + .into()); + } + let (w, h) = (pic.p.w.max(0) as u32, pic.p.h.max(0) as u32); + // Colour rides the SEQUENCE header, which AV1 re-sends whenever it changes — the + // same per-picture contract the H.264 leg gets from the SPS, so an in-band + // SDR↔HDR flip is followed rather than latched. + if let Some(seq) = pic.seq_hdr { + // SAFETY: `seq_hdr` belongs to the picture we hold a reference to, so it is + // live for this call; these are plain scalar field reads. + let seq = unsafe { seq.as_ref() }; + *color = ColorDesc { + primaries: seq.pri as u8, + transfer: seq.trc as u8, + matrix: seq.mtrx as u8, + full_range: seq.color_range != 0, + }; + } + let keyframe = pic.frame_hdr.is_some_and(|f| { + // SAFETY: same as `seq_hdr` above — owned by the live picture, scalar read. + unsafe { f.as_ref() }.frame_type == rav1d::include::dav1d::headers::DAV1D_FRAME_TYPE_KEY + }); + let (_, ch) = CpuPlanarFrame::chroma_dims(w, h); + // dav1d gives ONE chroma stride for both planes (`stride[1]`), which is why the + // triple below repeats it rather than looking for a third. + let strides = [ + pic.stride[0].max(0) as usize, + pic.stride[1].max(0) as usize, + pic.stride[1].max(0) as usize, + ]; + let sizes = [ + h as usize * strides[0], + ch as usize * strides[1], + ch as usize * strides[2], + ]; + let mut planes: [&[u8]; 3] = [&[], &[], &[]]; + for i in 0..3 { + let p = pic.data[i].with_context(|| format!("rav1d: plane {i} is null"))?; + // SAFETY: dav1d's picture contract is that plane `i` spans + // `height_of_plane * |stride|` bytes from `data[i]`, and the picture holds a + // reference to that allocation for as long as we do (unref'd by the caller, + // after this conversion returns). Negative strides (bottom-up pictures) are + // rejected above by the `max(0)` collapsing them to a zero size, which the + // copy below then refuses. + planes[i] = unsafe { std::slice::from_raw_parts(p.as_ptr().cast::(), sizes[i]) }; + } + // No local-recovery answer from this leg: AV1 has no recovery point SEI, and its + // intra-refresh equivalent (`frame_refs_short_signaling` / S-frames) is not + // something a punktfunk host emits — so the pump's re-anchor behaviour on AV1 is + // exactly the wire's, as it is on every lane but H.264/H.265. + CpuPlanarFrame::from_i420( + w, + h, + planes, + strides, + *color, + keyframe, + punktfunk_core::reanchor::LocalRecovery::NONE, + ) + } +} + +impl Drop for Av1Software { + fn drop(&mut self) { + if self.ctx.is_none() { + return; + } + // SAFETY: `self.ctx` is the one context `dav1d_open` produced and this is its + // sole owner, so this runs exactly once; `dav1d_close` takes it through the + // `&mut` and leaves `None`. + unsafe { dav1d_close(NonNull::new(&mut self.ctx as *mut Option)) }; + } +} + +/// The errno behind a `Dav1dResult`, or `None` for success. +/// +/// rav1d returns the NEGATED errno as a plain `c_int`, and the codes are `libc`'s own +/// (`Rav1dError::ENOPROTOOPT = libc::ENOPROTOOPT as u8`) — so they are matched against +/// `libc`'s rather than written out. Not a nicety: `EAGAIN` is 11 everywhere but +/// `ENOPROTOOPT` is **92 on Linux and 123 on Windows**, and a literal would therefore be +/// right on exactly one platform. The typed enum this would rather match on +/// (`Rav1dError`) lives in a `pub(crate)` module — rav1d re-exports only `Dav1dResult` — +/// so the errno is the only handle the crate actually offers. +fn dav1d_errno(r: rav1d::Dav1dResult) -> Option { + (r.0 < 0).then_some(-r.0) } #[cfg(test)] mod tests { use super::*; + use crate::video::csc_rows; - /// The wire → `ColorDesc` plumbing: an HDR10 stream's VUI (BT.2020 primaries, PQ - /// transfer, BT.2020-NCL matrix, limited range) must arrive on the decoded frame — - /// this is what the Windows host emits in-band for an HDR desktop, and mis-rendering - /// it as BT.709 is the washed-out-colors bug. Fixture: one 64×64 Main10 IDR - /// (`tests/pq-frame.h265`, x265 with explicit VUI). - #[test] - fn software_decode_carries_pq_signaling() { - let au = include_bytes!("../tests/pq-frame.h265"); - let mut dec = SoftwareDecoder::new(ffmpeg::codec::Id::HEVC).expect("hevc decoder"); - let mut got = dec.decode(au).expect("decode"); - if got.is_none() { - // Low-delay decoders may still hold the frame until a flush — send EOF. - dec.decoder.send_eof().ok(); - let mut frame = AvFrame::empty(); - if dec.decoder.receive_frame(&mut frame).is_ok() { - got = Some(dec.convert_rgba(&frame).expect("convert")); - } - } - let f = got.expect("no frame decoded from the PQ fixture"); - assert_eq!( - f.color, - ColorDesc { - primaries: 9, - transfer: 16, - matrix: 9, - full_range: false - } - ); - assert!(f.color.is_pq()); - assert_eq!((f.width, f.height), (64, 64)); + /// The nine bars every fixture encodes, in x order: eight fully-saturated + /// primaries/secondaries plus black and white, and then the one that carries the + /// RANGE axis. + /// + /// ⚠ `(192, 128, 64)` is not decoration. On saturated bars a limited↔full mismatch + /// only pushes values outside [0, 1], where the shader clamps — so the 709-FULL + /// fixture decoded with the WRONG range comes back with max error **0** over the + /// eight, and this test could not fail on range at all. Measured on these fixtures: + /// the mid-tone gives max error 11 under the wrong range. A 50% grey does not do the + /// job either (3, inside the ±4 tolerance) — it has to be OFF-neutral, so the chroma + /// scale is exercised and not just the luma one. + const BARS: [(u8, u8, u8); 9] = [ + (255, 255, 255), + (255, 255, 0), + (0, 255, 255), + (0, 255, 0), + (255, 0, 255), + (255, 0, 0), + (0, 0, 255), + (0, 0, 0), + (192, 128, 64), + ]; + + /// The presenter's planar CSC shader, on the CPU: sample the three planes and apply + /// `csc_rows` exactly as `planar_csc.frag` does (`rgb[i] = dot(r[i].xyz, yuv) + + /// r[i].w`, then clamp). 8-bit, no MSB packing — the software rung's only shape. + /// + /// This is a MODEL of the shader, and it is the honest one: `csc_rows` is the single + /// coefficient implementation the shader's push constants are filled from (and the + /// Windows client's constant buffer, and the Apple client's Swift port), so what this + /// exercises end to end is exactly what changes colour on screen — the decoder's + /// plane layout, and the `ColorDesc` it read out of the bitstream. Sampling is + /// nearest at bar centres, which is where the shader's quarter-texel 4:2:0 siting + /// correction and its linear filter both make no difference. + fn shader_rgb(f: &CpuPlanarFrame, x: u32, y: u32) -> [u8; 3] { + let rows = csc_rows(f.color, 8, false); + let (cw, _) = CpuPlanarFrame::chroma_dims(f.width, f.height); + let luma = f.plane(0)[(y * f.width + x) as usize]; + let (cx, cy) = (x / 2, y / 2); + let cb = f.plane(1)[(cy * cw + cx) as usize]; + let cr = f.plane(2)[(cy * cw + cx) as usize]; + let yuv = [luma as f32 / 255.0, cb as f32 / 255.0, cr as f32 / 255.0]; + core::array::from_fn(|i| { + let v = rows[i][0] * yuv[0] + rows[i][1] * yuv[1] + rows[i][2] * yuv[2] + rows[i][3]; + (v.clamp(0.0, 1.0) * 255.0).round() as u8 + }) } - /// Golden colour fixtures: one 256×64 LOSSLESS x265 IDR of 8 fully-saturated colour bars per - /// signaling variant (generated offline with ffmpeg/libx265; the RGB→YUV conversion matched - /// to the VUI each fixture declares, so the original RGB is recoverable ±1 code). Decoding - /// through the real CPU path (`SoftwareDecoder` → per-frame `ColorDesc` → swscale with the - /// signaled matrix/range) must reproduce the bars — the end-to-end guard for the - /// signaling-driven CSC across BT.601/709 × limited/full. A hardcoded-709 regression fails - /// the 601 fixture by tens of code points; a range mix-up fails the full-range one. + fn decode_one(codec: u8, au: &[u8]) -> CpuPlanarFrame { + let mut dec = SoftwareDecoder::new(codec).expect("software decoder"); + dec.decode(au) + .expect("decode") + .expect("no frame out of the fixture") + } + + /// **M8's exit criterion.** Three lossless-ish H.264 colour-bar fixtures whose VUIs + /// differ ONLY in matrix and range (see `tests/gen-bars.sh` for the recipe): decode + /// each through the real CPU rung, then convert with the real `csc_rows`, and require + /// the original RGB back. + /// + /// What it would have caught, one failure per axis: + /// + /// * **The BT.601 default** — the bug the deleted `convert_rgba` carried explicit + /// correction code for. swscale converts with BT.601 coefficients unless told + /// otherwise, so a rung that dropped the signalling (or hardcoded one matrix) + /// renders the 601 fixture with 709 coefficients or vice versa. On the saturated + /// bars that is tens of code points — e.g. pure red's green channel goes from 0 to + /// ~+40 — far outside the ±4 tolerance (measured on these fixtures: max error 22 + /// for 709 read as 601, 39 the other way). + /// * **Range** — the 709-full fixture differs from 709-limited by the 16..235 vs + /// 0..255 expansion only. ⚠ On the eight saturated bars this axis CANNOT fail: + /// every one of them is at an extreme, so a mismatch only pushes values outside + /// [0, 1] where the shader clamps, and the fixture decodes with max error **0** + /// under the wrong range. The ninth bar, `(192, 128, 64)`, is what makes the axis + /// testable (max error 11 wrong-range, ~1 right) — see [`BARS`]. + /// * **Plane order and stride** — a Cb/Cr swap turns red into blue, and a stride + /// mistake shears the bars sideways, so both show up as a wrong bar rather than a + /// wrong shade. + /// + /// It is deliberately NOT a "did it decode" test: every assertion is a pixel value + /// that depends on the colour signalling surviving the whole path. #[test] - fn software_decode_reproduces_golden_bars() { - const BARS: [(u8, u8, u8); 8] = [ - (255, 255, 255), - (255, 255, 0), - (0, 255, 255), - (0, 255, 0), - (255, 0, 255), - (255, 0, 0), - (0, 0, 255), - (0, 0, 0), - ]; + fn software_h264_reproduces_the_golden_bars_in_both_ranges() { let fixtures: [(&str, &[u8], ColorDesc); 3] = [ ( "601-limited", - include_bytes!("../tests/bars-601-limited.h265"), + include_bytes!("../tests/bars-601-limited.h264"), ColorDesc { primaries: 1, transfer: 1, @@ -228,7 +822,7 @@ mod tests { ), ( "709-limited", - include_bytes!("../tests/bars-709-limited.h265"), + include_bytes!("../tests/bars-709-limited.h264"), ColorDesc { primaries: 1, transfer: 1, @@ -238,39 +832,264 @@ mod tests { ), ( "709-full", - include_bytes!("../tests/bars-709-full.h265"), + include_bytes!("../tests/bars-709-full.h264"), ColorDesc { primaries: 1, transfer: 1, matrix: 1, - full_range: true, // the PUNKTFUNK_444_FULLRANGE experiment's signaling + full_range: true, }, ), ]; for (name, au, want_color) in fixtures { - let mut dec = SoftwareDecoder::new(ffmpeg::codec::Id::HEVC).expect("hevc decoder"); - let mut got = dec.decode(au).expect("decode"); - if got.is_none() { - dec.decoder.send_eof().ok(); - let mut frame = AvFrame::empty(); - if dec.decoder.receive_frame(&mut frame).is_ok() { - got = Some(dec.convert_rgba(&frame).expect("convert")); - } - } - let f = got.unwrap_or_else(|| panic!("{name}: no frame decoded")); - assert_eq!(f.color, want_color, "{name}: signaling"); - assert_eq!((f.width, f.height), (256, 64), "{name}: dims"); + let f = decode_one(punktfunk_core::quic::CODEC_H264, au); + assert_eq!(f.color, want_color, "{name}: signalling"); + assert_eq!((f.width, f.height), (288, 64), "{name}: dims"); + assert!(f.keyframe, "{name}: the fixture is a single IDR"); for (i, (r, g, b)) in BARS.iter().enumerate() { - let (cx, cy) = (i * 32 + 16, 32usize); - let o = cy * f.stride + cx * 4; - let px = &f.rgba[o..o + 3]; + let px = shader_rgb(&f, i as u32 * 32 + 16, 32); for (got, want) in px.iter().zip([r, g, b]) { assert!( - got.abs_diff(*want) <= 3, + got.abs_diff(*want) <= 4, "{name} bar {i}: got {px:?}, want ({r},{g},{b})" ); } } } } + + /// The same three fixtures, but asserting the thing a "it decoded" test cannot: the + /// 601 and 709 pictures are DIFFERENT pixels, so a rung that ignored the signalling + /// and converted both with one matrix would still pass a self-consistency check. + /// + /// Guards the fixtures themselves as much as the code — if a regeneration ever + /// produced two identical bitstreams the colour test above would go vacuously green. + #[test] + fn the_601_and_709_fixtures_really_do_carry_different_luma() { + let f601 = decode_one( + punktfunk_core::quic::CODEC_H264, + include_bytes!("../tests/bars-601-limited.h264"), + ); + let f709 = decode_one( + punktfunk_core::quic::CODEC_H264, + include_bytes!("../tests/bars-709-limited.h264"), + ); + // Pure red: Y = 0.299·255 ≈ 76 under 601, 0.2126·255 ≈ 54 under 709 (both then + // range-compressed to 16..235). Same displayed colour, different code points. + let (x, y) = (5 * 32 + 16, 32); + let a = f601.plane(0)[(y * f601.width + x) as usize]; + let b = f709.plane(0)[(y * f709.width + x) as usize]; + assert!( + a.abs_diff(b) > 10, + "601 luma {a} vs 709 luma {b} — the fixtures do not differ, so the colour \ + test above proves nothing" + ); + // ...and after the CSC both land on the same red. + for (f, name) in [(&f601, "601"), (&f709, "709")] { + let px = shader_rgb(f, x, y); + assert!( + px[0].abs_diff(255) <= 4 && px[1] <= 4 && px[2] <= 4, + "{name}: red bar came out {px:?}" + ); + } + } + + /// HEVC has no CPU rung and must say so with the TYPE the session layer keys its + /// reconnect off — not with a string, and not by quietly producing nothing. + #[test] + fn hevc_is_refused_with_the_typed_no_rung_error() { + let err = SoftwareDecoder::new(punktfunk_core::quic::CODEC_HEVC) + .err() + .expect("HEVC must not build a software decoder"); + let typed = err + .downcast_ref::() + .expect("the refusal must survive as NoSoftwareRung through anyhow"); + assert_eq!(typed.codec, punktfunk_core::quic::CODEC_HEVC); + assert_eq!(typed.shape, None, "the CODEC is missing, not a shape"); + assert!(err.to_string().contains("HEVC"), "{err}"); + // And the two codecs that DO have one still build. + assert!(SoftwareDecoder::new(punktfunk_core::quic::CODEC_H264).is_ok()); + assert!(SoftwareDecoder::new(punktfunk_core::quic::CODEC_AV1).is_ok()); + } + + /// A picture shape the CPU rung cannot decode must raise the SAME typed refusal as a + /// missing codec, because it has the same available answer (reconnect) and because + /// the alternative — an `Err` per AU forever, or 8-bit maths over 10-bit samples — is + /// respectively a frozen screen and a wrong one. + /// + /// Exercised as the pure rule plus the two shapes a punktfunk host can actually + /// resolve: Main 10 (an HDR desktop, flipped IN-BAND, which is why the check reads + /// the ACTIVE SPS and not the Welcome) and 4:4:4 (the "Full chroma" opt-in). + #[test] + fn a_shape_the_cpu_rung_cannot_decode_is_the_same_typed_refusal() { + use punktfunk_core::quic::{CHROMA_IDC_420, CHROMA_IDC_444}; + // 8-bit 4:2:0 is the whole envelope. + assert_eq!(unsupported_shape(CHROMA_IDC_420, 0), None); + assert_eq!( + unsupported_shape(CHROMA_IDC_420, 2), + Some("10-bit or deeper") + ); + assert_eq!( + unsupported_shape(CHROMA_IDC_444, 0), + Some("chroma other than 4:2:0") + ); + // Depth is reported FIRST when both are wrong: it is the one that silently + // mis-scales rather than merely mis-siting, so it is the more useful diagnosis. + assert_eq!( + unsupported_shape(CHROMA_IDC_444, 2), + Some("10-bit or deeper") + ); + // And the refusal reaches a caller as the type the session keys its reconnect + // off, with a message that says which stream, not just "decode failed". + let e: anyhow::Error = NoSoftwareRung { + codec: punktfunk_core::quic::CODEC_AV1, + shape: Some("10-bit or deeper"), + } + .into(); + let typed = e.downcast_ref::().expect("typed"); + assert_eq!(typed.shape, Some("10-bit or deeper")); + assert!(e.to_string().contains("AV1"), "{e}"); + assert!(e.to_string().contains("8-bit 4:2:0 only"), "{e}"); + } + + /// The 709-full fixture must be ABLE to fail on the range axis. It could not before + /// the M8 review — every bar was saturated, so a limited↔full mismatch only pushed + /// values past the shader's clamp and the decode came back byte-perfect with the + /// WRONG range honoured. + /// + /// Guards the fixture, not the code: if `BARS` ever loses its mid-tone (or a + /// regeneration drops the ninth bar), the range half of the test above goes vacuous + /// and this is what says so. + #[test] + fn the_full_range_fixture_is_decoded_wrong_by_the_wrong_range() { + let f = decode_one( + punktfunk_core::quic::CODEC_H264, + include_bytes!("../tests/bars-709-full.h264"), + ); + let wrong = ColorDesc { + full_range: false, + ..f.color + }; + let rows = csc_rows(wrong, 8, false); + let (cw, _) = CpuPlanarFrame::chroma_dims(f.width, f.height); + let mut worst = 0u8; + for (i, (r, g, b)) in BARS.iter().enumerate() { + let (x, y) = (i as u32 * 32 + 16, 32u32); + let luma = f.plane(0)[(y * f.width + x) as usize]; + let cb = f.plane(1)[((y / 2) * cw + x / 2) as usize]; + let cr = f.plane(2)[((y / 2) * cw + x / 2) as usize]; + let yuv = [luma as f32 / 255.0, cb as f32 / 255.0, cr as f32 / 255.0]; + let px: [u8; 3] = core::array::from_fn(|c| { + let v = + rows[c][0] * yuv[0] + rows[c][1] * yuv[1] + rows[c][2] * yuv[2] + rows[c][3]; + (v.clamp(0.0, 1.0) * 255.0).round() as u8 + }); + for (got, want) in px.iter().zip([r, g, b]) { + worst = worst.max(got.abs_diff(*want)); + } + } + assert!( + worst > 4, + "decoding the FULL-range fixture as LIMITED was off by only {worst}, inside \ + the ±4 tolerance — the fixture no longer tests the range axis at all" + ); + } + + /// **B1.** A 10-bit AV1 stream must be REFUSED with the typed error, not errored on + /// per AU forever. + /// + /// This is the shipping case, not a corner: AV1 is advertised only where hardware AV1 + /// exists, hardware AV1 + HDR is Main 10, and a mid-session hardware failure demotes + /// onto this rung. rav1d is built `bitdepth_8`, so its frame setup refuses with + /// `ENOPROTOOPT`; before the review that surfaced as a generic `anyhow` the pump read + /// as survivable — keyframe requested, next AU identically 10-bit, screen frozen for + /// the rest of the session with no fallback. + /// + /// The fixture is a whole 10-bit AV1 temporal unit (SVT-AV1, 64x64 red, one key + /// frame) inline rather than on disk: 38 bytes, and what is being tested is the + /// SEQUENCE HEADER inside it. + #[test] + fn a_10bit_av1_stream_is_refused_with_the_typed_no_rung_error() { + const TU_10BIT: [u8; 38] = [ + 0x12, 0x00, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x02, 0xaf, 0xff, 0x8d, 0x5f, 0x38, 0x08, + 0x32, 0x16, 0x10, 0x00, 0xba, 0x02, 0x0b, 0x2c, 0x51, 0x41, 0x00, 0x00, 0x08, 0x00, + 0x95, 0xd1, 0xe2, 0x7e, 0xac, 0x4f, 0x04, 0xad, 0xa4, 0x70, + ]; + let mut dec = SoftwareDecoder::new(punktfunk_core::quic::CODEC_AV1).expect("av1 decoder"); + let err = dec + .decode(&TU_10BIT) + .err() + .expect("a 10-bit AV1 AU must not decode on an 8-bit-only build"); + let typed = err.downcast_ref::().expect( + "the refusal must survive as NoSoftwareRung through anyhow — a generic \ + error here is the permanent freeze this test exists to prevent", + ); + assert_eq!(typed.codec, punktfunk_core::quic::CODEC_AV1); + assert_eq!(typed.shape, Some("10-bit or deeper")); + // ...and the session layer's rule reads it as a SHAPE loss, so the retry is not + // narrowed to codecs with a CPU rung. + assert_eq!(typed.loss(), crate::video::RungLoss::Shape); + // Every following AU raises the same refusal rather than the decoder wedging: + // the pump breaks out on the first one, but a stuck loop here is the failure + // mode, so prove it stays a refusal. + assert!(dec + .decode(&TU_10BIT) + .err() + .and_then(|e| e.downcast_ref::().copied()) + .is_some()); + } + + /// The AV1 leg decodes a real stream and reports the sequence header's own colour. + /// Fixture: the vendored cros-codecs AV1 vector (IVF), whose first temporal unit is a + /// key frame — enough to prove the rav1d FFI (open → send → get → unref → close), + /// the I420 plane copy and the colour read, none of which the H.264 leg exercises. + #[test] + fn software_av1_decodes_and_reports_its_sequence_colour() { + const IVF: &[u8] = include_bytes!( + "../../pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1" + ); + // IVF: 32-byte file header, then per-frame [u32 size][u64 pts][payload]. + let mut off = 32usize; + let mut dec = SoftwareDecoder::new(punktfunk_core::quic::CODEC_AV1).expect("av1 decoder"); + let mut first = None; + let (mut units, mut frames) = (0u32, 0u32); + // Drive the WHOLE vector, not just the first unit: the send loop runs once per + // temporal unit and its `Dav1dData` guard drops on every path through it, so a + // leak or a wedge shows up as the run failing rather than as a slow drift nobody + // reproduces. + while off + 12 <= IVF.len() { + let sz = u32::from_le_bytes(IVF[off..off + 4].try_into().unwrap()) as usize; + off += 12; + if off + sz > IVF.len() { + break; + } + units += 1; + if let Some(f) = dec.decode(&IVF[off..off + sz]).expect("av1 decode") { + frames += 1; + if first.is_none() { + first = Some(f); + } + } + off += sz; + } + assert!( + units > 100, + "expected the full 25 fps vector, got {units} units" + ); + assert_eq!(frames, units, "every temporal unit here shows a picture"); + let f = first.expect("no AV1 frame decoded"); + assert_eq!((f.width, f.height), (320, 240)); + assert!(f.keyframe, "the first temporal unit is a key frame"); + // The vector signals nothing, so E.2.1-equivalent "unspecified" (2) must come + // through UNTOUCHED — `csc_rows` is what resolves it to the BT.709 SDR default, + // and a decoder that resolved it early would make an in-band HDR flip invisible. + assert_eq!(f.color.matrix, 2, "unspecified matrix must survive as 2"); + assert!(!f.color.full_range); + // Planes are tightly packed at the picture's own size — the presenter uploads + // them with no stride, so this invariant is load-bearing, not cosmetic. + assert_eq!(f.plane(0).len(), (f.width * f.height) as usize); + let (cw, ch) = CpuPlanarFrame::chroma_dims(f.width, f.height); + assert_eq!(f.plane(1).len(), (cw * ch) as usize); + assert_eq!(f.plane(2).len(), (cw * ch) as usize); + } } diff --git a/crates/pf-client-core/tests/bars-601-limited.h264 b/crates/pf-client-core/tests/bars-601-limited.h264 new file mode 100644 index 0000000000000000000000000000000000000000..c1bcf75064af1abe9c849449b05c94276d8d51ee GIT binary patch literal 738 zcmXw1OGp(_7@kXLU=LdKvKJ?lAiR&6xsT+=Kp~ool%Pm!Go5?p+&j~qnKSpCWB1-5 zQj7>%)gppg6fPo&77E#-HU=$v5n`oO6o!I?H>ib+Q2R%5^Yi`x|9#&%=bKR!B~q}q zYvxhoxs)E!lnbqbV?0}5>HTIAkv6hM95@^Z`9_PH0cQ=Ox?qmyXrK<#%$&!kWc z5ymOfGHJwsMF3n4%t7 zK6a~u8fqn(RV$j7L>@J4T0x>O2k0;gL={9(O>P-lTm=ynd#fLbf97Lfo*$pe8!v2}_0)X=r4rlC9*(hNk=2|~uIK`Ag4!gtUcXvgT9_DpJ3N1iJ#F}QaD8L6dH4Lp zhim@^Z`R)YX?(x)&dbb$LhAC!xwFr^Yrpy@|BlSupZ(I^QC!@2`bYP|F6(ve<3n-d z(Dak>^ZjF2zN=%4%UgS9_IzH_#_!JUZ{IdFG}uxv9=+S#(Z9V{_3NAFntCp5%I=sR Rc=hR4?~$PsKO6QA{0G1`3^D)! literal 0 HcmV?d00001 diff --git a/crates/pf-client-core/tests/bars-601-limited.h265 b/crates/pf-client-core/tests/bars-601-limited.h265 deleted file mode 100644 index 4865468395d36e0ea6e75d4089d2ff06b8a33453..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4542 zcmZ`-30M?Y5+33a4-|qZY&_^7$r7bufB}^_3gU?%N*2^8LQhY_w9HHo-921UM>HCB zB~cJBRD>vrCW=P!j6y^NBvAq;EWslx zyeG*h6p9H7h2N48iY7uoBo;+3p|zAg7IwMI-Q|%>d=-R>6(fm>@Eq?cm!4kmgd1kN z!>t~(XG8=NG^b{0m1MjR%I4EQS-XrQy}!Obkb8C_#grVRQzL z2Y<9wG8~5)&cNu&Sk6G`SUnlXB!lY^1<|X?30Nex92ZC7cftvkB^H2{q|}rF_Janf zQ94#_StV60g>UES*N%EY^gZ{Gf|jKrVRAyC89hTAMWsS6dCUqT*)vD*lo|toK{4P1 zSOX7vii9*l!Zidf8kLZ7>^*HH1vZ5NlwcRn&>|iw9fU?Dk)mbM+lv5I0*Ew&>S>vP zOM51?td14o2#!F_#rG0Hoveo-X_ALLxXaCc0a?H%1U6Q$Vali+k0YaEvf~YMVHrtrN0-i{^ zj#JA004|EdSHU1r)3O9<0v`oN3t49q;3&l@6$C@^+GJ8n25(LY_&@=^5`_2~DO1uM zwg5maCkR?b5P(s=n=teuueMyI2EtNWF&T1c5Rx$zUKk3q&c*iv0|y{mkYpg8CcO z0LjLb;S!)!X$r3<0K+(`7l=w9s16J?1jA`L-oUdupaBp|FT_YKMVi)8;6oB@rZqgL zqX0)Zpk^VbR5F>!>eXB#zCjCR<|dcQNNdzlFonq{sa`%lxEp$rc*ej%MGGpZu>>Ov zy$hViyoC;fZD==U!&2Q+w}qBW#v4e3LF!1@0$fNfu5+!F#hxV{Zt zfu*0g%YAV6@G5Xc&1gjmuEtXmJh=5^L!;(BrTj|@-i~M0f3a9Kk9!2pEl`wUp_2mZ z1y+Drqrmi*eDAbC0k8khnwFz5+g?8hfN2?EHZ5`(Lo*d2Ie^b!OsOeYR&@ygphbti zMgmN58b<|3AOdsE;|j#9IO~>qE>X&w8d{#^c(2=HvcXIdLozOAcmalyGU86z;~spd zr3CeKH*?v*3k5(IqQL3FX=M^zz+hbI9}o%R3Z)oUJZ2ti2yAAqzj(b(SjHw zxu)W2Jet4zJiQuz*tlBd_d8>2ZCYI;yL^6)G5NyeDEs4cK^#g}EVRhVlNAffe%bHy zbNB)O+J#e5!Kp|^@`HV*m2G!cwmW`^x{GtHTE08fCoJ^R)~BdrSOG$JtBW4CT|_%4 ztp(XtkhRz%8y=wJO{<;qtBbAW#n$nqqvA_Pq|CP}M;(Fv1B|ndKZyTq-d-nkZL4cY zNs8u^n+e*JCuWpCMHSiOtt{4yjC&oTc6P{3$Ltekj+NjPlmStvorij+WreK z?x50+e=Twu78o$asmy;=6v}*uTf1e{*z$&Dbv5Yr@xhJLvhN+O3wZB?z7eMG^43KG zRfif)H6P?YMYkiq%>5=XHUFCT(ttuI6xmcU>FAg*ZpL7_3z7%WpV5W6B$q zB$h6ZDvOZH?zlFD`Yy9_*rYfbQLhJyc~51LcORynMxC2G$y=RCcR%<2eqezuLj7FM zXXyU<*3{OhtsxH;Uc=C{%Fhnie&C=S(yZQ~zJa>#yN>PG-d}U_;AUdXKb-$SFT4_U zifi?&?)(tQyWKH$n~n`9!fy0$UrP?y;W0p|j6w~Aa{G0-q|bGv);m%gICSH&k7i*< zuu!Vk(Hk=m($A|M>(=_@>Fq9%7uU5tVl~pP$kDkeBz=xcddgw=`?B)wD75F$)q0)F zyy!5GJQF(h(0>It&i_V6b6%c(IQk=Dm+^4ph_aO#nrPI;EY*BH;>b*N?jt+5Y*asK zTHBc&uNvB_$NU|26x)YAZ=HNze|r4+)yoP|Ky5{KWVx&FsOyfowRg~$e+>Mj!E@We zv?Cd7H(M=2U2}7**2I;je!8V-gln*=JF_^tH2wG8&zrI|i~EX?SK98DJ{PVSr#&}wS$9~!>ibI}**dCMinJ~+fb-EPD7y344` zJFTRmZ9&;jL4R|$No~qRXpiR%kI)Y*PBrpZ^HJ7{75kjC-aPZuLc(D$YIi9+)|B*d z=IFGDji`P2>6ha!#!YQ5I6;f>-XAUu9)xf=+)zMpQkn2x9bqz`apbAeR|$4 z(WkN|Fmi}AM3ikcp`0Td!YNySrZQ`8@siI`<*Bb6E(9EDDBnG5)X2!Z9q9S{3#Ou% zNB@?yO8Bw#CF*)J291sj-uAFqzR~tZssp+&^K8C^vM!za`Mb2BLe$<-_iNd9w0QQ} zn^(n%jmc0dZ?!lLwwvQ}0Q!UcvI2EIe)jX`9RC>^q5U@16{1eM{rGP&t#0Y%ST0sP zwn+KR4w=ipze&YyWd-gl#oiZ9XyCk1zYaxOK*5k{h25tKqIIn~p)EDK{Tb223%g&8&J$v9dOE9;B0bOrmBTOVrj|yYS`+fa zT7+7vvvT)7*c`(bd0c0*P+QSe=e}RL{4tu2{N!^qJql$vqTpe^vdPZnms&fXqU)_F zZ(x>e%v8B@OI-r`VTMG^bjXwVI@*96S=X$^DUKF~0=Sm~P8O!E_Klk9^Xf|B+~*)g+XqIQCA0Nw57b?CYft9gcW+Iq-U||J|Gh#>c@jUO0|2R%MChc33MK9iok5HKT3aQQR%WAp%TKO2qRaF8F1qU8o-LX8(4&#NXK$b3o_=%THz!^*b}h>^bz2>( zto?q(xnpf>x?VFkriF%m=P0lE$doTJ}UC@8#&RrCSpvp>wFQt3>q2!&8$-{ ZmxFRErgm>VdtjqF>=9X#ymQU!{{TcO&hP*L diff --git a/crates/pf-client-core/tests/bars-709-full.h264 b/crates/pf-client-core/tests/bars-709-full.h264 new file mode 100644 index 0000000000000000000000000000000000000000..a2c788427439cef5f92f4434a349b7e9072a005c GIT binary patch literal 737 zcmXw1O-L0{7@db?P%YX8k$Rz&p!aiU-jDinAQ4dmDIzLpG2OXy-<#ph%$;}d)%RXe zBu3OC5EpK8p{-kliwvukt01JPMWTVB2*E>J)vD|p#m&e0zVDoK?>#e`rX|YG$>T5I zoEz;4iKJS;H`poy`|;p1@)_4TQ|-)xn`u4)xy{0H(tzECzVg@L5SA1LJJUz(yX1u2NAk zld08e>2c3t+$Uk0i*hDDCGD!f2M&*v$3s~}l-PutIufh|)nT^JX~oowxQG!6iQkaS z#Pw_u*L6LM0%p3bj^vmPs2fGH0V1fDa7;aoK?DT_UY$8s>IFny!2@D} zR7XlM-}fY-o_dc%6#!ZdK;SUq+~pxN4Lzyrs6?c)B3bdGm~miGjVxX&Nv6z{fs`vi zAx#7MoL7hojdeKFk@P(ZcR3tj6pH;bs230w-@^+P6FvbF%x!;6kcLHh5D^7Eg@}|O zUI-RI6eQ-91PPb~RAXM*RS}>HYrq=Z4uBPSAvBV*kkPT&XkQ#BBA9fc7u2NH2n9C8 z0QsV7{5mg!sNl0ek~s*!YGUmn{4wmwqsqTOUl%_v{OaJZkA3fIUD}$ezCC)ed8@a* zx#0F~Zp`L_cXDX0L+#?kJSb1yC@pRFWk@Y>DC%Wa?6 zms>vc-#&fi)ZS*>RL8aH`*+%Vj92C2?58a^!6sWb?DTZ!_}rKI2j|ZOUgzXeZUxV; F{s&q(2iO1r literal 0 HcmV?d00001 diff --git a/crates/pf-client-core/tests/bars-709-full.h265 b/crates/pf-client-core/tests/bars-709-full.h265 deleted file mode 100644 index cc0da18d0cf3a75d5ea7fcc6ed1e9fc582e7acd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4552 zcmZ`*30M=?7EYiRR8(B5_UY4cX%%50K|n-OT(BylsEF%R7$yVEBgurBBoNRB7pzvT z3%G%bwF2&nmMSjbf-Lf?xLdJB+z?Sj)Bu9qckU!u`@ZkxJKx-M&Ryo*^Z(~eBoc{q zwA4Y0kTgspv6ZaH4-m9zYkLXIjg?COXJVJVz2jUaVq@T8jg5_sJzgw6ibv!1{?hX? z`?`TLRoV_&f7e`g|JgpL*M#!ZA$wX)7h0yqxqEpML&No~N=dlOy_^V=@Nji^mXnN! zhsOvH51;u{D4GZgCgv=dPpc?RB&>3kxylyI_wpdZS=|D{8=lT`nfP>vC!8?R6;5@V zIDXbRg65PA4X$yV*2uGwQ98n1CLd11!DWUE{bO;rJ#@#8jg(BYVn{|!#gdCWe_wx zHU)uI6Dlr}R51odrI6w9m5f#wrSK+LjaIKCX+aAoMQ8f{-0dd0zgS&fe2 zRTRy@W*w)aRHPV%0H$~*$tr2kqu`#Tj%9c_Odq8r!* zcvO@Twu6V$G8!_H(-LY{Lq;=32pw)geAHwFOp+>&i>B~9VMn<2E&wY@DJdVXc$_rQ+UY4h*?2Lw)qI2QeprwC|bM$ zYY-t%k&q@xcoaeF^a{v0j-J+&0vpc&O0bG&XdNCYHC&B~BX!ov;qC;eB0!`CRJqjw zhqgIXteVxq7JLIG7xR$_N}~oYNs~O}!Bu973&;XCA+V8}DAvL!oe<3e!+1-mIDSzD zoW(5wrvO)AHH^}-f&i1#L|P0|P;;CviiHZoajO|Vk^w?kLV{acIc%{pg9Sp(0D%O3 zIE?OK@KAk>!gIu5XsZnn4rd`Dsnm-`l$s%88C+?iX;Cdq(oia6JVOeAo|dHrLdk@y zI67J(^8vUh4j%=BNKNY$qzPga7!_olHNaMiQ+N;z#jA{@m<-;M5{Q8Ud_@THF=D1f zAGQEM6(sdPrjrB*OvDBLj=7M+Xv2n=k1Y(bKNbk;NJ z@E9!@0U4ALH2+cMX$2jlC1SJ?F{|Y%Xdnu?E3i=;-ewG!5mXF`lMP!z1H2ebBs7FJ z765;Fh9TlP4FlDxhZ+)*hIK3r?IN5fVj?Z%1g^qOA+wQ~0q|A;S>ZJjE3C${OXLS> z2sc2oF=aRdlqyZ(SpzVP6MKP9;R)4&ftEw>RB^nPXVstsu{1)2*iuBdnt~YO@S-T5 zQ&WH=9HV3*r<9S5Wi?7J796w+%FIP3mXS)Yroe^CC#vqAp12#jlh~u>prVCvs4;_{ zh28~0W8OlC!8WuTi(##9vD-pRCS$auRx5TSYymE$3fH+x%wpS=8n<{-O9=ugu7VA1 zjlkMZTxFg(dw3SOqGVJ$3XaAu5gy$7u@R-@Sb@a|;w@Sm>m{ zdVv+ds~4D$CI8VaP{8Z|GpFJx%(hSW0bnWym`&?AjG={!kQ~70Z>HE3tg~tZ;AGXI zuaN)~?8di(EpP)%&EpEhvpDNkKNl-zO$jZ}y8UC=I-?eomWIzNE@pTEhLO_aPT3Y7 ze5l0)wRJa3*}w}0Ko@R-(}UB>7-&c|Rt1BY(g>D;zXJfS_#MENqam%N#5MU5Jqmxc z0V##-lTVqIeI%|(Mkf&BS06$R=X5$w4GR1rfQsWq-a#eeYrw0aE+7$r#b0#6ZZ$3F zBE-BYBq zdEN@yGy3=0erUr*7uiJzdEb~KkR(5JZ^gYi`hZdAcP~{SG|-TbTJ9RR4Sv=|k3!#} zP_yg9TB|(Rj?9WO+nOuF_9~y-IftSH-|n(aMo;x+imP=~uk&klrq)&d-|JGIhOD1E zJ|opN$kbAkI^bBbuk7vMct7O7d)%o2VOX(Cs`2MXsAk=!8T<^7;_B7@$Xw^MY|+22 z+&s0)&)H$Uc9*2iWo7k`{U3a8{K=Tu%?~X;qs-lM@>w6-8V+5q&Y6+v)Y-$s_QhoIJ>4@`oD^Z zighS*C3+saaI*2{b-&hvA;yIJn@9gv#O>`c^eSq7RerO~H}3VLGGg=+o6qdVArzAM zb^3$j%AdbqxxIE%`n|1*F7JN#dHTLg|7{s*gQc>wqg8BAG##lxEdhrIY5J7jbhUYa z#r#TRm8#-CCUn|ia=A^!{*<>bZ+E)shq5!idFfkix6i*K*HPl(-W|PKUe!n?4z4I2 z@FaUid-VRa!=lP^nR!#lyDuuJUg>B@Q?;=wp+~$|r#?k}8&Ks3lmFectMQs%-v3#1 zp-a$g)DYr4Z2$iK$Bb1wja75=TdO9m{ao5Irel_8QTHJT_2@TX;ulS4&X(Q>DLr_~ zDhD2VeU<3eY+oo}!ySsAciYQ$GOD>mk6}7E*!Mnl2Q}1(26xW8^YkCL^s?PY(87f? zQi29J*=OXRLCxpQC9wr7H`fk4+cO9~S1l)63cLGHnK;XY7DSxi=5*rW_d@^sHt$el zvZg$@He#~x2f8pY0oD5*4cL3B^Y>GizuV@Oc+k`ulyK-I$GcN%Q-drQyr(h&ivrg5P;C(<6-|vDStgKH~9d% z_aOQ5szO_SrSGO0ko z)}y!DMKRgo8OOfN*@EipZs%miK2oghl@o+E6@DFj!PM+~VnX3%W3dT6?-88!?W*G) z9kW3kx}A5iN`5J7jYByLe_MQVXPP;7`>)Z*Z=adxlO$Q7SvPxBLnkuSZBO3D_mit- zHly21220lCVNms!!{@wD1g=hZURs!JzF6Kxr8sJCNvr2d^1l{`lXo|m(4Y0`eCVEv zvDbpDE0?4ttxU*%jXu0Q|LiN~k3qjZ^gVk^`>MtHySs+eQ1mLiv0uT^TepHIpOlVD zk?eT3&~OC3n*HPS0af>VCsYW$Q+aY#+a6A4oz&ew*wcQ7rvxa`T@JdA@Y-Lf701KQy9KLJ0S2X2`?|M^Lh{ z?}x%)mpbOvr>xFF_1hijt{GdWRxWt(=~SY#sU^#{cL5vG{p9R8XY}~b34Sq$b}e^1 zvv0i#A*yRwUc5)U_lsO?l1=MIO^U1L&|7W6abL;Rn3Up_`-87dtgd&=+UqD~}_x!ZEB-!bk>Xco>4oJbd_}=EG>+I32QR9>E?J_QuurHeW4sG2vIBgPop{(g=`=s9WKvN1byX)6U z>zjIoyjO&BD89Dy**BS&pE9*~(AikKg-tgY!TqU^u>`%yxSo=FKX}EJz{Np`9x|t{ zzuN|V=+6~nQNv|6V5*nh#@8HM>M*K5m`KA$`Ec2DzC{eC=x3q@H|7B30t zW|wK}wauw{mBh&_yEOQ;Ur*;0r?FVB1U|OPxOQ6*iurId=fsAvac4$d1bL9x(JH4a z!g`_V8=+w{mM^JJ{4F!!%n@`WGw)nRd)ph*w7^Z0xp!Wn^5Mrm?>*C3`?}6J+R%Vr zZkErrt2R$48kzklazsz`!Kbm{%@yAlf>Um4V1}u6ho`yS(24!FI^0w4wHtYLEqE4% zL+?wl!ymYLv(dQ+|4h1+T$K~;ZA8f2s6KQoX}ZJFQjktwH`G=s4RCB%HpqUgV^Y@0 zD}wH^o2l7p+sx3xLsoR0V}s1W?tAkl208T13dx^^nwHMnm;b(Tb@li@=*i_J_paM7 zPBq@YW;C_*iXV`2ebF}aK?c=%+fgyfrkC@sh}yuPA}dy;_6rM(X> zd)qwd(e}HqkMDc;w@Vzj3|EYP(P7$ph3v~;N`j83wn*)kp53-*WMNP5%5O}q7rXX% zkWEYdVS19YtRNrFB6hxMt!=bV9zQAR%Cam}xmtdgO6c9_9QH-c^&6<}!p5Z!8rH|o z*i(Kmx;Wr9dOr1fgYA`d)xEn(Wj}t#-)x|Xlm2U_fXHKQ5+G* zDblhT#8bMiqhczPRWleCeF-2W`}g$p#?xr${%&Ael)$2!1$CE}B+^wi71vcg14CQN zAfHUuYPG~s$D+)|euD9GGCCz;OV0%k3#7w*kw*jzf27+i-^sc zhfR=bNOJ1BjsTPka~6>RNYw*@MX_~^`P9(04oyQPETkETiW5YPMT2T!vQkMkzoh(m+K%UpRv}9+uHP9A8rIVj#i1=nisRHwp6sETJb5 zk>uF%!2*aJN1PJJ9uxh@(U}6*lyo7^E5? zuLvyQGWX|YV{+_Uf!%MO-qJMOdXY5>xnE)D z#;?~d@0cDruAh7^dSB12V{0Ea{^e%Jf4=$cw})5m?LAT$TKVI~?mHjqhu=SIUOnEn zFFAaBGCAATdf?;ZyMLedGFf<6cyOU*bp4B)>CSfL;J}lKwzJnyAGwwOY@S)M;n1#u Lne(Z0TWkLTXmSU| literal 0 HcmV?d00001 diff --git a/crates/pf-client-core/tests/bars-709-limited.h265 b/crates/pf-client-core/tests/bars-709-limited.h265 deleted file mode 100644 index 9a1ad4142e047c0c47dc0d29082a8c3e5c5d8ebe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4446 zcmZ`*3se+W(jM@=fQzpXBgV${0cyj5AZQ!`F)KttqadOp_VhGNOV9Ms-NREIS9}C9 z>S~BCVn72%@j(=#W{DC|6k<%&RTPP$Aw+OhG`v(`?$+%YO!A-e&#BY5>b|ktiS714&ER1mDAk1(ujPssiHe9*=w2);i@*WoQCl7oaRQ5gtxob zWKWXu_V)Jm_MWjkl%k0Ri;1Pt%W00%$H6XlrMohE`801r%}UXPA3P^}D&?maJmH35 zcevFf`28h81kGz08m5AHLyEx0X(hr-={c2zhacfeyAh#APGV_ZV3H_-nL>o<@n)?g z8D@BRVBg$%Ar5AEgT(TBaRxyfjI2gA-H(VBC>=veT7jW7qRNxd^JJ32fCqyP-f2-R zgQyppriCO1Iq@6l1ME?!w4cl>Ge#qTHbEd;DO;~i{V9VX$1{96U!4?O4MR!7@dI^ zz#ooEf#Wd48yGzq#~TP8t0&``6mT7)Al_(70v1V*=i@2-PB@{q#saXCl!h|Ee$W6l zO2=xftE8Hx@a+QqhofE)eeXS_$gwmeOi73|qi1NNr1DnE9P3Jm14LRt^|VgF zrM(jzt79cNf+NuI32%s?Nzp@)G$}wH+?5u;fGl7WA{(dIvKBr`Vmu2B6D+P`|78_$ zmX83OB80%|8I5HJ0Vc1HvlyhP<9SKTLIq*Jb&L?l03j?cLDW_bTWri=k zLb5Ss_(UjGn!>Azz%X9!1(M1KssjTJ!SEa}7z9=aGyr1h#aOwe$kRFsd`N}`S}X86 z3UGu28WwU&rI1OiUc)Eh8#pMlDN4DFIHQh&DNH_D_44t--O!7~GX@?iT2w=gB^p`i zUEnn4Ep!-cL%XpU*6NnKEwp4Z!9W@ea!0}z;6ie^&N(@Yy-PaW;z?4)itx zYd>*U`rz#0Rp5$-;Uo&K##1spxb*<)&a=)g=N>Rvr2p z2{6HF92Fda2rM;^D-f^ZtXt>#BspsuXnEG-Z`_tr3>JzQQgAWD3owk75qHX7_uxY< zC#bi(S;_`pC<3|=1x^o6E0ah=qOmF_$|;Ry8TdN@;L6_tTseBuLQ1&u_ts8Q_@fO- zsg!^Clv&wF;)-M>k&wUo5RjUeBwhy!{2_pf6J*{&CE_sP)le6Z2*BcZy5MOYElRPn zYifbUqvg9V(CgukjjK&jzq5|^ruDZK7fUx7Q_jtd8Gdpeh*Ra7NUN+Zx$LeJR;Qd+&~d*^a2cds6^ z%0&^~=?wU5?%S6v3~FZwBh4%RS!dNpP(-7K$lYQyv*r%^<%86AlrR` zsXJop;iq31OPfRHq6QVWCM<2uqvE$xK5syejuz&+_%RpHzMX5{_hjDE=$DQ4i}}g+ zI}Le?XZO5szWb%)$X_@8st86s>s@`4JzY-E`ROU@tZlj4Fo>qB90HtU(13Zf?&Zb| z+p#j#)HSiZ@ckx5^4mAk`Yti`gyfn}m)b4eoLP_P#cLPb)}`MYx74=*-N-|a*Z7^Q zx)O+5A5QgFKT=iM36E{JPDSWY$^48tyF$hv%4QE`ukuH&K7oA$Tb^gVp6?R8#I#zi zD8A{?{G9zX%#pOc2I3dA(JHg+P1!vNeVvrhuOKrh$yWqP8}MbX>>_l?T+)1h*zM|X zmb5-Q{EI(2@VwhSI`r6O|FoeU4)z;S*Wd-4)xWr1eapf5RvYTBiL7Z^VQTx^#rM#h zv90LEe8tW{X>eV7-QGbxsKd;AlnlAQW9Cz$ZUxcSZbH7r`#&jgs|GalLs6^oL1Dw# zuFo>coag7FE+MgcWLy!yuCoQ?bXuHMk_X=TRB>M2(~?#{$#J7g|HBR*Xj{aWH=a-W z^>uR#YI>7bjzQZ4_HXQ3 z;Eyt^mv0(2*Y;vqr{bKcr!w#EKeET&QLPxUxU0 z6k4bpOy2yVTnaMYJoX{#{A#U~UiCxR_dne6u11~NzZG72-}o~Z*k0RkZ!$7_jMonj zb+#K46p6NW)a{)6Vu}zmw6(0~^tLgRQi7fkIX@z9Yqa|>8D80qhnkL}7ljdF8J@Rx zWW6@MOyBKTlvI~EJTTq^)fJ;>)tAgO0^QKlz8#V1_-yBMCEOP|Taa0MvwBy7!!WLH z+STIY@ylwy!jHT%)KGCp_e(|F_$yJQzz&cG3VjYQ>{oMtXiEo)0dkzw6jHV2EOljXVV9WH_OooJV~woVXuW zlAn1rcy-UJHIZXbZ3#cczPjRS`>LT)l_y?I9Jz-rK<4jje4Ta`$NJZ4=X~ZebZfA_ z3>7QxI#ISg6ZVWw58kpY?8=UD1Iti>=cq)VejmiorkU-kPiIYQL8#e($n8A!goqOE z%ROU!OF8Ey87bS4+57T_*^Zm$xD1@_G!fM<+c5a3>QKAarZXn=^70G1;_Bn5@w>3B z-BW9oT_69Z2>#{@WaZleXf*wkene-LQ4>4We)b1^(35?qqqA+7v|dUGE{!qQ%q|?A zdOxZE(ITX|ck0Qtp=Umux_(4i&#_J6Gupp-<`5ZUt}bP2^TzWt<0b?m)Z;>RG^NiB zq87(@Pd#+rCn}>1efQ66<(}n_N;5h}Y%#d~Tl4RAAC;j~%IjGz51+i(awkAAEuMI3 zei!Qco5RSJ=65fg@7XwDV!PKG4I4f{;d0=t=c8SrE_)v~o6yI7mn>}Evb}jqW_I*o zHnqMu1~mly$JsnMWcS#s4af3ry9!1f&B;YI(s3WZ@B!Uzb7xg;>>8K)U?OVYKP+U| zkeI5Pq2=GQnyfh~n?p)+&Hj6I-X7PglTJ@WI~wak?b^Je8zR=0^$c)XTeBkei`vaa zD6lHVxM$>%j)kcy)u?gQr2Wm!s(INO^rE|zJ+N-`yl+;&c0&235tAJL>$K818C?^c z?lG5Eul>YXU=xZ^Q)OH2vY{d4?z$e_>bidudbL#h@O;LWzXYBOSayD#BLDW8_{(;Z zFn!&gW{{7Xwp-=VizpLn7ar#nJZNm3wHV8_=zdlSk6s^xvNmrY)wI`;Kk8<`<|6dn zg}DxG^Zgb%JhrX*)YygE+RfFOPxEg2-wbOlMQy*GdC$k&=G>2$nmC-e{9B_{ax~vid`Uuu3pJ^nhpZeqJ#1W#yUu<=Ov5wX+-+c?9(N_F`lBp?dU1 z*4MR1@*JM6dvB%Cg8uW}Pfu=M>)+`=x@fFhyUD!d9Y6jcRk4#_=ya$IO?Ghhy{>#1 z?0eWQVtUcHjuiR#z#-~Q+x-^gqP!=;ODj~@FO0m9;~0ao{}N@l^loHFiLUO(H3@a; z)9VHf5o?o2u3Mgux^%9b^V~;;;lte=!#5UE5!Q6H~%ZhPpnBKYC-(Q}oPz(m~&(wdd?-qmHvD7gc(H{`H9y f@Sawm$NAaL`x=iOG`)(Pwnco)bDjITYYYDeA#9T_ diff --git a/crates/pf-client-core/tests/gen-bars.sh b/crates/pf-client-core/tests/gen-bars.sh new file mode 100755 index 00000000..204cb842 --- /dev/null +++ b/crates/pf-client-core/tests/gen-bars.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# Regenerate the software rung's colour fixtures (`video_software.rs`'s M8 exit test). +# +# Three single-IDR H.264 streams of the same NINE colour bars, whose VUIs differ ONLY in +# matrix coefficients and range. That is the point: the pictures are DIFFERENT code points +# that must converge on the SAME RGB once the signalled matrix and range are honoured — +# which is what makes the test able to fail against a hardcoded matrix (the swscale BT.601 +# default the old libav rung needed correction code for). +# +# ⚠ The NINTH bar (192,128,64) is load-bearing and must not be dropped for tidiness. The +# eight before it are fully saturated primaries/secondaries plus black and white, and on +# THOSE a limited↔full range mistake only pushes values outside [0,1] — where the shader +# clamps — so the whole fixture set decodes with max error 0 under the WRONG range and the +# range axis could not fail. Measured on this fixture: (192,128,64) gives max error 13 +# under the wrong range, while a 50% grey gives only 3, which is inside the test's ±4 +# tolerance. So it has to be a non-neutral mid-tone, not just a mid-tone. +# +# Not lossless: x264 refuses qp 0 outside High 4:4:4 Predictive, which openh264 cannot +# decode. qp 1 over flat bars is exact to within a code point or two at the bar centres +# the test samples, and the test's tolerance is ±4. +# +# Needs: ffmpeg with libx264. Run from this directory; overwrites the three fixtures. +set -e + +python3 - <<'PY' +BARS = [(255,255,255),(255,255,0),(0,255,255),(0,255,0),(255,0,255),(255,0,0),(0,0,255),(0,0,0), + (192,128,64)] +W, H = 32 * len(BARS), 64 +row = bytearray() +for x in range(W): + row += bytes(BARS[x // 32]) +open('bars.rgb', 'wb').write(bytes(row) * H) +print(f'{W}x{H}') +PY + +# 288x64: nine 32-px bars. Both dimensions stay macroblock-aligned (18x4), so there is no +# encoder padding for the crop to have to undo. +for spec in "601-limited bt470bg tv" "709-limited bt709 tv" "709-full bt709 pc"; do + set -- $spec + name=$1; mtx=$2; rng=$3 + ffmpeg -y -hide_banner -loglevel error -f rawvideo -pix_fmt rgb24 -s 288x64 -i bars.rgb \ + -vf "scale=in_range=full:out_color_matrix=$mtx:out_range=$rng,format=yuv420p" \ + -frames:v 1 -c:v libx264 -qp 1 -profile:v high \ + -x264-params "keyint=1:no-scenecut=1:colorprim=bt709:transfer=bt709:colormatrix=$mtx" \ + -color_primaries bt709 -color_trc bt709 -colorspace "$mtx" -color_range "$rng" \ + -f h264 "bars-$name.h264" +done +rm -f bars.rgb +ls -l bars-*.h264 diff --git a/crates/pf-client-core/tests/pq-frame.h265 b/crates/pf-client-core/tests/pq-frame.h265 deleted file mode 100644 index a9f2c1ea78b4225cfd3067bab64cd6a2e527c3d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3759 zcmZ`*2~-qk5@x&z8ZYobiKLCuK%|G6;SzKZgNk^78X+35^mP9-)6&!3)7`_&;DL%n zV~C0hiL!Btph1P0L~)5%5MARz^m)f)mFKZWNL0itO2XFPGmz}Qw>;i-RsCJnRrP&U z-9A1(ngq?OnwFM!gpW^qpEdXg{(}oX^YekTcQl&+IVdKUbhwPnYbWm1%=0JwU%_{K zeiE?5Xw4yM_ZuK;QZyY~TL1jQHedIv?3;9TUs~xO*)`26E<tjqPZstUmcerNHOiD~7^if)aRyTWQbR=P7mDz+|2N&=dr-dltX z612b|8cd0T=$2TERUr&IeFO{oIwGo_PAvu(ZI3uaYbudU&tVX?#V0qD@khCm9WU~k%!}VU9gk)oMqzme1 zc?C%vMI)n*Py~hINHr%JR5d2aGBn6mD4kSTBta;Ll_5Q9O;{SVWXUTA?;1$pppo$YoM*cQE*Z1XV+Vj5VvoKr&{5uu`%WCxdLFAVEGH z7zVgUTousdx_~3Nfqa>iU70f0%*!m z0?Q-Da|Z#cz+1eP$u>bytSoO)^R^+$f`AX6l%Q&r&;sevF@a^mh5#|yVS=jx6p7-& zB`W4mux*);j^N=S$v9NSYD0t*;n`DFkLV#1PjT)nM9P4k$kH;wAQLCh8Ae?kz(ol- z6kJqhdXJz@kfV$^=sKGTu9RSmBoHNWZc=TA6j3 zLPt<`5<>*AfCMNb-h!71P8cXJAw*;eJc9Xlz|^VafSXtv){99Z>=qB7fwE8nfMLO; zfsBC9#^_iqu)N1hr4*TI$^?AyG7&OixM>QXW&$Y%b$ckrC>SWr4S2f136dzWHe)OS zT;ydlUP93xyN!Y*TyR2LCBa4k_Ta&=fCJ?woh;7?P8@=R(G1h6qsPI91rr`LRn+SY zKv}_JAhAglU=7G77|cuu3;Pbz#$<=BgmqX^9>u#7U|$O)YLY-O+2A+0W|H6jps%{eRsVx)m_R6pfNx%ubaaG^~6?hf}9$S2hyY96MPPJ`AYCTiS|S^pHe! z;|%~0z%Wt{yk1+=0}`tZYF&wKvnB(&Pz7!e?k38lq0v~BGu4)wSp;7Z0IvG-z=Ok+ z9$Kn%(S{y{-~E7;QTLbEpO=0lo?)cOg!=IS1(^gz5o{p9cLfZbr1B015tjjvhRJ|N z02Y7J1)FWOteDl*OcIT+o-YT9UJM^ZA)1{5i;J=chn?7YrfY7@M>+CY+wbF&W4VSG z)h~X2e0b8;qPmu`RrM9Z&HIygZ43YR)oRfh5r1drlx`z^L(}FSD_hbSJN=)Zt!uhC z=lr{muOD3U+R5j;D#Y2jaofLX$q%0z6dFB6v{$9iN1Y~Lns@n0@YPQy-fNz);#^dE z0^cj^(_PIeeU6;^usPOeNHIG#y5E?6X`_0@hxGle?AJ7;?|lAco$Yq|73SLgFIQgN zop?OKcmFi$moX>BiVy26YvRPMT^_i6l8(=dK1{vi%RBc^BHurFZ@ACgq1WT`%c6en z9URyrKDI?ZH_QL#wEED5ygP&6ZuHM6d~4N)j%(|(k3Rb0QbnJ@8+rNxTO#<0aS1UI zqpf8ts;U!LmP{Q}k<+0zGPN*m`Q^sMNe>+{*Iup9w;b-)^QXHz`q)zrRrJrgM&Ek3 zPtECNQR0mp>qBEzJX2hGO9&B%jCgdQ^Wa{okCH#>@N=nQ(z6S`7iV6WGo)$Df-xr! zqpoEm7oX{QwDFNM?nLKKe)A8YGfNKFkAL7ecsC?u=iXZmlfD0j)KzPG&5V~qV zf%C_A?ld7GvAB1fI5aHf#cC(9=33C#i~C;ge|w5lTXcB<@oZ*$?hTF4QcG3)ZPst3 zIRP1ci%(?})4O*H>X{?y+w~9by}E8uQSh+|pI6MbX7ts>U%GUlrh95aSf5fkMH*C@ zRGmI!k1l_tcy+#wK;H0bi+=-4K zZThjmRsQ>^?v! z#R=oYi{17o{v>VnO~`HNzwnhVC;DtC4vIXp?8zZHX+hMYT9?)pGiC4JV>VCgmB0J_ zS;^bazvI6?`N8H@Bf4%pKlVb;shhe4fAwN@BW=xWx=lYT=z8>v0mHX#GN%7N;m4=z zSHRvjwXc+IIl7BqgLTVH#?@WQ^p zOfS5homA%^amztHmd$cVKse@jh2LRTEDcsZphImRz_`q341 z-o>a4|7Fg+#S7{-R|PU_{F*)pD|emle=hJ;e96&OLw(-ZdwFPX&9JU-fAQXS&7M-F zPrsb<2Bz@J!y?IkC&`)>czi=vj|J=WYyQFhD?, + /// The last host title a connect was raised for, kept past the connect itself so + /// [`Self::session_reconnecting`] can name the host it is re-dialing — that flow + /// raises no `Launch` of its own and therefore never passes a title through. + last_connect_title: Option, wake: Option, /// True while `wake` is the shell's own optimistic placeholder — raised the instant a /// screen queues `ConsoleCmd::Wake` (see [`Self::apply`]), before the service thread has @@ -119,6 +123,7 @@ impl Shell { deck: opts.deck, in_stream: false, connecting: None, + last_connect_title: None, wake: None, wake_optimistic: false, toast: None, @@ -151,6 +156,7 @@ impl Shell { pub(crate) fn set_connecting(&mut self, title: Option) { match title { Some(title) => { + self.last_connect_title = Some(title.clone()); self.connecting = Some(Connecting { title, canceling: false, @@ -181,6 +187,38 @@ impl Shell { } } + /// The stream stopped and the client is dialing again on its own (M8's codec + /// fallback). Says what changed — the picture is about to come back as a different + /// codec and silence would read as a glitch — and raises the connecting modal. + /// + /// The modal is not cosmetic. Nothing raises a `Launch` for this retry (the run loop + /// starts the pump itself), so without it the shell would be in a state no other flow + /// produces: not streaming, not connecting, and a live pump behind the console. All + /// three gates would open at once — menu events flowing, the console drawn + /// full-screen over a frozen picture, and no modal interlock — and pressing A would + /// launch a SECOND session on top of the running one. This is also what gives B + /// somewhere to go: the modal's Back raises `CancelConnect`, which the run loop + /// applies to the retry's pump exactly as it does to a first dial. + /// + /// `appear = 1.0`: the takeover is already the thing on screen (the retry follows a + /// live stream), so fading it in would read as a flash rather than a transition. + pub(crate) fn session_reconnecting(&mut self, msg: &str) { + self.in_stream = false; + self.connecting = Some(Connecting { + // The host this session was dialed to. `None` only if the shell never raised + // the connect itself (a `--connect` run has no console at all, so it never + // reaches here) — name the codec change instead of an empty string. + title: self + .last_connect_title + .clone() + .unwrap_or_else(|| "the host".to_string()), + canceling: false, + appear: 1.0, + request_access: false, + }); + self.show_toast(msg.to_string()); + } + fn show_toast(&mut self, text: String) { self.toast = Some(Toast { text, at: self.t() }); } diff --git a/crates/pf-console-ui/src/skia_overlay.rs b/crates/pf-console-ui/src/skia_overlay.rs index ea2c48cc..1debc4fa 100644 --- a/crates/pf-console-ui/src/skia_overlay.rs +++ b/crates/pf-console-ui/src/skia_overlay.rs @@ -333,6 +333,15 @@ impl Overlay for SkiaOverlay { shell.session_ended(reason); self.streaming_since = None; } + // The stream stopped but a new dial is already in flight: toast WHY (the + // codec changed under the user) AND raise the connecting takeover, because + // nothing else will — the run loop starts the retry's pump directly rather + // than through a `Launch`, so the shell would otherwise sit in a state where + // a menu press could start a second session over the running one. + SessionPhase::Reconnecting(msg) => { + shell.session_reconnecting(msg); + self.streaming_since = None; + } } } diff --git a/crates/pf-presenter/src/csc.rs b/crates/pf-presenter/src/csc.rs index 5e5bb48f..dfcccabe 100644 --- a/crates/pf-presenter/src/csc.rs +++ b/crates/pf-presenter/src/csc.rs @@ -40,9 +40,15 @@ impl CscPass { ) } - /// The planar 3-plane variant (separate Cb/Cr R8 planes — the PyroWave decode - /// output, design/pyrowave-codec-plan.md §4.5). Same push-constant contract. - #[cfg(feature = "pyrowave")] + /// The planar 3-plane variant (separate Cb/Cr R8 planes). Same push-constant + /// contract. + /// + /// Two producers now: the PyroWave decode output + /// (design/pyrowave-codec-plan.md §4.5) and — since M8 — the SOFTWARE rung, whose + /// I420 planes the presenter uploads and converts here instead of receiving swscale's + /// RGBA. That is why this is no longer feature-gated or probe-gated: the CPU rung is + /// the ladder's last one, so it must exist on every device, including the ones that + /// failed the pyrowave probe. pub fn new_planar(device: &ash::Device, attachment_format: vk::Format) -> Result { Self::build( device, @@ -222,14 +228,19 @@ impl CscPass { } /// Planar variant of [`bind_planes`](Self::bind_planes): three single-component - /// plane views in GENERAL layout (the pyrowave decode leaves them there; same - /// fence-wait safety contract). - #[cfg(feature = "pyrowave")] - pub fn bind_planes_planar(&self, device: &ash::Device, planes: [vk::ImageView; 3]) { + /// plane views, in the layout their producer left them in — GENERAL for the pyrowave + /// decode, `SHADER_READ_ONLY_OPTIMAL` for the software rung's uploaded planes. Same + /// fence-wait safety contract. + pub fn bind_planes_planar( + &self, + device: &ash::Device, + planes: [vk::ImageView; 3], + layout: vk::ImageLayout, + ) { let infos = planes.map(|view| { [vk::DescriptorImageInfo::default() .image_view(view) - .image_layout(vk::ImageLayout::GENERAL)] + .image_layout(layout)] }); let writes = [0u32, 1, 2].map(|b| { vk::WriteDescriptorSet::default() diff --git a/crates/pf-presenter/src/lib.rs b/crates/pf-presenter/src/lib.rs index db34f32f..c868e158 100644 --- a/crates/pf-presenter/src/lib.rs +++ b/crates/pf-presenter/src/lib.rs @@ -3,7 +3,9 @@ //! decoded frames, captures input on the `ui_stream` state-machine contract, and reports //! the unified stats window on stdout. No UI toolkit anywhere in the dependency tree. //! -//! Three frame paths: software (`CpuFrame` RGBA staging upload), Vulkan Video (the +//! Three frame paths: software (`CpuPlanarFrame` — I420 planes staged into three R8 +//! images and converted by the same CICP-driven CSC pass as the hardware lanes; before M8 +//! this lane arrived as swscale RGBA and skipped the pass entirely), Vulkan Video (the //! decoder's VkImage on THIS device — plane views + the CICP-driven CSC pass), and on //! Linux additionally VAAPI hardware (NV12 dmabuf imported per-plane — `dmabuf.rs`), //! all composited by a letterboxed blit. Devices without the import extensions, and any diff --git a/crates/pf-presenter/src/overlay.rs b/crates/pf-presenter/src/overlay.rs index 9bca9afa..ff2f929a 100644 --- a/crates/pf-presenter/src/overlay.rs +++ b/crates/pf-presenter/src/overlay.rs @@ -113,6 +113,15 @@ pub enum SessionPhase<'a> { Failed(&'a str), /// The session ran and ended (`Some` = abnormal reason for the status strip). Ended(Option<&'a str>), + /// The session ended and the client is DIALING AGAIN by itself — today only because + /// the negotiated codec ran out of decode rungs (M8's software-HEVC drop) and the + /// retry advertises a codec this device can actually finish. + /// + /// Distinct from [`Self::Ended`] and [`Self::Failed`] because the user's next action + /// is different: nothing. "Session ended — HEVC decoding failed" invites a manual + /// reconnect that is already in flight, and "Couldn't connect" is simply false — the + /// connect worked, the decode did not. + Reconnecting(&'a str), } /// The console-UI side. Object-safe; the session binary passes diff --git a/crates/pf-presenter/src/run.rs b/crates/pf-presenter/src/run.rs index ebb56d0d..44438961 100644 --- a/crates/pf-presenter/src/run.rs +++ b/crates/pf-presenter/src/run.rs @@ -221,10 +221,14 @@ struct StreamState { /// mid-stream re-syncs keep the end-to-end number honest after an NTP step / drift. clock_offset: Option>, hdr: bool, - /// The presented lane was the CPU/software one, where a PQ stream is shown RAW — the - /// software path has no tone-map pass at all (the presenter uploads swscale RGBA - /// as-is; the CSC mode-1 tonemap is hardware-lane only) — so the OSD badge reads - /// `HDR→SDR (raw)` there instead of claiming a tone-map that never ran. + /// The presented lane shows a PQ stream RAW — no tone-map pass ran — so the OSD badge + /// reads `HDR→SDR (raw)` instead of claiming one that never did. + /// + /// Nothing sets it since M8. It used to mark the software lane, which arrived as + /// swscale RGBA and skipped the CSC pass entirely; that lane now uploads planes into + /// the same planar CSC pass as the hardware lanes and tone-maps in mode 1 like them. + /// Kept — not deleted — because the badge's distinction is real and the next lane that + /// bypasses the pass must be able to say so rather than quietly claim a tone-map. hdr_untonemapped: bool, // Presenter-side 1 s window (design/stats-unification.md): end-to-end // capture→displayed (host-clock corrected) p50+p95, display = decoded→displayed p50. @@ -279,6 +283,10 @@ struct StreamState { /// warn on the first failure of a streak, then stay quiet until a present succeeds. #[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))] pyro_present_warned: bool, + /// The same latch for the SOFTWARE lane, which since M8 has real failure modes (three + /// plane images + their allocations and views, rebuilt on every size change, plus a + /// render pass) and — being the ladder's LAST rung — nothing left to demote to. + cpu_present_warned: bool, hw_fails: u32, /// The OSD's text (multi-line; rebuilt each Stats window and on a live tier cycle). osd_text: String, @@ -322,6 +330,15 @@ struct StreamState { /// `None` = nothing sent yet. Edge-detected each iteration from the live mouse model, so /// the chord, the M3 auto-flip, and engage/release all reconcile through one path. sent_client_draws: Option, + /// The params this session was started with, kept so a codec fallback can re-dial + /// with `exclude_codecs` widened — see [`SessionEvent::CodecFallback`]. Cloned once + /// per session start, so anything the SESSION changed after launch (an accepted mode + /// switch) is not in here and the retry re-reads it from the connector. + /// + /// The latch grid rides along by `Arc` on purpose — it is the presenter's, not the + /// session's. `force_software` does NOT: it is a per-session demote latch, and the + /// retry replaces it (a fallback would otherwise open on software). + params: SessionParams, } impl StreamState { @@ -343,6 +360,8 @@ impl StreamState { // pump reads (see `LatchGrid`), so keep the Arc before the params move. `None` // when the session didn't advertise the cap — the 1 Hz fold then skips the work. let latch_grid = params.phase_lock.then(|| params.latch_grid.clone()); + // Kept for a codec-fallback re-dial (`SessionEvent::CodecFallback`). + let retry_params = params.clone(); let handle = session::start(params); let (wake_tx, wake_rx) = async_channel::bounded(2); let pump_rx = handle.frames.clone(); @@ -392,6 +411,7 @@ impl StreamState { dmabuf_demoted: false, #[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))] pyro_present_warned: false, + cpu_present_warned: false, hw_fails: 0, osd_text: String::new(), last_stats: None, @@ -401,6 +421,7 @@ impl StreamState { shown_mode: None, resize_overlay: ResizeIndicator::default(), last_video: None, + params: retry_params, } } @@ -1187,6 +1208,22 @@ fn run_inner(mut opts: SessionOpts, mut mode: ModeCtl) -> Result opts.render_scale_max_dim, ); } + // A live pump here would be DETACHED by the assignment + // below — `StreamState` has no `Drop`, so its thread + // would keep decoding onto the shared Vulkan device that + // gets destroyed at exit. The console normally gates the + // launch behind `in_stream`/`connecting`, but M8's + // Reconnecting phase is the first state that is neither + // while the stream is still alive. Every other + // replacement site takes-and-shuts-down; so does this + // one. + if let Some(prev) = stream.take() { + tracing::warn!( + "launch while a session was still attached — \ + stopping it first" + ); + prev.shutdown(); + } stream = Some(StreamState::new( *params, force_software, @@ -1344,6 +1381,103 @@ fn run_inner(mut opts: SessionOpts, mut mode: ModeCtl) -> Result } } } + // M8's HEVC path, as a first-class flow rather than a dead session: the + // negotiated codec ran out of decode rungs, so re-dial the SAME host with + // that codec removed from the advertised caps and let the host pick + // again. The pump computed the retry (never re-offering the failed codec, + // and — when the CODEC is what has no CPU rung — never offering one + // without a CPU rung either) and left nothing of its own running before + // sending this: the mid-stream site joins the audio/pad/clipboard threads + // and drops the connector, the construction-time site never spawned them. + // So starting the new session here is a clean start, not an overlap. + // + // Applies in BOTH modes. In single (`--connect`) mode there is no console + // to fall back to, which is exactly where limping-on-software used to be + // the only option; browse mode gets the same retry plus a toast. + SessionEvent::CodecFallback { + exclude_codecs, + retry_caps, + msg, + } => { + tracing::warn!( + %msg, + exclude_codecs, + retry_caps, + "decode ladder exhausted — reconnecting with reduced codec caps" + ); + gamepad.detach(); + if let Some(cap) = &mut st.capture { + cap.release(true); + } + apply_capture(&mut window, &mouse, false, false, inhibit_shortcuts); + // Widen the exclusion rather than replace it: a second fallback in the + // same run must not re-offer what the first one already ruled out. + let mut params = st.params.clone(); + params.exclude_codecs |= exclude_codecs; + // The mode this session ENDED on, not the one it dialled with: a + // mid-session `Reconfigure` the host accepted lives only in the + // connector, and `st.params` is a clone taken at launch — re-sending + // it would silently undo the switch on the retry. + if let Some(c) = &st.connector { + params.mode = c.mode(); + } + // ...and then the window follower on top, exactly as + // `ActionOutcome::Start` does, so a retry lands on the size the + // window is NOW rather than the size it was at launch. + if opts.match_window.is_some() { + apply_match_window( + &mut params, + &window, + opts.render_scale, + opts.render_scale_max_dim, + ); + } + // A FRESH demote flag, like `ActionOutcome::Start` builds — never the + // old session's. `force_software` is a latch the presenter sets when + // the hardware PRESENT path fails three times; inheriting it made an + // HEVC→H.264 retry open a SOFTWARE H.264 decoder on a box with + // perfectly good hardware H.264. It is shared with `params` because + // both ends of it belong to this presenter. + let force_software = Arc::new(AtomicBool::new(false)); + params.force_software = force_software.clone(); + // ⚠ `params.launch` rides along VERBATIM, and that is a deliberate + // choice between two wrong answers, not an assumption of idempotence. + // The host has no "already running → attach" branch on the launch + // path (`punktfunk-host`'s `native/stream.rs` launches + // unconditionally; its "launched ONCE" guarantee is scoped to + // mid-stream rebuilds WITHIN a session), and the game survives the + // session end under the default `GameOnSessionEnd::Keep`. So the + // re-send is idempotent only where the LAUNCHER dedupes it — + // `steam://rungameid` focuses the running copy, an Epic/AUMID URI + // likewise — while a `gog:`/`custom:` target really does start a + // second copy. Dropping the field instead is worse where it matters + // most: on Linux the per-session gamescope is re-adopted through + // `pf-vdisplay`'s display registry, whose reuse key INCLUDES the + // launch command, so a retry without it would miss the lingering + // display and orphan the running game inside it. Keeping it is the + // only option that preserves that attach; the real fix is host-side + // (an idempotency key on `Hello::launch`, or a running-title check + // before the spawn) and is not M8's to make. + // + // Known and unfixed here for the same reason: the RETRY's game lease + // cannot adopt a game that predates its own launch stamp (`procscan` + // rejects anything started more than 2 s before it), so a reconnected + // session has no game-exit detection for the rest of its life. + if let Some(st) = stream.take() { + st.shutdown(); + } + if let Some(o) = overlay.as_mut() { + o.session_phase(SessionPhase::Reconnecting(&msg)); + } + stream = Some(StreamState::new( + params, + force_software, + events.event_sender(), + present_priority, + native.refresh_hz, + )); + break; + } } } @@ -1630,10 +1764,42 @@ fn run_inner(mut opts: SessionOpts, mut mode: ModeCtl) -> Result } DecodedImage::Cpu(c) => { st.hdr = c.color.is_pq(); - // The software lane shows PQ raw (no tone-map pass exists there) - // — the OSD badge must not claim `HDR→SDR` for it. - st.hdr_untonemapped = true; - presenter.present(&window, FrameInput::Cpu(&c), overlay_frame.as_ref())? + // Since M8 the software lane uploads planes into the SAME planar + // CSC pass as the hardware lanes, so a PQ stream is tone-mapped + // there exactly like theirs — the badge no longer has to warn + // that this lane shows PQ raw, because it does not. + st.hdr_untonemapped = false; + // Same treatment as the pyrowave arm below, and for the same + // reason: since M8 this arm allocates three plane images (plus + // memory and views) on every size change and runs a render pass, + // so it has failure modes a staging upload never had — and it is + // the LAST rung, so a present failure has nothing left to demote + // to. Drop the frame and keep the session; only a lost device + // ends it. + match presenter.present( + &window, + FrameInput::Cpu(&c), + overlay_frame.as_ref(), + ) { + Ok(p) => { + st.cpu_present_warned = false; + p + } + Err(e) => { + if device_lost(&e) { + return Err(e) + .context("GPU device lost — the session cannot continue"); + } + if !st.cpu_present_warned { + st.cpu_present_warned = true; + tracing::warn!( + error = %format!("{e:#}"), + "software present failed — suppressing repeats until it recovers" + ); + } + false + } + } } // Both VAAPI rungs — libavcodec's and M6's native one — hand over // the same thing: dmabuf fds plus a plane layout, with the guard @@ -2425,10 +2591,12 @@ const HINT_WITH_PAD: &str = "Click the stream to capture input · Ctrl+Alt+Shift /// /// The HDR tag is honest about the display path: `HDR` only when the swapchain actually /// runs HDR10 (`hdr_display`); a PQ stream tone-mapped onto an SDR surface (no HDR10 -/// format offered, HDR off in the compositor) shows `HDR→SDR`; and a PQ stream on the -/// software-decode lane (`hdr_untonemapped`) shows `HDR→SDR (raw)` — that lane has no -/// tone-map pass at all, so the washed-out picture is named for what it is rather than -/// passed off as a tone-map. +/// format offered, HDR off in the compositor) shows `HDR→SDR`; and a lane that shows PQ +/// with no tone-map pass at all (`hdr_untonemapped`) shows `HDR→SDR (raw)`, so a +/// washed-out picture is named for what it is rather than passed off as a tone-map. +/// ⚠ Since M8 no lane sets that flag — the software lane, which used to, now goes through +/// the same planar CSC pass as the hardware ones. The arm is kept for the next one that +/// does not; see `StreamState::hdr_untonemapped`. /// /// `profile` (the session's settings profile, `None` for the global defaults) closes the /// first line at every tier — the cheapest possible answer to "which profile am I on?" @@ -2622,6 +2790,19 @@ fn stats_text( text.push_str(&format!("\nintegrity: {}", parts.join(" · "))); } } + // M8's software-HEVC-drop telemetry ("telemetry on frequency", §7 risk register): + // how many times in THIS process a session's codec ran out of decode rungs and had + // to reconnect as another codec. Process-cumulative, appended LAST and only when + // nonzero — additive for the stdout `stats:` line's parsers, and invisible on the + // overwhelming majority of runs where it never happens. + // + // On the line rather than only in the log because the question it answers is a rate + // across a session history ("did dropping software HEVC cost anyone a stream?"), and + // a warn nobody greps for cannot answer it. + let fallbacks = pf_client_core::session::codec_fallbacks(); + if detailed && fallbacks > 0 { + text.push_str(&format!("\ncodec_fallbacks {fallbacks}")); + } text } @@ -3126,10 +3307,14 @@ mod tests { } } - /// The honest HDR badges: a PQ stream on the software-decode lane is shown WITHOUT - /// tone-mapping (that lane has no PQ→sRGB pass), so its badge must not read as the - /// hardware lane's `HDR→SDR` tone-map — and an HDR10 swapchain shows plain `HDR` - /// whatever the lane claims (a CPU frame forces the swapchain to SDR anyway). + /// The honest HDR badges. ⚠ **The `(raw)` arm is currently unreachable in + /// production**: `hdr_untonemapped` is written `false` on every present arm since M8 + /// took the software lane through the same planar CSC pass (and therefore the same + /// tone-map) as the hardware lanes — see `StreamState::hdr_untonemapped`. This tests + /// the FORMATTER, not a state the client can be in, and it is kept for the same + /// reason the field is: the next lane that bypasses the pass must be able to say so + /// rather than quietly claim a tone-map, and this is the assertion that will still be + /// here when it does. #[test] fn hdr_badge_names_the_untonemapped_cpu_lane() { let (s, p) = sample(); diff --git a/crates/pf-presenter/src/vk/mod.rs b/crates/pf-presenter/src/vk/mod.rs index 3d0f53f3..537ca777 100644 --- a/crates/pf-presenter/src/vk/mod.rs +++ b/crates/pf-presenter/src/vk/mod.rs @@ -1,13 +1,20 @@ -//! The Vulkan presenter: swapchain + two frame paths into one device-local RGBA video +//! The Vulkan presenter: swapchain + several frame paths into one device-local RGBA video //! image, then a letterboxed `vkCmdBlitImage` composite. //! -//! * **Software** (`FrameInput::Cpu`): staging upload + `copy_buffer_to_image` (row -//! stride via `buffer_row_length`) — transfer-only, runs on every GPU. +//! * **Software** (`FrameInput::Cpu`): since M8 the CPU rung hands over tightly-packed +//! 8-bit I420 PLANES, not RGBA. They are staged into three R8 images +//! (`CpuPlanes`, no `buffer_row_length` — the planes carry no stride) and converted by +//! the PLANAR CSC render pass, the same pass and the same `csc_rows` coefficients the +//! hardware lanes use. That is what deleted this lane's second colour implementation +//! (swscale's BT.601 default) and its missing tone-map along with it. //! * **Hardware** (`FrameInput::Dmabuf`): the decoder's NV12 dmabuf imported per-plane -//! (`dmabuf.rs`) and converted by the CSC render pass (`csc.rs`) — zero-copy, gated on -//! the four import extensions at device creation; boxes without them (NVIDIA +//! (`dmabuf.rs`) and converted by the two-plane CSC render pass (`csc.rs`) — zero-copy, +//! gated on the four import extensions at device creation; boxes without them (NVIDIA //! proprietary by design) report `supports_dmabuf() == false` and the caller keeps the //! decoder on software. +//! * Plus the lanes that arrive already on this device: `VkFrame`/`NativeVk` (Vulkan +//! Video), `D3d11` (Windows shared textures) and `PyroWave` (three compute-decoded +//! planes, through the same planar pass as the software lane). //! //! Pacing: one frame in flight (the submit fence is waited before each record), MAILBOX //! when available, FIFO otherwise (`PUNKTFUNK_PRESENT_MODE=fifo|mailbox|immediate` @@ -23,7 +30,7 @@ use crate::overlay::SharedDevice; use ash::vk; #[cfg(target_os = "linux")] use pf_client_core::video::DmabufFrame; -use pf_client_core::video::{CpuFrame, NativeVkFrame, VkVideoFrame}; +use pf_client_core::video::{CpuPlanarFrame, NativeVkFrame, VkVideoFrame}; mod gpu; mod overlay_pipe; @@ -39,7 +46,11 @@ pub use setup::{list_adapters, PresentPref}; pub enum FrameInput<'a> { /// No new frame — re-composite the retained video image (expose/resize). Redraw, - Cpu(&'a CpuFrame), + /// Software-decoded I420 planes (M8): uploaded into three R8 images and converted by + /// the planar CSC pass, exactly like the hardware lanes' planes — so PQ tone-mapping, + /// range and matrix all come from the ONE shader, and the CPU lane stops being the + /// odd one out that arrived pre-converted (and pre-converted wrong). + Cpu(&'a CpuPlanarFrame), #[cfg(target_os = "linux")] Dmabuf(DmabufFrame), /// FFmpeg Vulkan Video output — a VkImage already on THIS device (zero copy). @@ -107,8 +118,32 @@ struct OverlayPipe { framebuffers: Vec, } -/// The one video image (device-local RGBA the size of the decoded stream) + its staging. -/// `view`/`framebuffer` exist only on hw-capable devices (the CSC pass renders into it). +/// The software rung's plane images: three R8 pictures the CPU frame's tightly-packed +/// I420 is uploaded into, then sampled by the planar CSC pass. Sized to the LUMA picture +/// and its 4:2:0 chroma halves; rebuilt whenever the stream size changes. +/// +/// Owned by the presenter rather than parked in `Retired` like the imported hardware +/// frames: nothing outside this device ever refers to them, and the single in-flight +/// fence is waited before each record, so re-uploading into the same images is safe +/// without a ring. +struct CpuPlanes { + images: [vk::Image; 3], + memory: [vk::DeviceMemory; 3], + views: [vk::ImageView; 3], + /// Luma size; chroma is derived (`div_ceil(2)`), the same rule the frame uses. + width: u32, + height: u32, + /// True once the images have been transitioned out of UNDEFINED at least once — the + /// first upload must come from UNDEFINED (nothing to preserve), every later one from + /// SHADER_READ_ONLY_OPTIMAL (where the previous frame's CSC pass left them). + initialized: bool, +} + +/// The one video image: device-local RGBA the size of the decoded stream, the single +/// target every lane converges on before the letterboxed blit. `view` + `framebuffer` are +/// unconditional since M8 — the CSC pass renders into it on EVERY device, because the +/// software lane goes through the planar pass too and there is no lane left that writes +/// this image with a plain transfer. struct VideoImage { image: vk::Image, memory: vk::DeviceMemory, @@ -118,6 +153,8 @@ struct VideoImage { height: u32, } +/// The host-visible upload buffer the software rung's three planes are copied into before +/// the record step's `vkCmdCopyBufferToImage`s. Grows, never shrinks. struct Staging { buffer: vk::Buffer, memory: vk::DeviceMemory, @@ -146,10 +183,15 @@ pub struct Presenter { #[cfg(windows)] hw_win: Option, csc: CscPass, - /// The planar (3-plane) CSC variant for PyroWave frames; built only when the device - /// passed the pyrowave probe. - #[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))] - csc_planar: Option, + /// The planar (3-plane) CSC variant. Unconditional since M8: the SOFTWARE rung + /// renders through it, and the software rung is the ladder's last one — a device that + /// failed the pyrowave probe (or a build without the feature) still has to be able to + /// show a picture. + csc_planar: CscPass, + /// The software rung's three uploaded plane images (Y/Cb/Cr, R8), rebuilt on a + /// stream-size change. `None` until the first CPU frame — a hardware session never + /// allocates them. + cpu_planes: Option, /// FFmpeg Vulkan Video decode handles — `None` when the stack can't do it. video_export: Option, /// The console-UI composite quad (§6.1's presenter half). @@ -385,8 +427,8 @@ impl Drop for Presenter { #[cfg(target_os = "linux")] self.hw.take(); self.csc.destroy(&self.device); - #[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))] - if let Some(p) = &self.csc_planar { + self.csc_planar.destroy(&self.device); + if let Some(p) = self.cpu_planes.take() { p.destroy(&self.device); } self.overlay_pipe.destroy(&self.device); diff --git a/crates/pf-presenter/src/vk/present.rs b/crates/pf-presenter/src/vk/present.rs index 21e4eb7d..04138bbd 100644 --- a/crates/pf-presenter/src/vk/present.rs +++ b/crates/pf-presenter/src/vk/present.rs @@ -31,36 +31,17 @@ impl Presenter { // offers HDR10 — otherwise PQ stays on the SDR swapchain and the CSC shader // tonemaps (mode 1). // - // CPU frames NEVER take the HDR10 surface: software decode uploads swscale RGBA with - // no CSC/tonemap pass, so on a mode-0 swapchain that sRGB-encoded content would be - // composed as PQ — the field-reported psychedelic cyan/magenta picture (reproduced - // 2026-07-21: Fedora-class client, no hw HEVC decode, GNOME/Mesa offering HDR10 even - // on an SDR desktop). On the SDR swapchain the same frames are merely untonemapped - // (washed out) — wrong in the known, benign way until the CPU lane grows a real - // PQ→sRGB pass. + // The CPU lane used to be the exception here: it arrived as swscale RGBA with no + // CSC/tonemap pass at all, so it was pinned to the SDR swapchain (a mode-0 + // composite of sRGB content as PQ is the field-reported psychedelic cyan/magenta + // picture, reproduced 2026-07-21 on a Fedora-class client with no hw HEVC decode + // and GNOME/Mesa offering HDR10 on an SDR desktop) and a PQ stream simply came out + // washed out. Since M8 it goes through the SAME planar CSC pass as every hardware + // lane, so it gets the same answer as every hardware lane: PQ where the surface + // offers HDR10, the shader's mode-1 tonemap where it does not. let frame_pq = match &input { FrameInput::Redraw => None, - FrameInput::Cpu(f) => { - // The swapchain answer stays `false` (above) — but a PQ stream on this - // lane is then shown RAW: no PQ→sRGB pass exists here (the CSC mode-1 - // tonemap is hardware-lane only; CPU frames are a straight RGBA upload), - // so the picture is washed out and the pq-downgrade warn below never - // fires. Say so once, or the only trace is an OSD badge. (A process-once - // latch, same idiom as the decoders' first-frame layout dumps — the - // condition is a property of the lane, not of one Presenter.) - if f.color.is_pq() { - use std::sync::atomic::{AtomicBool, Ordering}; - static WARNED: AtomicBool = AtomicBool::new(false); - if !WARNED.swap(true, Ordering::Relaxed) { - tracing::warn!( - "HDR10 (PQ) stream on the software-decode lane — it has no \ - PQ→sRGB pass, so the picture is shown untonemapped (washed \ - out). Hardware decode restores correct colour." - ); - } - } - Some(false) - } + FrameInput::Cpu(f) => Some(f.color.is_pq()), #[cfg(target_os = "linux")] FrameInput::Dmabuf(d) => Some(d.color.is_pq()), FrameInput::VkFrame(v) => Some(v.color.is_pq()), @@ -100,6 +81,10 @@ impl Presenter { let mut native_frame: Option = None; #[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))] let mut pyro_frame: Option = None; + // A real frame that is NOT a CPU one — the signal that the software rung's plane + // images are dead weight (see below). `Redraw` is deliberately not one: it + // re-blits the retained video image and says nothing about which lane is decoding. + let mut hw_lane = false; let cpu_frame = match input { FrameInput::Redraw => None, FrameInput::Cpu(f) => Some(f), @@ -110,6 +95,7 @@ impl Presenter { .as_ref() .context("hardware frame without dmabuf support")?; hw_frame = Some(dmabuf::import(&self.device, &hw.ext_mem_fd, d)?); + hw_lane = true; None } #[cfg(windows)] @@ -119,22 +105,26 @@ impl Presenter { .as_ref() .context("D3D11 frame without win32 import support")?; win_frame = Some(crate::d3d11::import(&self.device, &hw.ext_mem_win32, &d)?); + hw_lane = true; None } FrameInput::VkFrame(v) => { let views = self.vkframe_plane_views(&v)?; vk_frame = Some((v, views)); + hw_lane = true; None } #[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))] FrameInput::PyroWave(f) => { pyro_frame = Some(f); + hw_lane = true; None } // Same device, and the decoder already made the per-plane views — no // import, no view creation, nothing that can fail out here. FrameInput::NativeVk(f) => { native_frame = Some(f); + hw_lane = true; None } }; @@ -153,10 +143,22 @@ impl Presenter { if let Some(old) = self.retired_hw.take() { old.destroy(&self.device); } - - if let Some(f) = cpu_frame { - self.stage_frame(f)?; + // A hardware frame after a software one: the plane images are ~12 MB at 4K and + // nothing will sample them again. This is not hypothetical — M8's codec fallback + // starts a NEW session on this same presenter, and that one can be hardware where + // the one that raised it was not. The fence wait above is what makes them + // unreferenced, so this is the first safe moment. + if hw_lane { + if let Some(p) = self.cpu_planes.take() { + tracing::debug!("freeing the software rung's plane images (hardware lane)"); + p.destroy(&self.device); + } } + + let cpu_offsets = match cpu_frame { + Some(f) => Some(self.stage_frame(f)?), + None => None, + }; #[cfg(target_os = "linux")] if let Some(f) = &hw_frame { if self @@ -235,11 +237,26 @@ impl Presenter { self.rebuild_video_image(f.width, f.height)?; tracing::info!(width = f.width, height = f.height, "video image (re)built"); } - let planar = self - .csc_planar + // The decode leaves them in GENERAL — the software rung's uploaded planes are + // the other producer for this pass and arrive in SHADER_READ_ONLY_OPTIMAL. + self.csc_planar.bind_planes_planar( + &self.device, + f.views.map(vk::ImageView::from_raw), + vk::ImageLayout::GENERAL, + ); + } + if cpu_offsets.is_some() { + // Safe while nothing in flight references the set — the fence wait above. + let views = self + .cpu_planes .as_ref() - .context("PyroWave frame but the device failed the pyrowave probe")?; - planar.bind_planes_planar(&self.device, f.views.map(vk::ImageView::from_raw)); + .context("software frame without plane images")? + .views; + self.csc_planar.bind_planes_planar( + &self.device, + views, + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + ); } if let Some(o) = overlay { // Point the composite at this overlay image (same fence-wait safety). @@ -476,40 +493,80 @@ impl Presenter { width: v.width, height: v.height, }; - self.record_csc_planar(v.framebuffer, extent, f.color); + // An HDR (PQ) pyrowave session carries P010-style 10-bit studio codes + // MSB-packed into 16-bit planes (design/pyrowave-444-hdr.md §2.2) — same + // sampling scale as the P010 path; SDR sessions are plain 8-bit BT.709 + // limited. Depth follows THIS codec's colour contract (negotiation + // couples 10-bit ⟺ PQ for it), which is why it is decided here and not + // inside the shared record. + let (depth, msb_packed) = if f.color.is_pq() { + (10, true) + } else { + (8, false) + }; + self.record_csc_planar(v.framebuffer, extent, f.color, depth, msb_packed); } - // New frame: staging → video image (stride carried by buffer_row_length). - if let (Some(f), Some(v), Some(s)) = (cpu_frame, &self.video, &self.staging) { - barrier( - &self.device, - self.cmd_buf, - v.image, - vk::ImageLayout::UNDEFINED, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - ); - let region = vk::BufferImageCopy::default() - .buffer_row_length((f.stride / 4) as u32) - .image_subresource(subresource_layers()) - .image_extent(vk::Extent3D { - width: v.width, - height: v.height, - depth: 1, - }); - self.device.cmd_copy_buffer_to_image( - self.cmd_buf, - s.buffer, - v.image, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &[region], - ); - barrier( - &self.device, - self.cmd_buf, - v.image, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - vk::ImageLayout::TRANSFER_SRC_OPTIMAL, - ); + // Software frame (M8): staging → three R8 plane images → the planar CSC pass, + // the same pass and the same `csc_rows` coefficients the hardware lanes use. + // The planes are tightly packed by construction (`CpuPlanarFrame`), so no + // `buffer_row_length` is needed and none is set — a stride here would be a + // second place for the layout to be wrong. + if let (Some(f), Some(offsets), Some(v), Some(s), Some(p)) = ( + cpu_frame, + cpu_offsets, + &self.video, + &self.staging, + &self.cpu_planes, + ) { + // First upload into freshly built images comes from UNDEFINED (there is + // nothing to preserve); every later one from where the previous frame's + // CSC pass left them. + let from = if p.initialized { + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL + } else { + vk::ImageLayout::UNDEFINED + }; + for (i, offset) in offsets.iter().enumerate() { + let (w, h) = f.plane_dims(i); + barrier( + &self.device, + self.cmd_buf, + p.images[i], + from, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + ); + let region = vk::BufferImageCopy::default() + .buffer_offset(*offset as u64) + .image_subresource(subresource_layers()) + .image_extent(vk::Extent3D { + width: w, + height: h, + depth: 1, + }); + self.device.cmd_copy_buffer_to_image( + self.cmd_buf, + s.buffer, + p.images[i], + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &[region], + ); + barrier( + &self.device, + self.cmd_buf, + p.images[i], + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + ); + } + let extent = vk::Extent2D { + width: v.width, + height: v.height, + }; + // Always 8-bit, no MSB packing — R8 planes, whatever the stream signals. + // A PQ AV1 stream on this rung therefore tone-maps through the shader's + // mode 1 like every other lane, instead of being read as 10-bit. + self.record_csc_planar(v.framebuffer, extent, f.color, 8, false); } // Swapchain image: discard old content, clear to black (the letterbox bars), @@ -628,6 +685,14 @@ impl Presenter { ); } self.device.end_command_buffer(self.cmd_buf)?; + // The plane images now have content and a real layout, so the NEXT upload + // must transition from SHADER_READ_ONLY_OPTIMAL rather than discard them from + // UNDEFINED. Recorded, not submitted — but the only path from here to another + // record goes through this command buffer, and a submit failure below tears + // the presenter down rather than re-recording. + if let Some(p) = self.cpu_planes.as_mut() { + p.initialized = true; + } let render_sem = self.render_sems[index as usize]; let cmd_bufs = [self.cmd_buf]; @@ -883,18 +948,22 @@ impl Presenter { } } - /// [`record_csc`] over the planar (PyroWave) pass — always 8-bit, no MSB packing. - #[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))] + /// [`record_csc`] over the planar (3-plane) pass — the PyroWave decode output and, + /// since M8, the software rung's uploaded I420. + /// + /// `depth`/`msb_packed` are the PRODUCER's, never inferred from the colour: pyrowave + /// couples 10-bit to PQ by negotiation, the software rung is 8-bit whatever it is + /// showing, and reading PQ as "therefore 10-bit MSB-packed" over an 8-bit plane + /// samples at a quarter scale — decoded correctly, displayed wrong. unsafe fn record_csc_planar( &self, framebuffer: vk::Framebuffer, extent: vk::Extent2D, color: pf_client_core::video::ColorDesc, + depth: u8, + msb_packed: bool, ) { - // The planar pass exists whenever a PyroWave frame reached us (checked at bind). - let Some(planar) = self.csc_planar.as_ref() else { - return; - }; + let planar = &self.csc_planar; // SAFETY: per the Vulkan contract above - recorded into a command buffer this code owns // and has begun, referencing handles it also owns; nothing is submitted until the // recording is ended. @@ -943,15 +1012,6 @@ impl Presenter { &[planar.desc_set], &[], ); - // An HDR (PQ) pyrowave session carries P010-style 10-bit studio codes MSB-packed - // into 16-bit planes (design/pyrowave-444-hdr.md §2.2) — same sampling scale as - // the P010 path; SDR sessions are plain 8-bit BT.709 limited. Depth follows the - // colour contract (negotiation couples 10-bit ⟺ PQ for this codec). - let (depth, msb_packed) = if color.is_pq() { - (10, true) - } else { - (8, false) - }; let rows = csc_rows(color, depth, msb_packed); // Mode 1 = PQ→SDR tonemap (PQ stream without an HDR10 surface); mode 0 passes // the transfer through — identical to the NV12 arm above. diff --git a/crates/pf-presenter/src/vk/reconfig.rs b/crates/pf-presenter/src/vk/reconfig.rs index 5e9d39a5..4ab6227a 100644 --- a/crates/pf-presenter/src/vk/reconfig.rs +++ b/crates/pf-presenter/src/vk/reconfig.rs @@ -274,14 +274,11 @@ impl Presenter { }; self.csc.destroy(&self.device); // fence-safe: only our cmd bufs reference it self.csc = CscPass::new(&self.device, self.video_format)?; - // The planar (PyroWave) pass renders to the same intermediate — rebuild it at the - // new format too (an HDR pyrowave session needs the 10-bit intermediate exactly - // like the H.26x path; 8-bit PQ bands visibly). - #[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))] - if let Some(p) = self.csc_planar.take() { - p.destroy(&self.device); - self.csc_planar = Some(CscPass::new_planar(&self.device, self.video_format)?); - } + // The planar pass (PyroWave + the software rung) renders to the same intermediate + // — rebuild it at the new format too (an HDR session needs the 10-bit + // intermediate exactly like the H.26x path; 8-bit PQ bands visibly). + self.csc_planar.destroy(&self.device); + self.csc_planar = CscPass::new_planar(&self.device, self.video_format)?; if let Some(v) = self.video.take() { // SAFETY: per the Vulkan contract above - this destroys objects this type owns, and // the GPU is known idle for them (the fence/queue-wait on the path here, or the diff --git a/crates/pf-presenter/src/vk/resources.rs b/crates/pf-presenter/src/vk/resources.rs index 4627e137..024ee469 100644 --- a/crates/pf-presenter/src/vk/resources.rs +++ b/crates/pf-presenter/src/vk/resources.rs @@ -1,10 +1,10 @@ //! Video-image / staging-buffer (re)build + retired-frame destruction. use super::gpu::subresource_range; -use super::{Presenter, Retired, Staging, VideoImage}; +use super::{CpuPlanes, Presenter, Retired, Staging, VideoImage}; use anyhow::Result; use ash::vk; -use pf_client_core::video::CpuFrame; +use pf_client_core::video::CpuPlanarFrame; impl Retired { pub(super) fn destroy(self, device: &ash::Device) { @@ -32,16 +32,49 @@ impl Retired { } } +/// Staging offset of plane `i` for a picture of this size, plus the total bytes needed. +/// +/// Each plane starts on a 16-byte boundary so `bufferOffset` satisfies the copy's +/// "multiple of 4" rule whatever the picture dimensions are — with a 1-byte-per-texel +/// format an odd width would otherwise land a later plane on an odd offset. +fn plane_staging_offsets(f: &CpuPlanarFrame) -> ([usize; 3], usize) { + let mut offsets = [0usize; 3]; + let mut at = 0usize; + for (i, off) in offsets.iter_mut().enumerate() { + let (w, h) = f.plane_dims(i); + *off = at; + at += (w as usize * h as usize).next_multiple_of(16); + } + (offsets, at) +} + +impl CpuPlanes { + /// Destroy every handle this value holds. Null handles are fine — Vulkan defines + /// destroy/free on `VK_NULL_HANDLE` as a no-op — which is what lets + /// [`Presenter::rebuild_cpu_planes`] unwind a build that failed part-way. + pub(super) fn destroy(self, device: &ash::Device) { + // SAFETY: per the Vulkan contract above - this destroys objects this type owns, and the + // GPU is known idle for them (the fence/queue-wait on the path here, or the swapchain + // being retired), which is the obligation that makes a destroy sound rather than the + // handle merely being non-null. + unsafe { + for i in 0..3 { + device.destroy_image_view(self.views[i], None); + device.destroy_image(self.images[i], None); + device.free_memory(self.memory[i], None); + } + } + } +} + impl Presenter { - /// Copy the frame's RGBA into the staging buffer and (re)build the video image on a - /// stream-size change. Rows keep their stride — `buffer_row_length` unpacks it. - pub(super) fn stage_frame(&mut self, f: &CpuFrame) -> Result<()> { - anyhow::ensure!( - f.stride % 4 == 0 && f.stride >= f.width as usize * 4, - "unexpected RGBA stride {} for width {}", - f.stride, - f.width - ); + /// Copy the frame's three tightly-packed planes into the staging buffer and (re)build + /// the plane images + video image on a stream-size change. + /// + /// Returns the per-plane staging offsets the record step copies from. Nothing here + /// touches the queue: a rebuild that fails must fail BEFORE the acquire, the same + /// rule the hardware imports follow. + pub(super) fn stage_frame(&mut self, f: &CpuPlanarFrame) -> Result<[usize; 3]> { if self .video .as_ref() @@ -50,15 +83,107 @@ impl Presenter { self.rebuild_video_image(f.width, f.height)?; tracing::info!(width = f.width, height = f.height, "video image (re)built"); } - let needed = f.stride * f.height as usize; + if self + .cpu_planes + .as_ref() + .is_none_or(|p| p.width != f.width || p.height != f.height) + { + self.rebuild_cpu_planes(f.width, f.height)?; + } + let (offsets, needed) = plane_staging_offsets(f); if self.staging.as_ref().is_none_or(|s| s.capacity < needed) { self.rebuild_staging(needed)?; } let s = self.staging.as_ref().unwrap(); - let n = f.rgba.len().min(needed); - // SAFETY: per the Vulkan contract above - the Vulkan handles used here are owned by this - // type and live for the call, and every builder struct is a local that outlives it. - unsafe { std::ptr::copy_nonoverlapping(f.rgba.as_ptr(), s.ptr, n) }; + for (i, off) in offsets.iter().enumerate() { + let plane = f.plane(i); + // SAFETY: per the Vulkan contract above - `s.ptr` maps a HOST_VISIBLE allocation of + // `s.capacity >= needed` bytes and `plane_staging_offsets` placed `off + plane.len()` + // inside `needed`; source and destination are distinct allocations. + unsafe { std::ptr::copy_nonoverlapping(plane.as_ptr(), s.ptr.add(*off), plane.len()) }; + } + Ok(offsets) + } + + /// (Re)build the software rung's three R8 plane images for a luma size. + fn rebuild_cpu_planes(&mut self, width: u32, height: u32) -> Result<()> { + // Fence-quiesce: the old images are only ever referenced by OUR command buffers. + self.quiesce_own()?; + if let Some(p) = self.cpu_planes.take() { + p.destroy(&self.device); + } + let (cw, ch) = CpuPlanarFrame::chroma_dims(width, height); + let dims = [(width, height), (cw, ch), (cw, ch)]; + // Built INTO the owning value, not into loose arrays: nine fallible steps (three + // images, three allocations, three views) used to `?` straight out and leak + // everything created before the one that failed — up to ~12 MB per size change at + // 4K, on the rung the client reaches because something already went wrong. + // `destroy` tolerates the nulls a partial build leaves (Vulkan defines + // destroy/free on `VK_NULL_HANDLE` as a no-op), so one call unwinds any prefix. + let mut planes = CpuPlanes { + images: [vk::Image::null(); 3], + memory: [vk::DeviceMemory::null(); 3], + views: [vk::ImageView::null(); 3], + width, + height, + initialized: false, + }; + for (i, dim) in dims.into_iter().enumerate() { + if let Err(e) = self.build_cpu_plane(&mut planes, i, dim) { + planes.destroy(&self.device); + return Err(e); + } + } + tracing::info!(width, height, "software plane images (re)built"); + self.cpu_planes = Some(planes); + Ok(()) + } + + /// One R8 plane of [`CpuPlanes`], written into `planes` as each handle is created so + /// a failure part-way leaves the caller something it can destroy. + fn build_cpu_plane(&self, planes: &mut CpuPlanes, i: usize, (w, h): (u32, u32)) -> Result<()> { + // SAFETY: per the Vulkan contract above - the Vulkan handles used here are owned by + // this type and live for the call, and every builder struct is a local that outlives + // it. + let image = unsafe { + self.device.create_image( + &vk::ImageCreateInfo::default() + .image_type(vk::ImageType::TYPE_2D) + .format(vk::Format::R8_UNORM) + .extent(vk::Extent3D { + width: w, + height: h, + depth: 1, + }) + .mip_levels(1) + .array_layers(1) + .samples(vk::SampleCountFlags::TYPE_1) + .tiling(vk::ImageTiling::OPTIMAL) + .usage(vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::SAMPLED) + .initial_layout(vk::ImageLayout::UNDEFINED), + None, + ) + }?; + planes.images[i] = image; + // SAFETY: per the Vulkan contract above - a read-only query on the live device. + let reqs = unsafe { self.device.get_image_memory_requirements(image) }; + planes.memory[i] = self.allocate(reqs, vk::MemoryPropertyFlags::DEVICE_LOCAL)?; + // SAFETY: per the Vulkan contract above - the Vulkan handles used here are owned by + // this type and live for the call. + unsafe { self.device.bind_image_memory(image, planes.memory[i], 0) }?; + // SAFETY: per the Vulkan contract above - the Vulkan handles used here are owned by + // this type and live for the call, and every builder struct is a local that outlives + // it. + planes.views[i] = unsafe { + self.device.create_image_view( + &vk::ImageViewCreateInfo::default() + .image(image) + .view_type(vk::ImageViewType::TYPE_2D) + .format(vk::Format::R8_UNORM) + .subresource_range(subresource_range()), + None, + ) + }?; Ok(()) } diff --git a/crates/pf-presenter/src/vk/setup.rs b/crates/pf-presenter/src/vk/setup.rs index 5ded5716..8977e39e 100644 --- a/crates/pf-presenter/src/vk/setup.rs +++ b/crates/pf-presenter/src/vk/setup.rs @@ -421,14 +421,14 @@ impl Presenter { ext_mem_win32: ash::khr::external_memory_win32::Device::new(&instance, &device), }); let csc = CscPass::new(&device, vk::Format::R8G8B8A8_UNORM)?; - // Starts SDR like `csc`; an HDR (PQ) pyrowave session rebuilds it at the 10-bit + // Starts SDR like `csc`; an HDR (PQ) session rebuilds it at the 10-bit // intermediate via `set_hdr_mode`, exactly like the H.26x pass. - #[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))] - let csc_planar = if pyrowave_ok { - Some(CscPass::new_planar(&device, vk::Format::R8G8B8A8_UNORM)?) - } else { - None - }; + // + // Unconditional since M8. It used to be built only for a device that passed the + // pyrowave probe; the SOFTWARE rung now renders through it too, and that rung is + // the ladder's last one — gating it on a probe would leave the boxes that failed + // the probe with no way to show a software-decoded frame at all. + let csc_planar = CscPass::new_planar(&device, vk::Format::R8G8B8A8_UNORM)?; // The exported handle bundle: FFmpeg Vulkan Video handles when the device can // decode, AND (Windows) the D3D11-interop facts — so it's built whenever EITHER @@ -590,8 +590,8 @@ impl Presenter { #[cfg(windows)] hw_win, csc, - #[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))] csc_planar, + cpu_planes: None, video_export, overlay_pipe, retired_hw: None,