mod adapters;
mod params;
#[cfg(test)]
mod tests;
pub use params::{
AmfAv1Params, AmfQualityPreset, AmfRateControl, MFX_CODINGOPTION_OFF, MFX_CODINGOPTION_ON,
NvencAv1Params, NvencRateControl, QsvAv1Params, QsvRateControl, Rav1eParams,
};
pub use adapters::{amf_av1_params, nvenc_av1_params, qsv_av1_params, rav1e_params};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum QualityTarget {
VisuallyLossless,
High,
#[default]
Standard,
Low,
Vmaf(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SpeedTier {
Draft,
#[default]
Standard,
Archive,
}
pub const NVENC_TUNING_HIGH_QUALITY: u32 = 1;
pub(self) const NV_ENC_PRESET_P5_GUID_BYTES: [u8; 16] = [
0xb4, 0xe6, 0xc6, 0x21, 0x7a, 0x29, 0xba, 0x4c, 0x99, 0x8f, 0xb6, 0xcb, 0xde, 0x72, 0xad, 0xe3,
];
pub(self) const NV_ENC_PRESET_P6_GUID_BYTES: [u8; 16] = [
0x79, 0xc2, 0x75, 0x8e, 0x99, 0x62, 0xb6, 0x4a, 0x83, 0x02, 0x0b, 0x21, 0x5a, 0x33, 0x5c, 0xf5,
];
pub(self) const NV_ENC_PRESET_P7_GUID_BYTES: [u8; 16] = [
0x12, 0x8c, 0x84, 0x84, 0x71, 0x6f, 0x13, 0x4c, 0x93, 0x1b, 0x53, 0xe2, 0x83, 0xf5, 0x79, 0x74,
];
pub fn libaom_cq_for_target(target: QualityTarget) -> u8 {
match target {
QualityTarget::VisuallyLossless => 20,
QualityTarget::High => 27,
QualityTarget::Standard => 32,
QualityTarget::Low => 38,
QualityTarget::Vmaf(v) => vmaf_to_libaom_cq(v),
}
}
fn nvenc_cq_for_target(target: QualityTarget) -> u8 {
match target {
QualityTarget::VisuallyLossless => 19,
QualityTarget::High => 25,
QualityTarget::Standard => 30,
QualityTarget::Low => 36,
QualityTarget::Vmaf(v) => vmaf_to_nvenc_cq(v),
}
}
const LIBAOM_ANCHORS: &[(i32, i32)] = &[
(100, 10), (98, 20),
(95, 27),
(90, 32),
(85, 38),
(70, 55), ];
const NVENC_ANCHORS: &[(i32, i32)] =
&[(100, 10), (98, 19), (95, 25), (90, 30), (85, 36), (70, 52)];
fn piecewise_cq(vmaf: u8, anchors: &[(i32, i32)]) -> u8 {
let v = vmaf as i32;
if v >= anchors[0].0 {
return anchors[0].1.clamp(0, 63) as u8;
}
let last = anchors.len() - 1;
if v <= anchors[last].0 {
return anchors[last].1.clamp(0, 63) as u8;
}
for pair in anchors.windows(2) {
let (v_hi, cq_hi) = pair[0];
let (v_lo, cq_lo) = pair[1];
if v <= v_hi && v >= v_lo {
let span = v_hi - v_lo;
if span == 0 {
return cq_hi.clamp(0, 63) as u8;
}
let t = v_hi - v; let cq = cq_hi + (cq_lo - cq_hi) * t / span;
return cq.clamp(0, 63) as u8;
}
}
anchors[last].1.clamp(0, 63) as u8
}
fn piecewise_quality(vmaf: u8, anchors: &[(i32, i32)], lo: i32, hi: i32) -> u8 {
let v = vmaf as i32;
if v >= anchors[0].0 {
return anchors[0].1.clamp(lo, hi) as u8;
}
let last = anchors.len() - 1;
if v <= anchors[last].0 {
return anchors[last].1.clamp(lo, hi) as u8;
}
for pair in anchors.windows(2) {
let (v_hi, q_hi) = pair[0];
let (v_lo, q_lo) = pair[1];
if v <= v_hi && v >= v_lo {
let span = v_hi - v_lo;
if span == 0 {
return q_hi.clamp(lo, hi) as u8;
}
let t = v_hi - v;
let q = q_hi + (q_lo - q_hi) * t / span;
return q.clamp(lo, hi) as u8;
}
}
anchors[last].1.clamp(lo, hi) as u8
}
fn vmaf_to_libaom_cq(vmaf: u8) -> u8 {
piecewise_cq(vmaf, LIBAOM_ANCHORS)
}
fn vmaf_to_nvenc_cq(vmaf: u8) -> u8 {
piecewise_cq(vmaf, NVENC_ANCHORS)
}
fn tile_grid_rav1e(width: u32, height: u32) -> (usize, usize) {
let max_dim = width.max(height);
if max_dim >= 3840 {
(4, 4) } else if max_dim >= 1920 {
(2, 2)
} else {
(1, 1)
}
}
fn tile_grid_nvenc(width: u32, height: u32) -> (usize, usize) {
let max_dim = width.max(height);
if max_dim >= 1920 { (2, 2) } else { (1, 1) }
}
fn tile_grid_hw(width: u32, height: u32) -> (usize, usize) {
tile_grid_nvenc(width, height)
}