feat(client): PyroWave session wiring — advertisement, opt-in, decoder selection (Phase 2b, part 3)
ci / web (push) Successful in 1m21s
ci / docs-site (push) Successful in 1m19s
ci / rust (push) Failing after 6m16s
decky / build-publish (push) Successful in 16s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 8s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 8s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 7s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 7s
arch / build-publish (push) Successful in 10m49s
ci / bench (push) Successful in 5m37s
docker / deploy-docs (push) Successful in 11s
flatpak / build-publish (push) Failing after 8m5s
deb / build-publish (push) Successful in 11m49s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m36s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m38s
android / android (push) Successful in 13m45s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m45s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m51s
apple / swift (push) Successful in 4m43s
apple / screenshots (push) Waiting to run
windows / build (aarch64-pc-windows-msvc) (push) Failing after 4m52s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 5m24s

The pump now advertises decodable_codecs_for(presenter device) — the
CODEC_PYROWAVE bit rides only when the device passed the compute-feature
probe — and PUNKTFUNK_PREFER_PYROWAVE=1 is the Phase-2 lab opt-in that
names the codec in preferred_codec (the only route resolve_codec will
take it, plan §3; a Settings toggle is Phase-3 productization). A
negotiated PyroWave session builds Decoder::new_pyrowave on the
presenter's device instead of an FFmpeg decoder. clients/session grows
the `pyrowave` feature forwarding both crate features.

With this the Phase-2 client chain is code-complete:
Hello bit → preference → Welcome::codec → pyrowave decode on the
presenter device → planar CSC → present. On-glass .21 run +
latency-probe/loss-harness numbers vs HEVC remain owed (plan Phase-2
exit criteria).

Validated on .21: session client + all crates compile with and without
the features, clippy clean, 26 + 308 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 01:48:36 +02:00
parent ef862454b0
commit fa4df1de9e
2 changed files with 38 additions and 3 deletions
+3
View File
@@ -14,6 +14,9 @@ path = "src/main.rs"
[features]
default = ["ui"]
# PyroWave client decode (the opt-in wired-LAN wavelet codec) — enables the decode
# backend + the planar present path; runtime opt-in stays PUNKTFUNK_PREFER_PYROWAVE=1.
pyrowave = ["pf-client-core/pyrowave", "pf-presenter/pyrowave"]
# The Skia console UI (stats OSD, capture HUD, later the gamepad library). Dropping it
# (`--no-default-features`) is the ~15 MB-smaller power-user build: same streaming,
# stats on stdout only.
+35 -3
View File
@@ -211,6 +211,21 @@ fn pump(
frame_tx: async_channel::Sender<DecodedFrame>,
stop: Arc<AtomicBool>,
) {
// PUNKTFUNK_PREFER_PYROWAVE=1 — the Phase-2 lab opt-in for the wired-LAN wavelet codec
// (a Settings toggle is the Phase-3 productization). Riding `preferred_codec` is exactly
// the plan-§3 contract: the host only ever picks PyroWave when the client names it.
#[allow(unused_mut)]
let mut preferred = params.preferred_codec;
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
if std::env::var("PUNKTFUNK_PREFER_PYROWAVE").as_deref() == Ok("1") {
if params.vulkan.as_ref().is_some_and(|v| v.pyrowave_decode) {
preferred = punktfunk_core::quic::CODEC_PYROWAVE;
} else {
tracing::warn!(
"PUNKTFUNK_PREFER_PYROWAVE=1 but the presenter device failed the pyrowave probe — keeping the normal codec preference"
);
}
}
let connector = match NativeClient::connect(
&params.host,
params.port,
@@ -220,8 +235,9 @@ fn pump(
params.bitrate_kbps,
params.video_caps,
params.audio_channels,
crate::video::decodable_codecs(), // codecs FFmpeg can decode (HEVC/H.264/AV1)
params.preferred_codec, // the user's soft codec preference (0 = auto)
// FFmpeg's codecs plus CODEC_PYROWAVE when the presenter device passed the probe.
crate::video::decodable_codecs_for(params.vulkan.as_ref()),
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).
punktfunk_core::client::display_hdr_env_override().or(params.display_hdr),
@@ -262,7 +278,23 @@ fn pump(
welcome_codec = connector.codec,
"negotiated video codec"
);
let mut decoder = match Decoder::new(codec_id, &params.decoder, params.vulkan.as_ref()) {
let built = 'decoder: {
// A negotiated PyroWave session decodes on the presenter's device, no FFmpeg —
// reachable only through the explicit preference above (resolve_codec never
// auto-picks the bit), so failing loudly here is failing an opted-in experiment.
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
if connector.codec == punktfunk_core::quic::CODEC_PYROWAVE {
let mode = connector.mode();
break 'decoder match params.vulkan.as_ref() {
Some(vk) => Decoder::new_pyrowave(vk, mode.width, mode.height),
None => Err(anyhow::anyhow!(
"pyrowave session without a presenter device"
)),
};
}
Decoder::new(codec_id, &params.decoder, params.vulkan.as_ref())
};
let mut decoder = match built {
Ok(d) => d,
Err(e) => {
let _ = ev_tx.send_blocking(SessionEvent::Ended(Some(format!("video decoder: {e}"))));