diff --git a/crates/pf-encode/src/enc/codec.rs b/crates/pf-encode/src/enc/codec.rs index b9ba5493..e87227ab 100644 --- a/crates/pf-encode/src/enc/codec.rs +++ b/crates/pf-encode/src/enc/codec.rs @@ -249,9 +249,16 @@ impl Codec { } } -/// Static capabilities an [`Encoder`] declares so the session glue routes loss-recovery and HDR -/// plumbing by *query* rather than relying on a method's no-op/`false` default. Cheap `Copy`; fixed -/// for the session (an HDR toggle re-initialises the encoder — re-query if that matters). +/// Static capabilities an [`Encoder`] declares so the session glue routes loss-recovery and +/// cursor plumbing by *query* rather than relying on a method's no-op/`false` default. Cheap +/// `Copy`; fixed for the session (an HDR toggle re-initialises the encoder — re-query if that +/// matters). +/// +/// (There is deliberately NO `supports_hdr_metadata` cap: in-band HDR SEI/OBU embedding needs no +/// host-side routing — every first-party client reads the static grade exclusively out-of-band +/// (the native 0xCE datagram / the GameStream 0x010e control message), both planes send it +/// unconditionally, and the in-band grade is a decoder-side bonus for stock clients. A cap field +/// nothing reads is a contract nobody honors; it was deleted after shipping write-only.) #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct EncoderCaps { /// The encoder can perform real reference-frame invalidation — i.e. @@ -261,10 +268,6 @@ pub struct EncoderCaps { /// AMF (user-LTR force-reference, when the driver accepted the LTR slots at open). The /// libavcodec paths (Linux NVENC, VAAPI, QSV) can't express it and always keyframe. pub supports_rfi: bool, - /// The encoder emits in-band HDR mastering/CLL SEI from [`set_hdr_meta`](Encoder::set_hdr_meta). - /// When `false`, `set_hdr_meta` is a no-op and no in-band grade reaches the client. Only the - /// Windows direct-NVENC path attaches it today. - pub supports_hdr_metadata: bool, /// The opened encoder is actually producing a full-chroma 4:4:4 (`chroma_format_idc = 3`) stream. /// `false` on every 4:2:0 session (the default) and on a backend that declined 4:4:4. Set by the /// NVENC backends (Linux + Windows). The chroma is committed to the wire (`Welcome::chroma_format`) @@ -333,11 +336,10 @@ pub trait Encoder: Send { let _ = wire_index; self.submit(frame) } - /// This encoder's static [capabilities](EncoderCaps) (RFI, HDR SEI), so the session glue can - /// route by query rather than rely on the no-op/`false` defaults of - /// [`invalidate_ref_frames`](Self::invalidate_ref_frames) / [`set_hdr_meta`](Self::set_hdr_meta). - /// Default: no optional capabilities (the SDR / libavcodec backends) — only the direct-NVENC - /// path overrides it. + /// This encoder's static [capabilities](EncoderCaps) (RFI, intra-refresh, chroma, cursor + /// blending), so the session glue can route by query rather than rely on the no-op/`false` + /// defaults of methods like [`invalidate_ref_frames`](Self::invalidate_ref_frames). + /// Default: no optional capabilities (the software / libavcodec backends). fn caps(&self) -> EncoderCaps { EncoderCaps::default() } @@ -345,10 +347,12 @@ pub trait Encoder: Send { /// reference-frame-invalidation request). Default: no-op. fn request_keyframe(&mut self) {} /// Set the source's static HDR mastering metadata (from the capturer). An HDR encoder emits it - /// as in-band SEI (`mastering_display_colour_volume` + `content_light_level_info`) on each - /// keyframe so any decoder — including stock Moonlight — tone-maps from the source's real grade. - /// Default: no-op (SDR encoders / libavcodec paths that don't attach it yet). Cheap to call - /// every frame; only the direct-NVENC path consumes it. + /// in-band (HEVC/H.264 `mastering_display_colour_volume` + `content_light_level_info` SEI, or + /// AV1 metadata OBUs) on keyframes so a stock decoder — e.g. stock Moonlight — tone-maps from + /// the source's real grade. Default: no-op (SDR encoders / paths that don't attach it). + /// Cheap to call every frame; consumed by Windows direct-NVENC, native AMF, and native QSV. + /// Every first-party client reads the grade out-of-band (the 0xCE datagram) regardless, so + /// this is a bonus for stock decoders, never the primary channel. fn set_hdr_meta(&mut self, _meta: Option) {} /// Invalidate a contiguous range of previously-encoded reference frames (client frame numbers /// — WIRE frame indexes, the domain [`submit_indexed`](Self::submit_indexed) pins the encoder's diff --git a/crates/pf-encode/src/enc/linux/nvenc_cuda.rs b/crates/pf-encode/src/enc/linux/nvenc_cuda.rs index 82db0401..2899ee0b 100644 --- a/crates/pf-encode/src/enc/linux/nvenc_cuda.rs +++ b/crates/pf-encode/src/enc/linux/nvenc_cuda.rs @@ -1850,7 +1850,6 @@ impl Encoder for NvencCudaEncoder { // Composites `frame.cursor` via the SPIR-V blend over the Vulkan-allocated input slot. blends_cursor: true, supports_rfi: self.rfi_supported, - supports_hdr_metadata: self.hdr, chroma_444: self.chroma_444, intra_refresh: false, intra_refresh_recovery: false, diff --git a/crates/pf-encode/src/enc/windows/amf.rs b/crates/pf-encode/src/enc/windows/amf.rs index ecfc28d1..8f72cdf2 100644 --- a/crates/pf-encode/src/enc/windows/amf.rs +++ b/crates/pf-encode/src/enc/windows/amf.rs @@ -2014,9 +2014,6 @@ impl Encoder for AmfEncoder { // frame, force a later one to re-reference it). True only when the live driver accepted // the LTR slots at open — otherwise loss recovery falls back to a full IDR. supports_rfi: self.ltr_active, - // In-band mastering/CLL via `*InHDRMetadata` (HEVC SEI / AV1 metadata OBU); AVC has - // no such property (and no HDR sessions negotiate H.264). - supports_hdr_metadata: self.ten_bit && self.props.hdr_metadata.is_some(), // Permanent: VCN hardware does not encode 4:4:4. chroma_444: false, // True only when `PUNKTFUNK_INTRA_REFRESH` asked for the wave AND the live driver @@ -2715,10 +2712,6 @@ mod tests { } }; enc.set_hdr_meta(Some(sample_hdr_meta())); - assert!( - enc.caps().supports_hdr_metadata, - "HEVC 10-bit reports HDR SEI capability" - ); let mut aus: Vec = Vec::new(); for i in 0..6 { let frame = CapturedFrame { diff --git a/crates/pf-encode/src/enc/windows/nvenc.rs b/crates/pf-encode/src/enc/windows/nvenc.rs index 94ae666d..9957f7ef 100644 --- a/crates/pf-encode/src/enc/windows/nvenc.rs +++ b/crates/pf-encode/src/enc/windows/nvenc.rs @@ -1690,17 +1690,14 @@ impl Encoder for NvencD3d11Encoder { } fn caps(&self) -> EncoderCaps { - // RFI is probed once at open (`rfi_supported`); HDR SEI rides keyframes whenever the - // session is in HDR mode. Both are the real capabilities the session glue routes on. + // RFI is probed once at open (`rfi_supported`) — the real capability the session glue + // routes on. (In-band HDR SEI needs no cap: it rides keyframes on HEVC/H.264 HDR + // sessions — see `submit` — and every first-party client reads the grade out-of-band + // via the 0xCE datagram regardless.) EncoderCaps { // The Windows capture path composites the pointer; this backend never reads `frame.cursor`. blends_cursor: false, supports_rfi: self.rfi_supported, - // In-band mastering/CLL is attached as keyframe SEI on HEVC/H.264 only — AV1 carries - // it in METADATA OBUs (`HDR_MDCV`/`HDR_CLL`), which this backend doesn't emit yet - // (see `submit`); the grade still reaches punktfunk clients out-of-band via the 0xCE - // datagram. Don't claim a capability the AV1 path doesn't have. - supports_hdr_metadata: self.hdr && self.codec != Codec::Av1, // Reflects what the session actually configured (cleared in `query_caps` if the GPU lacks // YUV444 encode), so the glue can confirm 4:4:4 vs the negotiated request. chroma_444: self.chroma_444, diff --git a/crates/pf-encode/src/enc/windows/qsv.rs b/crates/pf-encode/src/enc/windows/qsv.rs index fd12af75..ed49a1d1 100644 --- a/crates/pf-encode/src/enc/windows/qsv.rs +++ b/crates/pf-encode/src/enc/windows/qsv.rs @@ -1471,9 +1471,6 @@ impl Encoder for QsvEncoder { // As Windows NVENC: the capturer composites; this backend never reads `frame.cursor`. blends_cursor: false, supports_rfi: self.ltr_active, - // In-band mastering/CLL at IDR (HEVC prefix SEI / AV1 metadata OBU); AVC sessions - // are never HDR. - supports_hdr_metadata: self.ten_bit && self.codec != Codec::H264, chroma_444: false, intra_refresh: self.ir_active, // Unvalidated on-glass — the host keeps the IDR recovery path until then.