diff --git a/crates/punktfunk-core/src/quic/msgs.rs b/crates/punktfunk-core/src/quic/msgs.rs index edd957d4..10ca0da6 100644 --- a/crates/punktfunk-core/src/quic/msgs.rs +++ b/crates/punktfunk-core/src/quic/msgs.rs @@ -955,6 +955,7 @@ impl Welcome { codec: match b.get(66).copied() { Some(CODEC_H264) => CODEC_H264, Some(CODEC_AV1) => CODEC_AV1, + Some(CODEC_PYROWAVE) => CODEC_PYROWAVE, _ => CODEC_HEVC, }, // Optional trailing host-caps byte — absent on an older host → 0 (no gamepad-state diff --git a/crates/punktfunk-core/src/quic/tests.rs b/crates/punktfunk-core/src/quic/tests.rs index 4ce2f90d..0f184906 100644 --- a/crates/punktfunk-core/src/quic/tests.rs +++ b/crates/punktfunk-core/src/quic/tests.rs @@ -124,6 +124,45 @@ fn codec_negotiation_and_back_compat() { resolve_codec(CODEC_HEVC | CODEC_PYROWAVE, CODEC_HEVC, CODEC_PYROWAVE), Some(CODEC_HEVC) ); + // And the negotiated bit SURVIVES the Welcome wire roundtrip — the decode whitelist + // once folded unknown codec bytes (incl. PyroWave) to HEVC, which sent wavelet AUs + // into an FFmpeg HEVC decoder on the first on-glass run. + let mut pw_w = Welcome::decode( + &Welcome { + abi_version: 2, + udp_port: 1, + mode: Mode { + width: 1280, + height: 720, + refresh_hz: 60, + }, + fec: FecConfig { + scheme: FecScheme::Gf16, + fec_percent: 0, + max_data_per_block: 1024, + }, + shard_payload: 1024, + encrypt: false, + key: [0; 16], + salt: [0; 4], + frames: 0, + compositor: CompositorPref::Auto, + gamepad: GamepadPref::Auto, + bitrate_kbps: 0, + bit_depth: 8, + color: ColorInfo::SDR_BT709, + chroma_format: CHROMA_IDC_420, + audio_channels: 2, + codec: CODEC_PYROWAVE, + host_caps: 0, + } + .encode(), + ) + .unwrap(); + assert_eq!(pw_w.codec, CODEC_PYROWAVE); + // A genuinely unknown future bit still folds to the HEVC default. + pw_w.codec = 0x40; + assert_eq!(Welcome::decode(&pw_w.encode()).unwrap().codec, CODEC_HEVC); // A Hello advertising codecs roundtrips, and the wire form of a codec-only Hello decodes on // a build that ignores the trailing byte (back-compat: extra bytes are skipped). diff --git a/crates/punktfunk-host/src/encode.rs b/crates/punktfunk-host/src/encode.rs index d4d77be3..2c2b5344 100644 --- a/crates/punktfunk-host/src/encode.rs +++ b/crates/punktfunk-host/src/encode.rs @@ -674,9 +674,10 @@ fn open_video_backend( { tracing::warn!( ?codec, - "PUNKTFUNK_ENCODER=pyrowave: EXPERIMENTAL all-intra wavelet stream — \ - clients without a PyroWave decoder (all of them until CODEC_PYROWAVE \ - lands) cannot display it" + "PUNKTFUNK_ENCODER=pyrowave forces the all-intra wavelet stream \ + regardless of the negotiated codec — only a pyrowave-feature client \ + that ALSO preferred CODEC_PYROWAVE can display it (lab override; \ + normal sessions negotiate it instead)" ); pyrowave::PyroWaveEncoder::open(width, height, fps, bitrate_bps) .map(|e| (Box::new(e) as Box, "pyrowave")) diff --git a/crates/punktfunk-host/src/encode/linux/pyrowave.rs b/crates/punktfunk-host/src/encode/linux/pyrowave.rs index fe635729..0250ab18 100644 --- a/crates/punktfunk-host/src/encode/linux/pyrowave.rs +++ b/crates/punktfunk-host/src/encode/linux/pyrowave.rs @@ -430,8 +430,7 @@ impl PyroWaveEncoder { gpu = %props.device_name_as_c_str().unwrap_or(c"?").to_string_lossy(), mode = %format!("{w}x{h}@{fps}"), budget_kib = frame_budget / 1024, - "PyroWave encoder open (intra-only wavelet, BT.709 limited 4:2:0) — \ - EXPERIMENTAL: no shipping client decodes this until CODEC_PYROWAVE lands" + "PyroWave encoder open (intra-only wavelet, BT.709 limited 4:2:0)" ); Ok(Self {