fix(video): honor the signaled CSC matrix end-to-end + tvOS HDR presentation
Clients derive Y'CbCr->RGB from the stream's SIGNALED matrix x range x depth via shared csc rows (Rust csc_rows + Swift CscRows) instead of hardcoded 709/2020 - a BT.601-signaled stream (a Linux host's RGB-input NVENC) no longer renders with a constant hue error. Host-side signaling made honest across NVENC/VAAPI/openh264/GameStream and the session plan's chroma/bit-depth. Decoded color-bar fixtures (601/709 x limited/full) pin the math in tests on both cores. Same presenter, tvOS HDR: tvOS has no Metal EDR API and a bare PQ colorspace tag composites UNTONE-MAPPED (the "overblown" Apple TV report), so HDR now splits on the display's live EDR headroom - PQ passthrough when the per-session AVDisplayManager mode switch landed (a real HDR10 output tone-maps itself), else an in-shader PQ->SDR tone-map (203-nit reference white, extended-Reinhard 1000-nit knee, 2020->709) into the proven SDR layer config. The 10-bit stream keeps its full decode depth either way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,8 +53,8 @@ pub const SCM_AV1_MAIN10: u32 = 0x0002_0000;
|
||||
/// host can actually deliver it ([`host_hdr_capable`]); it is never a static claim, because a non-HDR
|
||||
/// host (Linux, or a Windows host without the `PUNKTFUNK_10BIT` opt-in) must not invite a client into
|
||||
/// an HDR mode it can't produce. (The previous placeholder 3843 = 0xF03 wrongly claimed HEVC Main10 +
|
||||
/// 4:4:4 and *no* AV1.) 4:4:4 stays off entirely: stock Moonlight is 4:2:0 and the Windows IDD-push
|
||||
/// capturer can't yet deliver full-chroma frames (`crate::capture::capturer_supports_444`).
|
||||
/// 4:4:4 and *no* AV1.) 4:4:4 stays off entirely on GameStream: stock Moonlight is 4:2:0 —
|
||||
/// full-chroma is a punktfunk/1-native negotiation only (`crate::capture::capturer_supports_444`).
|
||||
pub const SERVER_CODEC_MODE_SUPPORT: u32 = SCM_H264 | SCM_HEVC | SCM_AV1_MAIN8;
|
||||
|
||||
/// Whether this host can deliver an **HDR** (HEVC Main10 / BT.2020 PQ) GameStream — the single gate
|
||||
|
||||
@@ -380,6 +380,38 @@ fn stream_config(map: &HashMap<String, String>) -> Option<StreamConfig> {
|
||||
"client requested HDR (dynamicRangeMode != 0) but host is not HDR-capable — streaming 8-bit SDR"
|
||||
);
|
||||
}
|
||||
// The client's requested CSC (moonlight-common-c SdpGenerator.c: `encoderCscMode =
|
||||
// (colorspace << 1) | fullRange` — colorspace 0=Rec601, 1=Rec709, 2=Rec2020). Moonlight
|
||||
// renderers configure their YUV→RGB from this REQUESTED value (not the bitstream VUI), so a
|
||||
// host that encodes something else shifts the client's colours. INSTRUMENTATION ONLY for
|
||||
// now: we always encode BT.709 limited for SDR (the IDD VideoConverter / VUI-driven NVENC)
|
||||
// and BT.2020 PQ for HDR — log what clients actually ask for so honoring `encoderCscMode`
|
||||
// can be scoped from field data rather than guessed. (Absent on very old clients.)
|
||||
if let Some(csc) = parse_u("x-nv-video[0].encoderCscMode") {
|
||||
let (space, range) = (
|
||||
match csc >> 1 {
|
||||
0 => "Rec601",
|
||||
1 => "Rec709",
|
||||
2 => "Rec2020",
|
||||
_ => "unknown",
|
||||
},
|
||||
if csc & 1 != 0 { "full" } else { "limited" },
|
||||
);
|
||||
let ours = if hdr { "Rec2020 limited (PQ)" } else { "Rec709 limited" };
|
||||
let matches_ours = (hdr && csc >> 1 == 2 || !hdr && csc >> 1 == 1) && csc & 1 == 0;
|
||||
if matches_ours {
|
||||
tracing::info!(csc, space, range, "GameStream client requested CSC — matches ours");
|
||||
} else {
|
||||
tracing::warn!(
|
||||
csc,
|
||||
requested = format!("{space} {range}"),
|
||||
encoding = ours,
|
||||
"GameStream client requested a CSC we don't encode — Moonlight renders by its \
|
||||
REQUEST, so its colours will be shifted (honoring encoderCscMode is a known \
|
||||
follow-up; report this log line)"
|
||||
);
|
||||
}
|
||||
}
|
||||
// Parity floor the client asks for (protects small frames); clamp to a sane max.
|
||||
let min_fec = parse_u("x-nv-vqos[0].fec.minRequiredFecPackets")
|
||||
.unwrap_or(2)
|
||||
|
||||
Reference in New Issue
Block a user