#![allow(clippy::needless_range_loop)]
#[allow(unused_imports)]
use alloc::vec;
#[allow(unused_imports)]
use alloc::vec::Vec;
#[allow(unused_imports)]
use rusty_h264_common::fmath::{F32Ext as _, F64Ext as _};
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
use crate::cabac::CabacEncoder;
use crate::config::EncoderConfig;
use crate::signals::{self, FrameSignals};
use rusty_h264_common::aligned::AlignedBytes;
use rusty_h264_common::cavlc::{
encode_residual_block, scan_4x4_ac, scan_4x4_dcac, write_cbp_inter, write_cbp_intra,
};
use rusty_h264_common::inter::{
inter_partitions, mc_chroma, mc_luma, predict_mv, predict_partition_mv, MvNeighbor,
};
#[cfg(not(accel))]
use rusty_h264_common::predict::add_residual_4x4;
use rusty_h264_common::predict::{
add_residual_8x8, chroma8x8_pred, chroma_mode_available, chroma_qp, intra4x4_pred,
intra8x8_pred, luma16x16_pred, reconstruct_4x4, reconstruct_4x4_into, I16Mode,
CHROMA_4X4_SCAN_XY, LUMA_4X4_SCAN_XY,
};
#[cfg(not(accel))]
use rusty_h264_common::transform::inverse_dct_blocks;
use rusty_h264_common::transform::{
dequantize, forward_core, forward_core_8x8, forward_dct_blocks, forward_quant_chroma_dc,
forward_quant_luma_dc, inverse_quant_8x8, inverse_quant_chroma_dc, inverse_quant_luma_dc,
quantize, quantize_8x8, satd_4x4_sum,
};
use rusty_h264_common::{BitWriter, YuvPlanes};
pub(crate) static DEFER_SUBPEL: core::sync::atomic::AtomicU32 =
core::sync::atomic::AtomicU32::new(0);
pub(crate) static SPLIT_T: core::sync::atomic::AtomicU32 =
core::sync::atomic::AtomicU32::new(u32::MAX);
mod sub8_regret {
#[allow(unused_imports)]
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
#[cfg(feature = "std")]
#[cfg(feature = "std")]
use std::io::Write;
#[cfg(feature = "std")]
#[cfg(feature = "std")]
use std::sync::Mutex;
#[cfg(not(feature = "std"))]
#[allow(dead_code)]
fn sink() -> &'static Option<()> {
static NONE: Option<()> = None;
&NONE
}
#[cfg(feature = "std")]
fn sink() -> &'static Option<Mutex<std::fs::File>> {
static S: OnceLock<Option<Mutex<std::fs::File>>> = OnceLock::new();
S.get_or_init(|| {
rusty_h264_common::knob("RFF_SUB8_REGRET").and_then(|p| {
let mut f = std::fs::File::create(p).ok()?;
let _ = writeln!(f, "reverted,j_split,j_flat,lambda,dj_lambda,split_quads");
Some(Mutex::new(f))
})
})
}
#[inline]
pub fn enabled() -> bool {
sink().is_some()
}
#[cfg_attr(not(feature = "std"), allow(unused_variables))]
pub fn record(ja: f64, jb: f64, lambda: f64, split_quads: usize) {
#[cfg(feature = "std")]
if let Some(m) = sink() {
if let Ok(mut f) = m.lock() {
let dj = (ja - jb) / lambda.max(1e-9);
let _ = writeln!(
f,
"{},{:.1},{:.1},{:.4},{:.4},{}",
(jb <= ja) as u8,
ja,
jb,
lambda,
dj,
split_quads
);
}
}
}
}
mod sub8_harvest {
#[allow(unused_imports)]
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
#[cfg(feature = "std")]
#[cfg(feature = "std")]
use std::io::Write;
#[cfg(feature = "std")]
#[cfg(feature = "std")]
use std::sync::Mutex;
#[cfg(not(feature = "std"))]
#[allow(dead_code)]
fn sink() -> &'static Option<()> {
static NONE: Option<()> = None;
&NONE
}
#[cfg(feature = "std")]
fn sink() -> &'static Option<Mutex<std::fs::File>> {
static S: OnceLock<Option<Mutex<std::fs::File>>> = OnceLock::new();
S.get_or_init(|| {
rusty_h264_common::knob("RFF_SUB8_HARVEST").and_then(|p| {
let mut f = std::fs::File::create(p).ok()?;
let _ = writeln!(f, "j8,jsplit,st,lme,mbvar,j8_lme,mvdiv,rd_kept");
Some(Mutex::new(f))
})
})
}
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub struct Row {
pub j8: i64,
pub jsplit: i64,
pub st: u8,
pub lme: f64,
pub mbvar: i64,
pub mvdiv: i32,
}
#[inline]
pub fn enabled() -> bool {
sink().is_some()
}
#[cfg_attr(not(feature = "std"), allow(unused_variables))]
pub fn flush(rows: &[Row], rd_kept: bool) {
#[cfg(feature = "std")]
if let Some(m) = sink() {
if let Ok(mut f) = m.lock() {
for r in rows {
let jl = r.j8 as f64 / r.lme.max(1e-9);
let _ = writeln!(
f,
"{},{},{},{:.3},{},{:.2},{},{}",
r.j8, r.jsplit, r.st, r.lme, r.mbvar, jl, r.mvdiv, rd_kept as u8
);
}
}
}
}
}
fn intra_rd_on() -> bool {
rusty_h264_common::cached_knob!(
bool,
rusty_h264_common::knob("RFF_INTRA_RD")
.map(|v| v == "1")
.unwrap_or(false)
)
}
fn chroma_ssd_weight() -> f64 {
rusty_h264_common::cached_knob!(f64, {
rusty_h264_common::knob("RFF_RD_CHROMA_W")
.and_then(|v| v.parse().ok())
.unwrap_or(1.0)
})
}
fn shape_rd_tex_max() -> i64 {
rusty_h264_common::cached_knob!(i64, {
rusty_h264_common::knob("RFF_SHAPE_RD_TEXMAX")
.and_then(|v| v.parse().ok())
.unwrap_or(2000)
})
}
fn shape_rd_on() -> Option<bool> {
static ON: rusty_h264_common::once::OnceLock<Option<bool>> =
rusty_h264_common::once::OnceLock::new();
*ON.get_or_init(|| rusty_h264_common::knob("RFF_SHAPE_RD").map(|v| v == "1"))
}
fn intra_rd_grain_gate() -> bool {
static ON: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*ON.get_or_init(|| {
rusty_h264_common::knob("RFF_INTRA_RD_ALL")
.map(|v| v != "1")
.unwrap_or(true)
})
}
fn sub8_pay_cfg() -> (usize, usize) {
static C: rusty_h264_common::once::OnceLock<(usize, usize)> =
rusty_h264_common::once::OnceLock::new();
*C.get_or_init(|| {
let p = rusty_h264_common::knob("RFF_SUB8_MINPAY")
.and_then(|v| v.parse().ok())
.unwrap_or(0);
let l = rusty_h264_common::knob("RFF_SUB8_LEARN")
.and_then(|v| v.parse().ok())
.unwrap_or(64);
(p, l)
})
}
fn sub8_grain_veto_on() -> bool {
static ON: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*ON.get_or_init(|| {
rusty_h264_common::knob("RFF_SUB8_GRAIN")
.map(|v| v != "0")
.unwrap_or(true)
})
}
fn apply_screen_t8_veto(fe: &mut FrameEncoder, sig: &crate::signals::FrameSignals) {
if fe.t8_pick && screen_t8_veto_on() && sig.is_screen() {
fe.t8_pick = false;
}
}
fn screen_t8_veto_on() -> bool {
static ON: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*ON.get_or_init(|| {
rusty_h264_common::knob("RFF_T8_SCREEN")
.map(|v| v != "0")
.unwrap_or(true)
})
}
fn i8_margin() -> f64 {
static M: rusty_h264_common::once::OnceLock<f64> = rusty_h264_common::once::OnceLock::new();
*M.get_or_init(|| {
rusty_h264_common::knob("RFF_I8_MARGIN")
.and_then(|v| v.parse().ok())
.unwrap_or(0.0)
})
}
fn i8_in_p_on() -> bool {
static ON: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*ON.get_or_init(|| {
rusty_h264_common::knob("RFF_I8_IN_P")
.map(|v| v != "0")
.unwrap_or(true)
})
}
fn sub8_rd_on() -> bool {
static ON: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*ON.get_or_init(|| {
rusty_h264_common::knob("RFF_SUB8_RD")
.map(|v| v == "1")
.unwrap_or(false)
})
}
fn plan_rate_bits(plan: &InterPlan, sub_types: [u8; 4]) -> f64 {
let mut r = 0.0f64;
if plan.t8x8 {
for b in &plan.q8 {
for &l in b.iter() {
if l != 0 {
r += rdoq_rate((l as i64).abs());
}
}
}
} else {
for b in &plan.q_blocks {
for &l in b.iter() {
if l != 0 {
r += rdoq_rate((l as i64).abs());
}
}
}
}
for c in 0..2 {
for &l in &plan.c_dc_levels[c] {
if l != 0 {
r += rdoq_rate((l as i64).abs());
}
}
for b in &plan.c_q[c] {
for &l in b.iter() {
if l != 0 {
r += rdoq_rate((l as i64).abs());
}
}
}
}
for m in plan.mvds.iter().take(plan.n_mvd) {
r += (mvd_bits(m.0) + mvd_bits(m.1)) as f64;
}
for &st in &sub_types {
r += if st == 0 {
1.0
} else if st == 1 {
2.0
} else {
3.0
};
}
r + 16.0
}
fn sub8x8_split_on() -> bool {
static ON: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*ON.get_or_init(|| {
rusty_h264_common::knob("RFF_SUB8X8_SPLIT")
.map(|s| s == "1")
.unwrap_or(false)
})
}
fn split_t() -> f64 {
let v = SPLIT_T.load(core::sync::atomic::Ordering::Relaxed);
if v != u32::MAX {
return v as f64;
}
let d: u32 = rusty_h264_common::knob("RFF_SPLIT_T")
.and_then(|s| s.parse().ok())
.unwrap_or(0);
SPLIT_T.store(d, core::sync::atomic::Ordering::Relaxed);
d as f64
}
mod split_harvest {
#[allow(unused_imports)]
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
#[cfg(feature = "std")]
#[cfg(feature = "std")]
use std::fs::File;
#[cfg(feature = "std")]
#[cfg(feature = "std")]
use std::io::Write;
#[cfg(feature = "std")]
#[cfg(feature = "std")]
use std::sync::Mutex;
#[cfg(not(feature = "std"))]
#[allow(dead_code)]
fn sink() -> &'static Option<()> {
static NONE: Option<()> = None;
&NONE
}
#[cfg(feature = "std")]
fn sink() -> &'static Option<Mutex<File>> {
static S: OnceLock<Option<Mutex<File>>> = OnceLock::new();
S.get_or_init(|| {
rusty_h264_common::knob("RFF_SPLIT_HARVEST").and_then(|p| {
let mut f = File::create(p).ok()?;
let _ = writeln!(f, "c16,best,lambda,gate,won");
Some(Mutex::new(f))
})
})
}
#[inline]
pub fn enabled() -> bool {
sink().is_some()
}
#[cfg_attr(not(feature = "std"), allow(unused_variables))]
pub fn record(c16: i64, best: i64, lambda: f64, gate: i64, won: u8) {
#[cfg(feature = "std")]
if let Some(m) = sink() {
if let Ok(mut f) = m.lock() {
let _ = writeln!(f, "{c16},{best},{lambda:.4},{gate},{won}");
}
}
}
}
fn hpel_ref_enabled() -> bool {
static E: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*E.get_or_init(|| {
rusty_h264_common::knob("RFF_HPEL_REF")
.map(|v| v != "0")
.unwrap_or(true)
})
}
static MV_COST_TAB: rusty_h264_common::once::OnceLock<Vec<u16>> =
rusty_h264_common::once::OnceLock::new();
fn build_mv_cost() -> Vec<u16> {
(0..4096u32)
.map(|a| {
let c = 2.0 * rusty_h264_common::fmath::log2((a + 1) as f64)
+ 0.718
+ if a != 0 { 1.0 } else { 0.0 };
rusty_h264_common::fmath::round(c * 4.0) as u16
})
.collect()
}
static MV_SMOOTH: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(u32::MAX);
pub fn set_mv_smooth(on: bool) {
MV_SMOOTH.store(
if on { 2 } else { 0 },
core::sync::atomic::Ordering::Relaxed,
)
}
pub fn set_mv_smooth_mode(m: u32) {
MV_SMOOTH.store(m.min(3), core::sync::atomic::Ordering::Relaxed)
}
static MV_TRUE_BIASED: rusty_h264_common::once::OnceLock<Vec<u16>> =
rusty_h264_common::once::OnceLock::new();
fn build_true_biased() -> Vec<u16> {
let bias_q4 = rusty_h264_common::fmath::round(
rusty_h264_common::knob("RFF_MVCOST_BIAS")
.and_then(|v| v.parse::<f64>().ok())
.unwrap_or(1.0)
* 4.0,
) as u16;
crate::mvd_cost_tab::MVD_TRUE_COST4
.iter()
.enumerate()
.map(|(d, &c)| if d == 0 { c } else { c.saturating_add(bias_q4) })
.collect()
}
fn mv_smooth_t() -> f64 {
static T: rusty_h264_common::once::OnceLock<f64> = rusty_h264_common::once::OnceLock::new();
*T.get_or_init(|| {
rusty_h264_common::knob("RFF_MVCOST_T")
.and_then(|v| v.parse().ok())
.unwrap_or(0.10)
})
}
#[inline]
fn mv_cost_kind(frame_smooth: bool) -> u32 {
match mv_smooth_mode() {
0 => 0,
1 => frame_smooth as u32,
2 => 1, _ => 2, }
}
#[inline]
fn mv_smooth_mode() -> u32 {
match MV_SMOOTH.load(core::sync::atomic::Ordering::Relaxed) {
m @ 0..=3 => m,
_ => {
static E: rusty_h264_common::once::OnceLock<u32> =
rusty_h264_common::once::OnceLock::new();
*E.get_or_init(|| {
rusty_h264_common::knob("RFF_MVCOST")
.and_then(|v| v.parse().ok())
.unwrap_or(1)
})
}
}
}
#[cfg_attr(not(accel), allow(dead_code))] fn mectx_enabled() -> bool {
static E: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*E.get_or_init(|| {
rusty_h264_common::knob("RFF_MECTX")
.map(|v| v != "0")
.unwrap_or(true)
})
}
fn satd_avg_enabled() -> bool {
static E: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*E.get_or_init(|| {
rusty_h264_common::knob("RFF_SATD_AVG")
.map(|v| v != "0")
.unwrap_or(true)
})
}
static ME_SADFP: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(u32::MAX);
pub fn set_me_sadfp(on: bool) {
ME_SADFP.store(
if on { 2 } else { 0 },
core::sync::atomic::Ordering::Relaxed,
)
}
pub fn set_me_sadfp_mode(m: u32) {
ME_SADFP.store(m.min(2), core::sync::atomic::Ordering::Relaxed)
}
fn me_sadfp_mode() -> u32 {
match ME_SADFP.load(core::sync::atomic::Ordering::Relaxed) {
u32::MAX => {
static INIT: rusty_h264_common::once::OnceLock<u32> =
rusty_h264_common::once::OnceLock::new();
*INIT.get_or_init(|| {
rusty_h264_common::knob("RFF_ME_SADFP")
.and_then(|v| v.parse().ok())
.unwrap_or(1)
})
}
m => m,
}
}
fn me_sadt() -> f64 {
static T: rusty_h264_common::once::OnceLock<f64> = rusty_h264_common::once::OnceLock::new();
*T.get_or_init(|| {
rusty_h264_common::knob("RFF_ME_SADT")
.and_then(|s| s.parse().ok())
.unwrap_or(0.13)
})
}
fn me_sadt_dbg() -> bool {
static D: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*D.get_or_init(|| rusty_h264_common::knob("RFF_ME_SADT_DBG").is_some())
}
static SP_FC: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(u32::MAX);
pub fn set_sp_fc(on: bool) {
SP_FC.store(on as u32, core::sync::atomic::Ordering::Relaxed)
}
fn sp_fc_enabled() -> bool {
match SP_FC.load(core::sync::atomic::Ordering::Relaxed) {
0 => false,
1 => true,
_ => {
static E: rusty_h264_common::once::OnceLock<bool> =
rusty_h264_common::once::OnceLock::new();
*E.get_or_init(|| {
rusty_h264_common::knob("RFF_SP_FC")
.map(|v| v != "0")
.unwrap_or(false)
})
}
}
}
static ME_FC: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(u32::MAX);
pub fn set_me_fc(on: bool) {
ME_FC.store(on as u32, core::sync::atomic::Ordering::Relaxed)
}
fn me_fc_enabled() -> bool {
match ME_FC.load(core::sync::atomic::Ordering::Relaxed) {
0 => false,
1 => true,
_ => {
static E: rusty_h264_common::once::OnceLock<bool> =
rusty_h264_common::once::OnceLock::new();
*E.get_or_init(|| {
rusty_h264_common::knob("RFF_ME_FC")
.map(|v| v != "0")
.unwrap_or(true)
})
}
}
}
static SPLIT_MG: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(u32::MAX);
pub fn set_split_mg(milli: u32) {
SPLIT_MG.store(milli, core::sync::atomic::Ordering::Relaxed)
}
fn split_mg() -> f64 {
match SPLIT_MG.load(core::sync::atomic::Ordering::Relaxed) {
u32::MAX => {
static E: rusty_h264_common::once::OnceLock<f64> =
rusty_h264_common::once::OnceLock::new();
*E.get_or_init(|| {
rusty_h264_common::knob("RFF_SPLIT_MG")
.and_then(|v| v.parse().ok())
.unwrap_or(0.0)
})
}
m => m as f64 / 1000.0,
}
}
fn me_sad_dcmax() -> f64 {
static T: rusty_h264_common::once::OnceLock<f64> = rusty_h264_common::once::OnceLock::new();
*T.get_or_init(|| {
rusty_h264_common::knob("RFF_ME_SADDC")
.and_then(|s| s.parse().ok())
.unwrap_or(0.6)
})
}
static SP_MAXIT: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(u32::MAX);
pub fn set_sp_maxit(n: u32) {
SP_MAXIT.store(n, core::sync::atomic::Ordering::Relaxed)
}
fn sp_maxit() -> u32 {
match SP_MAXIT.load(core::sync::atomic::Ordering::Relaxed) {
u32::MAX => {
static INIT: rusty_h264_common::once::OnceLock<u32> =
rusty_h264_common::once::OnceLock::new();
*INIT.get_or_init(|| {
rusty_h264_common::knob("RFF_SP_MAXIT")
.and_then(|v| v.parse().ok())
.unwrap_or(0)
})
}
n => n,
}
}
fn me_sadfp_lambda() -> f64 {
static E: rusty_h264_common::once::OnceLock<f64> = rusty_h264_common::once::OnceLock::new();
*E.get_or_init(|| {
rusty_h264_common::knob("RFF_ME_SADL")
.and_then(|v| v.parse().ok())
.unwrap_or(0.5)
})
}
#[cfg(feature = "profile")]
pub mod spstats {
#[allow(unused_imports)]
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use core::sync::atomic::Ordering;
use rusty_h264_common::atomic::AtomicU64;
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
pub static POS: [AtomicU64; 2 * 8 * 2] = [const { AtomicU64::new(0) }; 32];
pub static IT: [AtomicU64; 2 * 6 * 2] = [const { AtomicU64::new(0) }; 24];
#[inline]
pub fn ev(st: usize, pos: usize, it: u32) {
POS[(st * 8 + pos.min(7)) * 2].fetch_add(1, Ordering::Relaxed);
IT[(st * 6 + (it.max(1) as usize - 1).min(5)) * 2].fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub fn imp(st: usize, pos: usize, it: u32) {
POS[(st * 8 + pos.min(7)) * 2 + 1].fetch_add(1, Ordering::Relaxed);
IT[(st * 6 + (it.max(1) as usize - 1).min(5)) * 2 + 1].fetch_add(1, Ordering::Relaxed);
}
pub static REDUNDANT: AtomicU64 = AtomicU64::new(0);
#[inline]
pub fn redundant() {
REDUNDANT.fetch_add(1, Ordering::Relaxed);
}
pub fn reset() {
for c in POS.iter() {
c.store(0, Ordering::Relaxed);
}
for c in IT.iter() {
c.store(0, Ordering::Relaxed);
}
REDUNDANT.store(0, Ordering::Relaxed);
}
pub fn snapshot() -> (Vec<u64>, Vec<u64>) {
(
POS.iter().map(|c| c.load(Ordering::Relaxed)).collect(),
IT.iter().map(|c| c.load(Ordering::Relaxed)).collect(),
)
}
pub fn redundant_count() -> u64 {
REDUNDANT.load(Ordering::Relaxed)
}
}
#[cfg(feature = "profile")]
pub mod satdpath {
#[allow(unused_imports)]
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use core::sync::atomic::Ordering;
use rusty_h264_common::atomic::AtomicU64;
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
pub static C: [AtomicU64; 3] = [const { AtomicU64::new(0) }; 3];
#[inline]
pub fn bump(i: usize) {
C[i].fetch_add(1, Ordering::Relaxed);
}
pub fn reset() {
for c in C.iter() {
c.store(0, Ordering::Relaxed);
}
}
pub fn snapshot() -> Vec<u64> {
C.iter().map(|c| c.load(Ordering::Relaxed)).collect()
}
}
pub const DIA_RUNGS: [i32; 5] = [64, 32, 16, 8, 4];
pub const DIA_DEFAULT: u32 = 0b11100;
pub static DIA_SUB_MASK: core::sync::atomic::AtomicU32 =
core::sync::atomic::AtomicU32::new(u32::MAX);
fn dia_sub_mask() -> u32 {
let m = DIA_SUB_MASK.load(core::sync::atomic::Ordering::Relaxed);
if m != u32::MAX {
return m;
}
static INIT: rusty_h264_common::once::OnceLock<u32> = rusty_h264_common::once::OnceLock::new();
*INIT.get_or_init(|| match rusty_h264_common::knob("RFF_DIA_SUB") {
Some(v) => {
let want: Vec<i32> = v.split(',').filter_map(|t| t.trim().parse().ok()).collect();
let mut m = 0u32;
for (i, r) in DIA_RUNGS.iter().enumerate() {
if want.contains(r) {
m |= 1 << i;
}
}
m
}
None => 0b10000,
})
}
pub static DIA_MASK: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(u32::MAX);
pub fn set_dia_mask(m: u32) {
DIA_MASK.store(m, core::sync::atomic::Ordering::Relaxed)
}
fn dia_mask() -> u32 {
let m = DIA_MASK.load(core::sync::atomic::Ordering::Relaxed);
if m != u32::MAX {
return m;
}
static INIT: rusty_h264_common::once::OnceLock<u32> = rusty_h264_common::once::OnceLock::new();
*INIT.get_or_init(|| match rusty_h264_common::knob("RFF_DIA_LADDER") {
Some(v) => {
let want: Vec<i32> = v.split(',').filter_map(|t| t.trim().parse().ok()).collect();
let mut m = 0u32;
for (i, r) in DIA_RUNGS.iter().enumerate() {
if want.contains(r) {
m |= 1 << i;
}
}
if m == 0 {
DIA_DEFAULT
} else {
m
}
}
None => DIA_DEFAULT,
})
}
#[cfg(feature = "profile")]
pub mod diastats {
#[allow(unused_imports)]
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use core::sync::atomic::Ordering;
use rusty_h264_common::atomic::AtomicU64;
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
pub static C: [AtomicU64; 12] = [const { AtomicU64::new(0) }; 12];
#[inline]
pub fn ev(i: usize) {
C[i * 2].fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub fn imp(i: usize) {
C[i * 2 + 1].fetch_add(1, Ordering::Relaxed);
}
pub fn reset() {
for c in C.iter() {
c.store(0, Ordering::Relaxed);
}
}
pub fn snapshot() -> Vec<(u64, u64)> {
(0..6)
.map(|i| {
(
C[i * 2].load(Ordering::Relaxed),
C[i * 2 + 1].load(Ordering::Relaxed),
)
})
.collect()
}
}
pub(crate) static SUBPEL_PAT: core::sync::atomic::AtomicU32 =
core::sync::atomic::AtomicU32::new(u32::MAX);
pub(crate) static SP_DISPATCH: core::sync::atomic::AtomicU32 =
core::sync::atomic::AtomicU32::new(u32::MAX);
fn sp_dispatch_cfg() -> (u32, i64) {
let forced = SP_DISPATCH.load(core::sync::atomic::Ordering::Relaxed);
if forced == 0 {
return (0, 0);
}
static C: OnceLock<(u32, i64)> = OnceLock::new();
*C.get_or_init(|| {
let on = rusty_h264_common::knob("RFF_SUBPEL_DISPATCH")
.map(|s| s != "0")
.unwrap_or(false);
if !on {
return (0, 0);
}
let k = rusty_h264_common::knob("RFF_SUBPEL_LEARN")
.and_then(|s| s.parse().ok())
.unwrap_or(200);
let t = rusty_h264_common::knob("RFF_SUBPEL_T")
.and_then(|s| s.parse().ok())
.unwrap_or(67);
(k, t)
})
}
fn subpel_pattern_override() -> Option<u32> {
let v = SUBPEL_PAT.load(core::sync::atomic::Ordering::Relaxed);
if v != u32::MAX {
return Some(v);
}
if let Some(e) = rusty_h264_common::knob("RFF_SUBPEL_PAT").and_then(|s| s.parse::<u32>().ok()) {
SUBPEL_PAT.store(e, core::sync::atomic::Ordering::Relaxed);
return Some(e);
}
None
}
mod subpel_harvest {
#[allow(unused_imports)]
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
#[cfg(feature = "std")]
#[cfg(feature = "std")]
use std::fs::File;
#[cfg(feature = "std")]
#[cfg(feature = "std")]
use std::io::Write;
#[cfg(feature = "std")]
#[cfg(feature = "std")]
use std::sync::Mutex;
#[cfg(not(feature = "std"))]
#[allow(dead_code)]
fn sink() -> &'static Option<()> {
static NONE: Option<()> = None;
&NONE
}
#[cfg(feature = "std")]
fn sink() -> &'static Option<Mutex<File>> {
static S: OnceLock<Option<Mutex<File>>> = OnceLock::new();
S.get_or_init(|| {
rusty_h264_common::knob("RFF_SUBPEL_HARVEST").and_then(|p| {
let mut f = File::create(p).ok()?;
let _ = writeln!(f, "pre,post,lambda,w,h,evals,to_best,ring1");
Some(Mutex::new(f))
})
})
}
#[inline]
pub fn enabled() -> bool {
sink().is_some()
}
#[cfg_attr(not(feature = "std"), allow(unused_variables))]
pub fn record(
pre: i64,
post: i64,
lambda: f64,
w: usize,
h: usize,
evals: u32,
to_best: u32,
ring1: i64,
) {
#[cfg(feature = "std")]
if let Some(m) = sink() {
if let Ok(mut f) = m.lock() {
let _ = writeln!(
f,
"{pre},{post},{lambda:.4},{w},{h},{evals},{to_best},{ring1}"
);
}
}
}
}
fn bdirect_planes_enabled() -> bool {
static ON: OnceLock<bool> = OnceLock::new();
*ON.get_or_init(|| {
rusty_h264_common::knob("RFF_BDIRECT_PLANES")
.map(|s| s != "0")
.unwrap_or(true)
})
}
fn me_batch_enabled() -> bool {
static ON: OnceLock<bool> = OnceLock::new();
*ON.get_or_init(|| {
rusty_h264_common::knob("RFF_ME_BATCH")
.map(|s| s != "0")
.unwrap_or(true)
})
}
#[cfg(accel)]
#[repr(align(16))]
struct AlignedMb([u8; 256]);
#[derive(Clone, Copy)]
struct BInter<'a> {
dir: u8,
l1: &'a crate::RefFrame,
mv0: (i32, i32),
mv1: (i32, i32),
mvmode: u8,
parts2: [(u8, (i32, i32), (i32, i32)); 2],
}
#[cfg(accel)]
#[repr(align(16))]
struct AlignedDct([i16; 256]);
fn aq_grain_veto_on() -> bool {
static ON: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*ON.get_or_init(|| {
rusty_h264_common::knob("RFF_AQ_GRAIN")
.map(|s| s != "0")
.unwrap_or(true)
})
}
fn aq_qp_map(sig: &FrameSignals, base_qp: u8, strength: f64) -> Vec<u8> {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncAq);
const AQ_DQP_MAX: i32 = 4;
let n = sig.n_mbs();
if strength == 0.0 || n == 0 {
return vec![base_qp; n];
}
let grain = aq_grain_veto_on() && sig.grain_signature();
signals::census::bump(signals::census::AQ_GRAIN, grain);
if grain {
return vec![base_qp; n];
}
let lvs = sig.log_vars();
let (lv, mean_lv) = (&lvs.0, lvs.1);
const AQ_SPREAD_LO: f64 = 1.5;
const AQ_SPREAD_HI: f64 = 5.0;
const AQ_SPREAD_MIN: f64 = 0.0; let std_lv = lvs.2; let factor =
(1.0 - (std_lv - AQ_SPREAD_LO) / (AQ_SPREAD_HI - AQ_SPREAD_LO)).clamp(AQ_SPREAD_MIN, 1.0);
let eff_strength = strength * factor;
let poly = crate::fastmath::polytier_on();
let dqp: Vec<i32> = lv
.iter()
.map(|&l| {
let x = eff_strength * (l - mean_lv);
(if poly {
crate::fastmath::round_ties_even_fast(x)
} else {
rusty_h264_common::fmath::round(x)
}) as i32
})
.map(|d| d.clamp(-AQ_DQP_MAX, AQ_DQP_MAX))
.collect();
let sum_v: f64 = sig.mb_vars().iter().map(|&v| (v + 1) as f64).sum();
static QSTEP: rusty_h264_common::once::OnceLock<[f64; (2 * AQ_DQP_MAX + 1) as usize]> =
rusty_h264_common::once::OnceLock::new();
let qstep = QSTEP.get_or_init(|| {
core::array::from_fn(|i| {
rusty_h264_common::fmath::powf(2f64, -((i as i32 - AQ_DQP_MAX) as f64) / 6.0)
})
});
let sum_vs: f64 = sig
.mb_vars()
.iter()
.map(|&v| (v + 1) as f64)
.zip(&dqp)
.map(|(v, &d)| v * qstep[(d + AQ_DQP_MAX) as usize])
.sum();
let c = rusty_h264_common::fmath::round(6.0 * rusty_h264_common::fmath::log2(sum_vs / sum_v))
as i32;
dqp.iter()
.map(|&d| (base_qp as i32 + c + d).clamp(0, 51) as u8)
.collect()
}
fn me_wide_hr_thresh() -> f64 {
static T: OnceLock<f64> = OnceLock::new();
*T.get_or_init(|| {
rusty_h264_common::knob("RFF_ME_HR")
.and_then(|s| s.parse().ok())
.unwrap_or(16.0)
})
}
fn me_wide_hr_dbg() -> bool {
static D: OnceLock<bool> = OnceLock::new();
*D.get_or_init(|| rusty_h264_common::knob("RFF_ME_HR_DBG").is_some())
}
fn apply_mbtree_qpo(aq_qp: &mut [u8], qpo: &[i32]) {
if qpo.len() == aq_qp.len() {
for (q, &o) in aq_qp.iter_mut().zip(qpo) {
*q = (*q as i32 + o).clamp(0, 51) as u8;
}
}
}
fn implicit_bi_weights(cur_poc: i32, l0_poc: i32, l1_poc: i32) -> (i32, i32) {
let td = (l1_poc - l0_poc).clamp(-128, 127);
let tb = (cur_poc - l0_poc).clamp(-128, 127);
if td == 0 {
return (32, 32);
}
const TX: [i32; 256] = {
let mut t = [0i32; 256];
let mut i = 0usize;
while i < 256 {
let td = i as i32 - 128;
if td != 0 {
t[i] = (16384 + (if td < 0 { -td } else { td }) / 2) / td;
}
i += 1;
}
t
};
let tx = TX[(td + 128) as usize];
let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
let w1 = dsf >> 2;
if !(-64..=128).contains(&w1) {
return (32, 32);
}
(64 - w1, w1)
}
#[inline(always)]
fn bi_blend(p: i32, q: i32, w: (i32, i32)) -> u8 {
((p * w.0 + q * w.1 + 32) >> 6).clamp(0, 255) as u8
}
#[cfg(accel)]
#[inline]
fn scan_4x4_dcac_i16(d: &[i16]) -> [i32; 16] {
[
d[0] as i32,
d[1] as i32,
d[4] as i32,
d[8] as i32,
d[5] as i32,
d[2] as i32,
d[3] as i32,
d[6] as i32,
d[9] as i32,
d[12] as i32,
d[13] as i32,
d[10] as i32,
d[7] as i32,
d[11] as i32,
d[14] as i32,
d[15] as i32,
]
}
pub struct FrameEncoder {
mb_w: usize,
mb_h: usize,
qp: u8, qpc: u8, cur_qp: u8,
bi_w: (i32, i32),
cw: usize, ccw: usize, rec_y: AlignedBytes,
rec_u: AlignedBytes,
rec_v: AlignedBytes,
nnz_y: Vec<u8>, nnz_c: [Vec<u8>; 2], modes_y: Vec<u8>, coded_y: Vec<bool>, mv_y: Vec<(i16, i16)>, inter_y: Vec<bool>, ref_idx_y: Vec<i8>, mv1_y: Vec<(i16, i16)>,
ref_idx1_y: Vec<i8>,
idz: i64, rdoq_strength: f64, wp: Vec<(i32, i32)>,
transform_8x8: bool, sub8x8: bool, me_wide: bool, sadfp: bool,
mv_smooth: bool,
do_splits: bool,
me_wide_var: u64, me_rescue: i64, me_wide_coh: f64, me_range: i32, me_fast: bool, me_learn: u32,
me_payoff_pct: u32,
sp_single_pass: bool,
sp_defer: core::cell::Cell<bool>,
sp_learn_n: core::cell::Cell<u32>,
sp_ring1: core::cell::Cell<i64>,
sp_total: core::cell::Cell<i64>,
sp_1pass: core::cell::Cell<bool>,
resc_n: core::cell::Cell<u32>, resc_big: core::cell::Cell<u32>, resc_off: core::cell::Cell<bool>, t8_pick: bool,
i8_margin: f64,
intra_in_p: bool,
inter8x8: u8,
inter8_pen: i64, fast: bool, #[cfg_attr(not(accel), allow(dead_code))] skip_accel_check: bool, coded_path_v2: bool, tune_lambda_scale: f64, tune_intra_penalty: f64,
satd_q: f64, subpel_force: bool, me_snap: bool, me_subpel_iter: bool, greedy_skip: bool, greedy_min_free: u32, rd_skip: bool, rd_skip_min_free: u32, rd_skip_fast_t: f64, satd_var_thresh: i64, aq_strength: f64, mb_use_satd: bool, nnz_l_cache: [u8; 25],
nnz_c_cache: [[u8; 9]; 2],
mb_skip_sad: Vec<u32>,
mb_was_skip: Vec<bool>,
}
type InterChoice = (u8, Vec<(i32, (i32, i32))>);
#[cfg(feature = "std")]
pub static EXT_MV: std::sync::Mutex<Vec<Vec<(i32, i32)>>> = std::sync::Mutex::new(Vec::new());
pub static MVCMP: [rusty_h264_common::atomic::AtomicU64; 7] = {
const Z: rusty_h264_common::atomic::AtomicU64 = rusty_h264_common::atomic::AtomicU64::new(0);
[Z; 7]
};
pub static MVCMP_FRAME: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
#[cfg_attr(not(feature = "std"), allow(dead_code))]
fn mv_force_on() -> bool {
static ON: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*ON.get_or_init(|| rusty_h264_common::knob("RFF_MV_FORCE").is_some_and(|v| v != "0"))
}
#[cfg_attr(not(feature = "std"), allow(dead_code))]
fn mv_cmp_on() -> bool {
static ON: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*ON.get_or_init(|| rusty_h264_common::knob("RFF_MV_CMP").is_some_and(|v| v != "0"))
}
pub static ME_PROBE: [rusty_h264_common::atomic::AtomicU64; 7] = {
const Z: rusty_h264_common::atomic::AtomicU64 = rusty_h264_common::atomic::AtomicU64::new(0);
[Z; 7]
};
fn me_oracle_on() -> bool {
static ON: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*ON.get_or_init(|| rusty_h264_common::knob("RFF_ME_ORACLE").is_some_and(|v| v != "0"))
}
#[inline]
fn wmv(m: (i16, i16)) -> (i32, i32) {
(i32::from(m.0), i32::from(m.1))
}
#[inline]
fn nmv(m: (i32, i32)) -> (i16, i16) {
(m.0 as i16, m.1 as i16)
}
#[derive(Default)]
struct MbState {
rec_y: Vec<u8>,
rec_u: Vec<u8>,
rec_v: Vec<u8>,
nnz_y: Vec<u8>,
nnz_c: [Vec<u8>; 2],
mv_y: Vec<(i16, i16)>,
inter_y: Vec<bool>,
ref_idx_y: Vec<i8>,
coded_y: Vec<bool>,
modes_y: Vec<u8>,
cur_qp: u8,
}
fn fast_intra_enabled() -> bool {
static ON: rusty_h264_common::once::OnceLock<bool> = rusty_h264_common::once::OnceLock::new();
*ON.get_or_init(|| rusty_h264_common::knob("RUSTY_FAST_INTRA").is_none_or(|v| v != "0"))
}
fn coded_source<'a>(
cfg: &EncoderConfig,
frame: &YuvPlanes<'a>,
) -> (
alloc::borrow::Cow<'a, [u8]>,
alloc::borrow::Cow<'a, [u8]>,
alloc::borrow::Cow<'a, [u8]>,
) {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncSource);
let cw = cfg.mb_width() * 16;
let ch = cfg.mb_height() * 16;
if frame.width == cw && frame.height == ch {
return (
alloc::borrow::Cow::Borrowed(frame.y),
alloc::borrow::Cow::Borrowed(frame.u),
alloc::borrow::Cow::Borrowed(frame.v),
);
}
let y = clamp_plane(frame.y, frame.width, frame.height, cw, ch);
let u = clamp_plane(
frame.u,
frame.chroma_width(),
frame.chroma_height(),
cw / 2,
ch / 2,
);
let v = clamp_plane(
frame.v,
frame.chroma_width(),
frame.chroma_height(),
cw / 2,
ch / 2,
);
(
alloc::borrow::Cow::Owned(y),
alloc::borrow::Cow::Owned(u),
alloc::borrow::Cow::Owned(v),
)
}
mod enc_scratch {
#[allow(unused_imports)]
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use core::cell::RefCell;
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
thread_local! {
static CS: RefCell<Option<super::CabacState>> = const { RefCell::new(None) };
static QPY: RefCell<Vec<u8>> = const { RefCell::new(Vec::new()) };
static REFID: RefCell<Vec<i8>> = const { RefCell::new(Vec::new()) };
static REFID1: RefCell<Vec<i8>> = const { RefCell::new(Vec::new()) };
static PAYLOAD: RefCell<Vec<u8>> = const { RefCell::new(Vec::new()) };
static BITS: RefCell<super::BitWriter> = RefCell::new(super::BitWriter::new());
static SNAP_A: RefCell<super::MbState> = RefCell::new(super::MbState::default());
static SNAP_B: RefCell<super::MbState> = RefCell::new(super::MbState::default());
}
pub(super) fn take_cs() -> Option<super::CabacState> {
CS.with(|c| c.borrow_mut().take())
}
pub(super) fn put_cs(v: super::CabacState) {
CS.with(|c| *c.borrow_mut() = Some(v));
}
pub(super) fn take_qpy() -> Vec<u8> {
QPY.with(|c| core::mem::take(&mut *c.borrow_mut()))
}
pub(super) fn put_qpy(v: Vec<u8>) {
QPY.with(|c| *c.borrow_mut() = v);
}
pub(super) fn take_refid() -> Vec<i8> {
REFID.with(|c| core::mem::take(&mut *c.borrow_mut()))
}
pub(super) fn put_refid(v: Vec<i8>) {
REFID.with(|c| *c.borrow_mut() = v);
}
pub(super) fn take_refid1() -> Vec<i8> {
REFID1.with(|c| core::mem::take(&mut *c.borrow_mut()))
}
pub(super) fn put_refid1(v: Vec<i8>) {
REFID1.with(|c| *c.borrow_mut() = v);
}
pub(super) fn take_payload() -> Vec<u8> {
PAYLOAD.with(|c| core::mem::take(&mut *c.borrow_mut()))
}
pub(super) fn put_payload(v: Vec<u8>) {
PAYLOAD.with(|c| *c.borrow_mut() = v);
}
pub(super) fn take_bits() -> super::BitWriter {
BITS.with(|c| core::mem::take(&mut *c.borrow_mut()))
}
pub(super) fn put_bits(v: super::BitWriter) {
BITS.with(|c| *c.borrow_mut() = v);
}
pub(super) fn take_snap_a() -> super::MbState {
SNAP_A.with(|c| core::mem::take(&mut *c.borrow_mut()))
}
pub(super) fn put_snap_a(v: super::MbState) {
SNAP_A.with(|c| *c.borrow_mut() = v);
}
pub(super) fn take_snap_b() -> super::MbState {
SNAP_B.with(|c| core::mem::take(&mut *c.borrow_mut()))
}
pub(super) fn put_snap_b(v: super::MbState) {
SNAP_B.with(|c| *c.borrow_mut() = v);
}
}
fn clamp_plane(plane: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
let mut out = vec![0u8; ow * oh];
for y in 0..oh {
let sy = y.min(h - 1);
let src = &plane[sy * w..sy * w + w];
let dst = &mut out[y * ow..y * ow + ow];
if ow <= w {
dst.copy_from_slice(&src[..ow]);
} else {
dst[..w].copy_from_slice(src);
dst[w..].fill(src[w - 1]);
}
}
out
}
#[cfg(test)]
fn clamp_plane_per_pixel(plane: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
let mut out = vec![0u8; ow * oh];
for y in 0..oh {
for x in 0..ow {
out[y * ow + x] = plane[y.min(h - 1) * w + x.min(w - 1)];
}
}
out
}
#[cfg(test)]
mod source_tests {
use super::*;
#[allow(unused_imports)]
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
#[test]
fn ref_bits_is_monotone() {
for n in 1..=16usize {
for r in 1..n {
assert!(
ref_bits(r, n) >= ref_bits(r - 1, n),
"ref_bits({r},{n}) < ref_bits({},{n})",
r - 1
);
}
}
assert_eq!(ref_bits(0, 1), 0);
assert_eq!(ref_bits(0, 2), 1);
assert_eq!(ref_bits(1, 2), 1);
}
#[test]
fn aq_qp_map_golden() {
let (cw, ch) = (160usize, 112usize);
let (mb_w, mb_h) = (cw / 16, ch / 16);
let mut sy = vec![64u8; cw * ch];
for j in 0..ch {
for i in 0..cw {
let k = 4 + (i / 16) * 12;
sy[j * cw + i] = 80 + ((i * 7 + j * 13) % k) as u8;
}
}
for j in 0..16 {
for i in 0..16 {
sy[j * cw + i] = 64;
}
}
let mut h = 0xcbf2_9ce4_8422_2325u64;
crate::fastmath::TEST_POLYTIER.with(|c| c.set(Some(false)));
for strength in [1.0f64, 0.5] {
let sig = signals::FrameSignals::new(&sy, cw, mb_w, mb_h, None);
assert!(sig.mb_vars().contains(&0), "no zero-variance MB");
let map = aq_qp_map(&sig, 26, strength);
assert!(
map.iter().any(|&q| q != 26),
"AQ map degenerate at {strength}"
);
crate::fastmath::TEST_POLYTIER.with(|c| c.set(Some(true)));
let sigp = signals::FrameSignals::new(&sy, cw, mb_w, mb_h, None);
assert_eq!(
aq_qp_map(&sigp, 26, strength),
map,
"poly arm map differs at {strength}"
);
crate::fastmath::TEST_POLYTIER.with(|c| c.set(Some(false)));
for &q in &map {
h ^= q as u64;
h = h.wrapping_mul(0x100_0000_01b3);
}
}
crate::fastmath::TEST_POLYTIER.with(|c| c.set(None));
assert_eq!(h, 8641818917227936570, "aq_qp_map golden");
}
#[test]
fn clamp_plane_matches_per_pixel_oracle() {
let mut s: u32 = 0xDEAD_BEEF;
let mut rnd = || {
s = s.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
(s >> 24) as u8
};
let cases = [
(1920usize, 1080usize, 1920usize, 1088usize), (960, 540, 960, 544), (352, 288, 352, 288), (100, 100, 112, 112), (37, 5, 48, 16), (16, 1, 16, 16), (1, 1, 16, 16), ];
for (w, h, ow, oh) in cases {
let plane: Vec<u8> = (0..w * h).map(|_| rnd()).collect();
assert_eq!(
clamp_plane(&plane, w, h, ow, oh),
clamp_plane_per_pixel(&plane, w, h, ow, oh),
"clamp mismatch for {w}x{h} -> {ow}x{oh}"
);
}
}
}
struct CtorEnv {
sub8x8: Option<bool>,
me_wide: Option<bool>,
me_wide_var: u64,
me_rescue: i64,
me_wide_coh: f64,
me_range: i32,
me_fast: bool,
me_learn: u32,
me_payoff_pct: u32,
defer_subpel: bool,
inter8: u8,
inter8_pen: i64,
subpel: Option<bool>,
rdskip_minfree: Option<u32>,
}
fn ctor_env() -> &'static CtorEnv {
static E: rusty_h264_common::once::OnceLock<CtorEnv> = rusty_h264_common::once::OnceLock::new();
E.get_or_init(|| CtorEnv {
sub8x8: rusty_h264_common::knob("RFF_SUB8X8").map(|s| s == "1"),
me_wide: rusty_h264_common::knob("RFF_ME_WIDE").map(|s| s == "1"),
me_wide_var: rusty_h264_common::knob("RFF_ME_WIDE_VAR")
.and_then(|s| s.parse().ok())
.unwrap_or(800),
me_rescue: rusty_h264_common::knob("RFF_ME_RESCUE")
.and_then(|s| s.parse().ok())
.unwrap_or(3),
me_wide_coh: rusty_h264_common::knob("RFF_ME_COH")
.and_then(|s| s.parse().ok())
.unwrap_or(4.0),
me_range: rusty_h264_common::knob("RFF_ME_RANGE")
.and_then(|s| s.parse().ok())
.unwrap_or(24),
me_fast: rusty_h264_common::knob("RFF_ME_FASTMO")
.map(|s| s != "0")
.unwrap_or(true),
me_learn: rusty_h264_common::knob("RFF_ME_LEARN")
.and_then(|s| s.parse().ok())
.unwrap_or(40),
me_payoff_pct: rusty_h264_common::knob("RFF_ME_PAYOFF")
.and_then(|s| s.parse().ok())
.unwrap_or(15),
defer_subpel: rusty_h264_common::knob("RFF_DEFER_SUBPEL")
.map(|v| v != "0")
.unwrap_or(false),
inter8: rusty_h264_common::knob("RFF_INTER8")
.and_then(|s| s.parse().ok())
.unwrap_or(0),
inter8_pen: rusty_h264_common::knob("RFF_INTER8_PEN")
.and_then(|s| s.parse().ok())
.unwrap_or(8),
subpel: rusty_h264_common::knob("RFF_SUBPEL").map(|v| v != "1"),
rdskip_minfree: rusty_h264_common::knob("RFF_RDSKIP_MINFREE").and_then(|v| v.parse().ok()),
})
}
impl FrameEncoder {
fn new(cfg: &EncoderConfig) -> Self {
let ke = ctor_env();
let (mb_w, mb_h) = (cfg.mb_width(), cfg.mb_height());
let (cw, ch) = (mb_w * 16, mb_h * 16);
let (ccw, cch) = (cw / 2, ch / 2);
Self {
mb_w,
mb_h,
qp: cfg.qp,
qpc: chroma_qp(cfg.qp),
cur_qp: cfg.qp,
bi_w: (32, 32),
cw,
ccw,
rec_y: AlignedBytes::zeroed(cw * ch),
rec_u: AlignedBytes::zeroed(ccw * cch),
rec_v: AlignedBytes::zeroed(ccw * cch),
nnz_y: vec![0; (mb_w * 4) * (mb_h * 4)],
nnz_c: [
vec![0; (mb_w * 2) * (mb_h * 2)],
vec![0; (mb_w * 2) * (mb_h * 2)],
],
modes_y: vec![2; (mb_w * 4) * (mb_h * 4)],
coded_y: vec![false; (mb_w * 4) * (mb_h * 4)],
mv_y: vec![(0, 0); (mb_w * 4) * (mb_h * 4)],
inter_y: vec![false; (mb_w * 4) * (mb_h * 4)],
ref_idx_y: vec![-1; (mb_w * 4) * (mb_h * 4)],
mv1_y: vec![(0, 0); (mb_w * 4) * (mb_h * 4)],
ref_idx1_y: vec![-1; (mb_w * 4) * (mb_h * 4)],
idz: if cfg.gop_size <= 1 { 2 } else { 3 },
rdoq_strength: 0.0, wp: Vec::new(),
transform_8x8: cfg.transform_8x8,
sub8x8: ke
.sub8x8
.or(cfg.sub_8x8)
.unwrap_or(cfg.preset == crate::config::Preset::Quality),
sadfp: me_sadfp_mode() == 2,
mv_smooth: false,
do_splits: true,
me_wide: ke
.me_wide
.or(cfg.me_wide)
.unwrap_or(cfg.preset == crate::config::Preset::Quality),
me_wide_var: ke.me_wide_var,
me_rescue: ke.me_rescue,
me_wide_coh: ke.me_wide_coh,
me_range: ke.me_range,
me_fast: ke.me_fast,
me_learn: ke.me_learn,
me_payoff_pct: ke.me_payoff_pct,
sp_single_pass: cfg.preset == crate::config::Preset::Balanced,
sp_defer: core::cell::Cell::new({
let a = DEFER_SUBPEL.load(core::sync::atomic::Ordering::Relaxed) != 0
|| ke.defer_subpel;
a && cfg.preset == crate::config::Preset::Quality
}),
sp_learn_n: core::cell::Cell::new(0),
sp_ring1: core::cell::Cell::new(0),
sp_total: core::cell::Cell::new(0),
sp_1pass: core::cell::Cell::new(false),
resc_n: core::cell::Cell::new(0),
resc_big: core::cell::Cell::new(0),
resc_off: core::cell::Cell::new(false),
t8_pick: cfg.transform_8x8,
i8_margin: i8_margin(),
intra_in_p: false,
inter8x8: ke.inter8,
inter8_pen: ke.inter8_pen,
fast: ke
.subpel
.unwrap_or(cfg.preset == crate::config::Preset::Fast || SeqFastPath::get()),
skip_accel_check: cfg.tune_skip_accel_check,
coded_path_v2: cfg.coded_path_v2,
aq_strength: cfg.aq_strength,
tune_lambda_scale: cfg.tune_lambda_scale,
tune_intra_penalty: cfg.tune_intra_penalty,
satd_q: cfg.tune_satd_q,
subpel_force: cfg.tune_subpel || cfg.preset == crate::config::Preset::Balanced,
me_snap: cfg.tune_me_snap,
me_subpel_iter: cfg.tune_me_subpel_iter,
greedy_skip: cfg.tune_greedy_skip,
greedy_min_free: cfg.tune_greedy_skip_min_free.unwrap_or(85),
rd_skip: cfg.tune_rd_skip,
rd_skip_fast_t: cfg.tune_rd_skip_fast_t.unwrap_or(0.0),
rd_skip_min_free: ke.rdskip_minfree.or(cfg.tune_rd_skip_min_free).unwrap_or(
if cfg.preset == crate::config::Preset::Fast {
60
} else {
90
},
),
satd_var_thresh: i64::MAX,
mb_use_satd: false,
nnz_l_cache: [0x80; 25],
nnz_c_cache: [[0x80; 9]; 2],
mb_skip_sad: vec![0; mb_w * mb_h],
mb_was_skip: vec![false; mb_w * mb_h],
}
}
fn pred_skip_sad(&self, mb_x: usize, mb_y: usize) -> u32 {
let mbw = self.mb_w;
let at = |x: isize, y: isize| -> Option<(bool, u32)> {
if x < 0 || y < 0 || x >= mbw as isize {
return None;
}
let i = y as usize * mbw + x as usize;
Some((self.mb_was_skip[i], self.mb_skip_sad[i]))
};
let a = at(mb_x as isize - 1, mb_y as isize); let b = at(mb_x as isize, mb_y as isize - 1); let c = at(mb_x as isize + 1, mb_y as isize - 1) .or_else(|| at(mb_x as isize - 1, mb_y as isize - 1)); let sad = |n: Option<(bool, u32)>| n.filter(|&(s, _)| s).map_or(0, |(_, v)| v);
let (sa, sb, sc) = (sad(a), sad(b), sad(c));
if b.is_none() && c.is_none() && a.is_some() {
return sa;
}
match (
a.is_some_and(|(s, _)| s),
b.is_some_and(|(s, _)| s),
c.is_some_and(|(s, _)| s),
) {
(true, false, false) => sa,
(false, true, false) => sb,
(false, false, true) => sc,
_ => sb.max(sa.min(sc)).min(sa.max(sc)), }
}
fn qp_delta(&mut self) -> i32 {
let d = self.qp as i32 - self.cur_qp as i32;
debug_assert!((-26..=25).contains(&d), "mb_qp_delta {d} outside [-26,25]");
self.cur_qp = self.qp;
d
}
fn mv_neighbors(&self, mb_x: usize, mb_y: usize) -> [MvNeighbor; 3] {
let w4 = self.mb_w * 4;
let get = |avail: bool, bx: isize, by: isize| {
if avail {
let idx = by as usize * w4 + bx as usize;
MvNeighbor {
available: true,
mv: wmv(self.mv_y[idx]),
ref_idx: i32::from(self.ref_idx_y[idx]),
}
} else {
MvNeighbor::NONE
}
};
let (bx, by) = (mb_x as isize * 4, mb_y as isize * 4);
let a = get(mb_x > 0, bx - 1, by);
let b = get(mb_y > 0, bx, by - 1);
let c = if mb_y > 0 && mb_x + 1 < self.mb_w {
get(true, bx + 4, by - 1)
} else {
get(mb_x > 0 && mb_y > 0, bx - 1, by - 1)
};
[a, b, c]
}
fn skip_mv(&self, mb_x: usize, mb_y: usize) -> (i32, i32) {
let [a, b, c] = self.mv_neighbors(mb_x, mb_y);
if !a.available
|| !b.available
|| (a.ref_idx == 0 && a.mv == (0, 0))
|| (b.ref_idx == 0 && b.mv == (0, 0))
{
(0, 0)
} else {
predict_mv(a, b, c, 0)
}
}
fn set_mb_mv(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32), inter: bool, refi: i32) {
let w4 = self.mb_w * 4;
for dy in 0..4 {
for dx in 0..4 {
let idx = (mb_y * 4 + dy) * w4 + (mb_x * 4 + dx);
self.mv_y[idx] = nmv(mv);
self.inter_y[idx] = inter;
self.ref_idx_y[idx] = if inter { refi as i8 } else { -1 };
}
}
}
fn mv_neighbors_block(&self, pbx: isize, pby: isize, pwb: isize) -> [MvNeighbor; 3] {
let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
let get = |bx: isize, by: isize| -> MvNeighbor {
if bx < 0 || by < 0 || bx >= w4 || by >= h4 || !self.coded_y[(by * w4 + bx) as usize] {
MvNeighbor::NONE
} else {
let idx = (by * w4 + bx) as usize;
MvNeighbor {
available: true,
mv: wmv(self.mv_y[idx]),
ref_idx: i32::from(self.ref_idx_y[idx]),
}
}
};
let a = get(pbx - 1, pby);
let b = get(pbx, pby - 1);
let mut c = get(pbx + pwb, pby - 1);
if !c.available {
c = get(pbx - 1, pby - 1); }
[a, b, c]
}
fn mv_neighbors_block_list(
&self,
pbx: isize,
pby: isize,
pwb: isize,
list: usize,
) -> [MvNeighbor; 3] {
let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
let (mvg, refg): (&[(i16, i16)], &[i8]) = if list == 0 {
(&self.mv_y, &self.ref_idx_y)
} else {
(&self.mv1_y, &self.ref_idx1_y)
};
let get = |bx: isize, by: isize| -> MvNeighbor {
if bx < 0 || by < 0 || bx >= w4 || by >= h4 || !self.coded_y[(by * w4 + bx) as usize] {
MvNeighbor::NONE
} else {
let idx = (by * w4 + bx) as usize;
MvNeighbor {
available: true,
mv: wmv(mvg[idx]),
ref_idx: i32::from(refg[idx]),
}
}
};
let a = get(pbx - 1, pby);
let b = get(pbx, pby - 1);
let mut c = get(pbx + pwb, pby - 1);
if !c.available {
c = get(pbx - 1, pby - 1); }
[a, b, c]
}
#[inline]
fn mc_satd_hp(
&self,
reference: &crate::RefFrame,
hp: Option<&rusty_h264_common::inter::HpelPlanes>,
hr_on: bool,
sa_on: bool,
src_row: &[u8],
lx: usize,
ly: usize,
rw: usize,
rh: usize,
mv: (i32, i32),
) -> i64 {
#[cfg(not(accel_x86))]
let _ = sa_on;
#[cfg(feature = "profile")]
let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
let ch = self.mb_h * 16;
let cw = self.cw;
let (ix0, iy0) = (
lx as isize + (mv.0 >> 2) as isize,
ly as isize + (mv.1 >> 2) as isize,
);
let interior_fullpel = mv.0 & 3 == 0
&& mv.1 & 3 == 0
&& ix0 >= 0
&& iy0 >= 0
&& ix0 + rw as isize <= cw as isize
&& iy0 + rh as isize <= ch as isize;
#[cfg(feature = "profile")]
{
let fullpel = mv.0 & 3 == 0 && mv.1 & 3 == 0;
satdpath::bump(if interior_fullpel {
0
} else if fullpel {
1
} else {
2
});
}
if interior_fullpel {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
let (rx0, ry0) = (ix0 as usize, iy0 as usize);
return satd_px(src_row, cw, &reference.y[ry0 * cw + rx0..], cw, rw, rh);
}
if let Some(hp) = hp {
if hr_on {
if let Some((plane, base, stride)) =
rusty_h264_common::inter::hpel_ref(hp, lx, ly, rw, rh, mv.0, mv.1)
{
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
return satd_px(src_row, cw, &plane[base..], stride, rw, rh);
}
}
#[cfg(accel_x86)]
if sa_on {
if let Some((pa, ba, pb, bb, stride)) =
rusty_h264_common::inter::hpel_qpel_refs(hp, lx, ly, rw, rh, mv.0, mv.1)
{
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
if let Some(v) = rusty_h264_accel::satd_avg(
src_row,
cw,
&pa[ba..],
&pb[bb..],
stride,
rw,
rh,
) {
return v as i64;
}
}
}
let mut pred = [0u8; 256];
if rusty_h264_common::inter::hpel_block(hp, lx, ly, rw, rh, mv.0, mv.1, &mut pred) {
return satd_px(src_row, cw, &pred, rw, rw, rh);
}
mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
return satd_px(src_row, cw, &pred, rw, rw, rh);
}
let mut pred = [0u8; 256];
mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
satd_px(src_row, cw, &pred, rw, rw, rh)
}
#[inline]
fn mc_sad_hp(
&self,
reference: &crate::RefFrame,
hp: Option<&rusty_h264_common::inter::HpelPlanes>,
hr_on: bool,
src_row: &[u8],
lx: usize,
ly: usize,
rw: usize,
rh: usize,
mv: (i32, i32),
_asrc: Option<&[u8; 256]>,
) -> i64 {
#[cfg(feature = "profile")]
let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
let ch = self.mb_h * 16;
let cw = self.cw;
let (ix0, iy0) = (
lx as isize + (mv.0 >> 2) as isize,
ly as isize + (mv.1 >> 2) as isize,
);
let interior_fullpel = mv.0 & 3 == 0
&& mv.1 & 3 == 0
&& ix0 >= 0
&& iy0 >= 0
&& ix0 + rw as isize <= cw as isize
&& iy0 + rh as isize <= ch as isize;
if interior_fullpel {
let (rx0, ry0) = (ix0 as usize, iy0 as usize);
#[cfg(accel)]
if rw == 16 && rh == 16 {
if let Some(src) = _asrc {
return rusty_h264_accel::sad_16x16(src, 16, &reference.y[ry0 * cw + rx0..], cw)
as i64;
}
}
return sad_strided(src_row, cw, &reference.y[ry0 * cw + rx0..], cw, rw, rh);
}
if let Some(hp) = hp {
if hr_on {
if let Some((plane, base, stride)) =
rusty_h264_common::inter::hpel_ref(hp, lx, ly, rw, rh, mv.0, mv.1)
{
return sad_strided(src_row, cw, &plane[base..], stride, rw, rh);
}
if let Some((pa, ba, pb, bb, stride)) =
rusty_h264_common::inter::hpel_qpel_refs(hp, lx, ly, rw, rh, mv.0, mv.1)
{
return sad_avg_strided(src_row, cw, &pa[ba..], &pb[bb..], stride, rw, rh);
}
}
let mut pred = [0u8; 256];
if rusty_h264_common::inter::hpel_block(hp, lx, ly, rw, rh, mv.0, mv.1, &mut pred) {
return sad_strided(src_row, cw, &pred, rw, rw, rh);
}
mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
return sad_strided(src_row, cw, &pred, rw, rw, rh);
}
let mut pred = [0u8; 256];
mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
sad_strided(src_row, cw, &pred, rw, rw, rh)
}
fn mc_sad(
&self,
reference: &crate::RefFrame,
sy: &[u8],
lx: usize,
ly: usize,
rw: usize,
rh: usize,
mv: (i32, i32),
_asrc: Option<&[u8; 256]>,
) -> i64 {
#[cfg(feature = "profile")]
let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
let ch = self.mb_h * 16;
let cw = self.cw;
let (ix0, iy0) = (
lx as isize + (mv.0 >> 2) as isize,
ly as isize + (mv.1 >> 2) as isize,
);
let interior_fullpel = mv.0 & 3 == 0
&& mv.1 & 3 == 0
&& ix0 >= 0
&& iy0 >= 0
&& ix0 + rw as isize <= cw as isize
&& iy0 + rh as isize <= ch as isize;
#[cfg(accel)]
if interior_fullpel && rw == 16 && rh == 16 {
if let Some(src) = _asrc {
let (rx0, ry0) = (ix0 as usize, iy0 as usize);
return rusty_h264_accel::sad_16x16(src, 16, &reference.y[ry0 * cw + rx0..], cw)
as i64;
}
}
let mut sad = 0u32;
if interior_fullpel {
let (rx0, ry0) = (ix0 as usize, iy0 as usize);
let refy = &reference.y;
for dy in 0..rh {
let s = &sy[(ly + dy) * cw + lx..][..rw];
let r = &refy[(ry0 + dy) * cw + rx0..][..rw];
sad += s
.iter()
.zip(r)
.map(|(&a, &b)| a.abs_diff(b) as u32)
.sum::<u32>();
}
} else {
let mut pred = [0u8; 256];
let from_planes = !self.fast
&& rusty_h264_common::inter::hpel_block(
reference.hpel(cw, ch),
lx,
ly,
rw,
rh,
mv.0,
mv.1,
&mut pred,
);
if !from_planes {
mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
}
for dy in 0..rh {
let s = &sy[(ly + dy) * cw + lx..][..rw];
let p = &pred[dy * rw..][..rw];
sad += s
.iter()
.zip(p)
.map(|(&a, &b)| a.abs_diff(b) as u32)
.sum::<u32>();
}
}
sad as i64
}
fn bi_dist_rect(
&self,
l0: &crate::RefFrame,
l1: &crate::RefFrame,
sy: &[u8],
lx: usize,
ly: usize,
rw: usize,
rh: usize,
mv0: (i32, i32),
mv1: (i32, i32),
) -> i64 {
let ch = self.mb_h * 16;
let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
mc_luma(&l0.y, self.cw, ch, lx, ly, rw, rh, mv0.0, mv0.1, &mut a);
mc_luma(&l1.y, self.cw, ch, lx, ly, rw, rh, mv1.0, mv1.1, &mut b);
let n = rw * rh;
let mut avg = [0u8; 256];
for i in 0..n {
avg[i] = bi_blend(a[i] as i32, b[i] as i32, self.bi_w);
}
if self.fast && !self.mb_use_satd {
let mut sad = 0u32;
for dy in 0..rh {
let s = &sy[(ly + dy) * self.cw + lx..][..rw];
let p = &avg[dy * rw..][..rw];
sad += s
.iter()
.zip(p)
.map(|(&x, &y)| x.abs_diff(y) as u32)
.sum::<u32>();
}
sad as i64
} else {
satd_px(&sy[ly * self.cw + lx..], self.cw, &avg, rw, rw, rh)
}
}
fn bi_dist(
&self,
l0: &crate::RefFrame,
l1: &crate::RefFrame,
sy: &[u8],
lx: usize,
ly: usize,
mv0: (i32, i32),
mv1: (i32, i32),
) -> i64 {
#[cfg(feature = "profile")]
let _site = rusty_h264_common::inter::mcstats::SiteTag::new(4);
let ch = self.mb_h * 16;
let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
mc_luma(&l0.y, self.cw, ch, lx, ly, 16, 16, mv0.0, mv0.1, &mut a);
mc_luma(&l1.y, self.cw, ch, lx, ly, 16, 16, mv1.0, mv1.1, &mut b);
let mut avg = [0u8; 256];
for i in 0..256 {
avg[i] = bi_blend(a[i] as i32, b[i] as i32, self.bi_w);
}
if self.fast && !self.mb_use_satd {
let mut sad = 0u32;
for dy in 0..16 {
let s = &sy[(ly + dy) * self.cw + lx..][..16];
let p = &avg[dy * 16..][..16];
sad += s
.iter()
.zip(p)
.map(|(&x, &y)| x.abs_diff(y) as u32)
.sum::<u32>();
}
sad as i64
} else {
satd_px(&sy[ly * self.cw + lx..], self.cw, &avg, 16, 16, 16)
}
}
fn pred_dist(&self, sy: &[u8], lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
if self.fast && !self.mb_use_satd {
let mut sad = 0u32;
for dy in 0..16 {
let s = &sy[(ly + dy) * self.cw + lx..][..16];
let p = &pred[dy * 16..][..16];
sad += s
.iter()
.zip(p)
.map(|(&a, &b)| a.abs_diff(b) as u32)
.sum::<u32>();
}
sad as i64
} else {
satd_px(&sy[ly * self.cw + lx..], self.cw, pred, 16, 16, 16)
}
}
fn col_zero(&self, l1: &crate::RefFrame, bx: usize, by: usize) -> bool {
if l1.w4 == 0 {
return false;
}
let idx = by * l1.w4 + bx;
if idx >= l1.ref_idx.len() {
return false;
}
if l1.ref_idx[idx] < 0 {
if let (Some(&r1), Some(&m1)) = (l1.ref_idx1.get(idx), l1.mv1.get(idx)) {
return r1 == 0 && m1.0.abs() <= 1 && m1.1.abs() <= 1;
}
return false;
}
l1.ref_idx[idx] == 0 && l1.mv[idx].0.abs() <= 1 && l1.mv[idx].1.abs() <= 1
}
fn b_mc_block(
&self,
l0: &crate::RefFrame,
l1: &crate::RefFrame,
mb_x: usize,
mb_y: usize,
dx: usize,
dy: usize,
refi0: i32,
m0: (i32, i32),
refi1: i32,
m1: (i32, i32),
pred_y: &mut [u8; 256],
c_pred: &mut [[u8; 64]; 2],
) {
#[cfg(feature = "profile")]
let _site = rusty_h264_common::inter::mcstats::SiteTag::new(4);
let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
let (px, py) = (mb_x * 16 + dx, mb_y * 16 + dy);
let (mut a, mut b) = ([0u8; 16], [0u8; 16]);
let mc4 = |r: &crate::RefFrame, mv: (i32, i32), out: &mut [u8; 16]| {
if !self.fast
&& bdirect_planes_enabled()
&& rusty_h264_common::inter::hpel_block(
r.hpel(self.cw, ch),
px,
py,
4,
4,
mv.0,
mv.1,
out,
)
{
return;
}
mc_luma(&r.y, self.cw, ch, px, py, 4, 4, mv.0, mv.1, out);
};
if refi0 >= 0 {
mc4(l0, m0, &mut a);
}
if refi1 >= 0 {
mc4(l1, m1, &mut b);
}
for yy in 0..4 {
for xx in 0..4 {
let i = yy * 4 + xx;
let v = match (refi0 >= 0, refi1 >= 0) {
(true, true) => bi_blend(a[i] as i32, b[i] as i32, self.bi_w),
(true, false) => a[i],
_ => b[i],
};
pred_y[(dy + yy) * 16 + (dx + xx)] = v;
}
}
let (cpx, cpy) = (mb_x * 8 + dx / 2, mb_y * 8 + dy / 2);
for c in 0..2 {
let (r0, r1) = if c == 0 {
(&l0.u, &l1.u)
} else {
(&l0.v, &l1.v)
};
let (mut ca, mut cb) = ([0u8; 4], [0u8; 4]);
if refi0 >= 0 {
mc_chroma(r0, self.ccw, cch, cpx, cpy, 2, 2, m0.0, m0.1, &mut ca);
}
if refi1 >= 0 {
mc_chroma(r1, self.ccw, cch, cpx, cpy, 2, 2, m1.0, m1.1, &mut cb);
}
for yy in 0..2 {
for xx in 0..2 {
let i = yy * 2 + xx;
let v = match (refi0 >= 0, refi1 >= 0) {
(true, true) => bi_blend(ca[i] as i32, cb[i] as i32, self.bi_w),
(true, false) => ca[i],
_ => cb[i],
};
c_pred[c][(dy / 2 + yy) * 8 + (dx / 2 + xx)] = v;
}
}
}
}
fn b_direct(
&self,
l0: &crate::RefFrame,
l1: &crate::RefFrame,
mb_x: usize,
mb_y: usize,
) -> (
[u8; 256],
[[u8; 64]; 2],
[(i32, (i32, i32), i32, (i32, i32)); 16],
) {
let (nbx, nby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
let n0 = self.mv_neighbors_block_list(nbx, nby, 4, 0);
let n1 = self.mv_neighbors_block_list(nbx, nby, 4, 1);
let min_pos = |a: i32, b: i32| {
if a < 0 {
b
} else if b < 0 {
a
} else {
a.min(b)
}
};
let rid = |n: &[MvNeighbor; 3]| min_pos(min_pos(n[0].ref_idx, n[1].ref_idx), n[2].ref_idx);
let (mut refi0, mut refi1) = (rid(&n0), rid(&n1));
let direct_zero = refi0 < 0 && refi1 < 0;
if direct_zero {
refi0 = 0;
refi1 = 0;
}
let mv0 = if refi0 >= 0 && !direct_zero {
predict_mv(n0[0], n0[1], n0[2], refi0)
} else {
(0, 0)
};
let mv1 = if refi1 >= 0 && !direct_zero {
predict_mv(n1[0], n1[1], n1[2], refi1)
} else {
(0, 0)
};
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
let mut motion = [(0i32, (0i32, 0i32), 0i32, (0i32, 0i32)); 16];
for sby in 0..4 {
for sbx in 0..4 {
let (czx, czy) = ((sbx / 2) * 3, (sby / 2) * 3);
let cz = !direct_zero && self.col_zero(l1, mb_x * 4 + czx, mb_y * 4 + czy);
let m0 = if refi0 == 0 && cz { (0, 0) } else { mv0 };
let m1 = if refi1 == 0 && cz { (0, 0) } else { mv1 };
motion[sby * 4 + sbx] = (refi0, m0, refi1, m1);
self.b_mc_block(
l0,
l1,
mb_x,
mb_y,
sbx * 4,
sby * 4,
refi0,
m0,
refi1,
m1,
&mut pred_y,
&mut c_pred,
);
}
}
(pred_y, c_pred, motion)
}
fn commit_direct_motion(
&mut self,
mb_x: usize,
mb_y: usize,
motion: &[(i32, (i32, i32), i32, (i32, i32)); 16],
) {
let w4 = self.mb_w * 4;
for sby in 0..4 {
for sbx in 0..4 {
let (refi0, m0, refi1, m1) = motion[sby * 4 + sbx];
let idx = (mb_y * 4 + sby) * w4 + (mb_x * 4 + sbx);
self.inter_y[idx] = true;
self.coded_y[idx] = true;
self.mv_y[idx] = nmv(m0);
self.ref_idx_y[idx] = refi0 as i8;
self.mv1_y[idx] = nmv(m1);
self.ref_idx1_y[idx] = refi1 as i8;
}
}
}
fn motion_search(
&self,
reference: &crate::RefFrame,
sy: &[u8],
lx: usize,
ly: usize,
rw: usize,
rh: usize,
predictors: &[(i32, i32)],
lambda_me: f64,
start: Option<(i32, i32)>,
) -> ((i32, i32), i64) {
let mvk = mv_cost_kind(self.mv_smooth);
let mvtab: &[u16] = match mvk {
1 => MV_COST_TAB.get_or_init(build_mv_cost),
2 => MV_TRUE_BIASED.get_or_init(build_true_biased),
_ => &[],
};
let mvbits = |d: i32| -> u32 {
match mvk {
1 | 2 => {
let a = d.unsigned_abs().min(4095) as usize;
mvtab[a] as u32
}
_ => {
let codenum = if d > 0 {
(2 * d - 1) as u32
} else {
(-2 * d) as u32
};
1 + 2 * (31 - (codenum + 1).leading_zeros())
}
}
};
let center = predictors[0];
let probe = me_oracle_on();
let sadfp = !self.fast && start.is_none() && self.sadfp;
#[cfg(accel)]
let asrc_buf = if (self.fast || sadfp) && rw == 16 && rh == 16 {
let mut a = AlignedMb([0u8; 256]);
for dy in 0..16 {
a.0[dy * 16..dy * 16 + 16].copy_from_slice(&sy[(ly + dy) * self.cw + lx..][..16]);
}
Some(a)
} else {
None
};
#[cfg(accel)]
let asrc: Option<&[u8; 256]> = asrc_buf.as_ref().map(|a| &a.0);
#[cfg(not(accel))]
let asrc: Option<&[u8; 256]> = None;
let use_sad = self.fast && !self.mb_use_satd;
let cw = self.cw;
let hp: Option<&rusty_h264_common::inter::HpelPlanes> = if !self.fast {
Some(reference.hpel(cw, self.mb_h * 16))
} else {
None
};
let hr_on = hpel_ref_enabled();
let sa_on = cfg!(accel_x86) && hr_on && satd_avg_enabled();
let src_row = &sy[ly * cw + lx..];
#[cfg(accel_x86)]
let mectx = if !use_sad && mectx_enabled() {
hp.and_then(|p| {
rusty_h264_accel::MeCtx::new(
src_row, cw, &p.f, &p.h, &p.v, &p.c, p.stride, p.pad, p.pw, p.ph, lx, ly, rw,
rh,
)
})
} else {
None
};
let cost = |mv: (i32, i32)| -> i64 {
let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
let lam_r = if mvk != 0 {
lambda_me * 0.25
} else {
lambda_me
};
let dist = if use_sad {
self.mc_sad(reference, sy, lx, ly, rw, rh, mv, asrc)
} else {
#[cfg(accel_x86)]
{
match mectx.as_ref().and_then(|c| c.eval(mv.0, mv.1)) {
Some(d) => d as i64,
None => self
.mc_satd_hp(reference, hp, hr_on, sa_on, src_row, lx, ly, rw, rh, mv),
}
}
#[cfg(not(accel_x86))]
{
self.mc_satd_hp(reference, hp, hr_on, sa_on, src_row, lx, ly, rw, rh, mv)
}
};
dist + (lam_r * rate as f64) as i64
};
let lam_fp = lambda_me * if sadfp { me_sadfp_lambda() } else { 1.0 };
let cost_fp = |mv: (i32, i32)| -> i64 {
if !sadfp {
return cost(mv);
}
let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
self.mc_sad_hp(reference, hp, hr_on, src_row, lx, ly, rw, rh, mv, asrc)
+ (lam_fp * rate as f64) as i64
};
let refine_only = start.is_some();
let (mut best, mut best_c) = match start {
Some(mv) => (mv, cost(mv)),
None => {
let mut b = (0, 0);
let mut bc = cost_fp(b);
for &p in predictors {
let pc = cost_fp(p);
if pc < bc {
bc = pc;
b = p;
}
}
(b, bc)
}
};
let (seed_mv, mut seed_c) = (best, best_c);
if !refine_only && self.me_snap && (best.0 & 3 != 0 || best.1 & 3 != 0) {
let snapped = (
(best.0 + 2).div_euclid(4) * 4,
(best.1 + 2).div_euclid(4) * 4,
);
best_c = cost_fp(snapped);
best = snapped;
}
let mut ladder = [0i32; 5];
let mut nladder = 0usize;
let steps: &[i32] = if self.fast {
&[16, 4]
} else {
let m = if rw < 8 || rh < 8 {
dia_sub_mask()
} else {
dia_mask()
};
for (i, r) in DIA_RUNGS.iter().enumerate() {
if m & (1 << i) != 0 {
ladder[nladder] = *r;
nladder += 1;
}
}
&ladder[..nladder]
};
let _gd = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeDiamond);
#[cfg_attr(not(accel_x86), allow(unused_variables))] let fc = !self.fast
&& cfg!(accel_x86)
&& me_fc_enabled()
&& matches!((rw, rh), (16, 16) | (16, 8) | (8, 16) | (8, 8));
#[cfg_attr(not(accel_x86), allow(unused_variables))] let ch_px = self.mb_h as isize * 16;
#[allow(clippy::unused_enumerate_index)] for (_si, &step) in steps.iter().enumerate() {
if refine_only {
break;
}
loop {
#[cfg(accel_x86)]
if fc && best.0 & 3 == 0 && best.1 & 3 == 0 {
let s = (step >> 2) as isize;
let (bx, by) = (
lx as isize + (best.0 >> 2) as isize,
ly as isize + (best.1 >> 2) as isize,
);
if bx - s >= 0
&& by - s >= 0
&& bx + s + rw as isize <= cw as isize
&& by + s + rh as isize <= ch_px
{
let offs = [
(by * cw as isize + bx + s) as usize,
(by * cw as isize + bx - s) as usize,
((by + s) * cw as isize + bx) as usize,
((by - s) * cw as isize + bx) as usize,
];
let batch = if rw != 16 {
None
} else if sadfp {
rusty_h264_accel::sad_x4(src_row, cw, &reference.y, offs, cw, rw, rh)
} else {
rusty_h264_accel::satd_x4(src_row, cw, &reference.y, offs, cw, rw, rh)
};
{
let ring = [(step, 0), (-step, 0), (0, step), (0, -step)];
let (mut bi, mut bc) = (usize::MAX, best_c);
for (i, &(dx, dy)) in ring.iter().enumerate() {
let mv = (best.0 + dx, best.1 + dy);
let cc = match batch {
Some(sads) => {
let rate =
mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
sads[i] as i64 + (lam_fp * rate as f64) as i64
}
None => cost_fp(mv),
};
#[cfg(feature = "profile")]
diastats::ev(_si);
if cc < bc {
bc = cc;
bi = i;
}
}
if bi == usize::MAX {
break;
}
best_c = bc;
best = (best.0 + ring[bi].0, best.1 + ring[bi].1);
#[cfg(feature = "profile")]
diastats::imp(_si);
continue;
}
}
}
let mut improved = false;
for &(dx, dy) in &[(step, 0), (-step, 0), (0, step), (0, -step)] {
let c = (best.0 + dx, best.1 + dy);
let cc = cost_fp(c);
#[cfg(feature = "profile")]
diastats::ev(_si);
if cc < best_c {
best_c = cc;
best = c;
improved = true;
#[cfg(feature = "profile")]
diastats::imp(_si);
}
}
if !improved {
break;
}
}
}
drop(_gd);
if sadfp {
best_c = cost(best);
seed_c = cost(seed_mv);
}
let _gr = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeRescue);
let flat = |sself: &Self| {
!refine_only && {
let (mut s, mut ss) = (0u64, 0u64);
for dy in 0..rh {
for dx in 0..rw {
let v = sy[(ly + dy) * sself.cw + lx + dx] as u64;
s += v;
ss += v * v;
}
}
let n = (rw * rh) as u64;
debug_assert!(n.is_power_of_two(), "partition area must be a power of two");
let sh = n.trailing_zeros();
((ss - ((s * s) >> sh)) >> sh) < sself.me_wide_var
}
};
if self.me_wide && !self.fast && (self.me_fast || flat(self)) && !self.resc_off.get() {
let rate_b = mvbits(best.0 - center.0) + mvbits(best.1 - center.1);
let dist = best_c - (lambda_me * rate_b as f64) as i64;
if dist / (rw * rh).max(1) as i64 > self.me_rescue {
let pre_c = best_c;
let (cx, cy) = (
(best.0 + 2).div_euclid(4) * 4,
(best.1 + 2).div_euclid(4) * 4,
);
let mut gb = best;
let cw = self.cw;
let r = self.me_range;
let batched = rw == 16 && rh == 16 && cfg!(accel) && {
let (icdx, icdy) = (cx >> 2, cy >> 2);
lx as i32 + icdx >= r
&& lx as i32 + icdx + r + 16 <= cw as i32
&& ly as i32 + icdy >= r
&& ly as i32 + icdy + r + 16 <= (self.mb_h * 16) as i32
&& me_batch_enabled()
};
#[cfg(accel)]
if batched {
let (icdx, icdy) = ((cx >> 2), (cy >> 2));
let src = &sy[ly * cw + lx..];
let mut dy = -r;
while dy <= r {
let rby = (ly as i32 + icdy + dy) as usize;
let mut dx = -r;
while dx <= r {
let rbx = (lx as i32 + icdx + dx) as usize;
let satd = 2 * rusty_h264_accel::satd_16x16(
src,
cw,
&reference.y[rby * cw + rbx..],
cw,
) as i64;
let mv = (cx + dx * 4, cy + dy * 4);
let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
let cc = satd + (lambda_me * rate as f64) as i64;
if cc < best_c {
best_c = cc;
gb = mv;
}
dx += 2;
}
dy += 2;
}
}
if !batched {
let mut dy = -r;
while dy <= r {
let mut dx = -r;
while dx <= r {
let cc = cost((cx + dx * 4, cy + dy * 4));
if cc < best_c {
best_c = cc;
gb = (cx + dx * 4, cy + dy * 4);
}
dx += 2;
}
dy += 2;
}
}
best = gb;
for dy in -1..=1 {
for dx in -1..=1 {
let c = (best.0 + dx * 4, best.1 + dy * 4);
let cc = cost(c);
if cc < best_c {
best_c = cc;
best = c;
}
}
}
let n = self.resc_n.get();
if n < self.me_learn {
self.resc_n.set(n + 1);
if best_c * 16 <= pre_c * 15 {
self.resc_big.set(self.resc_big.get() + 1);
}
if n + 1 == self.me_learn
&& self.resc_big.get() * 100 < self.me_learn * self.me_payoff_pct
{
self.resc_off.set(true);
}
}
}
}
drop(_gr);
let _gs = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeSubpel);
if probe {
let mut ob = center;
let mut oc = i64::MAX;
for gy in -24i32..=24 {
for gx in -24i32..=24 {
let c = (center.0 + gx * 4, center.1 + gy * 4);
let cc = cost(c);
if cc < oc {
oc = cc;
ob = c;
}
}
}
let fullpel_best = ob;
for &st in &[2i32, 1] {
for &(dx, dy) in &[(st, 0), (-st, 0), (0, st), (0, -st)] {
let c = (ob.0 + dx, ob.1 + dy);
let cc = cost(c);
if cc < oc {
oc = cc;
ob = c;
}
}
}
let mut oc_sp = oc;
for dy in -3i32..=3 {
for dx in -3i32..=3 {
let c = (fullpel_best.0 + dx, fullpel_best.1 + dy);
let cc = cost(c);
if cc < oc_sp {
oc_sp = cc;
}
}
}
let (mut mb_, mut mc_) = (best, best_c);
for &st in &[2i32, 1] {
for &(dx, dy) in &[(st, 0), (-st, 0), (0, st), (0, -st)] {
let c = (mb_.0 + dx, mb_.1 + dy);
let cc = cost(c);
if cc < mc_ {
mc_ = cc;
mb_ = c;
}
}
}
use core::sync::atomic::Ordering::Relaxed;
ME_PROBE[0].fetch_add(1, Relaxed);
ME_PROBE[1].fetch_add(mc_.max(0) as u64, Relaxed);
ME_PROBE[2].fetch_add(oc.max(0) as u64, Relaxed);
ME_PROBE[3].fetch_add((mc_ > oc) as u64, Relaxed);
ME_PROBE[5].fetch_add(oc_sp.max(0) as u64, Relaxed);
ME_PROBE[6].fetch_add((mc_ > oc_sp) as u64, Relaxed);
}
let subpel: &[i32] =
if (self.fast && !self.subpel_force) || (self.sp_defer.get() && !refine_only) {
&[]
} else {
&[2, 1]
};
let (hv_pre, mut hv_evals) = (best_c, 0u32);
let (mut hv_to_best, mut hv_ring1) = (0u32, i64::MIN);
let sub_shape = rw < 8 || rh < 8;
let mut pat = subpel_pattern_override().unwrap_or_else(|| {
if sub_shape {
static P: rusty_h264_common::once::OnceLock<u32> =
rusty_h264_common::once::OnceLock::new();
*P.get_or_init(|| {
rusty_h264_common::knob("RFF_SUBPEL_SUB")
.and_then(|v| v.parse().ok())
.unwrap_or(2)
})
} else if self.sp_single_pass {
2
} else {
0
}
});
let (sp_learn, sp_t) = sp_dispatch_cfg();
let sp_dispatching = sp_learn > 0 && pat == 0 && !subpel.is_empty();
if sp_dispatching && self.sp_learn_n.get() >= sp_learn && self.sp_1pass.get() {
pat = 2;
}
const SP_MEMO_N: usize = 64;
#[inline(always)]
fn sp_slot(mv: (i32, i32)) -> usize {
((mv.0 & 7) as usize) | (((mv.1 & 7) as usize) << 3)
}
let mut memo_mv = [(i32::MIN, i32::MIN); SP_MEMO_N];
let mut memo_c = [0i64; SP_MEMO_N];
if !subpel.is_empty() {
let s0 = sp_slot(best);
memo_mv[s0] = best;
memo_c[s0] = best_c;
}
#[cfg(feature = "profile")]
let mut seen: Vec<(i32, i32)> = Vec::with_capacity(64);
#[cfg(feature = "profile")]
{
seen.push(best);
}
let sp_cap = sp_maxit();
#[cfg_attr(not(accel_x86), allow(unused_variables))] let sp_fc = sp_fc_enabled()
&& !self.fast
&& cfg!(accel_x86)
&& matches!((rw, rh), (16, 16) | (16, 8) | (8, 16) | (8, 8));
for &step in subpel {
let ring8 = [
(step, 0),
(-step, 0),
(0, step),
(0, -step),
(step, step),
(-step, -step),
(step, -step),
(-step, step),
];
let ring4 = [(step, 0), (-step, 0), (0, step), (0, -step)];
let ring: &[(i32, i32)] = if pat & 1 != 0 { &ring4 } else { &ring8 };
let mut _iter = 0u32;
loop {
#[cfg(accel_x86)]
if sp_fc && step == 1 && pat & 1 == 0 {
_iter += 1;
let hp8 = hp.expect("sp_fc implies non-fast, which resolves hp");
let ring8 = [
(1, 0),
(-1, 0),
(0, 1),
(0, -1),
(1, 1),
(-1, -1),
(1, -1),
(-1, 1),
];
let mut prs: [Option<(&[u8], usize, &[u8], usize, usize)>; 8] = [None; 8];
let mut all = true;
for (i, &(dx, dy)) in ring8.iter().enumerate() {
prs[i] = rusty_h264_common::inter::hpel_qpel_refs(
hp8,
lx,
ly,
rw,
rh,
best.0 + dx,
best.1 + dy,
);
all &= prs[i].is_some();
}
if all {
let stride = prs[0].unwrap().4;
let pack = |a: usize, b: usize, c2: usize, d: usize| {
if rw != 16 {
return None;
}
let g = |i: usize| {
let (pa, oa, pb, ob, _) = prs[i].unwrap();
(pa, oa, pb, ob)
};
rusty_h264_accel::satd_avg_x4(
src_row,
cw,
[g(a), g(b), g(c2), g(d)],
stride,
rw,
rh,
)
};
{
let (ax, di) = (pack(0, 1, 2, 3), pack(4, 5, 6, 7));
let (mut bi, mut bc) = (usize::MAX, best_c);
for i in 0..8 {
let (dx, dy) = ring8[i];
let mv = (best.0 + dx, best.1 + dy);
let cc = match (i < 4, &ax, &di) {
(true, Some(ax), _) => {
let rate =
mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
ax[i] as i64 + (lambda_me * rate as f64) as i64
}
(false, _, Some(di)) => {
let rate =
mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
di[i - 4] as i64 + (lambda_me * rate as f64) as i64
}
_ => cost(mv),
};
hv_evals += 1;
if cc < bc {
bc = cc;
bi = i;
}
}
if hv_ring1 == i64::MIN {
hv_ring1 = if bi == usize::MAX { best_c } else { bc };
}
if bi == usize::MAX
|| !self.me_subpel_iter
|| pat & 2 != 0
|| (sp_cap != 0 && _iter >= sp_cap)
{
if bi != usize::MAX {
best_c = bc;
best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
hv_to_best = hv_evals;
}
break;
}
best_c = bc;
best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
hv_to_best = hv_evals;
continue;
}
}
_iter -= 1;
}
#[cfg(accel_x86)]
if sp_fc && step == 2 && best.0 & 3 == 0 && best.1 & 3 == 0 && pat & 1 == 0 {
_iter += 1;
let hp8 = hp.expect("sp_fc implies non-fast, which resolves hp");
let ring8 = [
(step, 0),
(-step, 0),
(0, step),
(0, -step),
(step, step),
(-step, -step),
(step, -step),
(-step, step),
];
let mut refs8: [Option<(&[u8], usize, usize)>; 8] = [None; 8];
let mut all = true;
for (i, &(dx, dy)) in ring8.iter().enumerate() {
refs8[i] = rusty_h264_common::inter::hpel_ref(
hp8,
lx,
ly,
rw,
rh,
best.0 + dx,
best.1 + dy,
);
all &= refs8[i].is_some();
}
if all {
let stride = refs8[0].unwrap().2;
let pack = |a: usize, b: usize, c2: usize, d: usize| {
if rw != 16 {
return None;
}
let g = |i: usize| {
let (p, o, _) = refs8[i].unwrap();
(p, o)
};
rusty_h264_accel::satd_x4p(
src_row,
cw,
[g(a), g(b), g(c2), g(d)],
stride,
rw,
rh,
)
};
{
let (ax, di) = (pack(0, 1, 2, 3), pack(4, 5, 6, 7));
let (mut bi, mut bc) = (usize::MAX, best_c);
for i in 0..8 {
let (dx, dy) = ring8[i];
let mv = (best.0 + dx, best.1 + dy);
let cc = match (i < 4, &ax, &di) {
(true, Some(ax), _) => {
let rate =
mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
ax[i] as i64 + (lambda_me * rate as f64) as i64
}
(false, _, Some(di)) => {
let rate =
mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
di[i - 4] as i64 + (lambda_me * rate as f64) as i64
}
_ => cost(mv),
};
hv_evals += 1;
if cc < bc {
bc = cc;
bi = i;
}
}
if hv_ring1 == i64::MIN {
hv_ring1 = if bi == usize::MAX { best_c } else { bc };
}
if bi == usize::MAX
|| !self.me_subpel_iter
|| pat & 2 != 0
|| (sp_cap != 0 && _iter >= sp_cap)
{
if bi != usize::MAX {
best_c = bc;
best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
hv_to_best = hv_evals;
}
break;
}
best_c = bc;
best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
hv_to_best = hv_evals;
continue;
}
}
_iter -= 1; }
let mut improved = false;
_iter += 1;
#[allow(clippy::unused_enumerate_index)] for (_pi, &(dx, dy)) in ring.iter().enumerate() {
let c = (best.0 + dx, best.1 + dy);
let slot = sp_slot(c);
let cc = if memo_mv[slot] == c {
memo_c[slot]
} else {
let v = cost(c);
memo_mv[slot] = c;
memo_c[slot] = v;
v
};
hv_evals += 1;
#[cfg(feature = "profile")]
{
spstats::ev(if step == 2 { 0 } else { 1 }, _pi, _iter);
if seen.contains(&c) {
spstats::redundant();
} else {
seen.push(c);
}
}
if cc < best_c {
best_c = cc;
best = c;
improved = true;
hv_to_best = hv_evals;
#[cfg(feature = "profile")]
spstats::imp(if step == 2 { 0 } else { 1 }, _pi, _iter);
}
}
if hv_ring1 == i64::MIN {
hv_ring1 = best_c;
}
if !improved
|| !self.me_subpel_iter
|| pat & 2 != 0
|| (sp_cap != 0 && _iter >= sp_cap)
{
break;
}
}
}
if sp_dispatching {
let n = self.sp_learn_n.get();
if n < sp_learn {
self.sp_learn_n.set(n + 1);
if hv_ring1 != i64::MIN {
self.sp_ring1
.set(self.sp_ring1.get() + (hv_pre - hv_ring1).max(0));
self.sp_total
.set(self.sp_total.get() + (hv_pre - best_c).max(0));
}
if n + 1 == sp_learn {
let tot = self.sp_total.get();
self.sp_1pass
.set(tot > 0 && self.sp_ring1.get() * 100 >= tot * sp_t);
}
}
}
if !subpel.is_empty() && subpel_harvest::enabled() {
subpel_harvest::record(
hv_pre, best_c, lambda_me, rw, rh, hv_evals, hv_to_best, hv_ring1,
);
}
if self.me_snap && seed_c < best_c {
best = seed_mv;
best_c = seed_c;
}
(best, best_c)
}
fn encode_inter_mb(
&mut self,
w: &mut BitWriter,
refs: &[crate::RefFrame],
sy: &[u8],
su: &[u8],
sv: &[u8],
mb_x: usize,
mb_y: usize,
mode: u8,
parts: &[(i32, (i32, i32))],
) {
if self.coded_path_v2 && self.rdoq_strength <= 0.0 {
self.encode_inter_mb_v2(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
} else {
self.encode_inter_mb_v1(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
}
}
fn encode_inter_mb_v2(
&mut self,
w: &mut BitWriter,
refs: &[crate::RefFrame],
sy: &[u8],
su: &[u8],
sv: &[u8],
mb_x: usize,
mb_y: usize,
mode: u8,
parts: &[(i32, (i32, i32))],
) {
#[cfg(feature = "profile")]
let _site = rusty_h264_common::inter::mcstats::SiteTag::new(1);
#[cfg(not(accel))]
{
self.encode_inter_mb_v1(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
}
#[cfg(accel)]
{
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncInterCode);
let (qp, qpc) = (self.qp, self.qpc);
let w4 = self.mb_w * 4;
let (_ch, cch) = (self.mb_h * 16, self.mb_h * 8);
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
let mut mvds = [(0i32, 0i32); 4];
let mut n_mvd = 0;
let _g_mc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
for (part, &(rx, ry, rw, rh)) in inter_partitions(mode).iter().enumerate() {
let (refi, mv) = parts[part];
let reference = &refs[refi as usize];
let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
let pmv = predict_partition_mv(mode, part, a, b, c, refi);
mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
n_mvd += 1;
for by in ry / 4..ry / 4 + rh / 4 {
let d = (mb_y * 4 + by) * w4 + mb_x * 4 + rx / 4;
let n4 = rw / 4;
self.mv_y[d..d + n4].fill(nmv(mv));
self.inter_y[d..d + n4].fill(true);
self.ref_idx_y[d..d + n4].fill(refi as i8);
self.coded_y[d..d + n4].fill(true);
}
if rw == 16 && rh == 16 {
self.mc_luma_cached(
reference,
mb_x * 16,
mb_y * 16,
16,
16,
mv.0,
mv.1,
&mut pred_y,
);
self.wp_luma(refi, &mut pred_y, 0, 0, 16, 16);
} else {
let mut tmp = [0u8; 256];
self.mc_luma_cached(
reference,
mb_x * 16 + rx,
mb_y * 16 + ry,
rw,
rh,
mv.0,
mv.1,
&mut tmp,
);
if rw == 8 {
for dy in 0..rh {
pred_y[(ry + dy) * 16 + rx..][..8].copy_from_slice(&tmp[dy * 8..][..8]);
}
} else {
for dy in 0..rh {
pred_y[(ry + dy) * 16 + rx..][..16]
.copy_from_slice(&tmp[dy * 16..][..16]);
}
}
self.wp_luma(refi, &mut pred_y, rx, ry, rw, rh);
}
let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
for cc in 0..2 {
let rc = if cc == 0 { &reference.u } else { &reference.v };
if crw == 8 && crh == 8 {
mc_chroma(
rc,
self.ccw,
cch,
mb_x * 8,
mb_y * 8,
8,
8,
mv.0,
mv.1,
&mut c_pred[cc],
);
} else {
let mut tc = [0u8; 64];
mc_chroma(
rc,
self.ccw,
cch,
mb_x * 8 + crx,
mb_y * 8 + cry,
crw,
crh,
mv.0,
mv.1,
&mut tc,
);
if crw == 4 {
for dy in 0..crh {
c_pred[cc][(cry + dy) * 8 + crx..][..4]
.copy_from_slice(&tc[dy * 4..][..4]);
}
} else {
for dy in 0..crh {
c_pred[cc][(cry + dy) * 8 + crx..][..8]
.copy_from_slice(&tc[dy * 8..][..8]);
}
}
}
}
}
let mut dctw = AlignedDct([0i16; 256]);
let dct = &mut dctw.0;
let mut cbp_luma = 0u32;
drop(_g_mc);
let _g_tq = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncTq);
let base = mb_y * 16 * self.cw + mb_x * 16;
for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)]
.iter()
.enumerate()
{
rusty_h264_accel::dct_four_t4(
&mut dct[qi * 64..qi * 64 + 64],
&sy[base + qy * self.cw + qx..],
self.cw,
&pred_y[qy * 16 + qx..],
16,
);
}
let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
for qi in 0..4 {
rusty_h264_accel::quant_four_4x4(&mut dct[qi * 64..qi * 64 + 64], &ff, mf);
}
for blk in 0..16 {
if dct[blk * 16..blk * 16 + 16].iter().any(|&v| v != 0) {
cbp_luma |= 1 << (blk / 4);
}
}
let mut c_dc_levels = [[0i32; 4]; 2];
let mut c_recon_dc = [[0i32; 4]; 2];
let mut c_q = [[[0i32; 16]; 4]; 2];
let (mut any_ac, mut any_dc) = (false, false);
for c in 0..2 {
let src = if c == 0 { su } else { sv };
let dc2x2 = {
#[repr(align(16))]
struct A([i16; 64]);
let mut cdct = A([0i16; 64]);
rusty_h264_accel::dct_four_t4(
&mut cdct.0,
&src[(mb_y * 8) * self.ccw + mb_x * 8..],
self.ccw,
&c_pred[c],
8,
);
let dc = [
cdct.0[0] as i32,
cdct.0[16] as i32,
cdct.0[32] as i32,
cdct.0[48] as i32,
];
let ffc = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
let mfc = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
rusty_h264_accel::quant_four_4x4(&mut cdct.0, &ffc, mfc);
for i in 0..4 {
let q = &mut c_q[c][i];
q[0] = 0;
for j in 1..16 {
let v = cdct.0[i * 16 + j] as i32;
q[j] = v;
if v != 0 {
any_ac = true;
}
}
}
dc
};
let dl = forward_quant_chroma_dc(&dc2x2, qpc, false);
if dl.iter().any(|&v| v != 0) {
any_dc = true;
}
c_recon_dc[c] = inverse_quant_chroma_dc(&dl, qpc);
c_dc_levels[c] = dl;
}
let cbp_chroma: u32 = if any_ac {
2
} else if any_dc {
1
} else {
0
};
let cbp = cbp_luma | (cbp_chroma << 4);
drop(_g_tq);
let _g_syn = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
w.write_ue(mode as u32);
if mode == 3 {
for _ in 0..4 {
w.write_ue(0);
}
}
let num_refs = refs.len();
if num_refs > 1 {
for &(refi, _) in parts {
write_ref_idx(w, refi, num_refs);
}
}
for &(mvdx, mvdy) in &mvds[..n_mvd] {
w.write_se(mvdx);
w.write_se(mvdy);
}
write_cbp_inter(w, cbp);
if cbp_luma > 0 && self.transform_8x8 {
w.write_bit(false);
}
if cbp != 0 {
w.write_se(self.qp_delta()); }
self.nnz_cache_load(mb_x, mb_y);
drop(_g_syn);
let _g_scan = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Scatter);
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
let total = if cbp_luma & (1 << (blk / 4)) != 0 {
let nc = self.nc_pred(lbx, lby);
let scan16 = scan_4x4_dcac_i16(&dct[blk * 16..blk * 16 + 16]);
encode_residual_block(w, &scan16, 16, nc) as u8
} else {
0
};
self.nnz_cache_set(lbx, lby, total);
self.nnz_y[by * w4 + bx] = total;
}
if cbp_chroma != 0 {
for c in 0..2 {
encode_residual_block(w, &c_dc_levels[c], 4, -1);
}
}
if cbp_chroma == 2 {
self.chroma_cache_load(mb_x, mb_y);
let w2 = self.mb_w * 2;
for c in 0..2 {
for &(bx, by) in &CHROMA_4X4_SCAN_XY {
let nc = self.chroma_nc_pred(c, bx, by);
let ac = scan_4x4_ac(&c_q[c][by * 2 + bx]);
let total = encode_residual_block(w, &ac, 15, nc) as u8;
self.chroma_nnz_cache_set(c, bx, by, total);
self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
}
}
}
drop(_g_scan);
let _g_rec = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
#[repr(align(16))]
struct Align16([i16; 64]);
let mut dct_in = Align16([0i16; 64]);
for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)]
.iter()
.enumerate()
{
let rec_off = base + qy * self.cw + qx;
if cbp_luma & (1 << qi) == 0 {
for r in 0..8 {
let (dsti, srci) = (rec_off + r * self.cw, (qy + r) * 16 + qx);
self.rec_y[dsti..dsti + 8].copy_from_slice(&pred_y[srci..srci + 8]);
}
continue;
}
for k in 0..4 {
let blk = qi * 4 + k;
let mut lvl = [0i32; 16];
for i in 0..16 {
lvl[i] = dct[blk * 16 + i] as i32;
}
let deq = dequantize(&lvl, qp);
for i in 0..16 {
dct_in.0[k * 16 + i] = deq[i] as i16;
}
}
rusty_h264_accel::idct_four_t4_rec(
&mut self.rec_y[rec_off..],
self.cw,
&pred_y[qy * 16 + qx..],
16,
&dct_in.0,
);
}
for c in 0..2 {
let base_c = (mb_y * 8) * self.ccw + mb_x * 8;
let plane = if c == 0 {
&mut self.rec_u
} else {
&mut self.rec_v
};
if cbp_chroma == 0 {
for r in 0..8 {
let dsti = base_c + r * self.ccw;
plane[dsti..dsti + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
}
} else {
#[repr(align(16))]
struct A([i16; 64]);
let mut d = A([0i16; 64]);
for i in 0..4 {
let deq = dequantize(&c_q[c][i], qpc);
for j in 0..16 {
d.0[i * 16 + j] = deq[j] as i16;
}
d.0[i * 16] = c_recon_dc[c][i] as i16;
}
rusty_h264_accel::idct_four_t4_rec(
&mut plane[base_c..],
self.ccw,
&c_pred[c],
8,
&d.0,
);
}
}
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
}
}
}
fn encode_inter_mb_v1(
&mut self,
w: &mut BitWriter,
refs: &[crate::RefFrame],
sy: &[u8],
su: &[u8],
sv: &[u8],
mb_x: usize,
mb_y: usize,
mode: u8,
parts: &[(i32, (i32, i32))],
) {
self.encode_inter_mb_v1_b(w, refs, sy, su, sv, mb_x, mb_y, mode, parts, None);
}
fn plan_inter_mb(
&mut self,
refs: &[crate::RefFrame],
sy: &[u8],
su: &[u8],
sv: &[u8],
mb_x: usize,
mb_y: usize,
mode: u8,
parts: &[(i32, (i32, i32))],
bspec: Option<BInter>,
sub_types: [u8; 4],
) -> InterPlan {
crate::signals::census::work(crate::signals::census::W_MB_PLAN);
#[cfg(feature = "profile")]
let _site = rusty_h264_common::inter::mcstats::SiteTag::new(1);
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncInterCode);
let (qp, qpc) = (self.qp, self.qpc);
let w4 = self.mb_w * 4;
let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
let mut mvds = [(0i32, 0i32); 16]; let mut plan_refs = [0i32; 4]; let mut n_mvd = 0;
let _g_mc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
if let Some(b) = bspec.filter(|b| b.mvmode > 0) {
let (rects, _) = b_part_layout(b.mvmode);
let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
let mut pm: [[(i32, i32); 2]; 2] = [[(0, 0); 2]; 2]; for (part, &(rx, ry, rw, rh)) in rects.iter().enumerate() {
let (pred, mv0, mv1) = b.parts2[part];
let (u0, u1) = (pred == 1 || pred == 3, pred == 2 || pred == 3);
let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
if u0 {
let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, (rw / 4) as isize, 0);
let p = predict_partition_mv(b.mvmode, part, a, c0, c1, 0);
pm[part][0] = (mv0.0 - p.0, mv0.1 - p.1);
}
if u1 {
let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, (rw / 4) as isize, 1);
let p = predict_partition_mv(b.mvmode, part, a, c0, c1, 0);
pm[part][1] = (mv1.0 - p.0, mv1.1 - p.1);
}
let (lx, ly) = (mb_x * 16 + rx, mb_y * 16 + ry);
let (cx, cy) = (mb_x * 8 + rx / 2, mb_y * 8 + ry / 2);
let (cw2, ch2) = (rw / 2, rh / 2);
let mut ay = [0u8; 256];
let mut by_ = [0u8; 256];
let mut ac = [[0u8; 64]; 2];
let mut bc = [[0u8; 64]; 2];
if u0 {
mc_luma(
&refs[0].y, self.cw, ch, lx, ly, rw, rh, mv0.0, mv0.1, &mut ay,
);
mc_chroma(
&refs[0].u, self.ccw, cch, cx, cy, cw2, ch2, mv0.0, mv0.1, &mut ac[0],
);
mc_chroma(
&refs[0].v, self.ccw, cch, cx, cy, cw2, ch2, mv0.0, mv0.1, &mut ac[1],
);
}
if u1 {
mc_luma(&b.l1.y, self.cw, ch, lx, ly, rw, rh, mv1.0, mv1.1, &mut by_);
mc_chroma(
&b.l1.u, self.ccw, cch, cx, cy, cw2, ch2, mv1.0, mv1.1, &mut bc[0],
);
mc_chroma(
&b.l1.v, self.ccw, cch, cx, cy, cw2, ch2, mv1.0, mv1.1, &mut bc[1],
);
}
for r in 0..rh {
for c in 0..rw {
let d = (ry + r) * 16 + rx + c;
let sidx = r * rw + c;
pred_y[d] = match (u0, u1) {
(true, true) => bi_blend(ay[sidx] as i32, by_[sidx] as i32, self.bi_w),
(true, false) => ay[sidx],
_ => by_[sidx],
};
}
}
for cc in 0..2 {
for r in 0..ch2 {
for c in 0..cw2 {
let d = (ry / 2 + r) * 8 + rx / 2 + c;
let sidx = r * cw2 + c;
c_pred[cc][d] = match (u0, u1) {
(true, true) => {
bi_blend(ac[cc][sidx] as i32, bc[cc][sidx] as i32, self.bi_w)
}
(true, false) => ac[cc][sidx],
_ => bc[cc][sidx],
};
}
}
}
let (cmv0, cr0) = (if u0 { mv0 } else { (0, 0) }, if u0 { 0 } else { -1 });
let (cmv1, cr1) = (if u1 { mv1 } else { (0, 0) }, if u1 { 0 } else { -1 });
for by2 in ry / 4..(ry + rh) / 4 {
let d = (mb_y * 4 + by2) * w4 + mb_x * 4 + rx / 4;
let n4 = rw / 4;
self.inter_y[d..d + n4].fill(true);
self.coded_y[d..d + n4].fill(true);
self.mv_y[d..d + n4].fill(nmv(cmv0));
self.ref_idx_y[d..d + n4].fill(cr0);
self.mv1_y[d..d + n4].fill(nmv(cmv1));
self.ref_idx1_y[d..d + n4].fill(cr1);
}
}
for list in 0..2 {
for part in 0..2 {
let pred = b.parts2[part].0;
let used = if list == 0 {
pred == 1 || pred == 3
} else {
pred == 2 || pred == 3
};
if used {
mvds[n_mvd] = pm[part][list];
n_mvd += 1;
}
}
}
} else if let Some(b) = bspec.filter(|b| b.dir == 0) {
let (dp, dc, motion) = self.b_direct(&refs[0], b.l1, mb_x, mb_y);
pred_y = dp;
c_pred = dc;
self.commit_direct_motion(mb_x, mb_y, &motion);
} else if let Some(b) = bspec {
let use0 = b.dir == 1 || b.dir == 3;
let use1 = b.dir == 2 || b.dir == 3;
let (lx, ly) = (mb_x * 16, mb_y * 16);
let (cx, cy) = (mb_x * 8, mb_y * 8);
let (pbx, pby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
if use0 {
let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, 4, 0);
let p = predict_partition_mv(0, 0, a, c0, c1, 0);
mvds[n_mvd] = (b.mv0.0 - p.0, b.mv0.1 - p.1);
n_mvd += 1;
}
if use1 {
let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, 4, 1);
let p = predict_partition_mv(0, 0, a, c0, c1, 0);
mvds[n_mvd] = (b.mv1.0 - p.0, b.mv1.1 - p.1);
n_mvd += 1;
}
let mut a_y = [0u8; 256];
let mut b_y = [0u8; 256];
let mut a_c = [[0u8; 64]; 2];
let mut b_c = [[0u8; 64]; 2];
if use0 {
mc_luma(
&refs[0].y, self.cw, ch, lx, ly, 16, 16, b.mv0.0, b.mv0.1, &mut a_y,
);
mc_chroma(
&refs[0].u,
self.ccw,
cch,
cx,
cy,
8,
8,
b.mv0.0,
b.mv0.1,
&mut a_c[0],
);
mc_chroma(
&refs[0].v,
self.ccw,
cch,
cx,
cy,
8,
8,
b.mv0.0,
b.mv0.1,
&mut a_c[1],
);
}
if use1 {
mc_luma(
&b.l1.y, self.cw, ch, lx, ly, 16, 16, b.mv1.0, b.mv1.1, &mut b_y,
);
mc_chroma(
&b.l1.u,
self.ccw,
cch,
cx,
cy,
8,
8,
b.mv1.0,
b.mv1.1,
&mut b_c[0],
);
mc_chroma(
&b.l1.v,
self.ccw,
cch,
cx,
cy,
8,
8,
b.mv1.0,
b.mv1.1,
&mut b_c[1],
);
}
match (use0, use1) {
(true, true) => {
for i in 0..256 {
pred_y[i] = bi_blend(a_y[i] as i32, b_y[i] as i32, self.bi_w);
}
for c in 0..2 {
for i in 0..64 {
c_pred[c][i] = bi_blend(a_c[c][i] as i32, b_c[c][i] as i32, self.bi_w);
}
}
}
(true, false) => {
pred_y = a_y;
c_pred = a_c;
}
_ => {
pred_y = b_y;
c_pred = b_c;
}
}
let (cmv0, cr0) = (if use0 { b.mv0 } else { (0, 0) }, if use0 { 0 } else { -1 });
let (cmv1, cr1) = (if use1 { b.mv1 } else { (0, 0) }, if use1 { 0 } else { -1 });
for by in 0..4 {
let d = (mb_y * 4 + by) * w4 + mb_x * 4;
self.inter_y[d..d + 4].fill(true);
self.coded_y[d..d + 4].fill(true);
self.mv_y[d..d + 4].fill(nmv(cmv0));
self.ref_idx_y[d..d + 4].fill(cr0);
self.mv1_y[d..d + 4].fill(nmv(cmv1));
self.ref_idx1_y[d..d + 4].fill(cr1);
}
} else if mode == 3 && sub_types != [0u8; 4] {
let mut k = 0usize;
for p8 in 0..4usize {
let (b8x, b8y) = ((p8 % 2) * 8, (p8 / 2) * 8);
plan_refs[p8] = parts[k].0;
for &(srx, sry, srw, srh) in sub_mb_partitions_p(sub_types[p8]) {
let (refi, mv) = parts[k];
debug_assert_eq!(refi, plan_refs[p8], "ref_idx_l0 is per 8x8");
k += 1;
let (px, py) = (b8x + srx, b8y + sry);
let (pbx, pby) = ((mb_x * 4 + px / 4) as isize, (mb_y * 4 + py / 4) as isize);
let [a, b, c] = self.mv_neighbors_block(pbx, pby, (srw / 4) as isize);
let pmv = predict_mv(a, b, c, refi);
mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
n_mvd += 1;
for by in py / 4..(py + srh) / 4 {
for bx in px / 4..(px + srw) / 4 {
let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
self.mv_y[idx] = nmv(mv);
self.inter_y[idx] = true;
self.ref_idx_y[idx] = refi as i8;
self.coded_y[idx] = true;
}
}
let reference = &refs[refi as usize];
let mut tmp = [0u8; 256];
mc_luma(
&reference.y,
self.cw,
ch,
mb_x * 16 + px,
mb_y * 16 + py,
srw,
srh,
mv.0,
mv.1,
&mut tmp,
);
for dy in 0..srh {
pred_y[(py + dy) * 16 + px..][..srw]
.copy_from_slice(&tmp[dy * srw..][..srw]);
}
self.wp_luma(refi, &mut pred_y, px, py, srw, srh);
let (crx, cry, crw, crh) = (px / 2, py / 2, srw / 2, srh / 2);
for cc in 0..2 {
let rc = if cc == 0 { &reference.u } else { &reference.v };
let mut tc = [0u8; 64];
mc_chroma(
rc,
self.ccw,
cch,
mb_x * 8 + crx,
mb_y * 8 + cry,
crw,
crh,
mv.0,
mv.1,
&mut tc,
);
for dy in 0..crh {
c_pred[cc][(cry + dy) * 8 + crx..][..crw]
.copy_from_slice(&tc[dy * crw..][..crw]);
}
}
}
}
} else {
for (part, &(rx, ry, rw, rh)) in inter_partitions(mode).iter().enumerate() {
let (refi, mv) = parts[part];
plan_refs[part] = refi; let reference = &refs[refi as usize];
let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
let pmv = predict_partition_mv(mode, part, a, b, c, refi);
mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
n_mvd += 1;
for by in ry / 4..ry / 4 + rh / 4 {
for bx in rx / 4..rx / 4 + rw / 4 {
let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
self.mv_y[idx] = nmv(mv);
self.inter_y[idx] = true;
self.ref_idx_y[idx] = refi as i8;
self.coded_y[idx] = true;
}
}
if rw == 16 && rh == 16 {
self.mc_luma_cached(
reference,
mb_x * 16,
mb_y * 16,
16,
16,
mv.0,
mv.1,
&mut pred_y,
);
} else {
let mut tmp = [0u8; 256];
self.mc_luma_cached(
reference,
mb_x * 16 + rx,
mb_y * 16 + ry,
rw,
rh,
mv.0,
mv.1,
&mut tmp,
);
if rw == 8 {
for dy in 0..rh {
pred_y[(ry + dy) * 16 + rx..][..8].copy_from_slice(&tmp[dy * 8..][..8]);
}
} else {
for dy in 0..rh {
pred_y[(ry + dy) * 16 + rx..][..16]
.copy_from_slice(&tmp[dy * 16..][..16]);
}
}
}
self.wp_luma(refi, &mut pred_y, rx, ry, rw, rh);
let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
for cc in 0..2 {
let rc = if cc == 0 { &reference.u } else { &reference.v };
if crw == 8 && crh == 8 {
mc_chroma(
rc,
self.ccw,
cch,
mb_x * 8,
mb_y * 8,
8,
8,
mv.0,
mv.1,
&mut c_pred[cc],
);
} else {
let mut tc = [0u8; 64];
mc_chroma(
rc,
self.ccw,
cch,
mb_x * 8 + crx,
mb_y * 8 + cry,
crw,
crh,
mv.0,
mv.1,
&mut tc,
);
if crw == 4 {
for dy in 0..crh {
c_pred[cc][(cry + dy) * 8 + crx..][..4]
.copy_from_slice(&tc[dy * 4..][..4]);
}
} else {
for dy in 0..crh {
c_pred[cc][(cry + dy) * 8 + crx..][..8]
.copy_from_slice(&tc[dy * 8..][..8]);
}
}
}
}
}
}
let mut q_blocks = [[0i32; 16]; 16]; let mut cbp_luma = 0u32;
#[allow(unused_mut)]
let mut t8x8 = false;
#[allow(unused_mut)]
let mut q8 = [[0i32; 64]; 4];
drop(_g_mc);
let _g_tq = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncTq);
#[cfg(accel)]
{
let mut dctw = AlignedDct([0i16; 256]);
let dct = &mut dctw.0;
let base = mb_y * 16 * self.cw + mb_x * 16;
for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)]
.iter()
.enumerate()
{
rusty_h264_accel::dct_four_t4(
&mut dct[qi * 64..qi * 64 + 64],
&sy[base + qy * self.cw + qx..],
self.cw,
&pred_y[qy * 16 + qx..],
16,
);
}
if self.rdoq_strength > 0.0 {
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
let coeffs: [i32; 16] = core::array::from_fn(|i| dct[blk * 16 + i] as i32);
let q = rdoq(&coeffs, qp, 6, self.rdoq_strength, 0);
if q.iter().any(|&v| v != 0) {
cbp_luma |= 1 << (blk / 4);
}
q_blocks[lby * 4 + lbx] = q;
}
} else {
let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
for qi in 0..4 {
rusty_h264_accel::quant_four_4x4(&mut dct[qi * 64..qi * 64 + 64], &ff, mf);
}
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
let mut nz = false;
for i in 0..16 {
let v = dct[blk * 16 + i] as i32;
q_blocks[lby * 4 + lbx][i] = v;
nz |= v != 0;
}
if nz {
cbp_luma |= 1 << (blk / 4);
}
}
} }
#[cfg(not(accel))]
{
let mut res_blocks = [[0i32; 16]; 16]; for lby in 0..4 {
for lbx in 0..4 {
let b = &mut res_blocks[lby * 4 + lbx];
for dy in 0..4 {
let row =
&sy[(mb_y * 16 + lby * 4 + dy) * self.cw + mb_x * 16 + lbx * 4..][..4];
for dx in 0..4 {
b[dy * 4 + dx] = row[dx] as i32
- pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
}
}
}
}
let mut coeffs = [[0i32; 16]; 16];
forward_dct_blocks(&res_blocks, &mut coeffs);
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
let q = rdoq(&coeffs[lby * 4 + lbx], qp, 6, self.rdoq_strength, 0);
if q.iter().any(|&v| v != 0) {
cbp_luma |= 1 << (blk / 4);
}
q_blocks[lby * 4 + lbx] = q;
}
}
{
let allow_t8 = sub_types == [0u8; 4];
if self.t8_pick && self.inter8x8 != 0 && allow_t8 {
let lambda = 0.85 * self.tune_lambda_scale * crate::fastmath::lambda_qp(qp);
let mut ssd4 = 0i64;
let mut rate4 = 0f64;
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
let mut predb = [0i32; 16];
for dy in 0..4 {
for dx in 0..4 {
predb[dy * 4 + dx] =
pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
}
}
let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
let s = reconstruct_4x4(&deq, &predb);
for dy in 0..4 {
for dx in 0..4 {
let sx = mb_x * 16 + lbx * 4 + dx;
let syy = mb_y * 16 + lby * 4 + dy;
let d = s[dy * 4 + dx] as i64 - sy[syy * self.cw + sx] as i64;
ssd4 += d * d;
}
}
for &l in &q_blocks[lby * 4 + lbx] {
if l != 0 {
rate4 += rdoq_rate((l as i64).abs());
}
}
}
let (q8c, cbp8, rate8, _rec8, ssd8) =
plan_inter8_luma(sy, self.cw, mb_x, mb_y, &pred_y, qp);
let j4 = ssd4 as f64 + lambda * (rate4 + 16.0);
let j8 = ssd8 as f64 + lambda * (rate8 + 16.0 + self.inter8_pen as f64);
if cbp8 > 0 && j8 < j4 {
t8x8 = true;
cbp_luma = cbp8;
q8 = q8c;
}
}
}
let mut c_dc_levels = [[0i32; 4]; 2];
let mut c_recon_dc = [[0i32; 4]; 2];
let mut c_q = [[[0i32; 16]; 4]; 2];
let (mut any_ac, mut any_dc) = (false, false);
for c in 0..2 {
let src = if c == 0 { su } else { sv };
#[cfg(accel)]
let (mut dc2x2, applied) = if self.rdoq_strength > 0.0 {
([0i32; 4], false)
} else {
#[repr(align(16))]
struct A([i16; 64]);
let mut dct = A([0i16; 64]);
rusty_h264_accel::dct_four_t4(
&mut dct.0,
&src[(mb_y * 8) * self.ccw + mb_x * 8..],
self.ccw,
&c_pred[c],
8,
);
let dc = [
dct.0[0] as i32,
dct.0[16] as i32,
dct.0[32] as i32,
dct.0[48] as i32,
];
let ffc = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
let mfc = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
rusty_h264_accel::quant_four_4x4(&mut dct.0, &ffc, mfc);
for i in 0..4 {
let q = &mut c_q[c][i];
q[0] = 0;
for j in 1..16 {
let v = dct.0[i * 16 + j] as i32;
q[j] = v;
if v != 0 {
any_ac = true;
}
}
}
(dc, true)
};
#[cfg(not(accel))]
let (mut dc2x2, applied) = ([0i32; 4], false);
if !applied {
let mut res_blocks = [[0i32; 16]; 4];
for by in 0..2 {
for bx in 0..2 {
let b = &mut res_blocks[by * 2 + bx];
for dy in 0..4 {
let row = &src
[(mb_y * 8 + by * 4 + dy) * self.ccw + mb_x * 8 + bx * 4..][..4];
for dx in 0..4 {
b[dy * 4 + dx] = row[dx] as i32
- c_pred[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
}
}
}
}
let mut coeffs = [[0i32; 16]; 4];
forward_dct_blocks(&res_blocks, &mut coeffs);
for i in 0..4 {
dc2x2[i] = coeffs[i][0];
let mut q = rdoq(&coeffs[i], qpc, 6, self.rdoq_strength, 1);
q[0] = 0;
if q[1..].iter().any(|&v| v != 0) {
any_ac = true;
}
c_q[c][i] = q;
}
}
let dl = forward_quant_chroma_dc(&dc2x2, qpc, false);
if dl.iter().any(|&v| v != 0) {
any_dc = true;
}
c_recon_dc[c] = inverse_quant_chroma_dc(&dl, qpc);
c_dc_levels[c] = dl;
}
let cbp_chroma: u32 = if any_ac {
2
} else if any_dc {
1
} else {
0
};
let cbp = cbp_luma | (cbp_chroma << 4);
drop(_g_tq);
let _g_rec = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
#[cfg(accel)]
if t8x8 {
let weight = [16i32; 64];
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let res_r = inverse_quant_8x8(&q8[b8], qp, &weight);
let predb: [i32; 64] = core::array::from_fn(|i| {
pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
});
let recon = add_residual_8x8(&res_r, &predb);
for dy in 0..8 {
let d = (mb_y * 16 + b8y * 8 + dy) * self.cw + mb_x * 16 + b8x * 8;
self.rec_y[d..d + 8].copy_from_slice(&recon[dy * 8..dy * 8 + 8]);
}
}
} else {
#[repr(align(16))]
struct Align16([i16; 64]);
let mut dct_in = Align16([0i16; 64]);
let base = mb_y * 16 * self.cw + mb_x * 16;
for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)]
.iter()
.enumerate()
{
let rec_off = base + qy * self.cw + qx;
if cbp_luma & (1 << qi) == 0 {
for r in 0..8 {
let (dsti, srci) = (rec_off + r * self.cw, (qy + r) * 16 + qx);
self.rec_y[dsti..dsti + 8].copy_from_slice(&pred_y[srci..srci + 8]);
}
continue;
}
for k in 0..4 {
let blk = qi * 4 + k;
let (lbx, lby) = LUMA_4X4_SCAN_XY[blk];
let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
for i in 0..16 {
dct_in.0[k * 16 + i] = deq[i] as i16;
}
}
rusty_h264_accel::idct_four_t4_rec(
&mut self.rec_y[rec_off..],
self.cw,
&pred_y[qy * 16 + qx..],
16,
&dct_in.0,
);
}
}
#[cfg(not(accel))]
if t8x8 {
let weight = [16i32; 64];
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let res_r = inverse_quant_8x8(&q8[b8], qp, &weight);
let predb: [i32; 64] = core::array::from_fn(|i| {
pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
});
let recon = add_residual_8x8(&res_r, &predb);
for dy in 0..8 {
let d = (mb_y * 16 + b8y * 8 + dy) * self.cw + mb_x * 16 + b8x * 8;
self.rec_y[d..d + 8].copy_from_slice(&recon[dy * 8..dy * 8 + 8]);
}
}
} else {
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
reconstruct_4x4_into(
&deq,
&pred_y,
(lby * 4) * 16 + lbx * 4,
16,
&mut self.rec_y,
(mb_y * 16 + lby * 4) * self.cw + mb_x * 16 + lbx * 4,
self.cw,
);
}
}
for c in 0..2 {
#[cfg(accel)]
{
let base = (mb_y * 8) * self.ccw + mb_x * 8;
let plane = if c == 0 {
&mut self.rec_u
} else {
&mut self.rec_v
};
if cbp_chroma == 0 {
for r in 0..8 {
let dsti = base + r * self.ccw;
plane[dsti..dsti + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
}
} else {
#[repr(align(16))]
struct A([i16; 64]);
let mut d = A([0i16; 64]);
for i in 0..4 {
let deq = dequantize(&c_q[c][i], qpc);
for j in 0..16 {
d.0[i * 16 + j] = deq[j] as i16;
}
d.0[i * 16] = c_recon_dc[c][i] as i16;
}
rusty_h264_accel::idct_four_t4_rec(
&mut plane[base..],
self.ccw,
&c_pred[c],
8,
&d.0,
);
}
}
#[cfg(not(accel))]
{
let mut deq_blocks = [[0i32; 16]; 4];
for i in 0..4 {
deq_blocks[i] = dequantize(&c_q[c][i], qpc);
deq_blocks[i][0] = c_recon_dc[c][i];
}
let mut res = [[0i32; 16]; 4];
inverse_dct_blocks(&deq_blocks, &mut res);
let plane = if c == 0 {
&mut self.rec_u
} else {
&mut self.rec_v
};
for by in 0..2 {
for bx in 0..2 {
let mut predb = [0i32; 16];
for dy in 0..4 {
for dx in 0..4 {
predb[dy * 4 + dx] =
c_pred[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
}
}
let s = add_residual_4x4(&res[by * 2 + bx], &predb);
store(plane, self.ccw, mb_x * 8 + bx * 4, mb_y * 8 + by * 4, &s);
}
}
}
}
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
}
InterPlan {
mvds,
plan_refs,
n_mvd,
cbp,
q_blocks,
c_dc_levels,
c_q,
t8x8,
q8,
sub_types,
}
}
fn encode_inter_mb_v1_b(
&mut self,
w: &mut BitWriter,
refs: &[crate::RefFrame],
sy: &[u8],
su: &[u8],
sv: &[u8],
mb_x: usize,
mb_y: usize,
mode: u8,
parts: &[(i32, (i32, i32))],
bspec: Option<BInter>,
) {
let plan = self.plan_inter_mb(refs, sy, su, sv, mb_x, mb_y, mode, parts, bspec, [0u8; 4]);
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
self.emit_inter_cavlc(w, refs.len(), mb_x, mb_y, mode, parts, bspec, &plan);
}
fn emit_inter_cavlc(
&mut self,
w: &mut BitWriter,
num_refs: usize,
mb_x: usize,
mb_y: usize,
mode: u8,
parts: &[(i32, (i32, i32))],
bspec: Option<BInter>,
plan: &InterPlan,
) {
let w4 = self.mb_w * 4;
let (cbp, cbp_luma, cbp_chroma) = (plan.cbp, plan.cbp & 15, plan.cbp >> 4);
let allow8 = true;
let _ = &bspec; w.write_ue(bspec.map_or(mode as u32, |b| b.dir as u32)); if mode == 3 {
for _ in 0..4 {
w.write_ue(0);
}
}
if num_refs > 1 {
for &(refi, _) in parts {
write_ref_idx(w, refi, num_refs);
}
}
for &(mvdx, mvdy) in &plan.mvds[..plan.n_mvd] {
w.write_se(mvdx);
w.write_se(mvdy);
}
write_cbp_inter(w, cbp);
if cbp_luma > 0 && self.transform_8x8 && allow8 {
w.write_bit(plan.t8x8);
}
if cbp != 0 {
w.write_se(self.qp_delta()); }
self.nnz_cache_load(mb_x, mb_y);
if plan.t8x8 {
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let scan8 = scan_8x8_fwd(&plan.q8[b8]);
for sub in 0..4usize {
let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
let total = if cbp_luma & (1 << b8) != 0 {
let nc = self.nc_pred(cx, cy);
let blk: [i32; 16] = core::array::from_fn(|k| scan8[4 * k + sub]);
encode_residual_block(w, &blk, 16, nc) as u8
} else {
0
};
self.nnz_cache_set(cx, cy, total);
self.nnz_y[by * w4 + bx] = total;
}
}
} else {
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
let total = if cbp_luma & (1 << (blk / 4)) != 0 {
let nc = self.nc_pred(lbx, lby);
let scan16 = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
encode_residual_block(w, &scan16, 16, nc) as u8
} else {
0
};
self.nnz_cache_set(lbx, lby, total);
self.nnz_y[by * w4 + bx] = total;
}
}
if cbp_chroma != 0 {
for c in 0..2 {
encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
}
}
if cbp_chroma == 2 {
self.chroma_cache_load(mb_x, mb_y);
let w2 = self.mb_w * 2;
for c in 0..2 {
for &(bx, by) in &CHROMA_4X4_SCAN_XY {
let nc = self.chroma_nc_pred(c, bx, by);
let ac = scan_4x4_ac(&plan.c_q[c][by * 2 + bx]);
let total = encode_residual_block(w, &ac, 15, nc) as u8;
self.chroma_nnz_cache_set(c, bx, by, total);
self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
}
}
}
}
#[inline]
fn mc_luma_cached(
&self,
reference: &crate::RefFrame,
x0: usize,
y0: usize,
bw: usize,
bh: usize,
mvx: i32,
mvy: i32,
out: &mut [u8],
) {
let ch = self.mb_h * 16;
let cw = self.cw;
if !self.fast {
let p = reference.hpel(cw, ch);
if rusty_h264_common::inter::hpel_block(p, x0, y0, bw, bh, mvx, mvy, out) {
return;
}
if let Some((plane, base, stride)) =
rusty_h264_common::inter::hpel_ref(p, x0, y0, bw, bh, mvx, mvy)
{
for r in 0..bh {
out[r * bw..r * bw + bw].copy_from_slice(&plane[base + r * stride..][..bw]);
}
return;
}
}
mc_luma(&reference.y, cw, ch, x0, y0, bw, bh, mvx, mvy, out);
}
#[inline]
fn wp_luma(&self, r: i32, buf: &mut [u8; 256], x0: usize, y0: usize, rw: usize, rh: usize) {
let Some(&(lw, lo)) = self.wp.get(r.max(0) as usize) else {
return;
};
if lw == 64 && lo == 0 {
return;
}
for dy in 0..rh {
for p in buf[(y0 + dy) * 16 + x0..][..rw].iter_mut() {
*p = (((*p as i32 * lw + 32) >> 6) + lo).clamp(0, 255) as u8;
}
}
}
fn skip_predict_luma(
&self,
refs: &[crate::RefFrame],
mb_x: usize,
mb_y: usize,
mv: (i32, i32),
) -> [u8; 256] {
#[cfg(feature = "profile")]
let _site = rusty_h264_common::inter::mcstats::SiteTag::new(3);
let reference = &refs[0]; let mut pred_y = [0u8; 256];
self.mc_luma_cached(
reference,
mb_x * 16,
mb_y * 16,
16,
16,
mv.0,
mv.1,
&mut pred_y,
);
self.wp_luma(0, &mut pred_y, 0, 0, 16, 16);
pred_y
}
fn skip_predict_chroma(
&self,
refs: &[crate::RefFrame],
mb_x: usize,
mb_y: usize,
mv: (i32, i32),
) -> [[u8; 64]; 2] {
let reference = &refs[0];
let cch = self.mb_h * 8;
let mut pred_c = [[0u8; 64]; 2];
for c in 0..2 {
let rc = if c == 0 { &reference.u } else { &reference.v };
mc_chroma(
rc,
self.ccw,
cch,
mb_x * 8,
mb_y * 8,
8,
8,
mv.0,
mv.1,
&mut pred_c[c],
);
}
pred_c
}
fn skip_luma_is_free(&self, sy: &[u8], mb_x: usize, mb_y: usize, pred_y: &[u8; 256]) -> bool {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFree);
let qp = self.qp;
#[cfg(accel)]
if self.skip_accel_check {
#[repr(align(16))]
struct Align16([i16; 64]);
let mut dct = Align16([0i16; 64]);
let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
rusty_h264_accel::dct_four_t4(
&mut dct.0,
&sy[(mb_y * 16 + qy) * self.cw + mb_x * 16 + qx..],
self.cw,
&pred_y[qy * 16 + qx..],
16,
);
rusty_h264_accel::quant_four_4x4(&mut dct.0, &ff, mf);
if dct.0.iter().any(|&v| v != 0) {
return false;
}
}
return true;
}
let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
let ceil8 = &rusty_h264_common::transform::CEIL_65536_MF[qp as usize];
let mut t_min = i32::MAX;
for p in 0..8 {
t_min = t_min.min(ceil8[p] - ff[p] as i32);
}
let t_dc = ceil8[0] - ff[0] as i32;
for by in 0..4 {
for bx in 0..4 {
let mut res = [0i32; 16];
let (mut sad, mut dc) = (0i32, 0i32);
for dy in 0..4 {
let row = &sy[(mb_y * 16 + by * 4 + dy) * self.cw + mb_x * 16 + bx * 4..][..4];
for dx in 0..4 {
let d = row[dx] as i32 - pred_y[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
res[dy * 4 + dx] = d;
sad += d.abs();
dc += d;
}
}
if 4 * sad < t_min {
continue; }
if dc.abs() >= t_dc {
return false; }
if quantize(&forward_core(&res), qp, 6).iter().any(|&v| v != 0) {
return false;
}
}
}
true
}
fn skip_chroma_is_free(
&self,
su: &[u8],
sv: &[u8],
mb_x: usize,
mb_y: usize,
pred_c: &[[u8; 64]; 2],
) -> bool {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFree);
let qpc = self.qpc;
#[cfg(accel)]
if self.skip_accel_check {
#[repr(align(16))]
struct Align16C([i16; 64]);
let mut dct = Align16C([0i16; 64]);
let ff = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
for c in 0..2 {
let src = if c == 0 { su } else { sv };
rusty_h264_accel::dct_four_t4(
&mut dct.0,
&src[(mb_y * 8) * self.ccw + mb_x * 8..],
self.ccw,
&pred_c[c],
8,
);
let dc2x2 = [
dct.0[0] as i32,
dct.0[16] as i32,
dct.0[32] as i32,
dct.0[48] as i32,
];
rusty_h264_accel::quant_four_4x4(&mut dct.0, &ff, mf);
for b in 0..4 {
if dct.0[b * 16 + 1..b * 16 + 16].iter().any(|&v| v != 0) {
return false;
}
}
if forward_quant_chroma_dc(&dc2x2, qpc, false)
.iter()
.any(|&v| v != 0)
{
return false;
}
}
return true;
}
for c in 0..2 {
let src = if c == 0 { su } else { sv };
let mut dc2x2 = [0i32; 4];
for &(bx, by) in &CHROMA_4X4_SCAN_XY {
let mut res = [0i32; 16];
for dy in 0..4 {
let row = &src[(mb_y * 8 + by * 4 + dy) * self.ccw + mb_x * 8 + bx * 4..][..4];
for dx in 0..4 {
res[dy * 4 + dx] =
row[dx] as i32 - pred_c[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
}
}
let coeffs = forward_core(&res);
dc2x2[by * 2 + bx] = coeffs[0];
if quantize(&coeffs, qpc, 6)[1..].iter().any(|&v| v != 0) {
return false;
}
}
if forward_quant_chroma_dc(&dc2x2, qpc, false)
.iter()
.any(|&v| v != 0)
{
return false;
}
}
true
}
fn pred_ssd(
&self,
sy: &[u8],
su: &[u8],
sv: &[u8],
mb_x: usize,
mb_y: usize,
pred_y: &[u8; 256],
pred_c: &[[u8; 64]; 2],
) -> i64 {
let mut ssd = 0i64;
for dy in 0..16 {
let i = (mb_y * 16 + dy) * self.cw + mb_x * 16;
for (a, b) in sy[i..i + 16].iter().zip(&pred_y[dy * 16..dy * 16 + 16]) {
let d = *a as i64 - *b as i64;
ssd += d * d;
}
}
for c in 0..2 {
let src = if c == 0 { su } else { sv };
for dy in 0..8 {
let i = (mb_y * 8 + dy) * self.ccw + mb_x * 8;
for (a, b) in src[i..i + 8].iter().zip(&pred_c[c][dy * 8..dy * 8 + 8]) {
let d = *a as i64 - *b as i64;
ssd += d * d;
}
}
}
ssd
}
fn mb_ssd(&self, sy: &[u8], su: &[u8], sv: &[u8], mb_x: usize, mb_y: usize) -> i64 {
let mut ssd = 0i64;
for dy in 0..16 {
let i = (mb_y * 16 + dy) * self.cw + mb_x * 16;
for (a, b) in sy[i..i + 16].iter().zip(&self.rec_y[i..i + 16]) {
let d = *a as i64 - *b as i64;
ssd += d * d;
}
}
let cw_ = chroma_ssd_weight();
if cw_ != 0.0 {
let mut cssd = 0i64;
for c in 0..2 {
let (src, rec) = if c == 0 {
(su, &self.rec_u)
} else {
(sv, &self.rec_v)
};
for dy in 0..8 {
let i = (mb_y * 8 + dy) * self.ccw + mb_x * 8;
for (a, b) in src[i..i + 8].iter().zip(&rec[i..i + 8]) {
let d = *a as i64 - *b as i64;
cssd += d * d;
}
}
}
ssd += if cw_ == 1.0 {
cssd
} else {
(cssd as f64 * cw_) as i64
};
}
ssd
}
fn commit_pred_pixels(
&mut self,
mb_x: usize,
mb_y: usize,
pred_y: &[u8; 256],
pred_c: &[[u8; 64]; 2],
) {
let base = mb_y * 16 * self.cw + mb_x * 16;
for r in 0..16 {
let d = base + r * self.cw;
self.rec_y[d..d + 16].copy_from_slice(&pred_y[r * 16..r * 16 + 16]);
}
let cbase = mb_y * 8 * self.ccw + mb_x * 8;
for c in 0..2 {
let plane = if c == 0 {
&mut self.rec_u
} else {
&mut self.rec_v
};
for r in 0..8 {
let d = cbase + r * self.ccw;
plane[d..d + 8].copy_from_slice(&pred_c[c][r * 8..r * 8 + 8]);
}
}
}
fn commit_skip(
&mut self,
mb_x: usize,
mb_y: usize,
mv: (i32, i32),
pred_y: &[u8; 256],
pred_c: &[[u8; 64]; 2],
) {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MvGrid);
let base = mb_y * 16 * self.cw + mb_x * 16;
for r in 0..16 {
let d = base + r * self.cw;
self.rec_y[d..d + 16].copy_from_slice(&pred_y[r * 16..r * 16 + 16]);
}
let cbase = mb_y * 8 * self.ccw + mb_x * 8;
for c in 0..2 {
let plane = if c == 0 {
&mut self.rec_u
} else {
&mut self.rec_v
};
for r in 0..8 {
let d = cbase + r * self.ccw;
plane[d..d + 8].copy_from_slice(&pred_c[c][r * 8..r * 8 + 8]);
}
}
self.set_mb_mv(mb_x, mb_y, mv, true, 0);
let w4 = self.mb_w * 4;
for row in 0..4 {
let st = (mb_y * 4 + row) * w4 + mb_x * 4;
self.modes_y[st..st + 4].fill(2);
self.coded_y[st..st + 4].fill(true);
}
}
#[cfg_attr(not(feature = "std"), allow(dead_code))]
fn trial_inter(
&mut self,
refs: &[crate::RefFrame],
sy: &[u8],
su: &[u8],
sv: &[u8],
mb_x: usize,
mb_y: usize,
mode: u8,
parts: &[(i32, (i32, i32))],
) -> (i64, usize) {
let snap = self.save_mb(mb_x, mb_y);
let mut scratch = BitWriter::new();
self.encode_inter_mb(&mut scratch, refs, sy, su, sv, mb_x, mb_y, mode, parts);
let bits = scratch.bit_len();
let ssd = self.mb_ssd(sy, su, sv, mb_x, mb_y);
self.load_mb(mb_x, mb_y, &snap);
(ssd, bits)
}
fn trial_intra(
&mut self,
sy: &[u8],
su: &[u8],
sv: &[u8],
mb_x: usize,
mb_y: usize,
is_p: bool,
) -> (i64, usize) {
let mut snap = enc_scratch::take_snap_b();
self.save_mb_into(mb_x, mb_y, &mut snap);
let mut scratch = enc_scratch::take_bits();
scratch.clear();
encode_mb(self, &mut scratch, mb_x, mb_y, sy, su, sv, is_p);
let bits = scratch.bit_len();
enc_scratch::put_bits(scratch);
let ssd = self.mb_ssd(sy, su, sv, mb_x, mb_y);
self.load_mb(mb_x, mb_y, &snap);
enc_scratch::put_snap_b(snap);
(ssd, bits)
}
#[inline]
fn best_part(
&self,
refs: &[crate::RefFrame],
sy: &[u8],
nb: &[MvNeighbor; 3],
num_refs: usize,
rx: usize,
ry: usize,
rw: usize,
rh: usize,
extra: &[(i32, i32)],
lme: f64,
) -> (i32, (i32, i32), i64) {
crate::signals::census::work(crate::signals::census::W_BEST_PART);
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMe);
let [a, b, c] = *nb;
let (mut br, mut bmv, mut bc) = (0i32, (0, 0), i64::MAX);
for r in 0..num_refs {
let rb = (lme * ref_bits(r, num_refs) as f64) as i64;
if rb >= bc {
break;
}
crate::signals::census::work(crate::signals::census::W_REF_SEARCH);
debug_assert!(extra.len() <= 3, "seed budget");
let mut sbuf = [(0i32, 0i32); 4];
sbuf[0] = predict_mv(a, b, c, r as i32);
let n = 1 + extra.len().min(3);
sbuf[1..n].copy_from_slice(&extra[..n - 1]);
let seeds = &sbuf[..n];
let (mv, cost) = self.motion_search(&refs[r], sy, rx, ry, rw, rh, seeds, lme, None);
let cost = cost + rb;
if cost < bc {
bc = cost;
br = r as i32;
bmv = mv;
}
}
(br, bmv, bc)
}
fn refine_part(
&self,
refs: &[crate::RefFrame],
sy: &[u8],
nb: &[MvNeighbor; 3],
num_refs: usize,
rx: usize,
ry: usize,
rw: usize,
rh: usize,
lme: f64,
r: i32,
mv: (i32, i32),
) -> ((i32, i32), i64) {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMe);
let [a, b, c] = *nb;
let rb = (lme * ref_bits(r as usize, num_refs) as f64) as i64;
let seeds = [predict_mv(a, b, c, r)];
let (m, cc) =
self.motion_search(&refs[r as usize], sy, rx, ry, rw, rh, &seeds, lme, Some(mv));
(m, cc + rb)
}
fn best_i16_sad(&self, sy: &[u8], mb_x: usize, mb_y: usize) -> i64 {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCost);
let (lx, ly) = (mb_x * 16, mb_y * 16);
let (avail_top, avail_left) = (mb_y > 0, mb_x > 0);
let mut top = [0u8; 16];
let mut left = [0u8; 16];
if avail_top {
let r = (ly - 1) * self.cw + lx;
top.copy_from_slice(&self.rec_y[r..r + 16]);
}
if avail_left {
let col = &self.rec_y[ly * self.cw + lx - 1..(ly + 15) * self.cw + lx];
for i in 0..16 {
left[i] = col[i * self.cw];
}
}
let corner = if avail_top && avail_left {
self.rec_y[(ly - 1) * self.cw + lx - 1]
} else {
0
};
let mut best = i64::MAX;
for mode in [
I16Mode::Dc,
I16Mode::Vertical,
I16Mode::Horizontal,
I16Mode::Plane,
] {
if !mode.available(avail_top, avail_left) {
continue;
}
let pred = i16_pred(
self, mode, avail_top, avail_left, &top, &left, corner, lx, ly,
);
best = best.min(sad_16x16(sy, self.cw, lx, ly, &pred));
}
best
}
fn best_i16_satd(&self, sy: &[u8], mb_x: usize, mb_y: usize) -> i64 {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCost);
let (lx, ly) = (mb_x * 16, mb_y * 16);
let (avail_top, avail_left) = (mb_y > 0, mb_x > 0);
let mut top = [0u8; 16];
let mut left = [0u8; 16];
if avail_top {
let r = (ly - 1) * self.cw + lx;
top.copy_from_slice(&self.rec_y[r..r + 16]);
}
if avail_left {
let col = &self.rec_y[ly * self.cw + lx - 1..(ly + 15) * self.cw + lx];
for i in 0..16 {
left[i] = col[i * self.cw];
}
}
let corner = if avail_top && avail_left {
self.rec_y[(ly - 1) * self.cw + lx - 1]
} else {
0
};
let mut best = i64::MAX;
for mode in [
I16Mode::Dc,
I16Mode::Vertical,
I16Mode::Horizontal,
I16Mode::Plane,
] {
if !mode.available(avail_top, avail_left) {
continue;
}
let pred = i16_pred(
self, mode, avail_top, avail_left, &top, &left, corner, lx, ly,
);
best = best.min(satd_16x16(sy, self.cw, lx, ly, &pred));
}
best
}
fn save_mb(&self, mb_x: usize, mb_y: usize) -> MbState {
let mut d = MbState::default();
self.save_mb_into(mb_x, mb_y, &mut d);
d
}
fn save_mb_into(&self, mb_x: usize, mb_y: usize, d: &mut MbState) {
let w4 = self.mb_w * 4;
let w2 = self.mb_w * 2;
macro_rules! reg4 {
($v:expr, $o:expr) => {{
$o.clear();
for dy in 0..4 {
for dx in 0..4 {
$o.push($v[(mb_y * 4 + dy) * w4 + mb_x * 4 + dx]);
}
}
}};
}
macro_rules! regn {
($v:expr, $o:expr, $n:expr, $ox:expr, $oy:expr, $stride:expr) => {{
$o.clear();
for dy in 0..$n {
for dx in 0..$n {
$o.push($v[($oy + dy) * $stride + $ox + dx]);
}
}
}};
}
regn!(self.rec_y, d.rec_y, 16, mb_x * 16, mb_y * 16, self.cw);
regn!(self.rec_u, d.rec_u, 8, mb_x * 8, mb_y * 8, self.ccw);
regn!(self.rec_v, d.rec_v, 8, mb_x * 8, mb_y * 8, self.ccw);
reg4!(self.nnz_y, d.nnz_y);
regn!(self.nnz_c[0], d.nnz_c[0], 2, mb_x * 2, mb_y * 2, w2);
regn!(self.nnz_c[1], d.nnz_c[1], 2, mb_x * 2, mb_y * 2, w2);
reg4!(self.mv_y, d.mv_y);
reg4!(self.inter_y, d.inter_y);
reg4!(self.ref_idx_y, d.ref_idx_y);
reg4!(self.coded_y, d.coded_y);
reg4!(self.modes_y, d.modes_y);
d.cur_qp = self.cur_qp;
}
fn load_mb(&mut self, mb_x: usize, mb_y: usize, s: &MbState) {
let w4 = self.mb_w * 4;
let w2 = self.mb_w * 2;
macro_rules! put4 {
($v:expr, $src:expr) => {
for dy in 0..4 {
let d = (mb_y * 4 + dy) * w4 + mb_x * 4;
$v[d..d + 4].copy_from_slice(&$src[dy * 4..dy * 4 + 4]);
}
};
}
macro_rules! putn {
($v:expr, $src:expr, $n:expr, $ox:expr, $oy:expr, $stride:expr) => {
for dy in 0..$n {
let d = ($oy + dy) * $stride + $ox;
$v[d..d + $n].copy_from_slice(&$src[dy * $n..dy * $n + $n]);
}
};
}
putn!(self.rec_y, s.rec_y, 16, mb_x * 16, mb_y * 16, self.cw);
putn!(self.rec_u, s.rec_u, 8, mb_x * 8, mb_y * 8, self.ccw);
putn!(self.rec_v, s.rec_v, 8, mb_x * 8, mb_y * 8, self.ccw);
put4!(self.nnz_y, s.nnz_y);
putn!(self.nnz_c[0], s.nnz_c[0], 2, mb_x * 2, mb_y * 2, w2);
putn!(self.nnz_c[1], s.nnz_c[1], 2, mb_x * 2, mb_y * 2, w2);
put4!(self.mv_y, s.mv_y);
put4!(self.inter_y, s.inter_y);
put4!(self.ref_idx_y, s.ref_idx_y);
put4!(self.coded_y, s.coded_y);
put4!(self.modes_y, s.modes_y);
self.cur_qp = s.cur_qp;
}
fn nnz_cache_load(&mut self, mb_x: usize, mb_y: usize) {
let w4 = self.mb_w * 4;
for lbx in 0..4 {
self.nnz_l_cache[1 + lbx] = if mb_y == 0 {
0x80
} else {
self.nnz_y[(mb_y * 4 - 1) * w4 + (mb_x * 4 + lbx)]
};
}
for lby in 0..4 {
self.nnz_l_cache[(lby + 1) * 5] = if mb_x == 0 {
0x80
} else {
self.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 - 1)]
};
}
}
#[inline]
fn nc_pred(&self, lbx: usize, lby: usize) -> i32 {
let left = self.nnz_l_cache[(lby + 1) * 5 + lbx] as i32; let top = self.nnz_l_cache[lby * 5 + (lbx + 1)] as i32; let r = left + top;
if r < 0x80 {
(r + 1) >> 1
} else {
r & 0x7f
}
}
#[inline]
fn nnz_cache_set(&mut self, lbx: usize, lby: usize, total: u8) {
self.nnz_l_cache[(lby + 1) * 5 + (lbx + 1)] = total;
}
fn chroma_cache_load(&mut self, mb_x: usize, mb_y: usize) {
let w2 = self.mb_w * 2;
for c in 0..2 {
for bx in 0..2 {
self.nnz_c_cache[c][1 + bx] = if mb_y == 0 {
0x80
} else {
self.nnz_c[c][(mb_y * 2 - 1) * w2 + (mb_x * 2 + bx)]
};
}
for by in 0..2 {
self.nnz_c_cache[c][(by + 1) * 3] = if mb_x == 0 {
0x80
} else {
self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 - 1)]
};
}
}
}
#[inline]
fn chroma_nc_pred(&self, c: usize, bx: usize, by: usize) -> i32 {
let left = self.nnz_c_cache[c][(by + 1) * 3 + bx] as i32;
let top = self.nnz_c_cache[c][by * 3 + (bx + 1)] as i32;
let r = left + top;
if r < 0x80 {
(r + 1) >> 1
} else {
r & 0x7f
}
}
#[inline]
fn chroma_nnz_cache_set(&mut self, c: usize, bx: usize, by: usize, total: u8) {
self.nnz_c_cache[c][(by + 1) * 3 + (bx + 1)] = total;
}
}
#[inline(never)]
fn derive_mb_bs_from(
fe: &FrameEncoder,
mb_x: usize,
mb_y: usize,
kind: rusty_h264_common::deblock::MbKind,
) -> rusty_h264_common::deblock::MbBs {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncBs);
let view = rusty_h264_common::deblock::BlockInfo {
inter: &fe.inter_y,
nnz: &fe.nnz_y,
mv: &fe.mv_y,
ref_id: &fe.ref_idx_y,
mv1: &[],
ref_id1: &[],
w4: fe.mb_w * 4,
t8x8: &[],
poc0: &[],
poc1: &[],
bs: &[],
kind: &[],
};
rusty_h264_common::deblock::derive_mb_kind(&view, mb_x, mb_y, kind)
}
pub(crate) fn encode_slice_data(
w: &mut BitWriter,
cfg: &EncoderConfig,
frame: &YuvPlanes<'_>,
qp: u8,
is_p: bool,
refs: &[crate::RefFrame],
qpo: &[i32],
aq_probe: Option<&YuvPlanes<'_>>,
wp: &[(i32, i32)],
) -> crate::RefFrame {
let _g_prep = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncPrep);
let mut fe = FrameEncoder::new(cfg);
fe.wp = wp.to_vec();
let precomp = rusty_h264_common::deblock::precomputed_bs_enabled();
let mut bs_grid =
vec![rusty_h264_common::deblock::MbBs::UNSET; if precomp { fe.mb_w * fe.mb_h } else { 0 }];
fe.qp = qp;
fe.qpc = chroma_qp(qp);
fe.cur_qp = qp;
if cfg.cabac_dz_div > 0 {
fe.idz = cfg.cabac_dz_div; } let (sy, su, sv) = coded_source(cfg, frame);
let probe_y: Option<Vec<u8>> = if refs.is_empty() {
aq_probe.map(|f| coded_source(cfg, f).0.into_owned())
} else {
None
};
let sig = FrameSignals::new(
&sy,
fe.cw,
fe.mb_w,
fe.mb_h,
refs.first().map(|r| &r.y[..]).or(probe_y.as_deref()),
);
apply_screen_t8_veto(&mut fe, &sig);
let lambda = 0.85 * fe.tune_lambda_scale * crate::fastmath::lambda_qp(qp);
let num_refs = refs.len();
if is_p && fe.me_wide && !refs.is_empty() && sig.gmc_residual() < fe.me_wide_coh {
fe.me_wide = false;
}
if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
let hr = sig.headroom();
if me_wide_hr_dbg() {
eprintln!("ME_HR qp{qp} headroom={hr:.2}");
}
if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
fe.me_wide = false;
}
}
if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
let (mg, dc) = sig.mgain_dc();
if me_sadt_dbg() {
eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
}
fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
if mv_smooth_mode() == 1 {
fe.mv_smooth = mg >= mv_smooth_t() && dc <= me_sad_dcmax();
}
let smg = split_mg();
if smg > 0.0 {
fe.do_splits = mg >= smg;
}
}
if is_p && fe.satd_q > 0.0 {
fe.satd_var_thresh = sig.var_percentile_thresh(fe.satd_q);
}
let mut aq_qp = aq_qp_map(&sig, qp, fe.aq_strength);
apply_mbtree_qpo(&mut aq_qp, qpo); signals::harvest(
&sig,
if is_p { 'P' } else { 'I' },
qp,
&signals::GateDecisions {
me_wide: fe.me_wide,
sadfp: fe.sadfp,
mv_smooth: fe.mv_smooth,
do_splits: fe.do_splits,
lme_scale: 1.0,
satd_thresh: fe.satd_var_thresh,
},
);
let mut mb_qpy = enc_scratch::take_qpy();
mb_qpy.clear();
mb_qpy.resize(fe.mb_w * fe.mb_h, qp);
let mut skip_run = 0u32;
if is_p && mv_cmp_on() {
MVCMP_FRAME.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}
let mut rdskip_snap = MbState::default();
let mut rdskip_free = 0usize;
let mut rdskip_seen = 0usize;
let mut rdskip_on = false;
let mut greedy_on = fe.greedy_min_free == 0; let rdskip_learn = (fe.mb_w * fe.mb_h / 8).max(64);
let rdskip_min_free = fe.rd_skip_min_free as usize;
drop(_g_prep);
let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
for mb_y in 0..fe.mb_h {
for mb_x in 0..fe.mb_w {
let mb_idx = mb_y * fe.mb_w + mb_x;
let mb_qp = aq_qp[mb_idx];
fe.qp = mb_qp;
fe.qpc = chroma_qp(mb_qp);
let mut inter: Option<InterChoice> = None;
let mut coded: Option<BitWriter> = None;
if is_p {
if num_refs > 0 {
let _g_skip =
rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncSkip);
let _g_smc =
rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Neighbors);
rdskip_seen += 1;
if rdskip_seen >= rdskip_learn {
rdskip_on = rdskip_free * 100 >= rdskip_seen * rdskip_min_free;
greedy_on = fe.greedy_min_free == 0
|| rdskip_free * 100 >= rdskip_seen * fe.greedy_min_free as usize;
}
let mv_skip = fe.skip_mv(mb_x, mb_y);
let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
drop(_g_smc);
let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
let skip_c = if luma_free || !fe.fast {
fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
} else {
[[0u8; 64]; 2]
};
let is_free =
luma_free && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &skip_c);
let skip_sad = if fe.fast {
0
} else {
let (lx, ly) = (mb_x * 16, mb_y * 16);
let mut s = 0u32;
for dy in 0..16 {
let src = &sy[(ly + dy) * fe.cw + lx..][..16];
let p = &skip_y[dy * 16..][..16];
s += src
.iter()
.zip(p)
.map(|(&a, &b)| a.abs_diff(b) as u32)
.sum::<u32>();
}
s
};
if is_free {
fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
if !fe.fast {
fe.mb_was_skip[mb_idx] = true;
fe.mb_skip_sad[mb_idx] = skip_sad;
}
mb_qpy[mb_idx] = fe.cur_qp; rdskip_free += 1;
if precomp {
bs_grid[mb_idx] = derive_mb_bs_from(
&fe,
mb_x,
mb_y,
rusty_h264_common::deblock::MbKind::Skip,
);
}
skip_run += 1;
continue;
}
drop(_g_skip);
let (lx, ly) = (mb_x * 16, mb_y * 16);
let nb = {
let _g = rusty_h264_common::prof::scope(
rusty_h264_common::prof::Stage::EncMvPred,
);
fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
};
let lme = rusty_h264_common::fmath::sqrt(lambda);
if fe.fast {
fe.mb_use_satd = fe.satd_q > 0.0
&& sig.mb_vars()[mb_y * fe.mb_w + mb_x] >= fe.satd_var_thresh;
let (r16, mv16, cost_inter) =
fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
let cost_intra = if fe.mb_use_satd {
fe.best_i16_satd(&sy, mb_x, mb_y)
} else {
fe.best_i16_sad(&sy, mb_x, mb_y)
} + (lme * fe.tune_intra_penalty) as i64;
inter = if cost_intra < cost_inter {
None } else {
Some((0, vec![(r16, mv16)]))
};
} else {
if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
fe.mb_was_skip[mb_idx] = true;
fe.mb_skip_sad[mb_idx] = skip_sad;
mb_qpy[mb_idx] = fe.cur_qp; if precomp {
bs_grid[mb_idx] = derive_mb_bs_from(
&fe,
mb_x,
mb_y,
rusty_h264_common::deblock::MbKind::Skip,
);
}
skip_run += 1;
continue;
}
let (r16, mv16, c16) =
fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
let mut best_c = c16;
let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
let split_t = split_t();
if fe.do_splits
&& c16 > split_gate
&& (split_t <= 0.0 || (c16 as f64) >= split_t * lme)
{
let (rt, mvt, ct) =
fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 8, &[mv16], lme);
let (rb, mvb, cb) = fe.best_part(
refs,
&sy,
&nb,
num_refs,
lx,
ly + 8,
16,
8,
&[mv16],
lme,
);
let (rl, mvl, cl) =
fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 8, 16, &[mv16], lme);
let (rr, mvr, cr) = fe.best_part(
refs,
&sy,
&nb,
num_refs,
lx + 8,
ly,
8,
16,
&[mv16],
lme,
);
if ct + cb < best_c {
best_c = ct + cb;
pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
}
if cl + cr < best_c {
best_c = cl + cr;
pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
}
if fe.sub8x8 {
let mut c8 = (lme * 4.0) as i64; let mut p8 = Vec::with_capacity(4);
for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
let (r, mv, c) = fe.best_part(
refs,
&sy,
&nb,
num_refs,
lx + qx,
ly + qy,
8,
8,
&[mv16],
lme,
);
c8 += c;
p8.push((r, mv));
}
if c8 < best_c {
best_c = c8;
pick = Some((3u8, p8));
}
}
}
if fe.sp_defer.get() {
if let Some((mode, parts)) = pick.as_mut() {
let regions: &[(usize, usize, usize, usize)] = match mode {
1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
3 => &[(0, 0, 8, 8), (8, 0, 8, 8), (0, 8, 8, 8), (8, 8, 8, 8)],
_ => &[(0, 0, 16, 16)],
};
let mut tot = if *mode == 3 { (lme * 4.0) as i64 } else { 0 };
for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
let (r, mv) = parts[i];
let (m2, c2) = fe.refine_part(
refs,
&sy,
&nb,
num_refs,
lx + qx,
ly + qy,
pw,
ph,
lme,
r,
mv,
);
parts[i] = (r, m2);
tot += c2;
}
best_c = tot;
}
}
if split_harvest::enabled() {
let won = match pick.as_ref().map(|p| p.0) {
Some(0) | None => 0u8,
Some(m) => m,
};
split_harvest::record(c16, best_c, lme, split_gate, won);
}
let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
+ (lme * fe.tune_intra_penalty) as i64;
inter = if c_intra < best_c { None } else { pick };
fe.mb_was_skip[mb_idx] = false;
fe.mb_skip_sad[mb_idx] = skip_sad;
}
if fe.rd_skip && rdskip_on && inter.is_some() {
let skip_cp = fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip);
let ssd_s = fe.pred_ssd(&sy, &su, &sv, mb_x, mb_y, &skip_y, &skip_cp);
debug_assert_eq!(
ssd_s,
{
let snap = fe.save_mb(mb_x, mb_y);
fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
let v = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y);
fe.load_mb(mb_x, mb_y, &snap);
v
},
"skip prediction SSD must equal the committed-skip reconstruction SSD"
);
let j_skip = ssd_s as f64 + lambda;
let take_skip = if fe.rd_skip_fast_t > 0.0
&& (ssd_s as f64) <= lambda * fe.rd_skip_fast_t
{
true
} else {
fe.save_mb_into(mb_x, mb_y, &mut rdskip_snap);
let mut scratch = enc_scratch::take_bits();
scratch.clear();
if let Some((m, p)) = inter.as_ref() {
fe.encode_inter_mb(
&mut scratch,
refs,
&sy,
&su,
&sv,
mb_x,
mb_y,
*m,
p,
);
}
let bits_c = scratch.bit_len();
let ssd_c = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y);
let won = j_skip <= ssd_c as f64 + lambda * bits_c as f64;
if won {
fe.load_mb(mb_x, mb_y, &rdskip_snap); enc_scratch::put_bits(scratch); true
} else {
coded = Some(scratch); false
}
};
if take_skip {
fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
if !fe.fast {
fe.mb_was_skip[mb_idx] = true;
fe.mb_skip_sad[mb_idx] = skip_sad;
}
mb_qpy[mb_idx] = fe.cur_qp;
if precomp {
bs_grid[mb_idx] = derive_mb_bs_from(
&fe,
mb_x,
mb_y,
rusty_h264_common::deblock::MbKind::Skip,
);
}
skip_run += 1;
continue;
}
}
}
w.write_ue(skip_run); skip_run = 0;
}
#[cfg(feature = "std")]
if mv_force_on() && is_p && inter.is_some() {
let fi = MVCMP_FRAME.load(core::sync::atomic::Ordering::Relaxed);
let ext = EXT_MV.lock().unwrap();
if let Some(field) = ext.get(fi) {
let w4 = fe.mb_w * 4;
let b0 = (mb_y * 4) * w4 + mb_x * 4;
let uniform =
(0..4).all(|r| (0..4).all(|c| field.get(b0 + r * w4 + c) == field.get(b0)));
if uniform {
if let Some(&emv) = field.get(b0) {
inter = Some((0, vec![(0, emv)]));
MVCMP[6].fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}
}
}
}
#[cfg(feature = "std")]
if mv_cmp_on() && is_p {
if let Some((mode, parts)) = inter.as_ref() {
let fi = MVCMP_FRAME.load(core::sync::atomic::Ordering::Relaxed);
let ext = EXT_MV.lock().unwrap();
if let Some(field) = ext.get(fi) {
let bidx = (mb_y * 4) * (fe.mb_w * 4) + mb_x * 4;
if let Some(&emv) = field.get(bidx) {
let (mode, parts) = (*mode, parts.clone());
drop(ext);
let (so, bo) =
fe.trial_inter(refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts);
let (se, be) =
fe.trial_inter(refs, &sy, &su, &sv, mb_x, mb_y, 0, &[(0, emv)]);
let jo = so as f64 + lambda * bo as f64;
let je = se as f64 + lambda * be as f64;
use core::sync::atomic::Ordering::Relaxed;
MVCMP[0].fetch_add(1, Relaxed);
MVCMP[1].fetch_add(bo as u64, Relaxed);
MVCMP[2].fetch_add(be as u64, Relaxed);
MVCMP[3].fetch_add(so.max(0) as u64, Relaxed);
MVCMP[4].fetch_add(se.max(0) as u64, Relaxed);
MVCMP[5].fetch_add((je < jo) as u64, Relaxed);
MVCMP[6].fetch_add((parts[0].1 != emv) as u64, Relaxed);
}
}
}
}
let mb_kind = match &inter {
Some((_, parts)) if parts.len() == 1 => {
rusty_h264_common::deblock::MbKind::InterUniform
}
Some(_) => rusty_h264_common::deblock::MbKind::Inter,
None => rusty_h264_common::deblock::MbKind::Intra,
};
match inter {
Some((mode, parts)) => match coded {
Some(sc) => {
w.append(&sc);
enc_scratch::put_bits(sc);
}
None => fe.encode_inter_mb(w, refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts),
},
None => encode_mb(&mut fe, w, mb_x, mb_y, &sy, &su, &sv, is_p),
}
mb_qpy[mb_idx] = fe.cur_qp; if precomp {
bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, mb_kind);
}
}
}
debug_assert!(
!precomp
|| bs_grid
.iter()
.all(|b| *b != rusty_h264_common::deblock::MbBs::UNSET),
"a macroblock loop exit failed to store its boundary strengths"
);
if is_p && skip_run > 0 {
w.write_ue(skip_run); }
w.rbsp_trailing_bits();
drop(_g_loop);
let _g_fin = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFinal);
let info = rusty_h264_common::deblock::BlockInfo {
inter: &fe.inter_y,
nnz: &fe.nnz_y,
mv: &fe.mv_y,
ref_id: &fe.ref_idx_y,
mv1: &[],
ref_id1: &[],
w4: fe.mb_w * 4,
t8x8: &[],
bs: &bs_grid,
poc0: &[],
poc1: &[],
kind: &[],
};
drop(_g_fin);
rusty_h264_common::deblock::filter_frame(
&mut fe.rec_y,
&mut fe.rec_u,
&mut fe.rec_v,
fe.mb_w,
fe.mb_h,
&mb_qpy,
0, 0, 0, &info,
);
enc_scratch::put_qpy(mb_qpy);
let w4 = fe.mb_w * 4;
crate::RefFrame {
y: fe.rec_y,
u: fe.rec_u,
v: fe.rec_v,
poc: 0, frame_num: 0, mv: fe.mv_y,
ref_idx: fe.ref_idx_y,
mv1: Vec::new(),
ref_idx1: Vec::new(),
w4,
hpel: crate::HpelOnce::new(),
}
}
pub(crate) fn encode_slice_data_b(
w: &mut BitWriter,
cfg: &EncoderConfig,
frame: &YuvPlanes<'_>,
qp: u8,
poc: i32,
l0: &crate::RefFrame,
l1: &crate::RefFrame,
_qpo: &[i32],
) {
let mut fe = FrameEncoder::new(cfg);
fe.qp = qp;
fe.qpc = chroma_qp(qp);
fe.cur_qp = qp;
if cfg.cabac_dz_div > 0 {
fe.idz = cfg.cabac_dz_div; } fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
let (sy, su, sv) = coded_source(cfg, frame);
let sig = FrameSignals::new(&sy, fe.cw, fe.mb_w, fe.mb_h, Some(&l0.y[..]));
apply_screen_t8_veto(&mut fe, &sig);
let lambda = 0.85 * fe.tune_lambda_scale * crate::fastmath::lambda_qp(qp);
let lme = rusty_h264_common::fmath::sqrt(lambda);
let refs = core::slice::from_ref(l0); if fe.satd_q > 0.0 {
fe.satd_var_thresh = sig.var_percentile_thresh(fe.satd_q);
}
signals::harvest(
&sig,
'b',
qp,
&signals::GateDecisions {
satd_thresh: fe.satd_var_thresh,
..Default::default()
},
);
let mut skip_run = 0u32; for mb_y in 0..fe.mb_h {
for mb_x in 0..fe.mb_w {
let (lx, ly) = (mb_x * 16, mb_y * 16);
let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
fe.mb_use_satd =
fe.satd_q > 0.0 && sig.mb_vars()[mb_y * fe.mb_w + mb_x] >= fe.satd_var_thresh;
let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
if fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
&& fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc)
{
fe.commit_direct_motion(mb_x, mb_y, &dmotion);
fe.commit_pred_pixels(mb_x, mb_y, &dp, &dc);
skip_run += 1;
continue;
}
let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
let r_bi = mvd_bits(mv0.0 - pmv0.0)
+ mvd_bits(mv0.1 - pmv0.1)
+ mvd_bits(mv1.0 - pmv1.0)
+ mvd_bits(mv1.1 - pmv1.1);
let j_bi = d_bi + (lme * r_bi as f64) as i64;
let (mut dir, mut best) = (0u8, d_direct);
if j0 < best {
dir = 1;
best = j0;
}
if j1 < best {
dir = 2;
best = j1;
}
if j_bi < best {
dir = 3;
best = j_bi;
}
let _ = best; w.write_ue(skip_run); skip_run = 0;
let bspec = BInter {
dir,
l1,
mv0,
mv1,
mvmode: 0,
parts2: [(0, (0, 0), (0, 0)); 2],
};
fe.encode_inter_mb_v1_b(w, refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
}
}
if skip_run > 0 {
w.write_ue(skip_run); }
w.rbsp_trailing_bits();
}
#[inline(always)]
fn mvd_bits(d: i32) -> u32 {
let codenum = if d > 0 {
(2 * d - 1) as u32
} else {
(-2 * d) as u32
};
1 + 2 * (31 - (codenum + 1).leading_zeros())
}
fn write_ref_idx(w: &mut BitWriter, refi: i32, num_refs: usize) {
if num_refs == 2 {
w.write_bit(refi == 0); } else {
w.write_ue(refi as u32);
}
}
fn ref_bits(r: usize, num_refs: usize) -> u32 {
if num_refs <= 1 {
0
} else if num_refs == 2 {
1
} else {
let mut n = r as u32 + 1;
let mut len = 1;
while n > 1 {
n >>= 1;
len += 2;
}
len
}
}
fn residual(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[i32; 16]) -> [i32; 16] {
let mut r = [0i32; 16];
for dy in 0..4 {
let row = &src[(y0 + dy) * stride + x0..][..4];
for dx in 0..4 {
r[dy * 4 + dx] = row[dx] as i32 - pred[dy * 4 + dx];
}
}
r
}
#[cfg_attr(not(feature = "std"), allow(dead_code))]
#[allow(dead_code)]
fn store(plane: &mut [u8], stride: usize, x0: usize, y0: usize, s: &[u8; 16]) {
for dy in 0..4 {
let d = (y0 + dy) * stride + x0;
plane[d..d + 4].copy_from_slice(&s[dy * 4..dy * 4 + 4]);
}
}
#[cfg(not(accel))]
fn pred_block(pred: &[u8; 256], bx: usize, by: usize) -> [i32; 16] {
let mut p = [0i32; 16];
for dy in 0..4 {
let row = &pred[(by * 4 + dy) * 16 + bx * 4..][..4];
for dx in 0..4 {
p[dy * 4 + dx] = row[dx] as i32;
}
}
p
}
#[inline]
pub(crate) fn satd_px(src: &[u8], ss: usize, pred: &[u8], ps: usize, w: usize, h: usize) -> i64 {
#[cfg(accel)]
{
let asm = match (w, h) {
(16, 16) => Some(rusty_h264_accel::satd_16x16(src, ss, pred, ps)),
(16, 8) => Some(rusty_h264_accel::satd_16x8(src, ss, pred, ps)),
(8, 16) => Some(rusty_h264_accel::satd_8x16(src, ss, pred, ps)),
(8, 8) => Some(rusty_h264_accel::satd_8x8(src, ss, pred, ps)),
(4, 4) => Some(rusty_h264_accel::satd_4x4(src, ss, pred, ps)),
(8, 4) => Some(
rusty_h264_accel::satd_4x4(src, ss, pred, ps)
+ rusty_h264_accel::satd_4x4(&src[4..], ss, &pred[4..], ps),
),
(4, 8) => Some(
rusty_h264_accel::satd_4x4(src, ss, pred, ps)
+ rusty_h264_accel::satd_4x4(&src[4 * ss..], ss, &pred[4 * ps..], ps),
),
_ => None,
};
if let Some(v) = asm {
return 2 * v as i64;
}
}
let (nbx, nby) = (w / 4, h / 4);
let mut blocks = [[0i32; 16]; 16];
let mut bi = 0;
for by in 0..nby {
for bx in 0..nbx {
let blk = &mut blocks[bi];
for dy in 0..4 {
for dx in 0..4 {
blk[dy * 4 + dx] = src[(by * 4 + dy) * ss + bx * 4 + dx] as i32
- pred[(by * 4 + dy) * ps + bx * 4 + dx] as i32;
}
}
bi += 1;
}
}
satd_4x4_sum(&blocks[..nbx * nby])
}
#[inline]
fn sad_strided(src: &[u8], ss: usize, r: &[u8], rs: usize, w: usize, h: usize) -> i64 {
#[cfg(accel)]
{
match (w, h) {
(16, 16) => return rusty_h264_accel::sad_16x16(src, ss, r, rs) as i64,
(16, 8) => return rusty_h264_accel::sad_16x8(src, ss, r, rs) as i64,
(8, 16) => return rusty_h264_accel::sad_8x16(src, ss, r, rs) as i64,
_ => {}
}
}
let mut sad = 0u32;
for dy in 0..h {
let a = &src[dy * ss..][..w];
let b = &r[dy * rs..][..w];
sad += a
.iter()
.zip(b)
.map(|(&x, &y)| x.abs_diff(y) as u32)
.sum::<u32>();
}
sad as i64
}
#[inline]
fn sad_avg_strided(
src: &[u8],
ss: usize,
a: &[u8],
b: &[u8],
rs: usize,
w: usize,
h: usize,
) -> i64 {
let mut sad = 0u32;
for dy in 0..h {
let s = &src[dy * ss..][..w];
let pa = &a[dy * rs..][..w];
let pb = &b[dy * rs..][..w];
for i in 0..w {
let p = ((pa[i] as u16 + pb[i] as u16 + 1) >> 1) as u8;
sad += s[i].abs_diff(p) as u32;
}
}
sad as i64
}
fn satd_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
satd_px(&src[ly * stride + lx..], stride, pred, 16, 16, 16)
}
fn sad_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
let mut sad = 0u32;
for dy in 0..16 {
let s = &src[(ly + dy) * stride + lx..][..16];
let p = &pred[dy * 16..][..16];
sad += s
.iter()
.zip(p)
.map(|(&a, &b)| a.abs_diff(b) as u32)
.sum::<u32>();
}
sad as i64
}
fn satd_8x8(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[u8; 64]) -> i64 {
satd_px(&src[y0 * stride + x0..], stride, pred, 8, 8, 8)
}
fn satd_4x4(src: &[u8], stride: usize, px: usize, py: usize, pred: &[u8; 16]) -> i64 {
satd_px(&src[py * stride + px..], stride, pred, 4, 4, 4)
}
fn i4_mode_available(mode: u8, top: bool, left: bool) -> bool {
match mode {
0 | 3 | 7 => top, 1 | 8 => left, 2 => true, _ => top && left, }
}
struct I4Plan {
modes: [u8; 16], q: [[i32; 16]; 16], cbp_luma: u32, nonzero: i64, }
struct MbPlan {
use_i4: bool,
i16_mode: I16Mode,
i16_cbp15: bool,
i16_dc_levels: [i32; 16],
i16_q: [[i32; 16]; 16],
i4: Option<I4Plan>,
i8: Option<I8Plan>,
chroma_mode: u8,
cbp_chroma: u32,
c_dc_levels: [[i32; 4]; 2],
c_q_blocks: [[[i32; 16]; 4]; 2],
}
struct InterPlan {
mvds: [(i32, i32); 16],
plan_refs: [i32; 4], sub_types: [u8; 4],
n_mvd: usize,
cbp: u32,
q_blocks: [[i32; 16]; 16], c_dc_levels: [[i32; 4]; 2],
c_q: [[[i32; 16]; 4]; 2],
t8x8: bool, q8: [[i32; 64]; 4], }
fn gather_i4(
fe: &FrameEncoder,
px: usize,
py: usize,
avail_top: bool,
avail_left: bool,
bx: usize,
by: usize,
) -> ([u8; 8], [u8; 4], u8) {
let (cw, w4) = (fe.cw, fe.mb_w * 4);
let mut top = [0u8; 8];
let mut left = [0u8; 4];
let mut corner = 0;
if avail_top {
let r = (py - 1) * cw + px;
top[..4].copy_from_slice(&fe.rec_y[r..r + 4]);
let tr_avail = bx + 1 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 1)];
if tr_avail {
top[4..8].copy_from_slice(&fe.rec_y[r + 4..r + 8]);
} else {
let t3 = top[3];
top[4..8].fill(t3);
}
}
if avail_left {
let col = &fe.rec_y[py * cw + px - 1..(py + 3) * cw + px];
for i in 0..4 {
left[i] = col[i * cw];
}
}
if avail_top && avail_left {
corner = fe.rec_y[(py - 1) * cw + px - 1];
}
(top, left, corner)
}
#[inline]
fn modes_at(
fe: &FrameEncoder,
modes: &[u8; 16],
lbx: usize,
lby: usize,
dx: isize,
dy: isize,
bx: usize,
by: usize,
) -> u8 {
let (nx, ny) = (lbx as isize + dx, lby as isize + dy);
if (0..4).contains(&nx) && (0..4).contains(&ny) {
modes[ny as usize * 4 + nx as usize]
} else {
let w4 = fe.mb_w * 4;
let gx = (bx as isize + dx) as usize;
let gy = (by as isize + dy) as usize;
fe.modes_y[gy * w4 + gx]
}
}
fn plan_i4x4(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I4Plan {
let w4 = fe.mb_w * 4;
let mut modes = [2u8; 16];
let mut q = [[0i32; 16]; 16];
let mut cbp_luma = 0u32;
let mut nonzero = 0i64;
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
let (px, py) = (bx * 4, by * 4);
let avail_top = by > 0;
let avail_left = bx > 0;
let (top, left, corner) = gather_i4(fe, px, py, avail_top, avail_left, bx, by);
let mut best_m = 2u8;
let mut best_cost = i64::MAX;
let mut best_pred = [0u8; 16];
if fe.fast && fast_intra_enabled() {
let lm = if bx > 0 {
modes_at(fe, &modes, lbx, lby, -1, 0, bx, by)
} else {
2
};
let tm = if by > 0 {
modes_at(fe, &modes, lbx, lby, 0, -1, bx, by)
} else {
2
};
let mpm = lm.min(tm);
let mut cands = [mpm, 2u8, 0, 1];
for i in 1..4 {
for j in 0..i {
if cands[i] == cands[j] {
cands[i] = 255;
}
}
}
for &m in cands.iter() {
if m == 255 || !i4_mode_available(m, avail_top, avail_left) {
continue;
}
let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
let cost = satd_4x4(sy, fe.cw, px, py, &pred);
if cost < best_cost {
best_cost = cost;
best_m = m;
best_pred = pred;
}
}
} else {
for m in 0..9u8 {
if !i4_mode_available(m, avail_top, avail_left) {
continue;
}
let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
let cost = satd_4x4(sy, fe.cw, px, py, &pred);
if cost < best_cost {
best_cost = cost;
best_m = m;
best_pred = pred;
}
}
}
let pred = best_pred;
let mut predb = [0i32; 16];
for i in 0..16 {
predb[i] = pred[i] as i32;
}
let res = residual(sy, fe.cw, px, py, &predb);
let qb = rdoq(&forward_core(&res), qp, fe.idz, fe.rdoq_strength, 0); reconstruct_4x4_into(
&dequantize(&qb, qp),
&pred,
0,
4,
&mut fe.rec_y,
py * fe.cw + px,
fe.cw,
);
fe.coded_y[by * w4 + bx] = true;
let nz = qb.iter().filter(|&&v| v != 0).count();
if nz > 0 {
cbp_luma |= 1 << ((lby / 2) * 2 + (lbx / 2));
}
nonzero += nz as i64;
modes[lby * 4 + lbx] = best_m;
q[lby * 4 + lbx] = qb;
}
I4Plan {
modes,
q,
cbp_luma,
nonzero,
}
}
struct I8Plan {
modes: [u8; 4], q: [[i32; 64]; 4], cbp_luma: u32, nonzero: i64, }
const ZIGZAG_8X8: [usize; 64] = [
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20,
13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
];
#[inline]
fn scan_8x8_fwd(raster: &[i32; 64]) -> [i32; 64] {
core::array::from_fn(|i| raster[ZIGZAG_8X8[i]])
}
fn gather_i8_enc(
fe: &FrameEncoder,
px: usize,
py: usize,
avail_top: bool,
avail_left: bool,
bx: usize,
by: usize,
) -> ([u8; 16], [u8; 8], u8, bool) {
let (cw, w4) = (fe.cw, fe.mb_w * 4);
let mut top = [0u8; 16];
let mut left = [0u8; 8];
let mut corner = 0;
if avail_top {
let r = (py - 1) * cw + px;
top[..8].copy_from_slice(&fe.rec_y[r..r + 8]);
let tr_avail = bx + 2 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 2)];
if tr_avail {
top[8..16].copy_from_slice(&fe.rec_y[r + 8..r + 16]);
} else {
let t7 = top[7];
top[8..16].fill(t7);
}
}
if avail_left {
let col = &fe.rec_y[py * cw + px - 1..(py + 7) * cw + px];
for i in 0..8 {
left[i] = col[i * cw];
}
}
let avail_corner = avail_top && avail_left;
if avail_corner {
corner = fe.rec_y[(py - 1) * cw + px - 1];
}
(top, left, corner, avail_corner)
}
fn plan_i8x8(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I8Plan {
let w4 = fe.mb_w * 4;
let mut modes = [2u8; 4];
let mut q = [[0i32; 64]; 4];
let mut cbp_luma = 0u32;
let mut nonzero = 0i64;
let weight = [16i32; 64];
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2); let avail_top = b8y > 0 || mb_y > 0;
let avail_left = b8x > 0 || mb_x > 0;
let (top, left, corner, avail_corner) =
gather_i8_enc(fe, px, py, avail_top, avail_left, bx, by);
let predicted = predict_i4_mode(fe, bx, by);
let mut best_m = 2u8;
let mut best_cost = i64::MAX;
let mut best_pred = [0u8; 64];
for m in 0..9u8 {
if !i4_mode_available(m, avail_top, avail_left) {
continue;
}
let pred = intra8x8_pred(m, avail_top, avail_left, avail_corner, &top, &left, corner);
let mut cost = satd_8x8(sy, fe.cw, px, py, &pred);
if m != predicted {
cost += 4 * fe.qp as i64; }
if cost < best_cost {
best_cost = cost;
best_m = m;
best_pred = pred;
}
}
modes[b8] = best_m;
let pred = best_pred;
let mut res = [0i32; 64];
for dy in 0..8 {
let row = &sy[(py + dy) * fe.cw + px..][..8];
for dx in 0..8 {
res[dy * 8 + dx] = row[dx] as i32 - pred[dy * 8 + dx] as i32;
}
}
let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, fe.idz);
let nz = levels.iter().filter(|&&v| v != 0).count();
if nz > 0 {
cbp_luma |= 1 << b8;
}
nonzero += nz as i64;
q[b8] = levels;
let res_r = inverse_quant_8x8(&levels, qp, &weight);
let predb: [i32; 64] = core::array::from_fn(|i| pred[i] as i32);
let recon = add_residual_8x8(&res_r, &predb);
for dy in 0..8 {
let d = (py + dy) * fe.cw + px;
fe.rec_y[d..d + 8].copy_from_slice(&recon[dy * 8..dy * 8 + 8]);
}
for sry in 0..2 {
let d = (by + sry) * w4 + bx;
fe.modes_y[d..d + 2].fill(best_m);
fe.coded_y[d..d + 2].fill(true);
}
}
I8Plan {
modes,
q,
cbp_luma,
nonzero,
}
}
fn plan_inter8_luma(
sy: &[u8],
cw: usize,
mb_x: usize,
mb_y: usize,
pred_y: &[u8; 256],
qp: u8,
) -> ([[i32; 64]; 4], u32, f64, [u8; 256], i64) {
let weight = [16i32; 64];
let mut q8 = [[0i32; 64]; 4];
let mut cbp = 0u32;
let mut rate = 0f64;
let mut rec = [0u8; 256];
let mut ssd = 0i64;
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let mut res = [0i32; 64];
for dy in 0..8 {
let row = &sy[(mb_y * 16 + b8y * 8 + dy) * cw + mb_x * 16 + b8x * 8..][..8];
for dx in 0..8 {
let p = pred_y[(b8y * 8 + dy) * 16 + (b8x * 8 + dx)] as i32;
res[dy * 8 + dx] = row[dx] as i32 - p;
}
}
let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, 6);
let mut nz = false;
for &l in &levels {
if l != 0 {
nz = true;
rate += rdoq_rate((l as i64).abs());
}
}
if nz {
cbp |= 1 << b8;
}
q8[b8] = levels;
let res_r = inverse_quant_8x8(&levels, qp, &weight);
let predb: [i32; 64] =
core::array::from_fn(|i| pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32);
let recon = add_residual_8x8(&res_r, &predb);
for dy in 0..8 {
let row = &sy[(mb_y * 16 + b8y * 8 + dy) * cw + mb_x * 16 + b8x * 8..][..8];
for dx in 0..8 {
let ri = (b8y * 8 + dy) * 16 + (b8x * 8 + dx);
rec[ri] = recon[dy * 8 + dx];
let d = recon[dy * 8 + dx] as i64 - row[dx] as i64;
ssd += d * d;
}
}
}
(q8, cbp, rate, rec, ssd)
}
#[inline]
fn i16_pred(
fe: &FrameEncoder,
mode: I16Mode,
avail_top: bool,
avail_left: bool,
top: &[u8; 16],
left: &[u8; 16],
corner: u8,
lx: usize,
ly: usize,
) -> [u8; 256] {
#[cfg(accel)]
if avail_top && avail_left {
let mode_n = match mode {
I16Mode::Vertical => 0,
I16Mode::Horizontal => 1,
I16Mode::Dc => 2,
I16Mode::Plane => 3,
};
let mut p = AlignedMb([0; 256]);
rusty_h264_accel::i16x16_luma_pred(mode_n, &mut p.0, &fe.rec_y[..], ly * fe.cw + lx, fe.cw);
return p.0;
}
let _ = (fe, lx, ly);
luma16x16_pred(mode, avail_top, avail_left, top, left, corner)
}
#[inline]
fn chroma_pred(
fe: &FrameEncoder,
mode: u8,
avail_top: bool,
avail_left: bool,
c: usize,
top: &[u8; 8],
left: &[u8; 8],
corner: u8,
cx: usize,
cy: usize,
) -> [u8; 64] {
#[cfg(accel)]
if avail_top && avail_left && (mode == 2 || mode == 3) {
let plane = if c == 0 { &fe.rec_u } else { &fe.rec_v };
#[repr(align(16))]
struct A64([u8; 64]);
let mut p = A64([0; 64]);
rusty_h264_accel::chroma8x8_pred(mode, &mut p.0, &plane[..], cy * fe.ccw + cx, fe.ccw);
return p.0;
}
let _ = (fe, c, cx, cy);
chroma8x8_pred(mode, avail_top, avail_left, top, left, corner)
}
fn predict_i4_mode(fe: &FrameEncoder, bx: usize, by: usize) -> u8 {
if bx == 0 || by == 0 {
return 2;
}
let w4 = fe.mb_w * 4;
fe.modes_y[by * w4 + (bx - 1)].min(fe.modes_y[(by - 1) * w4 + bx])
}
const RDOQ_ZZ: [usize; 16] = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
#[inline]
fn rdoq_rate(level: i64) -> f64 {
const LUT: [f64; 16] = [
1.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 16.0,
];
LUT[(level.min(15)) as usize]
}
fn rdoq_qstep(qp: u8) -> &'static [f64; 8] {
static T: rusty_h264_common::once::OnceLock<[[f64; 8]; 52]> =
rusty_h264_common::once::OnceLock::new();
&T.get_or_init(|| {
core::array::from_fn(|q| {
core::array::from_fn(|k| {
65536.0 / rusty_h264_common::transform::QUANT_MF_OH[q][k] as f64
})
})
})[qp as usize]
}
fn rdoq(coeffs: &[i32; 16], qp: u8, dz_div: i64, strength: f64, first: usize) -> [i32; 16] {
let mut q = quantize(coeffs, qp, dz_div);
if strength <= 0.0 {
return q;
}
let lambda = strength * crate::fastmath::lambda_qp(qp);
const POS: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7];
let qstep = rdoq_qstep(qp);
let dist = |p: usize, level: i64| -> f64 {
let e = coeffs[p].unsigned_abs() as f64 - level as f64 * qstep[POS[p]];
e * e
};
for i in first..16 {
let p = RDOQ_ZZ[i];
let m = q[p].unsigned_abs() as i64;
if m == 0 {
continue;
}
let j_keep = dist(p, m) + lambda * rdoq_rate(m);
let j_down = dist(p, m - 1) + lambda * rdoq_rate(m - 1);
if j_down < j_keep {
let nl = (m - 1) as i32;
q[p] = if q[p] < 0 { -nl } else { nl };
}
}
while let Some(li) = (first..16).rev().find(|&i| q[RDOQ_ZZ[i]] != 0) {
let p = RDOQ_ZZ[li];
let m = q[p].unsigned_abs() as i64;
let prev = (first..li).rev().find(|&i| q[RDOQ_ZZ[i]] != 0);
let base = prev.map_or(first, |j| j + 1);
let bits = rdoq_rate(m) + 1.0 + (li - base) as f64; let d_add = dist(p, 0) - dist(p, m);
if d_add < lambda * bits {
q[p] = 0;
} else {
break;
}
}
q
}
fn plan_mb(
fe: &mut FrameEncoder,
mb_x: usize,
mb_y: usize,
sy: &[u8],
su: &[u8],
sv: &[u8],
) -> MbPlan {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCode);
let qp = fe.qp;
let qpc = fe.qpc;
let lambda = 0.85 * fe.tune_lambda_scale * crate::fastmath::lambda_qp(qp);
let (lx, ly) = (mb_x * 16, mb_y * 16);
let avail_top = mb_y > 0;
let avail_left = mb_x > 0;
let mut top = [0u8; 16];
let mut left = [0u8; 16];
if avail_top {
let r = (ly - 1) * fe.cw + lx;
top.copy_from_slice(&fe.rec_y[r..r + 16]);
}
if avail_left {
let col = &fe.rec_y[ly * fe.cw + lx - 1..(ly + 15) * fe.cw + lx];
for i in 0..16 {
left[i] = col[i * fe.cw];
}
}
let corner = if avail_top && avail_left {
fe.rec_y[(ly - 1) * fe.cw + lx - 1]
} else {
0
};
let w4 = fe.mb_w * 4;
let mut i16_mode = I16Mode::Dc;
let mut best_pred = i16_pred(
fe,
I16Mode::Dc,
avail_top,
avail_left,
&top,
&left,
corner,
lx,
ly,
);
let mut best_cost = satd_16x16(sy, fe.cw, lx, ly, &best_pred);
for mode in [I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
if !mode.available(avail_top, avail_left) {
continue;
}
let pred = i16_pred(fe, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
let cost = satd_16x16(sy, fe.cw, lx, ly, &pred);
if cost < best_cost {
best_cost = cost;
i16_mode = mode;
best_pred = pred;
}
}
let mut dc4x4 = [0i32; 16];
let mut i16_q = [[0i32; 16]; 16];
#[cfg(accel)]
let (i16_dc_levels, _i16_recon_dc, recon16) = {
#[repr(align(16))]
struct A([i16; 256]);
let mut dct = A([0i16; 256]);
let base = ly * fe.cw + lx;
for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)]
.iter()
.enumerate()
{
rusty_h264_accel::dct_four_t4(
&mut dct.0[qi * 64..qi * 64 + 64],
&sy[base + qy * fe.cw + qx..],
fe.cw,
&best_pred[qy * 16 + qx..],
16,
);
}
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
dc4x4[lby * 4 + lbx] = dct.0[blk * 16] as i32;
}
if fe.rdoq_strength > 0.0 {
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
let coeffs: [i32; 16] = core::array::from_fn(|i| dct.0[blk * 16 + i] as i32);
let mut q = rdoq(&coeffs, qp, fe.idz, fe.rdoq_strength, 1);
q[0] = 0;
i16_q[lby * 4 + lbx] = q;
}
} else {
let ff = rusty_h264_common::transform::quant_dz_ff(qp, fe.idz);
let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
for qi in 0..4 {
rusty_h264_accel::quant_four_4x4(&mut dct.0[qi * 64..qi * 64 + 64], &ff, mf);
}
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
let q = &mut i16_q[lby * 4 + lbx];
q[0] = 0;
for i in 1..16 {
q[i] = dct.0[blk * 16 + i] as i32;
}
}
}
let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
let mut recon16 = [0u8; 256];
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
let mut deq = dequantize(&i16_q[lby * 4 + lbx], qp);
deq[0] = i16_recon_dc[lby * 4 + lbx];
for i in 0..16 {
dct.0[blk * 16 + i] = deq[i] as i16;
}
}
for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)]
.iter()
.enumerate()
{
rusty_h264_accel::idct_four_t4_rec(
&mut recon16[qy * 16 + qx..],
16,
&best_pred[qy * 16 + qx..],
16,
&dct.0[qi * 64..qi * 64 + 64],
);
}
(i16_dc_levels, i16_recon_dc, recon16)
};
#[cfg(not(accel))]
let (i16_dc_levels, _i16_recon_dc, recon16) = {
let mut res_blocks = [[0i32; 16]; 16];
for by in 0..4 {
for bx in 0..4 {
let predb = pred_block(&best_pred, bx, by);
res_blocks[by * 4 + bx] = residual(sy, fe.cw, lx + bx * 4, ly + by * 4, &predb);
}
}
let mut coeffs = [[0i32; 16]; 16];
forward_dct_blocks(&res_blocks, &mut coeffs);
for i in 0..16 {
dc4x4[i] = coeffs[i][0];
let mut q = rdoq(&coeffs[i], qp, fe.idz, fe.rdoq_strength, 1);
q[0] = 0;
i16_q[i] = q;
}
let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
let mut recon16 = [0u8; 256];
let mut deq_blocks = [[0i32; 16]; 16];
for i in 0..16 {
deq_blocks[i] = dequantize(&i16_q[i], qp);
deq_blocks[i][0] = i16_recon_dc[i];
}
let mut idct = [[0i32; 16]; 16];
inverse_dct_blocks(&deq_blocks, &mut idct);
for by in 0..4 {
for bx in 0..4 {
let s = add_residual_4x4(&idct[by * 4 + bx], &pred_block(&best_pred, bx, by));
for dy in 0..4 {
for dx in 0..4 {
recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)] = s[dy * 4 + dx];
}
}
}
}
(i16_dc_levels, i16_recon_dc, recon16)
};
let i16_cbp15 = i16_q.iter().any(|b| b[1..].iter().any(|&c| c != 0));
let i16_dc_nz = i16_dc_levels.iter().filter(|&&v| v != 0).count() as i64;
let i16_ac_nz: i64 = i16_q
.iter()
.map(|b| b[1..].iter().filter(|&&v| v != 0).count() as i64)
.sum();
let i16_rate = i16_dc_nz + i16_ac_nz + if i16_cbp15 { 16 } else { 0 };
let mut ssd16 = 0i64;
for dy in 0..16 {
let sr = (ly + dy) * fe.cw + lx;
for (r, sv2) in recon16[dy * 16..dy * 16 + 16].iter().zip(&sy[sr..sr + 16]) {
let d = *r as i64 - *sv2 as i64;
ssd16 += d * d;
}
}
let (cx, cy) = (mb_x * 8, mb_y * 8);
let mut ntop = [[0u8; 8]; 2];
let mut nleft = [[0u8; 8]; 2];
let mut ncorner = [0u8; 2];
for c in 0..2 {
let rec_c = if c == 0 { &fe.rec_u } else { &fe.rec_v };
if avail_top {
let r = (cy - 1) * fe.ccw + cx;
ntop[c].copy_from_slice(&rec_c[r..r + 8]);
}
if avail_left {
let col = &rec_c[cy * fe.ccw + cx - 1..(cy + 7) * fe.ccw + cx];
for i in 0..8 {
nleft[c][i] = col[i * fe.ccw];
}
}
if avail_top && avail_left {
ncorner[c] = rec_c[(cy - 1) * fe.ccw + cx - 1];
}
}
let mut chroma_mode = 0u8;
let mut best_c_cost = i64::MAX;
let mut best_pred8 = [[0u8; 64]; 2];
for m in 0..4u8 {
if !chroma_mode_available(m, avail_top, avail_left) {
continue;
}
let mut cost = 0i64;
let mut cur = [[0u8; 64]; 2];
for c in 0..2 {
let src = if c == 0 { su } else { sv };
cur[c] = chroma_pred(
fe, m, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy,
);
cost += satd_8x8(src, fe.ccw, cx, cy, &cur[c]);
}
if cost < best_c_cost {
best_c_cost = cost;
chroma_mode = m;
best_pred8 = cur;
}
}
let mut c_dc_levels = [[0i32; 4]; 2];
let mut c_q_blocks = [[[0i32; 16]; 4]; 2];
let mut any_chroma_ac = false;
let mut any_chroma_dc = false;
for c in 0..2 {
let src = if c == 0 { su } else { sv };
let pred8 = best_pred8[c]; #[cfg_attr(accel, allow(unused_variables))] let pblk = |bx: usize, by: usize| -> [i32; 16] {
let mut predb = [0i32; 16];
for dy in 0..4 {
for dx in 0..4 {
predb[dy * 4 + dx] = pred8[(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
}
}
predb
};
let mut dc2x2 = [0i32; 4];
let mut qbs = [[0i32; 16]; 4];
#[cfg(accel)]
let recon_dc = {
#[repr(align(16))]
struct A([i16; 64]);
let mut d = A([0i16; 64]);
rusty_h264_accel::dct_four_t4(&mut d.0, &src[cy * fe.ccw + cx..], fe.ccw, &pred8, 8);
for i in 0..4 {
dc2x2[i] = d.0[i * 16] as i32;
}
if fe.rdoq_strength > 0.0 {
for i in 0..4 {
let coeffs: [i32; 16] = core::array::from_fn(|j| d.0[i * 16 + j] as i32);
let mut q = rdoq(&coeffs, qpc, fe.idz, fe.rdoq_strength, 1);
q[0] = 0;
if q[1..].iter().any(|&v| v != 0) {
any_chroma_ac = true;
}
qbs[i] = q;
}
} else {
let ff = rusty_h264_common::transform::quant_dz_ff(qpc, fe.idz);
let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
rusty_h264_accel::quant_four_4x4(&mut d.0, &ff, mf);
for i in 0..4 {
let q = &mut qbs[i];
q[0] = 0;
for j in 1..16 {
let v = d.0[i * 16 + j] as i32;
q[j] = v;
if v != 0 {
any_chroma_ac = true;
}
}
}
}
let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
if dl.iter().any(|&v| v != 0) {
any_chroma_dc = true;
}
let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
for i in 0..4 {
let deq = dequantize(&qbs[i], qpc);
for j in 0..16 {
d.0[i * 16 + j] = deq[j] as i16;
}
d.0[i * 16] = recon_dc[i] as i16;
}
let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
rusty_h264_accel::idct_four_t4_rec(
&mut plane[cy * fe.ccw + cx..],
fe.ccw,
&pred8,
8,
&d.0,
);
c_dc_levels[c] = dl;
recon_dc
};
#[cfg(not(accel))]
let recon_dc = {
let mut res_blocks = [[0i32; 16]; 4];
for by in 0..2 {
for bx in 0..2 {
res_blocks[by * 2 + bx] =
residual(src, fe.ccw, cx + bx * 4, cy + by * 4, &pblk(bx, by));
}
}
let mut coeffs = [[0i32; 16]; 4];
forward_dct_blocks(&res_blocks, &mut coeffs);
for i in 0..4 {
dc2x2[i] = coeffs[i][0];
let mut q = rdoq(&coeffs[i], qpc, fe.idz, fe.rdoq_strength, 1);
q[0] = 0;
qbs[i] = q;
if q[1..].iter().any(|&v| v != 0) {
any_chroma_ac = true;
}
}
let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
if dl.iter().any(|&v| v != 0) {
any_chroma_dc = true;
}
let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
let mut deq_blocks = [[0i32; 16]; 4];
for i in 0..4 {
deq_blocks[i] = dequantize(&qbs[i], qpc);
deq_blocks[i][0] = recon_dc[i];
}
let mut idct = [[0i32; 16]; 4];
inverse_dct_blocks(&deq_blocks, &mut idct);
let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
for by in 0..2 {
for bx in 0..2 {
let s = add_residual_4x4(&idct[by * 2 + bx], &pblk(bx, by));
store(plane, fe.ccw, cx + bx * 4, cy + by * 4, &s);
}
}
c_dc_levels[c] = dl;
recon_dc
};
let _ = recon_dc;
c_q_blocks[c] = qbs;
}
let cbp_chroma: u32 = if any_chroma_ac {
2
} else if any_chroma_dc {
1
} else {
0
};
let base = ly * fe.cw + lx;
let i4 = if i16_rate > 2 {
Some(plan_i4x4(fe, sy, mb_x, mb_y, qp))
} else {
None
};
let (j4, i4_recon) = match &i4 {
Some(p) => {
let mut ssd = 0i64;
let mut rec = [0u8; 256];
for dy in 0..16 {
let sr = base + dy * fe.cw;
let row = &fe.rec_y[sr..sr + 16];
rec[dy * 16..dy * 16 + 16].copy_from_slice(row);
for (a, b) in row.iter().zip(&sy[sr..sr + 16]) {
let d = *a as i64 - *b as i64;
ssd += d * d;
}
}
(ssd as f64 + lambda * (p.nonzero + 16) as f64, Some(rec))
}
None => (f64::INFINITY, None),
};
let i8 = if fe.t8_pick && (!fe.intra_in_p || i8_in_p_on()) {
Some(plan_i8x8(fe, sy, mb_x, mb_y, qp))
} else {
None
};
let j8 = match &i8 {
Some(p) => {
let mut ssd = 0i64;
for dy in 0..16 {
let sr = base + dy * fe.cw;
for (a, b) in fe.rec_y[sr..sr + 16].iter().zip(&sy[sr..sr + 16]) {
let d = *a as i64 - *b as i64;
ssd += d * d;
}
}
ssd as f64 + lambda * (p.nonzero + 16) as f64
}
None => f64::INFINITY,
};
let j16 = ssd16 as f64 + lambda * i16_rate as f64;
let m8 = fe.i8_margin * lambda;
let (use_i4, i4, i8) = if i8.is_some() && j8 + m8 <= j4 && j8 + m8 <= j16 {
(true, None, i8)
} else if i4.is_some() && j4 < j16 {
let rec = i4_recon.unwrap();
for dy in 0..16 {
let d = base + dy * fe.cw;
fe.rec_y[d..d + 16].copy_from_slice(&rec[dy * 16..dy * 16 + 16]);
}
let modes = i4.as_ref().unwrap().modes;
for lby in 0..4 {
let d = (mb_y * 4 + lby) * w4 + mb_x * 4;
fe.modes_y[d..d + 4].copy_from_slice(&modes[lby * 4..lby * 4 + 4]);
}
(true, i4, None)
} else {
for dy in 0..16 {
let d = (ly + dy) * fe.cw + lx;
fe.rec_y[d..d + 16].copy_from_slice(&recon16[dy * 16..dy * 16 + 16]);
}
for lby in 0..4 {
let d = (mb_y * 4 + lby) * w4 + mb_x * 4;
fe.modes_y[d..d + 4].fill(2);
}
(false, None, None)
};
for lby in 0..4 {
let d = (mb_y * 4 + lby) * w4 + mb_x * 4;
fe.coded_y[d..d + 4].fill(true);
}
MbPlan {
use_i4,
i16_mode,
i16_cbp15,
i16_dc_levels,
i16_q,
i4,
i8,
chroma_mode,
cbp_chroma,
c_dc_levels,
c_q_blocks,
}
}
fn encode_mb(
fe: &mut FrameEncoder,
w: &mut BitWriter,
mb_x: usize,
mb_y: usize,
sy: &[u8],
su: &[u8],
sv: &[u8],
is_p: bool,
) {
fe.intra_in_p = is_p;
let plan = plan_mb(fe, mb_x, mb_y, sy, su, sv);
let mb_type_offset = if is_p { 5 } else { 0 };
let w4 = fe.mb_w * 4;
let cbp_chroma = plan.cbp_chroma;
if let Some(i8) = plan.i8.as_ref().filter(|_| plan.use_i4) {
let cbp = i8.cbp_luma | (cbp_chroma << 4);
w.write_ue(mb_type_offset); w.write_bit(true); for b8 in 0..4usize {
let (bx, by) = (mb_x * 4 + (b8 % 2) * 2, mb_y * 4 + (b8 / 2) * 2);
let predicted = predict_i4_mode(fe, bx, by);
let actual = i8.modes[b8];
if actual == predicted {
w.write_bit(true);
} else {
w.write_bit(false);
let rem = if actual < predicted {
actual
} else {
actual - 1
};
w.write_bits(rem as u32, 3);
}
}
w.write_ue(plan.chroma_mode as u32); write_cbp_intra(w, cbp);
if cbp != 0 {
w.write_se(fe.qp_delta());
}
fe.nnz_cache_load(mb_x, mb_y);
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let scan8 = scan_8x8_fwd(&i8.q[b8]);
for sub in 0..4usize {
let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
let total = if i8.cbp_luma & (1 << b8) != 0 {
let nc = fe.nc_pred(cx, cy);
let blk: [i32; 16] = core::array::from_fn(|k| scan8[4 * k + sub]);
encode_residual_block(w, &blk, 16, nc) as u8
} else {
0
};
fe.nnz_cache_set(cx, cy, total);
fe.nnz_y[by * w4 + bx] = total;
}
}
} else if plan.use_i4 {
let i4 = plan.i4.as_ref().unwrap();
let cbp = i4.cbp_luma | (cbp_chroma << 4);
w.write_ue(mb_type_offset); if fe.transform_8x8 {
w.write_bit(false); }
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
let predicted = predict_i4_mode(fe, bx, by);
let actual = i4.modes[lby * 4 + lbx];
if actual == predicted {
w.write_bit(true);
} else {
w.write_bit(false);
let rem = if actual < predicted {
actual
} else {
actual - 1
};
w.write_bits(rem as u32, 3);
}
}
w.write_ue(plan.chroma_mode as u32); write_cbp_intra(w, cbp);
if cbp != 0 {
w.write_se(fe.qp_delta()); }
fe.nnz_cache_load(mb_x, mb_y);
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
let total = if i4.cbp_luma & (1 << (blk / 4)) != 0 {
let nc = fe.nc_pred(lbx, lby);
let scan16 = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
encode_residual_block(w, &scan16, 16, nc) as u8
} else {
0
};
fe.nnz_cache_set(lbx, lby, total);
fe.nnz_y[by * w4 + bx] = total;
}
} else {
let mb_type =
1 + plan.i16_mode as u32 + 4 * cbp_chroma + if plan.i16_cbp15 { 12 } else { 0 };
w.write_ue(mb_type + mb_type_offset);
w.write_ue(plan.chroma_mode as u32); w.write_se(fe.qp_delta()); fe.nnz_cache_load(mb_x, mb_y);
let nc_dc = fe.nc_pred(0, 0);
let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
encode_residual_block(w, &dc_scan, 16, nc_dc);
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
fe.nnz_cache_set(lbx, lby, 0);
fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
}
if plan.i16_cbp15 {
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
let nc = fe.nc_pred(lbx, lby);
let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
let total = encode_residual_block(w, &ac, 15, nc) as u8;
fe.nnz_cache_set(lbx, lby, total);
fe.nnz_y[by * w4 + bx] = total;
}
}
}
if cbp_chroma != 0 {
for c in 0..2 {
encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
}
}
if cbp_chroma == 2 {
fe.chroma_cache_load(mb_x, mb_y);
let w2 = fe.mb_w * 2;
for c in 0..2 {
for &(bx, by) in &CHROMA_4X4_SCAN_XY {
let nc = fe.chroma_nc_pred(c, bx, by);
let ac = scan_4x4_ac(&plan.c_q_blocks[c][by * 2 + bx]);
let total = encode_residual_block(w, &ac, 15, nc) as u8;
fe.chroma_nnz_cache_set(c, bx, by, total);
fe.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
}
}
}
}
use rusty_h264_common::cabac_tables::{
CACHE30 as CB_CACHE30, G_SCAN4 as CB_G_SCAN4, NZC_CACHE as CB_NZC_CACHE, RES_CBF as CB_RES_CBF,
RES_MAP as CB_RES_MAP, RES_MAXC2 as CB_RES_MAXC2, RES_MAXPOS as CB_RES_MAXPOS,
RES_ONE as CB_RES_ONE,
};
const CB_RP_I16_DC: usize = 1;
const CB_RP_I16_AC: usize = 2;
const CB_RP_LUMA_4X4: usize = 3;
const CB_RP_CHROMA_DC: usize = 7;
const CB_RP_CHROMA_AC: usize = 9;
const CB_RP_LUMA_8X8: usize = 6;
fn cb_unary(cab: &mut CabacEncoder, ctx: usize, off: usize, value: u32) {
if value == 0 {
cab.encode_decision(ctx, 0);
return;
}
cab.encode_decision(ctx, 1);
for _ in 0..value - 1 {
cab.encode_decision(ctx + off, 1);
}
cab.encode_decision(ctx + off, 0);
}
fn cb_exp_bypass(cab: &mut CabacEncoder, mut k: i32, mut n: u32) {
while n >= (1 << k) {
cab.encode_bypass(1);
n -= 1 << k;
k += 1;
}
cab.encode_bypass(0);
cab.encode_bypass_bits(n, k as u32);
}
fn cb_ueg_level(cab: &mut CabacEncoder, ctx: usize, value: u32) {
if value == 0 {
cab.encode_decision(ctx, 0);
return;
}
let ones = value.min(13);
for _ in 0..ones {
cab.encode_decision(ctx, 1);
}
if value < 13 {
cab.encode_decision(ctx, 0);
} else {
cb_exp_bypass(cab, 0, value - 13);
}
}
pub fn cb_mb_qp_delta(cab: &mut CabacEncoder, last_delta_qp: &mut i32, delta: i32) {
const O: usize = 60;
let ctx_inc = (*last_delta_qp != 0) as usize;
if delta == 0 {
cab.encode_decision(O + ctx_inc, 0);
} else {
cab.encode_decision(O + ctx_inc, 1);
let code = 2 * delta.unsigned_abs() - (delta > 0) as u32;
cb_unary(cab, O + 2, 1, code - 1);
}
*last_delta_qp = delta;
}
fn cb_chroma_pred_mode(cab: &mut CabacEncoder, ctx_inc: usize, mode: u8) {
const C: usize = 64;
if mode == 0 {
cab.encode_decision(C + ctx_inc, 0);
return;
}
cab.encode_decision(C + ctx_inc, 1);
if mode == 1 {
cab.encode_decision(C + 3, 0);
} else if mode == 2 {
cab.encode_decision(C + 3, 1);
cab.encode_decision(C + 3, 0);
} else {
cab.encode_decision(C + 3, 1);
cab.encode_decision(C + 3, 1);
}
}
fn cb_mb_type_i(
cab: &mut CabacEncoder,
ctx_inc: usize,
use_i4: bool,
i16_mode: u32,
cbp_chroma: u32,
cbp_luma15: bool,
) {
const O: usize = 3;
if use_i4 {
cab.encode_decision(O + ctx_inc, 0); return;
}
cab.encode_decision(O + ctx_inc, 1);
cab.encode_terminate(false); cab.encode_decision(O + 3, cbp_luma15 as u32);
if cbp_chroma != 0 {
cab.encode_decision(O + 4, 1);
cab.encode_decision(O + 5, (cbp_chroma == 2) as u32);
} else {
cab.encode_decision(O + 4, 0);
}
cab.encode_decision(O + 6, (i16_mode >> 1) & 1);
cab.encode_decision(O + 7, i16_mode & 1);
}
fn cb_intra4x4_pred_mode(cab: &mut CabacEncoder, predicted: u8, actual: u8) {
const IPR: usize = 68;
if actual == predicted {
cab.encode_decision(IPR, 1);
} else {
cab.encode_decision(IPR, 0);
let rem = if actual < predicted {
actual
} else {
actual - 1
} as u32;
cab.encode_decision(IPR + 1, rem & 1);
cab.encode_decision(IPR + 1, (rem >> 1) & 1);
cab.encode_decision(IPR + 1, (rem >> 2) & 1);
}
}
pub fn cb_cbp(cab: &mut CabacEncoder, top: Option<u8>, left: Option<u8>, cbp: u32) {
const CBP: usize = 73;
let t = |m: u32| top.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
let l = |m: u32| left.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
let nb = |x: u32| (x == 0) as u32;
let b0 = cbp & 1;
let b1 = (cbp >> 1) & 1;
let b2 = (cbp >> 2) & 1;
let b3 = (cbp >> 3) & 1;
cab.encode_decision(CBP + (l(1 << 1) + (t(1 << 2) << 1)) as usize, b0);
cab.encode_decision(CBP + (nb(b0) + (t(1 << 3) << 1)) as usize, b1);
cab.encode_decision(CBP + (l(1 << 3) + (nb(b0) << 1)) as usize, b2);
cab.encode_decision(CBP + (nb(b2) + (nb(b1) << 1)) as usize, b3);
let cbp_chroma = cbp >> 4;
let ct = top.map_or(0u32, |c| ((c >> 4) != 0) as u32);
let cl = left.map_or(0u32, |c| ((c >> 4) != 0) as u32);
cab.encode_decision(
CBP + 4 + (cl + (ct << 1)) as usize,
(cbp_chroma != 0) as u32,
);
if cbp_chroma != 0 {
let ct2 = top.map_or(0u32, |c| ((c >> 4) == 2) as u32);
let cl2 = left.map_or(0u32, |c| ((c >> 4) == 2) as u32);
cab.encode_decision(
CBP + 8 + (cl2 + (ct2 << 1)) as usize,
(cbp_chroma == 2) as u32,
);
}
}
fn cb_residual(
cab: &mut CabacEncoder,
nzc: &mut [u8; 48],
cbf_dc: &mut u16,
iz: usize,
rp: usize,
is_intra: bool,
ndc: (Option<u16>, Option<u16>),
coeffs: &[i32],
) -> u32 {
let is8 = rp == CB_RP_LUMA_8X8;
let is_dc = rp == CB_RP_I16_DC || rp == CB_RP_CHROMA_DC || rp == CB_RP_CHROMA_DC + 1;
let (mut na, mut nb) = (is_intra as u8, is_intra as u8);
let scan = CB_NZC_CACHE[iz.min(23)];
if is_dc {
if let Some(t) = ndc.0 {
nb = ((t >> rp) & 1) as u8;
}
if let Some(l) = ndc.1 {
na = ((l >> rp) & 1) as u8;
}
} else {
if nzc[scan - 8] != 0xff {
nb = (nzc[scan - 8] != 0) as u8;
}
if nzc[scan - 1] != 0xff {
na = (nzc[scan - 1] != 0) as u8;
}
}
let maxpos = CB_RES_MAXPOS[rp] as usize;
let coeff_num = coeffs[..=maxpos].iter().filter(|&&c| c != 0).count() as u32;
let cbf = coeff_num != 0;
if !is8 {
cab.encode_decision(85 + CB_RES_CBF[rp] + (na + (nb << 1)) as usize, cbf as u32);
if !cbf {
if !is_dc {
nzc[scan] = 0;
}
return 0;
}
} else if !cbf {
debug_assert!(
false,
"cat 5 called with an all-zero block; CBP should have gated it"
);
nzc[scan] = 0;
return 0;
}
if is_dc {
*cbf_dc |= 1 << rp;
}
let map = 105 + CB_RES_MAP[rp];
let last = 166 + CB_RES_MAP[rp];
let lastnz = (0..=maxpos).rev().find(|&i| coeffs[i] != 0).unwrap();
for i in 0..maxpos {
let s = coeffs[i] != 0;
let (sig_ctx, last_ctx) = if is8 {
(
rusty_h264_common::cabac_tables::CAT5_SIG_BASE
+ rusty_h264_common::cabac_tables::SIG8X8[i] as usize,
rusty_h264_common::cabac_tables::CAT5_LAST_BASE
+ rusty_h264_common::cabac_tables::LAST8X8[i] as usize,
)
} else {
(map + i, last + i)
};
cab.encode_decision(sig_ctx, s as u32);
if s {
let is_last = i == lastnz;
cab.encode_decision(last_ctx, is_last as u32);
if is_last {
break;
}
}
}
let one = 227 + CB_RES_ONE[rp];
let abs = 232 + CB_RES_ONE[rp];
let maxc2 = CB_RES_MAXC2[rp];
let (mut c1, mut c2) = (1i32, 0i32);
for i in (0..=maxpos).rev() {
if coeffs[i] != 0 {
let av = coeffs[i].unsigned_abs();
let gt1 = av > 1;
cab.encode_decision(one + c1 as usize, gt1 as u32);
if gt1 {
cb_ueg_level(cab, abs + c2 as usize, av - 2);
c2 = (c2 + 1).min(maxc2);
c1 = 0;
} else if c1 != 0 {
c1 = (c1 + 1).min(4);
}
cab.encode_bypass((coeffs[i] < 0) as u32);
}
}
if is8 {
for k in 0..4 {
nzc[CB_NZC_CACHE[(iz + k).min(23)]] = coeff_num as u8;
}
} else if !is_dc {
nzc[scan] = coeff_num as u8;
}
coeff_num
}
fn cb_build_nzc(mb_nzc: &[[u8; 24]], top: Option<usize>, left: Option<usize>) -> [u8; 48] {
let mut nzc = [0xffu8; 48];
if let Some(t) = top {
let tn = mb_nzc[t];
nzc[1..5].copy_from_slice(&tn[12..16]);
(nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
(nzc[6], nzc[7]) = (tn[20], tn[21]);
(nzc[30], nzc[31]) = (tn[22], tn[23]);
}
if let Some(l) = left {
let ln = mb_nzc[l];
(nzc[8], nzc[16], nzc[24], nzc[32]) = (ln[3], ln[7], ln[11], ln[15]);
(nzc[13], nzc[21], nzc[37], nzc[45]) = (ln[17], ln[21], ln[19], ln[23]);
}
nzc
}
fn cb_export_nzc(nzc: &[u8; 48]) -> [u8; 24] {
let mut mn = [0u8; 24];
mn[0..4].copy_from_slice(&nzc[9..13]);
mn[4..8].copy_from_slice(&nzc[17..21]);
mn[8..12].copy_from_slice(&nzc[25..29]);
mn[12..16].copy_from_slice(&nzc[33..37]);
(mn[16], mn[17], mn[20], mn[21]) = (nzc[14], nzc[15], nzc[22], nzc[23]);
(mn[18], mn[19], mn[22], mn[23]) = (nzc[38], nzc[39], nzc[46], nzc[47]);
for v in mn.iter_mut() {
if *v == 0xff {
*v = 0;
}
}
mn
}
fn cb_sat_mvd(m: [[i16; 2]; 16]) -> [[u8; 2]; 16] {
core::array::from_fn(|i| core::array::from_fn(|c| m[i][c].unsigned_abs().min(33) as u8))
}
struct CabacState {
cat: Vec<u8>, cmode: Vec<i8>, mb_cbp: Vec<u8>, cbf_dc: Vec<u16>, mb_nzc: Vec<[u8; 24]>, mb_mvd: Vec<[[u8; 2]; 16]>, mb_ref: Vec<[i8; 16]>, mb_mvd1: Vec<[[u8; 2]; 16]>, mb_ref1: Vec<[i8; 16]>, mb_skip: Vec<bool>, mb_direct: Vec<bool>, mb_t8x8: Vec<bool>, last_delta_qp: i32,
}
impl CabacState {
fn refill(&mut self, n: usize) {
fn rf<T: Clone>(v: &mut Vec<T>, n: usize, val: T) {
v.clear();
v.resize(n, val);
}
rf(&mut self.cat, n, 0);
rf(&mut self.cmode, n, 0);
rf(&mut self.mb_cbp, n, 0);
rf(&mut self.cbf_dc, n, 0);
rf(&mut self.mb_nzc, n, [0u8; 24]);
rf(&mut self.mb_mvd, n, [[0u8; 2]; 16]);
rf(&mut self.mb_ref, n, [-1i8; 16]);
rf(&mut self.mb_mvd1, n, [[0u8; 2]; 16]);
rf(&mut self.mb_ref1, n, [-1i8; 16]);
rf(&mut self.mb_skip, n, false);
rf(&mut self.mb_direct, n, false);
rf(&mut self.mb_t8x8, n, false);
self.last_delta_qp = 0;
}
fn pooled(n: usize) -> Self {
let mut cs = enc_scratch::take_cs().unwrap_or_else(|| CabacState::new(0));
cs.refill(n);
cs
}
fn new(n: usize) -> Self {
CabacState {
cat: vec![0; n],
cmode: vec![0; n],
mb_cbp: vec![0; n],
cbf_dc: vec![0; n],
mb_nzc: vec![[0u8; 24]; n],
mb_mvd: vec![[[0u8; 2]; 16]; n],
mb_ref: vec![[-1i8; 16]; n],
mb_mvd1: vec![[[0u8; 2]; 16]; n],
mb_ref1: vec![[-1i8; 16]; n],
mb_skip: vec![false; n],
mb_direct: vec![false; n],
mb_t8x8: vec![false; n],
last_delta_qp: 0,
}
}
}
fn emit_mb_cabac_i(
fe: &mut FrameEncoder,
cab: &mut CabacEncoder,
cs: &mut CabacState,
plan: &MbPlan,
mb_x: usize,
mb_y: usize,
) {
let mb_w = fe.mb_w;
let addr = mb_y * mb_w + mb_x;
let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
let left = if mb_x > 0 { Some(addr - 1) } else { None };
let li = left.map_or(0, |a| (cs.cat[a] >= 2) as usize);
let ti = top.map_or(0, |a| (cs.cat[a] >= 2) as usize);
let acct = crate::bitacct::enabled();
let t0 = if acct { cab.pos() } else { 0 };
if plan.use_i4 {
cb_mb_type_i(cab, li + ti, true, 0, 0, false);
} else {
cb_mb_type_i(
cab,
li + ti,
false,
plan.i16_mode as u32,
plan.cbp_chroma,
plan.i16_cbp15,
);
}
if acct {
crate::bitacct::add(crate::bitacct::B::MbType, cab.pos() - t0);
}
let t1 = if acct { cab.pos() } else { 0 };
emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
if acct {
crate::bitacct::add(crate::bitacct::B::IntraBody, cab.pos() - t1);
}
}
#[inline]
fn nnz_commit_rows(fe: &mut FrameEncoder, mb_x: usize, mb_y: usize, loc: &[u8; 16]) {
let w4 = fe.mb_w * 4;
for lby in 0..4 {
let d = (mb_y * 4 + lby) * w4 + mb_x * 4;
fe.nnz_y[d..d + 4].copy_from_slice(&loc[lby * 4..lby * 4 + 4]);
}
}
fn emit_intra_body_cabac(
fe: &mut FrameEncoder,
cab: &mut CabacEncoder,
cs: &mut CabacState,
plan: &MbPlan,
mb_x: usize,
mb_y: usize,
addr: usize,
top: Option<usize>,
left: Option<usize>,
) {
let cbp_chroma = plan.cbp_chroma;
let cci = left.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize)
+ top.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize);
let mut nzc;
let mut cbfdc = 0u16;
let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
if !plan.use_i4 {
cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
cs.cmode[addr] = plan.chroma_mode as i8;
cs.cat[addr] = 2;
cs.mb_cbp[addr] = ((cbp_chroma as u8) << 4) | if plan.i16_cbp15 { 15 } else { 0 };
nzc = cb_build_nzc(&cs.mb_nzc, top, left);
let delta = fe.qp_delta();
cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
cb_residual(
cab,
&mut nzc,
&mut cbfdc,
0,
CB_RP_I16_DC,
true,
ndc,
&dc_scan,
);
let mut nnz_loc = [0u8; 16];
for (iz, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
let total = if plan.i16_cbp15 {
let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_I16_AC, true, ndc, &ac)
} else {
nzc[CB_NZC_CACHE[iz]] = 0;
0
};
nnz_loc[lby * 4 + lbx] = total as u8;
}
nnz_commit_rows(fe, mb_x, mb_y, &nnz_loc);
cb_emit_chroma_residual(
cab,
fe,
&mut nzc,
&mut cbfdc,
ndc,
true,
plan.cbp_chroma,
&plan.c_dc_levels,
&plan.c_q_blocks,
mb_x,
mb_y,
);
} else if let Some(i8) = plan.i8.as_ref() {
let ta = left.map_or(0, |x| cs.mb_t8x8[x] as usize);
let tb = top.map_or(0, |x| cs.mb_t8x8[x] as usize);
cab.encode_decision(399 + ta + tb, 1);
cs.mb_t8x8[addr] = true;
for b8 in 0..4usize {
let (bx, by) = (mb_x * 4 + (b8 % 2) * 2, mb_y * 4 + (b8 / 2) * 2);
let predicted = predict_i4_mode(fe, bx, by);
cb_intra4x4_pred_mode(cab, predicted, i8.modes[b8]);
}
cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
cs.cmode[addr] = plan.chroma_mode as i8;
cs.cat[addr] = 0;
let cbp = i8.cbp_luma | (cbp_chroma << 4);
cb_cbp(
cab,
top.map(|a| cs.mb_cbp[a]),
left.map(|a| cs.mb_cbp[a]),
cbp,
);
cs.mb_cbp[addr] = cbp as u8;
nzc = cb_build_nzc(&cs.mb_nzc, top, left);
if cbp == 0 {
cs.last_delta_qp = 0;
nnz_commit_rows(fe, mb_x, mb_y, &[0u8; 16]);
} else {
let delta = fe.qp_delta();
cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
let mut nnz_loc = [0u8; 16];
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let total = if i8.cbp_luma & (1 << b8) != 0 {
let scan8 = scan_8x8_fwd(&i8.q[b8]);
cb_residual(
cab,
&mut nzc,
&mut cbfdc,
b8 * 4,
CB_RP_LUMA_8X8,
true,
ndc,
&scan8,
)
} else {
for k in 0..4 {
nzc[CB_NZC_CACHE[b8 * 4 + k]] = 0;
}
0
};
for sy in 0..2 {
for sx in 0..2 {
nnz_loc[(b8y * 2 + sy) * 4 + b8x * 2 + sx] = total as u8;
}
}
}
nnz_commit_rows(fe, mb_x, mb_y, &nnz_loc);
cb_emit_chroma_residual(
cab,
fe,
&mut nzc,
&mut cbfdc,
ndc,
true,
plan.cbp_chroma,
&plan.c_dc_levels,
&plan.c_q_blocks,
mb_x,
mb_y,
);
}
} else {
let i4 = plan.i4.as_ref().unwrap();
if fe.transform_8x8 {
let ta = left.map_or(0, |x| cs.mb_t8x8[x] as usize);
let tb = top.map_or(0, |x| cs.mb_t8x8[x] as usize);
cab.encode_decision(399 + ta + tb, 0);
}
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
let predicted = predict_i4_mode(fe, bx, by);
cb_intra4x4_pred_mode(cab, predicted, i4.modes[lby * 4 + lbx]);
}
cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
cs.cmode[addr] = plan.chroma_mode as i8;
cs.cat[addr] = 0;
let cbp = i4.cbp_luma | (cbp_chroma << 4);
cb_cbp(
cab,
top.map(|a| cs.mb_cbp[a]),
left.map(|a| cs.mb_cbp[a]),
cbp,
);
cs.mb_cbp[addr] = cbp as u8;
nzc = cb_build_nzc(&cs.mb_nzc, top, left);
if cbp == 0 {
cs.last_delta_qp = 0;
nnz_commit_rows(fe, mb_x, mb_y, &[0u8; 16]);
} else {
let delta = fe.qp_delta();
cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
let mut nnz_loc = [0u8; 16];
for id8 in 0..4usize {
for id4 in 0..4usize {
let iz = id8 * 4 + id4;
let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
let total = if i4.cbp_luma & (1 << id8) != 0 {
let sc = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
cb_residual(
cab,
&mut nzc,
&mut cbfdc,
iz,
CB_RP_LUMA_4X4,
true,
ndc,
&sc,
)
} else {
nzc[CB_NZC_CACHE[iz]] = 0;
0
};
nnz_loc[lby * 4 + lbx] = total as u8;
}
}
nnz_commit_rows(fe, mb_x, mb_y, &nnz_loc);
cb_emit_chroma_residual(
cab,
fe,
&mut nzc,
&mut cbfdc,
ndc,
true,
plan.cbp_chroma,
&plan.c_dc_levels,
&plan.c_q_blocks,
mb_x,
mb_y,
);
}
}
cs.cbf_dc[addr] = cbfdc;
cs.mb_nzc[addr] = cb_export_nzc(&nzc);
}
fn cb_emit_chroma_residual(
cab: &mut CabacEncoder,
fe: &mut FrameEncoder,
nzc: &mut [u8; 48],
cbfdc: &mut u16,
ndc: (Option<u16>, Option<u16>),
is_intra: bool,
cbp_chroma: u32,
c_dc_levels: &[[i32; 4]; 2],
c_q: &[[[i32; 16]; 4]; 2],
mb_x: usize,
mb_y: usize,
) {
let w2 = fe.mb_w * 2;
if cbp_chroma >= 1 {
for i in 0..2usize {
cb_residual(
cab,
nzc,
cbfdc,
16 + i * 4,
CB_RP_CHROMA_DC + i,
is_intra,
ndc,
&c_dc_levels[i],
);
}
}
if cbp_chroma == 2 {
for i in 0..2usize {
for (id4, &(bx, by)) in CHROMA_4X4_SCAN_XY.iter().enumerate() {
let ac = scan_4x4_ac(&c_q[i][by * 2 + bx]);
let total = cb_residual(
cab,
nzc,
cbfdc,
16 + i * 4 + id4,
CB_RP_CHROMA_AC + i,
is_intra,
ndc,
&ac,
);
fe.nnz_c[i][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total as u8;
}
}
}
}
pub(crate) fn encode_slice_data_cabac_intra(
w: &mut BitWriter,
cfg: &EncoderConfig,
frame: &YuvPlanes<'_>,
qp: u8,
qpo: &[i32],
aq_probe: Option<&YuvPlanes<'_>>,
) -> crate::RefFrame {
let mut fe = FrameEncoder::new(cfg);
fe.qp = qp;
fe.qpc = chroma_qp(qp);
fe.cur_qp = qp;
if cfg.cabac_dz_div > 0 {
fe.idz = cfg.cabac_dz_div; }
let (sy, su, sv) = coded_source(cfg, frame);
let probe_y: Option<Vec<u8>> = aq_probe.map(|f| coded_source(cfg, f).0.into_owned());
let sig = FrameSignals::new(&sy, fe.cw, fe.mb_w, fe.mb_h, probe_y.as_deref());
apply_screen_t8_veto(&mut fe, &sig);
let mut aq_qp = aq_qp_map(&sig, qp, fe.aq_strength);
apply_mbtree_qpo(&mut aq_qp, qpo); signals::harvest(&sig, 'I', qp, &signals::GateDecisions::default());
let mut mb_qpy = enc_scratch::take_qpy();
mb_qpy.clear();
mb_qpy.resize(fe.mb_w * fe.mb_h, qp);
fe.rdoq_strength = if cfg.gop_size <= 1 {
cfg.cabac_rdoq
} else {
0.0
};
let mut cab = CabacEncoder::new_with_out(qp as i32, 0, true, enc_scratch::take_payload());
let mut cs = CabacState::pooled(fe.mb_w * fe.mb_h);
let total = fe.mb_w * fe.mb_h;
let acct = crate::bitacct::enabled();
for mb_y in 0..fe.mb_h {
for mb_x in 0..fe.mb_w {
let mb_idx = mb_y * fe.mb_w + mb_x;
let mb_qp = aq_qp[mb_idx];
fe.qp = mb_qp;
fe.qpc = chroma_qp(mb_qp);
let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
emit_mb_cabac_i(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
mb_qpy[mb_idx] = fe.cur_qp;
cb_term_acct(&mut cab, mb_idx + 1 == total, acct);
}
}
enc_scratch::put_cs(cs);
while !w.is_byte_aligned() {
w.write_bit(true);
}
let payload = cab.into_bytes();
w.write_aligned_bytes(&payload);
enc_scratch::put_payload(payload);
let mut ref_id = enc_scratch::take_refid();
ref_id.clear();
ref_id.extend(
fe.ref_idx_y
.iter()
.map(|&r| if r >= 0 { r } else { i8::MIN }),
);
let info = rusty_h264_common::deblock::BlockInfo {
inter: &fe.inter_y,
nnz: &fe.nnz_y,
mv: &fe.mv_y,
ref_id: &ref_id,
mv1: &[],
ref_id1: &[],
w4: fe.mb_w * 4,
t8x8: &[],
poc0: &[],
poc1: &[],
bs: &[],
kind: &[],
};
rusty_h264_common::deblock::filter_frame(
&mut fe.rec_y,
&mut fe.rec_u,
&mut fe.rec_v,
fe.mb_w,
fe.mb_h,
&mb_qpy,
0,
0,
0,
&info,
);
enc_scratch::put_qpy(mb_qpy);
enc_scratch::put_refid(ref_id);
let w4 = fe.mb_w * 4;
crate::RefFrame {
y: fe.rec_y,
u: fe.rec_u,
v: fe.rec_v,
poc: 0,
frame_num: 0,
mv: fe.mv_y,
ref_idx: fe.ref_idx_y,
mv1: Vec::new(),
ref_idx1: Vec::new(),
w4,
hpel: crate::HpelOnce::new(),
}
}
fn cb_ueg_mv(cab: &mut CabacEncoder, base: usize, v: u32) {
const P2C: [usize; 8] = [0, 1, 2, 3, 3, 3, 3, 3];
if v == 0 {
cab.encode_decision(base, 0);
return;
}
cab.encode_decision(base, 1);
if v <= 7 {
let mut count = 1;
for _ in 0..v - 1 {
cab.encode_decision(base + P2C[count], 1);
count += 1;
}
cab.encode_decision(base + P2C[count], 0);
} else {
for count in 1..8 {
cab.encode_decision(base + P2C[count], 1);
}
let acct = crate::bitacct::enabled();
let tb = if acct { cab.pos() } else { 0 };
cb_exp_bypass(cab, 3, v - 8);
if acct {
crate::bitacct::add(crate::bitacct::B::MvdBypass, cab.pos() - tb);
}
}
}
fn cb_mvd(cab: &mut CabacEncoder, comp: usize, ctx_inc: usize, d: i32) {
let acct = crate::bitacct::enabled();
let th = if acct { cab.pos() } else { u64::MAX };
let base = 40 + comp * 7;
if d == 0 {
cab.encode_decision(base + ctx_inc, 0);
if th != u64::MAX {
crate::bitacct::add_mvd_sample(0, cab.pos() - th);
}
return;
}
cab.encode_decision(base + ctx_inc, 1);
cb_ueg_mv(cab, base + 3, d.unsigned_abs() - 1); let ts = if acct { cab.pos() } else { 0 };
cab.encode_bypass((d < 0) as u32);
if acct {
crate::bitacct::add(crate::bitacct::B::MvdSign, cab.pos() - ts);
}
if th != u64::MAX {
crate::bitacct::add_mvd_sample(d.unsigned_abs(), cab.pos() - th);
}
}
fn cb_mb_skip(cab: &mut CabacEncoder, ctx_inc: usize, skip: bool) {
cab.encode_decision(ctx_inc, skip as u32);
}
pub fn cb_ref_idx(cab: &mut CabacEncoder, ctx0: usize, r: u32) {
const B: usize = 54;
let mut v = r;
let mut bin_idx = 0u32;
loop {
let bin = (v > 0) as u32;
let ctx = match bin_idx {
0 => ctx0,
1 => 4,
_ => 5,
};
cab.encode_decision(B + ctx, bin);
if bin == 0 {
break;
}
v -= 1;
bin_idx += 1;
}
}
fn cb_mb_type_p_inter(cab: &mut CabacEncoder, mode: u8) {
const S: usize = 11;
cab.encode_decision(S + 3, 0); match mode {
0 => {
cab.encode_decision(S + 4, 0);
cab.encode_decision(S + 5, 0);
}
3 => {
cab.encode_decision(S + 4, 0);
cab.encode_decision(S + 5, 1);
}
1 => {
cab.encode_decision(S + 4, 1);
cab.encode_decision(S + 6, 1);
}
_ => {
cab.encode_decision(S + 4, 1);
cab.encode_decision(S + 6, 0);
}
}
}
fn cb_sub_mb_type_p(cab: &mut CabacEncoder, sub_type: u8) {
const S: usize = 21;
match sub_type {
0 => cab.encode_decision(S, 1),
1 => {
cab.encode_decision(S, 0);
cab.encode_decision(S + 1, 0);
}
2 => {
cab.encode_decision(S, 0);
cab.encode_decision(S + 1, 1);
cab.encode_decision(S + 2, 1);
}
3 => {
cab.encode_decision(S, 0);
cab.encode_decision(S + 1, 1);
cab.encode_decision(S + 2, 0);
}
_ => unreachable!("invalid P sub_mb_type"),
}
}
fn cb_mb_type_p_intra(cab: &mut CabacEncoder, plan: &MbPlan) {
const S: usize = 11;
cab.encode_decision(S + 3, 1); if plan.use_i4 {
cab.encode_decision(S + 6, 0); return;
}
cab.encode_decision(S + 6, 1); cab.encode_terminate(false); cab.encode_decision(S + 7, plan.i16_cbp15 as u32);
if plan.cbp_chroma != 0 {
cab.encode_decision(S + 8, 1);
cab.encode_decision(S + 8, (plan.cbp_chroma == 2) as u32);
} else {
cab.encode_decision(S + 8, 0);
}
cab.encode_decision(S + 9, (plan.i16_mode as u32 >> 1) & 1);
cab.encode_decision(S + 9, plan.i16_mode as u32 & 1);
}
fn p_partition_layout(mode: u8) -> &'static [(usize, &'static [usize])] {
match mode {
1 => &[
(0, &[0, 1, 2, 3, 4, 5, 6, 7]),
(8, &[8, 9, 10, 11, 12, 13, 14, 15]),
],
2 => &[
(0, &[0, 1, 2, 3, 8, 9, 10, 11]),
(4, &[4, 5, 6, 7, 12, 13, 14, 15]),
],
3 => &[
(0, &[0, 1, 2, 3]),
(4, &[4, 5, 6, 7]),
(8, &[8, 9, 10, 11]),
(12, &[12, 13, 14, 15]),
],
_ => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])],
}
}
fn sub_mb_partitions_p(sub_type: u8) -> &'static [(usize, usize, usize, usize)] {
match sub_type {
0 => &[(0, 0, 8, 8)],
1 => &[(0, 0, 8, 4), (0, 4, 8, 4)],
2 => &[(0, 0, 4, 8), (4, 0, 4, 8)],
_ => &[(0, 0, 4, 4), (4, 0, 4, 4), (0, 4, 4, 4), (4, 4, 4, 4)],
}
}
fn p_sub_partition_layout(p8: usize, sub_type: u8) -> &'static [(usize, &'static [usize])] {
const T: [[&[(usize, &[usize])]; 4]; 4] = [
[
&[(0, &[0, 1, 2, 3])],
&[(0, &[0, 1]), (2, &[2, 3])],
&[(0, &[0, 2]), (1, &[1, 3])],
&[(0, &[0]), (1, &[1]), (2, &[2]), (3, &[3])],
],
[
&[(4, &[4, 5, 6, 7])],
&[(4, &[4, 5]), (6, &[6, 7])],
&[(4, &[4, 6]), (5, &[5, 7])],
&[(4, &[4]), (5, &[5]), (6, &[6]), (7, &[7])],
],
[
&[(8, &[8, 9, 10, 11])],
&[(8, &[8, 9]), (10, &[10, 11])],
&[(8, &[8, 10]), (9, &[9, 11])],
&[(8, &[8]), (9, &[9]), (10, &[10]), (11, &[11])],
],
[
&[(12, &[12, 13, 14, 15])],
&[(12, &[12, 13]), (14, &[14, 15])],
&[(12, &[12, 14]), (13, &[13, 15])],
&[(12, &[12]), (13, &[13]), (14, &[14]), (15, &[15])],
],
];
T[p8][sub_type as usize]
}
fn cb_emit_mvd_partition(
cab: &mut CabacEncoder,
part_idx: usize,
zblocks: &[usize],
mvdc: &mut [[i16; 2]; 30],
refc: &mut [i8; 30],
mmvd: &mut [[i16; 2]; 16],
mref: &mut [i8; 16],
mvd: (i32, i32),
ref_idx: i8, ) {
let s = CB_CACHE30[part_idx];
let ctx = |comp: usize| -> usize {
let mut a = 0i32;
if refc[s - 6] >= 0 {
a += mvdc[s - 6][comp].unsigned_abs() as i32;
}
if refc[s - 1] >= 0 {
a += mvdc[s - 1][comp].unsigned_abs() as i32;
}
if a >= 3 {
1 + (a > 32) as usize
} else {
0
}
};
cb_mvd(cab, 0, ctx(0), mvd.0);
cb_mvd(cab, 1, ctx(1), mvd.1);
let (mx, my) = (mvd.0 as i16, mvd.1 as i16);
for &zb in zblocks {
mvdc[CB_CACHE30[zb]] = [mx, my];
refc[CB_CACHE30[zb]] = ref_idx;
mmvd[CB_G_SCAN4[zb]] = [mx, my];
mref[CB_G_SCAN4[zb]] = ref_idx;
}
}
fn emit_mb_cabac_p_inter(
fe: &mut FrameEncoder,
cab: &mut CabacEncoder,
cs: &mut CabacState,
mode: u8,
plan: &InterPlan,
mb_x: usize,
mb_y: usize,
num_refs: usize,
) {
let mb_w = fe.mb_w;
let addr = mb_y * mb_w + mb_x;
let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
let left = if mb_x > 0 { Some(addr - 1) } else { None };
let acct = crate::bitacct::enabled();
let mut t0 = if acct { cab.pos() } else { 0 };
cb_mb_type_p_inter(cab, mode);
if mode == 3 {
for &st in &plan.sub_types {
cb_sub_mb_type_p(cab, st);
}
}
if acct {
crate::bitacct::add(crate::bitacct::B::MbType, cab.pos() - t0);
t0 = cab.pos();
}
let mut mvdc = [[0i16; 2]; 30];
let mut refc = [-1i8; 30];
cb_fill_inter_cache(
&cs.mb_ref, &cs.mb_mvd, &mut refc, &mut mvdc, top, left, addr, mb_w,
);
let mut mmvd = [[0i16; 2]; 16];
let mut mref = [0i8; 16];
let layout = p_partition_layout(mode);
if num_refs > 1 {
for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
let r = plan.plan_refs[part];
let s = CB_CACHE30[part_idx];
let ctx0 = (refc[s - 1] > 0) as usize + 2 * (refc[s - 6] > 0) as usize;
cb_ref_idx(cab, ctx0, r as u32);
for &zb in zblocks {
refc[CB_CACHE30[zb]] = r as i8;
}
}
}
if acct {
crate::bitacct::add(crate::bitacct::B::RefIdx, cab.pos() - t0);
t0 = cab.pos();
}
if mode == 3 && plan.sub_types != [0u8; 4] {
let mut k = 0usize;
for p8 in 0..4usize {
for &(part_idx, zblocks) in p_sub_partition_layout(p8, plan.sub_types[p8]) {
cb_emit_mvd_partition(
cab,
part_idx,
zblocks,
&mut mvdc,
&mut refc,
&mut mmvd,
&mut mref,
plan.mvds[k],
plan.plan_refs[p8] as i8,
);
k += 1;
}
}
} else {
for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
cb_emit_mvd_partition(
cab,
part_idx,
zblocks,
&mut mvdc,
&mut refc,
&mut mmvd,
&mut mref,
plan.mvds[part],
plan.plan_refs[part] as i8,
);
}
}
if acct {
crate::bitacct::add(crate::bitacct::B::Mvd, cab.pos() - t0);
}
cs.mb_mvd[addr] = cb_sat_mvd(mmvd);
cs.mb_ref[addr] = mref;
cs.cat[addr] = 100;
let allow8 = plan.sub_types == [0u8; 4];
cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left, allow8);
}
fn cb_emit_inter_residual(
fe: &mut FrameEncoder,
cab: &mut CabacEncoder,
cs: &mut CabacState,
plan: &InterPlan,
mb_x: usize,
mb_y: usize,
addr: usize,
top: Option<usize>,
left: Option<usize>,
allow8: bool,
) {
let w4 = fe.mb_w * 4;
let cbp = plan.cbp;
let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
let acct = crate::bitacct::enabled();
let mut t0 = if acct { cab.pos() } else { 0 };
cb_cbp(
cab,
top.map(|a| cs.mb_cbp[a]),
left.map(|a| cs.mb_cbp[a]),
cbp,
);
if acct {
crate::bitacct::add(crate::bitacct::B::Cbp, cab.pos() - t0);
}
cs.mb_cbp[addr] = cbp as u8;
let t8_present = cbp_luma > 0 && fe.transform_8x8 && allow8;
if t8_present {
let ta = left.map_or(0, |x| cs.mb_t8x8[x] as usize);
let tb = top.map_or(0, |x| cs.mb_t8x8[x] as usize);
cab.encode_decision(399 + ta + tb, plan.t8x8 as u32);
}
cs.mb_t8x8[addr] = t8_present && plan.t8x8;
let mut nzc = cb_build_nzc(&cs.mb_nzc, top, left);
let mut cbfdc = 0u16;
let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
if cbp == 0 {
cs.last_delta_qp = 0;
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
}
} else {
let delta = fe.qp_delta();
if acct {
t0 = cab.pos();
}
cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
if acct {
crate::bitacct::add(crate::bitacct::B::QpDelta, cab.pos() - t0);
t0 = cab.pos();
}
if plan.t8x8 {
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let total = if cbp_luma & (1 << b8) != 0 {
let scan8 = scan_8x8_fwd(&plan.q8[b8]);
cb_residual(
cab,
&mut nzc,
&mut cbfdc,
b8 * 4,
CB_RP_LUMA_8X8,
false,
ndc,
&scan8,
)
} else {
for k in 0..4 {
nzc[CB_NZC_CACHE[b8 * 4 + k]] = 0;
}
0
};
for sy in 0..2 {
for sx in 0..2 {
fe.nnz_y[(mb_y * 4 + b8y * 2 + sy) * w4 + (mb_x * 4 + b8x * 2 + sx)] =
total as u8;
}
}
}
} else {
for id8 in 0..4usize {
for id4 in 0..4usize {
let iz = id8 * 4 + id4;
let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
let total = if cbp_luma & (1 << id8) != 0 {
let sc = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
cb_residual(
cab,
&mut nzc,
&mut cbfdc,
iz,
CB_RP_LUMA_4X4,
false,
ndc,
&sc,
)
} else {
nzc[CB_NZC_CACHE[iz]] = 0;
0
};
fe.nnz_y[by * w4 + bx] = total as u8;
}
}
}
if acct {
crate::bitacct::add(crate::bitacct::B::ResidLuma, cab.pos() - t0);
t0 = cab.pos();
}
cb_emit_chroma_residual(
cab,
fe,
&mut nzc,
&mut cbfdc,
ndc,
false,
cbp_chroma,
&plan.c_dc_levels,
&plan.c_q,
mb_x,
mb_y,
);
if acct {
crate::bitacct::add(crate::bitacct::B::ResidChroma, cab.pos() - t0);
}
}
cs.cbf_dc[addr] = cbfdc;
cs.mb_nzc[addr] = cb_export_nzc(&nzc);
}
fn emit_mb_cabac_p_intra(
fe: &mut FrameEncoder,
cab: &mut CabacEncoder,
cs: &mut CabacState,
plan: &MbPlan,
mb_x: usize,
mb_y: usize,
) {
let mb_w = fe.mb_w;
let addr = mb_y * mb_w + mb_x;
let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
let left = if mb_x > 0 { Some(addr - 1) } else { None };
let acct = crate::bitacct::enabled();
let t0 = if acct { cab.pos() } else { 0 };
cb_mb_type_p_intra(cab, plan);
emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
if acct {
crate::bitacct::add(crate::bitacct::B::IntraBody, cab.pos() - t0);
}
}
fn emit_p_skip_cabac(
cab: &mut CabacEncoder,
cs: &mut CabacState,
addr: usize,
top: Option<usize>,
left: Option<usize>,
) {
let sctx = 11
+ left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
+ top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
let acct = crate::bitacct::enabled();
let t0 = if acct { cab.pos() } else { 0 };
cb_mb_skip(cab, sctx, true);
if acct {
crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - t0);
}
cs.mb_skip[addr] = true;
cs.cat[addr] = 100;
cs.last_delta_qp = 0;
}
fn lme_q_env() -> Option<f64> {
static E: rusty_h264_common::once::OnceLock<Option<f64>> =
rusty_h264_common::once::OnceLock::new();
*E.get_or_init(|| rusty_h264_common::knob("RFF_LME_Q").and_then(|v| v.parse().ok()))
}
fn me_lambda_scale(cfg: &EncoderConfig, sig: &FrameSignals, per_mb_tex: bool) -> f64 {
let hi = match cfg.tune_lme_hi {
Some(v) if v > 0.0 => v,
_ => return cfg.cabac_lambda_scale,
};
if !per_mb_tex && sig.median_var() >= cfg.tune_lme_tex_thresh.unwrap_or(650) {
return cfg.cabac_lambda_scale;
}
if sig.has_ref() {
let mot = cfg.tune_lme_motion_thresh.unwrap_or(26.0);
if sig.gmc_residual() >= mot {
return cfg.cabac_lambda_scale;
}
}
hi
}
pub(crate) fn encode_slice_data_cabac_p(
w: &mut BitWriter,
cfg: &EncoderConfig,
frame: &YuvPlanes<'_>,
qp: u8,
refs: &[crate::RefFrame],
qpo: &[i32],
wp: &[(i32, i32)],
) -> crate::RefFrame {
let mut fe = FrameEncoder::new(cfg);
fe.wp = wp.to_vec();
fe.qp = qp;
fe.qpc = chroma_qp(qp);
fe.cur_qp = qp;
if cfg.cabac_dz_div > 0 {
fe.idz = cfg.cabac_dz_div; }
fe.rdoq_strength = 0.0; {
static T: rusty_h264_common::once::OnceLock<Option<f64>> =
rusty_h264_common::once::OnceLock::new();
if let Some(t) =
*T.get_or_init(|| rusty_h264_common::knob("RFF_RDSKIP_T").and_then(|v| v.parse().ok()))
{
fe.rd_skip = t > 0.0;
fe.rd_skip_fast_t = t;
}
}
let (sy, su, sv) = coded_source(cfg, frame);
let sig = FrameSignals::new(&sy, fe.cw, fe.mb_w, fe.mb_h, refs.first().map(|r| &r.y[..]));
apply_screen_t8_veto(&mut fe, &sig);
fe.rdoq_strength = if sig.grain_signature() || sig.is_screen() {
cfg.cabac_rdoq_p
} else {
0.0
};
let lambda = 0.85 * fe.tune_lambda_scale * crate::fastmath::lambda_qp(qp);
let shape_rd_tex_veto = sig.median_var() > shape_rd_tex_max();
let lme_q = lme_q_env().or(cfg.tune_lme_q).filter(|&q| q > 0.0);
let lme_scale = me_lambda_scale(cfg, &sig, lme_q.is_some());
let lme_mb_thresh: Option<i64> = match lme_q {
Some(q) if lme_scale != cfg.cabac_lambda_scale => Some(sig.var_percentile_thresh(q)),
_ => None,
};
let num_refs = refs.len();
if fe.me_wide && !refs.is_empty() {
let coh = sig.gmc_residual();
if rusty_h264_common::knob("RFF_ME_COH_DBG").is_some() {
eprintln!("ME_COH qp{qp} residual={coh:.2}");
}
if coh < fe.me_wide_coh {
fe.me_wide = false;
}
}
if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
let hr = sig.headroom();
if me_wide_hr_dbg() {
eprintln!("ME_HR qp{qp} headroom={hr:.2}");
}
if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
fe.me_wide = false;
}
}
if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
let (mg, dc) = sig.mgain_dc();
if me_sadt_dbg() {
eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
}
fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
if mv_smooth_mode() == 1 {
fe.mv_smooth = mg >= mv_smooth_t() && dc <= me_sad_dcmax();
}
let smg = split_mg();
if smg > 0.0 {
fe.do_splits = mg >= smg;
}
}
if fe.satd_q > 0.0 {
fe.satd_var_thresh = sig.var_percentile_thresh(fe.satd_q);
}
let mut aq_qp = aq_qp_map(&sig, qp, fe.aq_strength);
apply_mbtree_qpo(&mut aq_qp, qpo); signals::harvest(
&sig,
'P',
qp,
&signals::GateDecisions {
me_wide: fe.me_wide,
sadfp: fe.sadfp,
mv_smooth: fe.mv_smooth,
do_splits: fe.do_splits,
lme_scale,
satd_thresh: fe.satd_var_thresh,
},
);
let mut mb_qpy = enc_scratch::take_qpy();
mb_qpy.clear();
mb_qpy.resize(fe.mb_w * fe.mb_h, qp);
let (sub8_minpay, sub8_learn) = sub8_pay_cfg();
let (mut sub8_seen, mut sub8_gain) = (0usize, 0f64);
let mut sub8_paying = true;
let mut greedy_free = 0usize;
let mut greedy_seen = 0usize;
let mut greedy_on = fe.greedy_min_free == 0;
let greedy_learn = (fe.mb_w * fe.mb_h / 8).max(64);
let mut cab = CabacEncoder::new_with_out(
qp as i32,
cfg.cabac_init_idc,
false,
enc_scratch::take_payload(),
); let mut cs = CabacState::pooled(fe.mb_w * fe.mb_h);
let total = fe.mb_w * fe.mb_h;
let lme_base = rusty_h264_common::fmath::sqrt(lambda);
let grain = sig.grain_signature();
let split_t_v = split_t();
let shape_rd = shape_rd_on().unwrap_or(cfg.tune_shape_rd);
let want_split_knob = sub8x8_split_on() || cfg.tune_sub8x8_split;
let sub8_gveto = sub8_grain_veto_on();
let sub8_rd = sub8_rd_on() || cfg.tune_sub8_rd;
let use_rd_frame = (intra_rd_on() || cfg.tune_intra_rd) && (!intra_rd_grain_gate() || grain);
let acct = crate::bitacct::enabled();
let greedy_zero = fe.greedy_min_free == 0;
let lme_vars = lme_mb_thresh.map(|t| (sig.mb_vars(), t));
let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
for mb_y in 0..fe.mb_h {
for mb_x in 0..fe.mb_w {
let mb_idx = mb_y * fe.mb_w + mb_x;
let addr = mb_idx;
let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
let left = if mb_x > 0 { Some(addr - 1) } else { None };
let mb_qp = aq_qp[mb_idx];
fe.qp = mb_qp;
fe.qpc = chroma_qp(mb_qp);
let lam_mb = if cfg.tune_rd_lambda_mb {
0.85 * fe.tune_lambda_scale * crate::fastmath::lambda_qp(fe.qp)
} else {
lambda
};
let mut inter: Option<InterChoice> = None;
let mut inter_subs = [0u8; 4];
let mut did_skip = false;
if num_refs > 0 {
let mv_skip = fe.skip_mv(mb_x, mb_y);
let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
let skip_c: Option<[[u8; 64]; 2]> = if luma_free || !fe.fast {
Some(fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip))
} else {
None
};
let is_free = luma_free
&& skip_c
.as_ref()
.is_some_and(|c| fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, c));
let skip_sad = if fe.fast {
0
} else {
let (lx, ly) = (mb_x * 16, mb_y * 16);
let mut s = 0u32;
for dy in 0..16 {
let src = &sy[(ly + dy) * fe.cw + lx..][..16];
let p = &skip_y[dy * 16..][..16];
s += src
.iter()
.zip(p)
.map(|(&a, &b)| a.abs_diff(b) as u32)
.sum::<u32>();
}
s
};
greedy_seen += 1;
if greedy_seen >= greedy_learn {
greedy_on = greedy_zero
|| greedy_free * 100 >= greedy_seen * fe.greedy_min_free as usize;
}
if is_free {
fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, skip_c.as_ref().unwrap());
if !fe.fast {
fe.mb_was_skip[mb_idx] = true;
fe.mb_skip_sad[mb_idx] = skip_sad;
}
greedy_free += 1;
did_skip = true;
} else {
signals::census::work(signals::census::W_MB_CODED);
let (lx, ly) = (mb_x * 16, mb_y * 16);
let nb = {
let _g = rusty_h264_common::prof::scope(
rusty_h264_common::prof::Stage::EncMvPred,
);
fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
};
let lme = lme_base
* match lme_vars {
Some((v, t)) if v[mb_idx] >= t => cfg.cabac_lambda_scale,
_ => lme_scale,
};
if fe.fast {
fe.mb_use_satd =
fe.satd_q > 0.0 && sig.mb_vars()[mb_idx] >= fe.satd_var_thresh;
let (r16, mv16, cost_inter) =
fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
let cost_intra = if fe.mb_use_satd {
fe.best_i16_satd(&sy, mb_x, mb_y)
} else {
fe.best_i16_sad(&sy, mb_x, mb_y)
} + (lme * fe.tune_intra_penalty) as i64;
inter = if cost_intra < cost_inter {
None
} else {
Some((0, vec![(r16, mv16)]))
};
} else {
if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, skip_c.as_ref().unwrap());
fe.mb_was_skip[mb_idx] = true;
fe.mb_skip_sad[mb_idx] = skip_sad;
did_skip = true;
} else {
let (r16, mv16, c16) =
fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
let mut best_c = c16;
let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
let mut pick_subs = [0u8; 4];
let mut shape_cands: Vec<(u8, Vec<(i32, (i32, i32))>, [u8; 4])> =
Vec::new();
let mut shape_rd_intra: Option<bool> = None;
if shape_rd {
shape_cands.push((0u8, vec![(r16, mv16)], [0u8; 4]));
}
const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
if fe.do_splits
&& c16 > split_gate
&& (split_t_v <= 0.0 || (c16 as f64) >= split_t_v * lme)
{
let (rt, mvt, ct) = fe.best_part(
refs,
&sy,
&nb,
num_refs,
lx,
ly,
16,
8,
&[mv16],
lme,
);
let (rb, mvb, cb) = fe.best_part(
refs,
&sy,
&nb,
num_refs,
lx,
ly + 8,
16,
8,
&[mv16],
lme,
);
let (rl, mvl, cl) = fe.best_part(
refs,
&sy,
&nb,
num_refs,
lx,
ly,
8,
16,
&[mv16],
lme,
);
let (rr, mvr, cr) = fe.best_part(
refs,
&sy,
&nb,
num_refs,
lx + 8,
ly,
8,
16,
&[mv16],
lme,
);
if shape_rd {
shape_cands.push((1u8, vec![(rt, mvt), (rb, mvb)], [0u8; 4]));
shape_cands.push((2u8, vec![(rl, mvl), (rr, mvr)], [0u8; 4]));
}
if ct + cb < best_c {
best_c = ct + cb;
pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
}
if cl + cr < best_c {
best_c = cl + cr;
pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
}
let want_split = want_split_knob && num_refs == 1;
let grain_veto = sub8_gveto && grain;
if want_split {
signals::census::bump(signals::census::SUB8_GRAIN, grain_veto);
}
let split_arm = want_split && !grain_veto && sub8_paying;
if fe.sub8x8 && !split_arm {
let mut c8 = (lme * 4.0) as i64;
let mut p8 = Vec::with_capacity(4);
for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
let (r, mv, c) = fe.best_part(
refs,
&sy,
&nb,
num_refs,
lx + qx,
ly + qy,
8,
8,
&[mv16],
lme,
);
c8 += c;
p8.push((r, mv));
}
if shape_rd {
shape_cands.push((3u8, p8.clone(), [0u8; 4]));
}
if c8 < best_c {
best_c = c8;
pick = Some((3u8, p8));
}
} else if fe.sub8x8 {
debug_assert!(split_arm);
let mut c8 = 0i64;
let mut p8: Vec<(i32, (i32, i32))> = Vec::with_capacity(4);
let mut subs = [0u8; 4];
let mut p8_flat: Vec<(i32, (i32, i32))> = Vec::with_capacity(4);
let mut c8_flat = 0i64;
let mut hrows: Vec<sub8_harvest::Row> = Vec::new();
for (q, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)]
.iter()
.enumerate()
{
let (r, mv, c) = fe.best_part(
refs,
&sy,
&nb,
num_refs,
lx + qx,
ly + qy,
8,
8,
&[mv16],
lme,
);
let j8 = c + lme as i64; let mut q_best = j8;
let mut q_parts: Vec<(i32, (i32, i32))> = vec![(r, mv)];
let mut q_st = 0u8;
let mut j_split = i64::MAX; {
for st in 1u8..=3 {
let bins = if st == 1 { 2.0 } else { 3.0 };
let mut cs = (lme * bins) as i64;
let mut ps: Vec<(i32, (i32, i32))> =
Vec::with_capacity(4);
for &(srx, sry, srw, srh) in sub_mb_partitions_p(st)
{
let (rr, mm, cc) = fe.best_part(
refs,
&sy,
&nb,
num_refs,
lx + qx + srx,
ly + qy + sry,
srw,
srh,
&[mv16, mv],
lme,
);
cs += cc;
ps.push((rr, mm));
}
j_split = j_split.min(cs);
if cs < q_best {
q_best = cs;
q_parts = ps;
q_st = st;
}
}
}
if sub8_harvest::enabled() {
hrows.push(sub8_harvest::Row {
j8,
jsplit: j_split,
st: q_st,
lme,
mbvar: sig.mb_vars()[mb_idx],
mvdiv: (mv.0 - mv16.0).abs()
+ (mv.1 - mv16.1).abs(),
});
}
signals::census::bump(
signals::census::SUB8_SPLIT,
q_st != 0,
);
c8 += q_best;
c8_flat += j8;
subs[q] = q_st;
p8.extend(q_parts);
p8_flat.push((r, mv));
}
let mut split_gain = 0f64;
if sub8_rd && subs != [0u8; 4] {
let mut snap = enc_scratch::take_snap_a();
fe.save_mb_into(mb_x, mb_y, &mut snap);
let pa = fe.plan_inter_mb(
refs, &sy, &su, &sv, mb_x, mb_y, 3, &p8, None, subs,
);
let ja = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y) as f64
+ lam_mb * plan_rate_bits(&pa, subs);
let pb = fe.plan_inter_mb(
refs, &sy, &su, &sv, mb_x, mb_y, 3, &p8_flat, None,
[0u8; 4],
);
let jb = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y) as f64
+ lam_mb * plan_rate_bits(&pb, [0u8; 4]);
fe.load_mb(mb_x, mb_y, &snap);
enc_scratch::put_snap_a(snap);
signals::census::bump(
signals::census::SUB8_RD_REVERT,
jb <= ja,
);
if sub8_regret::enabled() {
sub8_regret::record(
ja,
jb,
lam_mb,
subs.iter().filter(|&&x| x != 0).count(),
);
}
if jb <= ja {
subs = [0u8; 4];
p8 = core::mem::take(&mut p8_flat);
c8 = c8_flat;
} else {
split_gain = (jb - ja).max(0.0) / lam_mb.max(1e-9);
}
sub8_harvest::flush(&hrows, subs != [0u8; 4]);
} else {
sub8_harvest::flush(&hrows, subs != [0u8; 4]);
}
if sub8_minpay > 0 {
sub8_seen += 1;
sub8_gain += split_gain;
if sub8_seen >= sub8_learn {
sub8_paying =
sub8_gain >= sub8_seen as f64 * sub8_minpay as f64;
}
}
if shape_rd {
shape_cands.push((3u8, p8.clone(), subs));
}
if c8 < best_c {
best_c = c8;
pick = Some((3u8, p8));
pick_subs = subs;
}
}
}
if shape_rd && shape_cands.len() > 1 && !shape_rd_tex_veto {
let mut snap0 = enc_scratch::take_snap_a();
fe.save_mb_into(mb_x, mb_y, &mut snap0);
let mut best_j = f64::INFINITY;
let mut best_i = 0usize;
for (i, (m, parts, subs)) in shape_cands.iter().enumerate() {
let pl = fe.plan_inter_mb(
refs, &sy, &su, &sv, mb_x, mb_y, *m, parts, None, *subs,
);
let j = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y) as f64
+ lam_mb * plan_rate_bits(&pl, *subs);
if j < best_j {
best_j = j;
best_i = i;
}
}
fe.load_mb(mb_x, mb_y, &snap0);
enc_scratch::put_snap_a(snap0);
let (m, parts, subs) = shape_cands[best_i].clone();
signals::census::bump(
signals::census::SHAPE_RD_FLIP,
pick.as_ref().map(|p| p.0) != Some(m),
);
pick = Some((m, parts));
pick_subs = subs;
let (ssd_i, bits_i) =
fe.trial_intra(&sy, &su, &sv, mb_x, mb_y, true);
let j_intra = ssd_i as f64 + lam_mb * bits_i as f64;
shape_rd_intra = Some(j_intra < best_j);
}
if fe.sp_defer.get() {
if let Some((mode, parts)) = pick.as_mut() {
let split_regions: Vec<(usize, usize, usize, usize)> =
if *mode == 3 && pick_subs != [0u8; 4] {
(0..4)
.flat_map(|p8: usize| {
let (bx, by) = ((p8 % 2) * 8, (p8 / 2) * 8);
sub_mb_partitions_p(pick_subs[p8]).iter().map(
move |&(sx, sy, sw, sh)| {
(bx + sx, by + sy, sw, sh)
},
)
})
.collect()
} else {
Vec::new()
};
let regions: &[(usize, usize, usize, usize)] =
if !split_regions.is_empty() {
&split_regions
} else {
match mode {
1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
3 => &[
(0, 0, 8, 8),
(8, 0, 8, 8),
(0, 8, 8, 8),
(8, 8, 8, 8),
],
_ => &[(0, 0, 16, 16)],
}
};
let mut tot = if *mode == 3 {
if pick_subs == [0u8; 4] {
(lme * 4.0) as i64
} else {
pick_subs
.iter()
.map(|&st| {
if st == 0 {
lme as i64
} else {
(lme * if st == 1 { 2.0 } else { 3.0 })
as i64
}
})
.sum()
}
} else {
0
};
for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
let (r, mv) = parts[i];
let (m2, c2) = fe.refine_part(
refs,
&sy,
&nb,
num_refs,
lx + qx,
ly + qy,
pw,
ph,
lme,
r,
mv,
);
parts[i] = (r, m2);
tot += c2;
}
best_c = tot;
}
}
let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
+ (lme * fe.tune_intra_penalty) as i64;
let satd_says_intra = c_intra < best_c;
let use_rd = use_rd_frame;
let take_intra = if let Some(rd_says) = shape_rd_intra {
rd_says
} else if use_rd {
let (ssd_i, bits_i) =
fe.trial_intra(&sy, &su, &sv, mb_x, mb_y, true);
let j_intra = ssd_i as f64 + lambda * bits_i as f64;
let j_inter = match pick.as_ref() {
Some((m, parts)) => {
let mut snap = enc_scratch::take_snap_a();
fe.save_mb_into(mb_x, mb_y, &mut snap);
let pl = fe.plan_inter_mb(
refs, &sy, &su, &sv, mb_x, mb_y, *m, parts, None,
pick_subs,
);
let j = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y) as f64
+ lambda * plan_rate_bits(&pl, pick_subs);
fe.load_mb(mb_x, mb_y, &snap);
enc_scratch::put_snap_a(snap);
j
}
None => f64::INFINITY,
};
j_intra < j_inter
} else {
satd_says_intra
};
if use_rd {
signals::census::bump(
signals::census::INTRA_RD_FLIP,
take_intra != satd_says_intra,
);
}
inter = if take_intra { None } else { pick };
if matches!(inter, Some((3, _))) {
inter_subs = pick_subs; }
fe.mb_was_skip[mb_idx] = false;
fe.mb_skip_sad[mb_idx] = skip_sad;
}
}
}
if !did_skip
&& fe.rd_skip
&& fe.rd_skip_fast_t > 0.0
&& inter.is_some()
&& greedy_seen >= greedy_learn
&& greedy_free * 100 >= greedy_seen * fe.rd_skip_min_free as usize
{
let skip_cp = if fe.fast {
fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
} else {
skip_c.unwrap() };
let ssd_s = fe.pred_ssd(&sy, &su, &sv, mb_x, mb_y, &skip_y, &skip_cp);
if (ssd_s as f64) <= lambda * fe.rd_skip_fast_t {
fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
if !fe.fast {
fe.mb_was_skip[mb_idx] = true;
fe.mb_skip_sad[mb_idx] = skip_sad;
}
did_skip = true;
}
}
}
if did_skip {
emit_p_skip_cabac(&mut cab, &mut cs, addr, top, left);
mb_qpy[mb_idx] = fe.cur_qp;
cb_term_acct(&mut cab, mb_idx + 1 == total, acct);
continue;
}
let sctx = 11
+ left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
+ top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
let tskip = if acct { cab.pos() } else { 0 };
cb_mb_skip(&mut cab, sctx, false);
if acct {
crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - tskip);
}
cs.mb_skip[addr] = false;
match inter {
Some((mode, parts)) => {
let plan = fe.plan_inter_mb(
refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts, None, inter_subs,
);
let _ge =
rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
emit_mb_cabac_p_inter(
&mut fe, &mut cab, &mut cs, mode, &plan, mb_x, mb_y, num_refs,
);
signals::census::commit_mb(plan.t8x8);
}
None => {
fe.intra_in_p = true;
let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
let _ge =
rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
let t8 = plan.i8.is_some() && plan.use_i4;
emit_mb_cabac_p_intra(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
signals::census::commit_mb(t8);
}
}
mb_qpy[mb_idx] = fe.cur_qp;
cb_term_acct(&mut cab, mb_idx + 1 == total, acct);
}
}
enc_scratch::put_cs(cs);
while !w.is_byte_aligned() {
w.write_bit(true);
}
let payload = cab.into_bytes();
w.write_aligned_bytes(&payload);
enc_scratch::put_payload(payload);
let mut ref_id = enc_scratch::take_refid();
ref_id.clear();
ref_id.extend(
fe.ref_idx_y
.iter()
.map(|&r| if r >= 0 { r } else { i8::MIN }),
);
let info = rusty_h264_common::deblock::BlockInfo {
inter: &fe.inter_y,
nnz: &fe.nnz_y,
mv: &fe.mv_y,
ref_id: &ref_id,
mv1: &[],
ref_id1: &[],
w4: fe.mb_w * 4,
t8x8: &[],
poc0: &[],
poc1: &[],
bs: &[],
kind: &[],
};
rusty_h264_common::deblock::filter_frame(
&mut fe.rec_y,
&mut fe.rec_u,
&mut fe.rec_v,
fe.mb_w,
fe.mb_h,
&mb_qpy,
0,
0,
0,
&info,
);
enc_scratch::put_qpy(mb_qpy);
enc_scratch::put_refid(ref_id);
let w4 = fe.mb_w * 4;
crate::RefFrame {
y: fe.rec_y,
u: fe.rec_u,
v: fe.rec_v,
poc: 0,
frame_num: 0,
mv: fe.mv_y,
ref_idx: fe.ref_idx_y,
mv1: Vec::new(),
ref_idx1: Vec::new(),
w4,
hpel: crate::HpelOnce::new(),
}
}
#[inline]
fn cb_term_acct(cab: &mut CabacEncoder, last: bool, acct: bool) {
let tt = if acct { cab.pos() } else { 0 };
cab.encode_terminate(last);
if acct {
crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
}
}
fn cb_fill_inter_cache(
mb_ref: &[[i8; 16]],
mb_mvd: &[[[u8; 2]; 16]],
refc: &mut [i8; 30],
mvdc: &mut [[i16; 2]; 30],
top: Option<usize>,
left: Option<usize>,
addr: usize,
mb_w: usize,
) {
if let Some(l) = left {
let (rr, rm) = (&mb_ref[l], &mb_mvd[l]);
for (ci, bi) in [(6usize, 3usize), (12, 7), (18, 11), (24, 15)] {
refc[ci] = rr[bi];
mvdc[ci] = [rm[bi][0] as i16, rm[bi][1] as i16];
}
}
if let Some(t) = top {
for (ci, bi) in [(1usize, 12usize), (2, 13), (3, 14), (4, 15)] {
refc[ci] = mb_ref[t][bi];
mvdc[ci] = [mb_mvd[t][bi][0] as i16, mb_mvd[t][bi][1] as i16];
}
}
let mb_x = addr % mb_w;
let mb_y = addr / mb_w;
if mb_x > 0 && mb_y > 0 {
let a = addr - mb_w - 1;
(refc[0], mvdc[0]) =
(mb_ref[a][15], [mb_mvd[a][15][0] as i16, mb_mvd[a][15][1] as i16]);
}
if mb_y > 0 && mb_x + 1 < mb_w {
let a = addr - mb_w + 1;
(refc[5], mvdc[5]) =
(mb_ref[a][12], [mb_mvd[a][12][0] as i16, mb_mvd[a][12][1] as i16]);
}
}
pub fn b_part_mb_type(p0: u8, p1: u8, mvmode: u8) -> u32 {
let base = match (p0, p1) {
(1, 1) => 4,
(2, 2) => 6,
(1, 2) => 8,
(2, 1) => 10,
(1, 3) => 12,
(2, 3) => 14,
(3, 1) => 16,
(3, 2) => 18,
_ => 20, };
base + if mvmode == 2 { 1 } else { 0 }
}
fn b_part_layout(
mvmode: u8,
) -> (
[(usize, usize, usize, usize); 2],
[(usize, &'static [usize]); 2],
) {
if mvmode == 1 {
(
[(0, 0, 16, 8), (0, 8, 16, 8)],
[
(0, &[0, 1, 2, 3, 4, 5, 6, 7][..]),
(8, &[8, 9, 10, 11, 12, 13, 14, 15][..]),
],
)
} else {
(
[(0, 0, 8, 16), (8, 0, 8, 16)],
[
(0, &[0, 1, 2, 3, 8, 9, 10, 11][..]),
(4, &[4, 5, 6, 7, 12, 13, 14, 15][..]),
],
)
}
}
pub fn cb_mb_type_b(cab: &mut CabacEncoder, ctx_inc: usize, mb_type: u32) {
const B: usize = 27;
if mb_type == 0 {
cab.encode_decision(B + ctx_inc, 0); return;
}
cab.encode_decision(B + ctx_inc, 1);
if mb_type <= 2 {
cab.encode_decision(B + 3, 0);
cab.encode_decision(B + 5, mb_type - 1); return;
}
cab.encode_decision(B + 3, 1);
let (m4, extra) = if mb_type <= 10 {
(mb_type - 3, None)
} else if mb_type == 11 {
(14, None) } else if mb_type == 22 {
(15, None) } else {
let v = mb_type + 4;
(v >> 1, Some(v & 1))
};
cab.encode_decision(B + 4, (m4 >> 3) & 1);
cab.encode_decision(B + 5, (m4 >> 2) & 1);
cab.encode_decision(B + 5, (m4 >> 1) & 1);
cab.encode_decision(B + 5, m4 & 1);
if let Some(e) = extra {
cab.encode_decision(B + 5, e);
}
}
const CB_ALL16: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
fn cb_mb_type_b_intra(cab: &mut CabacEncoder, ctx_inc: usize, plan: &MbPlan) {
const B: usize = 27;
cab.encode_decision(B + ctx_inc, 1); cab.encode_decision(B + 3, 1); cab.encode_decision(B + 4, 1); cab.encode_decision(B + 5, 1);
cab.encode_decision(B + 5, 0);
cab.encode_decision(B + 5, 1);
const O: usize = 32; if plan.use_i4 {
cab.encode_decision(O, 0); return;
}
cab.encode_decision(O, 1);
cab.encode_terminate(false); cab.encode_decision(O + 1, plan.i16_cbp15 as u32);
if plan.cbp_chroma != 0 {
cab.encode_decision(O + 2, 1);
cab.encode_decision(O + 2, (plan.cbp_chroma == 2) as u32);
} else {
cab.encode_decision(O + 2, 0);
}
cab.encode_decision(O + 3, (plan.i16_mode as u32 >> 1) & 1);
cab.encode_decision(O + 3, plan.i16_mode as u32 & 1);
}
fn emit_mb_cabac_b(
fe: &mut FrameEncoder,
cab: &mut CabacEncoder,
cs: &mut CabacState,
dir: u8,
bsplit: Option<(u8, [(u8, (i32, i32), (i32, i32)); 2])>,
plan: &InterPlan,
mb_x: usize,
mb_y: usize,
) {
let mb_w = fe.mb_w;
let addr = mb_y * mb_w + mb_x;
let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
let left = if mb_x > 0 { Some(addr - 1) } else { None };
let bci = left.map_or(0, |a| (!cs.mb_direct[a]) as usize)
+ top.map_or(0, |a| (!cs.mb_direct[a]) as usize);
let bmt = match bsplit {
Some((mvmode, parts2)) => b_part_mb_type(parts2[0].0, parts2[1].0, mvmode),
None => dir as u32,
};
cb_mb_type_b(cab, bci, bmt);
let mut mvdc0 = [[0i16; 2]; 30];
let mut refc0 = [-1i8; 30];
let mut mvdc1 = [[0i16; 2]; 30];
let mut refc1 = [-1i8; 30];
cb_fill_inter_cache(
&cs.mb_ref, &cs.mb_mvd, &mut refc0, &mut mvdc0, top, left, addr, mb_w,
);
cb_fill_inter_cache(
&cs.mb_ref1,
&cs.mb_mvd1,
&mut refc1,
&mut mvdc1,
top,
left,
addr,
mb_w,
);
let mut mmvd0 = [[0i16; 2]; 16];
let mut mref0 = [-1i8; 16];
let mut mmvd1 = [[0i16; 2]; 16];
let mut mref1 = [-1i8; 16];
let (use0, use1) = (dir == 1 || dir == 3, dir == 2 || dir == 3);
if let Some((mvmode, parts2)) = bsplit {
let (_, zb) = b_part_layout(mvmode);
let mut k = 0;
for list in 0..2 {
for part in 0..2 {
let pred = parts2[part].0;
let used = if list == 0 {
pred == 1 || pred == 3
} else {
pred == 2 || pred == 3
};
if !used {
continue;
}
let (pidx, blocks) = zb[part];
if list == 0 {
cb_emit_mvd_partition(
cab,
pidx,
blocks,
&mut mvdc0,
&mut refc0,
&mut mmvd0,
&mut mref0,
plan.mvds[k],
0,
);
} else {
cb_emit_mvd_partition(
cab,
pidx,
blocks,
&mut mvdc1,
&mut refc1,
&mut mmvd1,
&mut mref1,
plan.mvds[k],
0,
);
}
k += 1;
}
}
} else if dir == 0 {
mref0 = [0i8; 16];
mref1 = [0i8; 16];
} else {
let mut k = 0;
if use0 {
cb_emit_mvd_partition(
cab,
0,
&CB_ALL16,
&mut mvdc0,
&mut refc0,
&mut mmvd0,
&mut mref0,
plan.mvds[k],
0,
);
k += 1;
}
if use1 {
cb_emit_mvd_partition(
cab,
0,
&CB_ALL16,
&mut mvdc1,
&mut refc1,
&mut mmvd1,
&mut mref1,
plan.mvds[k],
0,
);
}
}
cs.mb_mvd[addr] = cb_sat_mvd(mmvd0);
cs.mb_ref[addr] = mref0;
cs.mb_mvd1[addr] = cb_sat_mvd(mmvd1);
cs.mb_ref1[addr] = mref1;
cs.mb_direct[addr] = dir == 0 && bsplit.is_none();
cs.cat[addr] = 100;
let allow8 = true;
cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left, allow8);
}
fn emit_b_skip_cabac(
cab: &mut CabacEncoder,
cs: &mut CabacState,
addr: usize,
top: Option<usize>,
left: Option<usize>,
) {
let sctx = 24
+ left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
+ top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
let acct = crate::bitacct::enabled();
let t0 = if acct { cab.pos() } else { 0 };
cb_mb_skip(cab, sctx, true);
if acct {
crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - t0);
}
cs.mb_skip[addr] = true;
cs.cat[addr] = 100;
cs.mb_direct[addr] = true;
cs.mb_ref[addr] = [0i8; 16];
cs.mb_ref1[addr] = [0i8; 16];
cs.last_delta_qp = 0;
}
pub struct SeqFastPath;
#[cfg(any(feature = "std", test))]
::std::thread_local! {
static SEQ_FAST: core::cell::Cell<bool> = const { core::cell::Cell::new(false) };
}
#[cfg(any(feature = "std", test))]
fn seq_fast_store(on: bool) {
SEQ_FAST.with(|f| f.set(on));
}
#[cfg(any(feature = "std", test))]
fn seq_fast_load() -> bool {
SEQ_FAST.with(|f| f.get())
}
#[cfg(not(any(feature = "std", test)))]
static SEQ_FAST: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false);
#[cfg(not(any(feature = "std", test)))]
fn seq_fast_store(on: bool) {
SEQ_FAST.store(on, core::sync::atomic::Ordering::Relaxed);
}
#[cfg(not(any(feature = "std", test)))]
fn seq_fast_load() -> bool {
SEQ_FAST.load(core::sync::atomic::Ordering::Relaxed)
}
impl SeqFastPath {
pub(crate) fn set(on: bool) -> Self {
seq_fast_store(on);
SeqFastPath
}
pub(crate) fn get() -> bool {
seq_fast_load()
}
}
impl Drop for SeqFastPath {
fn drop(&mut self) {
seq_fast_store(false);
}
}
pub mod bstats {
#[allow(unused_imports)]
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use core::sync::atomic::Ordering::Relaxed;
use rusty_h264_common::atomic::AtomicU64;
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
pub static SKIP: AtomicU64 = AtomicU64::new(0);
pub static CODED: AtomicU64 = AtomicU64::new(0);
pub static DIRWIN: AtomicU64 = AtomicU64::new(0);
pub static SPLIT: AtomicU64 = AtomicU64::new(0);
pub static INTRA: AtomicU64 = AtomicU64::new(0);
pub fn on() -> bool {
static E: rusty_h264_common::once::OnceLock<bool> =
rusty_h264_common::once::OnceLock::new();
*E.get_or_init(|| rusty_h264_common::knob("RFF_BSTATS").is_some())
}
pub fn bump(c: &AtomicU64) {
if on() {
c.fetch_add(1, Relaxed);
}
}
pub fn dump() {
let (s, c) = (SKIP.load(Relaxed), CODED.load(Relaxed));
let t = (s + c).max(1) as f64;
eprintln!(
"B-slice census: B_Skip {:.1}% not-skipped {:.1}% direct-wins-of-coded {:.1}% 16x8/8x16-of-coded {:.1}% intra-of-coded {:.1}% (n={})",
s as f64 * 100.0 / t, c as f64 * 100.0 / t,
DIRWIN.load(Relaxed) as f64 * 100.0 / c.max(1) as f64,
SPLIT.load(Relaxed) as f64 * 100.0 / c.max(1) as f64,
INTRA.load(Relaxed) as f64 * 100.0 / c.max(1) as f64, s + c
);
}
}
pub(crate) fn encode_slice_data_cabac_b(
w: &mut BitWriter,
cfg: &EncoderConfig,
frame: &YuvPlanes<'_>,
qp: u8,
poc: i32,
l0: &crate::RefFrame,
l1: &crate::RefFrame,
qpo: &[i32],
is_ref: bool,
) -> Option<crate::RefFrame> {
let mut fe = FrameEncoder::new(cfg);
fe.qp = qp;
fe.qpc = chroma_qp(qp);
fe.cur_qp = qp;
let mut mb_qpy = enc_scratch::take_qpy();
mb_qpy.clear();
mb_qpy.resize(cfg.mb_width() * cfg.mb_height(), qp);
if cfg.cabac_dz_div > 0 {
fe.idz = cfg.cabac_dz_div; }
fe.rdoq_strength = cfg.cabac_rdoq_b;
fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
let (sy, su, sv) = coded_source(cfg, frame);
let sig = FrameSignals::new(&sy, fe.cw, fe.mb_w, fe.mb_h, Some(&l0.y[..]));
apply_screen_t8_veto(&mut fe, &sig);
let lambda = 0.85 * fe.tune_lambda_scale * crate::fastmath::lambda_qp(qp);
let lme_scale = me_lambda_scale(cfg, &sig, false);
let lme = rusty_h264_common::fmath::sqrt(lambda) * lme_scale;
let refs = core::slice::from_ref(l0);
if fe.satd_q > 0.0 {
fe.satd_var_thresh = sig.var_percentile_thresh(fe.satd_q);
}
let mut aq_qp = aq_qp_map(&sig, qp, fe.aq_strength);
apply_mbtree_qpo(&mut aq_qp, qpo); signals::harvest(
&sig,
'B',
qp,
&signals::GateDecisions {
lme_scale,
satd_thresh: fe.satd_var_thresh,
..Default::default()
},
);
let mut cab = CabacEncoder::new_with_out(
qp as i32,
cfg.cabac_init_idc,
false,
enc_scratch::take_payload(),
);
let mut cs = CabacState::pooled(fe.mb_w * fe.mb_h);
let total = fe.mb_w * fe.mb_h;
let acct = crate::bitacct::enabled();
let bskip_t = rusty_h264_common::knob("RFF_BSKIP_T")
.and_then(|v| v.parse::<f64>().ok())
.or(cfg.tune_bskip_rd)
.unwrap_or(0.0);
let bskip_busy_pct = rusty_h264_common::knob("RFF_BSKIP_BUSY")
.and_then(|v| v.parse::<usize>().ok())
.or(cfg.tune_bskip_busy_pct)
.unwrap_or(60);
let (mut b_seen, mut b_free) = (0usize, 0usize);
let bsplit_env = rusty_h264_common::knob("RFF_BSPLIT").and_then(|v| v.parse::<u32>().ok());
let bsplit_on = bsplit_env.map(|v| v == 1).unwrap_or(cfg.tune_b_split);
let bsplit_probe = bsplit_env.filter(|&v| v >= 2).unwrap_or(0);
let (mut b_coded, mut b_dirwin) = (0usize, 0usize);
let bskip_dirwin_pct = rusty_h264_common::knob("RFF_BSKIP_DIRWIN")
.and_then(|v| v.parse::<usize>().ok())
.or(cfg.tune_bskip_dirwin_pct)
.unwrap_or(10);
for mb_y in 0..fe.mb_h {
for mb_x in 0..fe.mb_w {
let mb_idx = mb_y * fe.mb_w + mb_x;
let addr = mb_idx;
let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
let left = if mb_x > 0 { Some(addr - 1) } else { None };
let mb_qp = aq_qp[mb_idx];
fe.qp = mb_qp;
fe.qpc = chroma_qp(mb_qp);
let (lx, ly) = (mb_x * 16, mb_y * 16);
let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
fe.mb_use_satd =
fe.satd_q > 0.0 && sig.mb_vars()[mb_y * fe.mb_w + mb_x] >= fe.satd_var_thresh;
let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
let free_skip = fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
&& fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc);
b_seen += 1;
if free_skip {
b_free += 1;
}
if free_skip {
bstats::bump(&bstats::SKIP);
fe.commit_direct_motion(mb_x, mb_y, &dmotion);
fe.commit_pred_pixels(mb_x, mb_y, &dp, &dc);
emit_b_skip_cabac(&mut cab, &mut cs, addr, top, left);
mb_qpy[mb_idx] = fe.cur_qp;
cb_term_acct(&mut cab, mb_idx + 1 == total, acct);
continue;
}
bstats::bump(&bstats::CODED);
let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
let r_bi = mvd_bits(mv0.0 - pmv0.0)
+ mvd_bits(mv0.1 - pmv0.1)
+ mvd_bits(mv1.0 - pmv1.0)
+ mvd_bits(mv1.1 - pmv1.1);
let j_bi = d_bi + (lme * r_bi as f64) as i64;
let (mut dir, mut best) = (0u8, d_direct);
if j0 < best {
dir = 1;
best = j0;
}
if j1 < best {
dir = 2;
best = j1;
}
if j_bi < best {
dir = 3;
best = j_bi;
}
if dir == 0 {
bstats::bump(&bstats::DIRWIN);
}
b_coded += 1;
if dir == 0 {
b_dirwin += 1;
}
if bskip_t > 0.0
&& dir == 0
&& b_seen >= 32
&& b_free * 100 < b_seen * bskip_busy_pct
&& b_coded >= 32
&& b_dirwin * 100 >= b_coded * bskip_dirwin_pct
&& (d_direct as f64) <= bskip_t * lambda
{
bstats::bump(&bstats::SKIP);
fe.commit_direct_motion(mb_x, mb_y, &dmotion);
fe.commit_pred_pixels(mb_x, mb_y, &dp, &dc);
emit_b_skip_cabac(&mut cab, &mut cs, addr, top, left);
mb_qpy[mb_idx] = fe.cur_qp;
cb_term_acct(&mut cab, mb_idx + 1 == total, acct);
continue;
}
let sctx = 24
+ left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
+ top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
let tskip = if acct { cab.pos() } else { 0 };
cb_mb_skip(&mut cab, sctx, false);
if acct {
crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - tskip);
}
cs.mb_skip[addr] = false;
let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y) + (lme * fe.tune_intra_penalty) as i64;
if c_intra < best {
bstats::bump(&bstats::INTRA);
fe.intra_in_p = true;
let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
let bci = left.map_or(0, |a| (!cs.mb_direct[a]) as usize)
+ top.map_or(0, |a| (!cs.mb_direct[a]) as usize);
cb_mb_type_b_intra(&mut cab, bci, &plan);
emit_intra_body_cabac(
&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y, addr, top, left,
);
mb_qpy[mb_idx] = fe.cur_qp;
cb_term_acct(&mut cab, mb_idx + 1 == total, acct);
continue;
}
let mut bsplit: Option<(u8, [(u8, (i32, i32), (i32, i32)); 2])> = None;
if bsplit_probe > 0 && dir != 0 {
let m = if bsplit_probe == 2 { 1u8 } else { 2u8 };
bsplit = Some((m, [(dir, mv0, mv1); 2]));
} else if bsplit_on {
for mvmode in 1u8..=2 {
let (rects, _) = b_part_layout(mvmode);
let mut cand = [(0u8, (0i32, 0i32), (0i32, 0i32)); 2];
let mut jsum = 0i64;
for (part, &(rx, ry, rw, rh)) in rects.iter().enumerate() {
let (px, py) = (lx + rx, ly + ry);
let (m0, c0) =
fe.motion_search(l0, &sy, px, py, rw, rh, &[pmv0], lme, None);
let (m1, c1) =
fe.motion_search(l1, &sy, px, py, rw, rh, &[pmv1], lme, None);
let dbi = fe.bi_dist_rect(l0, l1, &sy, px, py, rw, rh, m0, m1);
let rbi = mvd_bits(m0.0 - pmv0.0)
+ mvd_bits(m0.1 - pmv0.1)
+ mvd_bits(m1.0 - pmv1.0)
+ mvd_bits(m1.1 - pmv1.1);
let jbi = dbi + (lme * rbi as f64) as i64;
let (mut bp, mut bj) = (1u8, c0);
if c1 < bj {
bp = 2;
bj = c1;
}
if jbi < bj {
bp = 3;
bj = jbi;
}
cand[part] = (bp, m0, m1);
jsum += bj;
}
let jsplit = jsum + (lme * 4.0) as i64;
if jsplit < best {
best = jsplit;
bsplit = Some((mvmode, cand));
}
}
if bsplit.is_some() {
bstats::bump(&bstats::SPLIT);
}
}
let bspec = if let Some((mvmode, parts2)) = bsplit {
BInter {
dir,
l1,
mv0,
mv1,
mvmode,
parts2,
}
} else {
BInter {
dir,
l1,
mv0,
mv1,
mvmode: 0,
parts2: [(0, (0, 0), (0, 0)); 2],
}
};
let plan = fe.plan_inter_mb(
refs,
&sy,
&su,
&sv,
mb_x,
mb_y,
0,
&[],
Some(bspec),
[0u8; 4],
);
emit_mb_cabac_b(&mut fe, &mut cab, &mut cs, dir, bsplit, &plan, mb_x, mb_y);
mb_qpy[mb_idx] = fe.cur_qp;
cb_term_acct(&mut cab, mb_idx + 1 == total, acct);
}
}
enc_scratch::put_cs(cs);
while !w.is_byte_aligned() {
w.write_bit(true);
}
let payload = cab.into_bytes();
w.write_aligned_bytes(&payload);
enc_scratch::put_payload(payload);
if !is_ref {
enc_scratch::put_qpy(mb_qpy);
return None;
}
let mut ref_id = enc_scratch::take_refid();
ref_id.clear();
ref_id.extend(
fe.ref_idx_y
.iter()
.map(|&r| if r >= 0 { r } else { i8::MIN }),
);
let mut ref_id1 = enc_scratch::take_refid1();
ref_id1.clear();
ref_id1.extend(
fe.ref_idx1_y
.iter()
.map(|&r| if r >= 0 { r } else { i8::MIN }),
);
let poc0 = [l0.poc];
let poc1 = [l1.poc];
let info = rusty_h264_common::deblock::BlockInfo {
inter: &fe.inter_y,
nnz: &fe.nnz_y,
mv: &fe.mv_y,
ref_id: &ref_id,
mv1: &fe.mv1_y,
ref_id1: &ref_id1,
w4: fe.mb_w * 4,
t8x8: &[],
poc0: &poc0,
poc1: &poc1,
bs: &[],
kind: &[],
};
rusty_h264_common::deblock::filter_frame(
&mut fe.rec_y,
&mut fe.rec_u,
&mut fe.rec_v,
fe.mb_w,
fe.mb_h,
&mb_qpy,
0,
0,
0,
&info,
);
enc_scratch::put_refid(ref_id);
enc_scratch::put_refid1(ref_id1);
enc_scratch::put_qpy(mb_qpy);
let w4 = fe.mb_w * 4;
Some(crate::RefFrame {
y: fe.rec_y,
u: fe.rec_u,
v: fe.rec_v,
poc: 0,
frame_num: 0,
mv: fe.mv_y,
ref_idx: fe.ref_idx_y,
mv1: fe.mv1_y,
ref_idx1: fe.ref_idx1_y,
w4,
hpel: crate::HpelOnce::new(),
})
}
pub fn encode_all_skip_b_cabac(w: &mut BitWriter, cfg: &EncoderConfig, qp: u8, n: usize) {
let mut cab = CabacEncoder::new_with_out(
qp as i32,
cfg.cabac_init_idc,
false,
enc_scratch::take_payload(),
);
for i in 0..n {
cab.encode_decision(24, 1);
cab.encode_terminate(i + 1 == n);
}
while !w.is_byte_aligned() {
w.write_bit(true);
}
let payload = cab.into_bytes();
w.write_aligned_bytes(&payload);
enc_scratch::put_payload(payload);
}