hw: propagate stream colour tags to VAAPI frames (fixes washed-out HDR)
Symptom: properly-converted BT.2020 PQ/HLG clips rendered progressively desaturated vs the SDR reference — the signature of the transfer/primaries tags not reaching the NV12→RGB shader, so HDR code values were decoded as plain sRGB/BT.709 (no gamut expansion, wrong EOTF). VAAPI hardware-decoded frames frequently leave color_trc/color_primaries/ colorspace/color_range unspecified — the authoritative values live on the codec context (parsed from the bitstream), which the importer never sees. Before importing, copy any unspecified colour field from the codec context onto the frame, so the importer detects PQ/HLG + BT.2020 correctly. Also add a one-time importer log (LB_VIDEO_DEBUG) of the detected ten_bit/full_range/colorspace/primaries/trc to confirm what's picked up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
83410bd4b4
commit
7c2ac0e4d8
|
|
@ -460,6 +460,27 @@ impl VideoDecoder {
|
||||||
if gpu_out {
|
if gpu_out {
|
||||||
// Hardware + GPU consumer: import the VAAPI surface as wgpu NV12 textures
|
// Hardware + GPU consumer: import the VAAPI surface as wgpu NV12 textures
|
||||||
// (no CPU copy).
|
// (no CPU copy).
|
||||||
|
// VAAPI hw frames often don't carry the stream's colour tags, so the
|
||||||
|
// importer (which only sees the frame) would mis-detect transfer/gamut.
|
||||||
|
// Copy the authoritative values from the codec context (parsed from the
|
||||||
|
// bitstream) onto the frame when it left them unspecified.
|
||||||
|
unsafe {
|
||||||
|
use ffmpeg::ffi::*;
|
||||||
|
let fp = frame.as_mut_ptr();
|
||||||
|
let cp = decoder.as_ptr();
|
||||||
|
if (*fp).color_trc == AVColorTransferCharacteristic::AVCOL_TRC_UNSPECIFIED {
|
||||||
|
(*fp).color_trc = (*cp).color_trc;
|
||||||
|
}
|
||||||
|
if (*fp).color_primaries == AVColorPrimaries::AVCOL_PRI_UNSPECIFIED {
|
||||||
|
(*fp).color_primaries = (*cp).color_primaries;
|
||||||
|
}
|
||||||
|
if (*fp).colorspace == AVColorSpace::AVCOL_SPC_UNSPECIFIED {
|
||||||
|
(*fp).colorspace = (*cp).colorspace;
|
||||||
|
}
|
||||||
|
if (*fp).color_range == AVColorRange::AVCOL_RANGE_UNSPECIFIED {
|
||||||
|
(*fp).color_range = (*cp).color_range;
|
||||||
|
}
|
||||||
|
}
|
||||||
let importer = self.importer.as_ref().unwrap();
|
let importer = self.importer.as_ref().unwrap();
|
||||||
match unsafe { importer.import(frame.as_mut_ptr() as *mut std::ffi::c_void) } {
|
match unsafe { importer.import(frame.as_mut_ptr() as *mut std::ffi::c_void) } {
|
||||||
Some(gpu) => {
|
Some(gpu) => {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ use std::sync::{Arc, Mutex};
|
||||||
struct SharedHwImporter {
|
struct SharedHwImporter {
|
||||||
device: wgpu::Device,
|
device: wgpu::Device,
|
||||||
adapter: wgpu::Adapter,
|
adapter: wgpu::Adapter,
|
||||||
|
/// Log the detected colour info once (under LB_VIDEO_DEBUG) rather than per frame.
|
||||||
|
logged: std::sync::atomic::AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HwVideoImporter for SharedHwImporter {
|
impl HwVideoImporter for SharedHwImporter {
|
||||||
|
|
@ -99,6 +101,16 @@ impl HwVideoImporter for SharedHwImporter {
|
||||||
};
|
};
|
||||||
let coeffs = ycbcr_coeffs(kr, kb);
|
let coeffs = ycbcr_coeffs(kr, kb);
|
||||||
|
|
||||||
|
if std::env::var("LB_VIDEO_DEBUG").is_ok()
|
||||||
|
&& !self.logged.swap(true, std::sync::atomic::Ordering::Relaxed)
|
||||||
|
{
|
||||||
|
eprintln!(
|
||||||
|
"[hw_video] {}x{} ten_bit={} full_range={} colorspace={:?} primaries={:?} trc={:?}",
|
||||||
|
width, height, ten_bit, full_range,
|
||||||
|
(*frame).colorspace, (*frame).color_primaries, (*frame).color_trc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Transfer characteristic → which EOTF the compositor applies to reach scene-linear.
|
// Transfer characteristic → which EOTF the compositor applies to reach scene-linear.
|
||||||
let transfer = match (*frame).color_trc {
|
let transfer = match (*frame).color_trc {
|
||||||
ff::AVColorTransferCharacteristic::AVCOL_TRC_SMPTE2084 => VideoTransfer::Pq,
|
ff::AVColorTransferCharacteristic::AVCOL_TRC_SMPTE2084 => VideoTransfer::Pq,
|
||||||
|
|
@ -143,6 +155,7 @@ pub fn install(vm: &Arc<Mutex<VideoManager>>, device: &wgpu::Device, adapter: &w
|
||||||
let importer = Arc::new(SharedHwImporter {
|
let importer = Arc::new(SharedHwImporter {
|
||||||
device: device.clone(),
|
device: device.clone(),
|
||||||
adapter: adapter.clone(),
|
adapter: adapter.clone(),
|
||||||
|
logged: std::sync::atomic::AtomicBool::new(false),
|
||||||
});
|
});
|
||||||
if let Ok(mut vm) = vm.lock() {
|
if let Ok(mut vm) = vm.lock() {
|
||||||
vm.set_hardware_decode(
|
vm.set_hardware_decode(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue