diff --git a/crates/pf-encode/src/enc/linux/mod.rs b/crates/pf-encode/src/enc/linux/mod.rs index 7224c0ce..d98dbbf8 100644 --- a/crates/pf-encode/src/enc/linux/mod.rs +++ b/crates/pf-encode/src/enc/linux/mod.rs @@ -913,6 +913,43 @@ pub fn probe_can_encode_444(codec: Codec) -> bool { ok } +/// Probe whether this NVIDIA GPU + driver + libavcodec can actually encode 10-bit (HEVC Main10 / +/// 10-bit AV1) from a P010 input — the exact path [`NvencEncoder::open`] takes for a live HDR +/// stream (a tiny X2RGB10-sourced, P010-input open). The result is cached by the caller +/// ([`crate::can_encode_10bit`]); a GPU/driver/ffmpeg without the 10-bit encode fails the open +/// here, so the host resolves the session to 8-bit SDR before the Welcome (honest downgrade). +pub fn probe_can_encode_10bit(codec: Codec) -> bool { + if !codec.supports_10bit() { + return false; + } + if ffmpeg::init().is_err() { + return false; + } + // Quiet ffmpeg's open error on a GPU that lacks 10-bit — the probe failing is an expected outcome. + // SAFETY: libav initialized above; `av_log_{get,set}_level` only read/write the global int level + // (no pointer args) and are always sound post-init. + let prev = unsafe { + let p = ffi::av_log_get_level(); + ffi::av_log_set_level(ffi::AV_LOG_FATAL); + p + }; + let ok = NvencEncoder::open( + codec, + PixelFormat::X2Rgb10, + 640, + 480, + 30, + 2_000_000, + false, // CPU input (the HDR swscale path) + 10, + ChromaFormat::Yuv420, + ) + .is_ok(); + // SAFETY: restore the saved global log level (scalar arg, no pointers). + unsafe { ffi::av_log_set_level(prev) }; + ok +} + #[cfg(test)] mod hdr_tests { use super::*; @@ -977,40 +1014,3 @@ mod hdr_tests { } } } - -/// Probe whether this NVIDIA GPU + driver + libavcodec can actually encode 10-bit (HEVC Main10 / -/// 10-bit AV1) from a P010 input — the exact path [`NvencEncoder::open`] takes for a live HDR -/// stream (a tiny X2RGB10-sourced, P010-input open). The result is cached by the caller -/// ([`crate::can_encode_10bit`]); a GPU/driver/ffmpeg without the 10-bit encode fails the open -/// here, so the host resolves the session to 8-bit SDR before the Welcome (honest downgrade). -pub fn probe_can_encode_10bit(codec: Codec) -> bool { - if !codec.supports_10bit() { - return false; - } - if ffmpeg::init().is_err() { - return false; - } - // Quiet ffmpeg's open error on a GPU that lacks 10-bit — the probe failing is an expected outcome. - // SAFETY: libav initialized above; `av_log_{get,set}_level` only read/write the global int level - // (no pointer args) and are always sound post-init. - let prev = unsafe { - let p = ffi::av_log_get_level(); - ffi::av_log_set_level(ffi::AV_LOG_FATAL); - p - }; - let ok = NvencEncoder::open( - codec, - PixelFormat::X2Rgb10, - 640, - 480, - 30, - 2_000_000, - false, // CPU input (the HDR swscale path) - 10, - ChromaFormat::Yuv420, - ) - .is_ok(); - // SAFETY: restore the saved global log level (scalar arg, no pointers). - unsafe { ffi::av_log_set_level(prev) }; - ok -} diff --git a/crates/pf-encode/src/enc/pyrowave_wire.rs b/crates/pf-encode/src/enc/pyrowave_wire.rs index 170b5370..ce0bbf09 100644 --- a/crates/pf-encode/src/enc/pyrowave_wire.rs +++ b/crates/pf-encode/src/enc/pyrowave_wire.rs @@ -226,8 +226,8 @@ mod tests { let (aw, ah) = (align(w), align(h)); let mut n = 0u32; for level in (0..5u32).rev() { - let per = ((aw / 2 >> level).div_ceil(8).div_ceil(4)) - * ((ah / 2 >> level).div_ceil(8).div_ceil(4)); + let per = (((aw / 2) >> level).div_ceil(8).div_ceil(4)) + * (((ah / 2) >> level).div_ceil(8).div_ceil(4)); let bands = if level == 4 { 4 } else { 3 }; for c in 0..3 { if level == 0 && c != 0 && !c444 {