#![allow(clippy::needless_range_loop)]
use rusty_h264_common::bit_reader::OutOfData;
use rusty_h264_common::cavlc::{
decode_residual_block, read_cbp_inter, read_cbp_intra, un_scan_4x4_ac_into, un_scan_4x4_dcac,
};
use rusty_h264_common::inter::{
inter_partitions, mc_chroma_padded, mc_luma_padded, predict_mv, predict_partition_mv,
MvNeighbor,
};
use rusty_h264_common::predict::{
add_residual_8x8, chroma8x8_pred, chroma_qp, intra4x4_pred, intra8x8_pred, luma16x16_pred,
reconstruct_4x4, reconstruct_4x4_dc, reconstruct_4x4_dc_into, reconstruct_4x4_into, I16Mode,
CHROMA_4X4_SCAN_XY, LUMA_4X4_SCAN_XY,
};
use rusty_h264_common::transform::{
dequant_scatter_4x4, dequantize, dequantize_weighted, inverse_quant_8x8,
inverse_quant_chroma_dc,
inverse_quant_chroma_dc_weighted, inverse_quant_luma_dc, inverse_quant_luma_dc_weighted,
};
use rusty_h264_common::{BitReader, YuvFrame};
pub struct MvField {
pub mb_w: usize,
pub mb_h: usize,
pub mv: Vec<(i32, i32)>,
pub ref_idx: Vec<i32>,
pub inter: Vec<bool>,
}
pub static MV_DUMP: std::sync::Mutex<Vec<MvField>> = std::sync::Mutex::new(Vec::new());
pub fn mv_dump_on() -> bool {
static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*ON.get_or_init(|| std::env::var("RFF_MV_DUMP").map_or(false, |v| v != "0"))
}
pub struct FrameDecoder {
mb_w: usize,
mb_h: usize,
qp: u8,
cur_qp: u8,
chroma_qp_offset: i32,
cw: usize,
ch: usize,
ccw: usize,
cch: usize,
rec_y: Vec<u8>,
rec_u: Vec<u8>,
rec_v: Vec<u8>,
mb_qp: Vec<u8>,
slice_first_mb: usize,
nnz_y: Vec<u8>,
nnz_c: [Vec<u8>; 2],
modes_y: Vec<u8>,
coded_y: Vec<bool>,
mv_y: Vec<(i32, i32)>,
inter_y: Vec<bool>,
ref_idx_y: Vec<i32>,
mv1: Vec<(i32, i32)>,
ref_idx1: Vec<i32>,
refs1: Vec<crate::Ref>,
num_ref_active1: usize,
is_b: bool,
b_possible: bool,
direct_spatial: bool,
nnz_l_cache: [u8; 25],
nnz_c_cache: [[u8; 9]; 2],
refs: Vec<crate::Ref>,
num_ref_active: usize,
constrained_intra: bool,
scaling: Option<[[i32; 16]; 6]>,
scaling8: Option<[[i32; 64]; 2]>,
transform_8x8_mode: bool,
mb_t8x8: Vec<bool>,
bs_frame: Vec<rusty_h264_common::deblock::MbBs>,
bs_rows: usize,
flt_rows: usize,
pk_prev: Vec<rusty_h264_common::deblock::MbPack>,
pk_cur: Vec<rusty_h264_common::deblock::MbPack>,
nnz_dbr: Vec<u8>,
bak_y: Vec<u8>,
bak_u: Vec<u8>,
bak_v: Vec<u8>,
edc_jobs: Vec<EdcJob>,
edc_active: bool,
edc_tx: Option<std::sync::mpsc::SyncSender<EdcMsg>>,
edc_ctx_rx: Option<std::sync::mpsc::Receiver<PixelCtx>>,
edc_back_tx: Option<std::sync::mpsc::Sender<PixelCtx>>,
edc_parked: Option<PixelCtx>,
edc_regions: Option<Vec<BRegion>>,
edc_batch: Vec<EdcJob>,
bits_per_mb: f64,
db_ena: bool,
db_oa: i32,
db_ob: i32,
mb_kind: Vec<u8>,
weights: Option<WeightTable>,
cur_poc: i32,
weighted_bipred_idc: u8,
direct_8x8_inference: bool,
}
#[derive(Clone, Default)]
pub struct WeightTable {
pub luma_log2_denom: i32,
pub chroma_log2_denom: i32,
pub luma: [Vec<(i32, i32)>; 2],
pub chroma: [Vec<[(i32, i32); 2]>; 2],
}
impl WeightTable {
fn apply_luma(&self, sample: u8, list: usize, refi: usize) -> u8 {
let (w, o) = self.luma[list][refi];
let lwd = self.luma_log2_denom;
let v = if lwd >= 1 {
((sample as i32 * w + (1 << (lwd - 1))) >> lwd) + o
} else {
sample as i32 * w + o
};
v.clamp(0, 255) as u8
}
fn apply_chroma(&self, sample: u8, list: usize, refi: usize, cc: usize) -> u8 {
let (w, o) = self.chroma[list][refi][cc];
let cwd = self.chroma_log2_denom;
let v = if cwd >= 1 {
((sample as i32 * w + (1 << (cwd - 1))) >> cwd) + o
} else {
sample as i32 * w + o
};
v.clamp(0, 255) as u8
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MbError {
Truncated,
Unsupported(&'static str),
}
impl From<OutOfData> for MbError {
fn from(_: OutOfData) -> Self {
MbError::Truncated
}
}
#[derive(Default)]
pub struct GridPool {
bits_per_mb: f64,
mb_qp: Vec<u8>,
bs_frame: Vec<rusty_h264_common::deblock::MbBs>,
pk_prev: Vec<rusty_h264_common::deblock::MbPack>,
pk_cur: Vec<rusty_h264_common::deblock::MbPack>,
nnz_dbr: Vec<u8>,
bak_y: Vec<u8>,
bak_u: Vec<u8>,
bak_v: Vec<u8>,
nnz_y: Vec<u8>,
nnz_c0: Vec<u8>,
nnz_c1: Vec<u8>,
modes_y: Vec<u8>,
coded_y: Vec<bool>,
mv_y: Vec<(i32, i32)>,
inter_y: Vec<bool>,
ref_idx_y: Vec<i32>,
mv1: Vec<(i32, i32)>,
ref_idx1: Vec<i32>,
mb_t8x8: Vec<bool>,
mb_kind: Vec<u8>,
}
#[inline]
fn refill<T: Clone>(mut v: Vec<T>, n: usize, val: T) -> Vec<T> {
v.clear();
v.resize(n, val);
v
}
impl FrameDecoder {
pub fn new(
mb_w: usize,
mb_h: usize,
qp: u8,
chroma_qp_offset: i32,
refs: Vec<crate::Ref>,
num_ref_active: usize,
constrained_intra: bool,
transform_8x8_mode: bool,
b_possible: bool,
) -> Self {
Self::with_pool(
mb_w,
mb_h,
qp,
chroma_qp_offset,
refs,
num_ref_active,
constrained_intra,
transform_8x8_mode,
b_possible,
GridPool::default(),
)
}
#[allow(clippy::too_many_arguments)]
pub fn with_pool(
mb_w: usize,
mb_h: usize,
qp: u8,
chroma_qp_offset: i32,
refs: Vec<crate::Ref>,
num_ref_active: usize,
constrained_intra: bool,
transform_8x8_mode: bool,
b_possible: bool,
pool: GridPool,
) -> Self {
let (cw, ch) = (mb_w * 16, mb_h * 16);
let (ccw, cch) = (cw / 2, ch / 2);
let bits_per_mb = pool.bits_per_mb;
Self {
mb_w,
mb_h,
qp,
cur_qp: qp,
chroma_qp_offset,
cw,
ch,
ccw,
cch,
rec_y: vec![0; cw * ch],
rec_u: vec![0; ccw * cch],
rec_v: vec![0; ccw * cch],
mb_qp: refill(pool.mb_qp, mb_w * mb_h, qp),
slice_first_mb: 0,
nnz_y: refill(pool.nnz_y, (mb_w * 4) * (mb_h * 4), 0),
nnz_c: [
refill(pool.nnz_c0, (mb_w * 2) * (mb_h * 2), 0),
refill(pool.nnz_c1, (mb_w * 2) * (mb_h * 2), 0),
],
modes_y: refill(pool.modes_y, (mb_w * 4) * (mb_h * 4), 2),
coded_y: refill(pool.coded_y, (mb_w * 4) * (mb_h * 4), false),
mv_y: refill(pool.mv_y, (mb_w * 4) * (mb_h * 4), (0, 0)),
inter_y: refill(pool.inter_y, (mb_w * 4) * (mb_h * 4), false),
ref_idx_y: refill(pool.ref_idx_y, (mb_w * 4) * (mb_h * 4), -1),
mv1: refill(pool.mv1, (mb_w * 4) * (mb_h * 4), (0, 0)),
ref_idx1: refill(pool.ref_idx1, (mb_w * 4) * (mb_h * 4), -1),
refs1: Vec::new(),
num_ref_active1: 0,
is_b: false,
b_possible,
direct_spatial: true,
nnz_l_cache: [0x80; 25],
nnz_c_cache: [[0x80; 9]; 2],
refs,
num_ref_active,
constrained_intra,
scaling: None,
scaling8: None,
transform_8x8_mode,
mb_t8x8: refill(pool.mb_t8x8, mb_w * mb_h, false),
bs_frame: refill(pool.bs_frame, mb_w * mb_h, Default::default()),
bs_rows: 0,
flt_rows: 0,
pk_prev: {
let mut v = pool.pk_prev;
v.clear();
v
},
pk_cur: {
let mut v = pool.pk_cur;
v.clear();
v
},
nnz_dbr: refill(pool.nnz_dbr, (mb_w * 4) * (mb_h * 4), 0),
bak_y: refill(pool.bak_y, cw, 0),
bak_u: refill(pool.bak_u, ccw, 0),
bak_v: refill(pool.bak_v, ccw, 0),
edc_jobs: Vec::new(),
edc_active: false,
edc_tx: None,
edc_ctx_rx: None,
edc_back_tx: None,
edc_parked: None,
edc_regions: None,
edc_batch: Vec::new(),
bits_per_mb,
db_ena: false,
db_oa: 0,
db_ob: 0,
mb_kind: refill(
pool.mb_kind,
mb_w * mb_h,
rusty_h264_common::deblock::MB_KIND_UNSET,
),
weights: None,
cur_poc: 0,
weighted_bipred_idc: 0,
direct_8x8_inference: false,
}
}
pub fn set_weights(&mut self, weights: WeightTable) {
self.weights = Some(weights);
}
fn weight_partition(
&self,
pred_y: &mut [u8; 256],
c_pred: &mut [[u8; 64]; 2],
list: usize,
refi: usize,
rx: usize,
ry: usize,
rw: usize,
rh: usize,
) {
let Some(wt) = &self.weights else { return };
for dy in 0..rh {
for dx in 0..rw {
let i = (ry + dy) * 16 + (rx + dx);
pred_y[i] = wt.apply_luma(pred_y[i], list, refi);
}
}
let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
for cc in 0..2 {
for dy in 0..crh {
for dx in 0..crw {
let i = (cry + dy) * 8 + (crx + dx);
c_pred[cc][i] = wt.apply_chroma(c_pred[cc][i], list, refi, cc);
}
}
}
}
pub fn set_scaling(&mut self, scaling: [[i32; 16]; 6], scaling8: [[i32; 64]; 2]) {
self.scaling = Some(scaling);
self.scaling8 = Some(scaling8);
}
fn dequant(&self, levels: &[i32; 16], qp: u8, list: usize) -> [i32; 16] {
match &self.scaling {
Some(s) => dequantize_weighted(levels, qp, &s[list]),
None => dequantize(levels, qp),
}
}
fn dequant_dc4(&self, level: i32, qp: u8, list: usize) -> i32 {
rusty_h264_common::transform::dequantize_dc4(
level,
qp,
self.scaling.as_ref().map(|s| s[list][0]),
)
}
fn dequant_luma_dc(&self, levels: &[i32; 16], qp: u8, list: usize) -> [i32; 16] {
match &self.scaling {
Some(s) => inverse_quant_luma_dc_weighted(levels, qp, s[list][0]),
None => inverse_quant_luma_dc(levels, qp),
}
}
fn dequant_chroma_dc(&self, levels: &[i32; 4], qp: u8, list: usize) -> [i32; 4] {
match &self.scaling {
Some(s) => inverse_quant_chroma_dc_weighted(levels, qp, s[list][0]),
None => inverse_quant_chroma_dc(levels, qp),
}
}
#[allow(clippy::too_many_arguments)]
pub fn set_b_context(
&mut self,
refs1: Vec<crate::Ref>,
num_ref_active1: usize,
direct_spatial: bool,
cur_poc: i32,
weighted_bipred_idc: u8,
direct_8x8_inference: bool,
) {
self.is_b = true;
self.refs1 = refs1;
self.num_ref_active1 = num_ref_active1;
self.direct_spatial = direct_spatial;
self.cur_poc = cur_poc;
self.weighted_bipred_idc = weighted_bipred_idc;
self.direct_8x8_inference = direct_8x8_inference;
}
fn step_qp(&mut self, delta: i32) {
self.cur_qp = (self.cur_qp as i32 + delta + 52).rem_euclid(52) as u8;
}
fn chroma_qp_for(&self, qp_y: u8) -> u8 {
let qpi = (qp_y as i32 + self.chroma_qp_offset).clamp(0, 51) as u8;
chroma_qp(qpi)
}
pub fn begin_slice(&mut self, slice_qp: u8, refs: Vec<crate::Ref>, num_ref_active: usize) {
self.cur_qp = slice_qp;
self.qp = slice_qp;
self.refs = refs;
self.num_ref_active = num_ref_active;
self.weights = None; }
#[inline]
fn nbr_in_slice(&self, nbx: usize, nby: usize) -> bool {
nby * self.mb_w + nbx >= self.slice_first_mb
}
#[inline]
fn intra_nbr_ok(&self, nbx: usize, nby: usize) -> bool {
!self.constrained_intra || !self.inter_y[nby * (self.mb_w * 4) + nbx]
}
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: self.mv_y[idx],
ref_idx: 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 && self.nbr_in_slice(mb_x - 1, mb_y), bx - 1, by);
let b = get(mb_y > 0 && self.nbr_in_slice(mb_x, mb_y - 1), bx, by - 1);
let c = if mb_y > 0 && mb_x + 1 < self.mb_w && self.nbr_in_slice(mb_x + 1, mb_y - 1) {
get(true, bx + 4, by - 1)
} else {
get(mb_x > 0 && mb_y > 0 && self.nbr_in_slice(mb_x - 1, mb_y - 1), bx - 1, by - 1)
};
[a, b, c]
}
fn mv_neighbors_block(&self, pbx: isize, pby: isize, pwb: isize) -> [MvNeighbor; 3] {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Neighbors);
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]
|| !self.nbr_in_slice(bx as usize / 4, by as usize / 4)
{
MvNeighbor::NONE
} else {
let idx = (by * w4 + bx) as usize;
MvNeighbor { available: true, mv: self.mv_y[idx], ref_idx: 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 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] = mv;
self.inter_y[idx] = inter;
self.ref_idx_y[idx] = if inter { refi } else { -1 };
}
}
}
fn commit_inter_grid(&mut self, mb_x: usize, mb_y: usize, rx: usize, ry: usize, rw: usize, rh: usize, mv: (i32, i32), refi: i8) {
let w4 = self.mb_w * 4;
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] = mv;
self.inter_y[idx] = true;
self.ref_idx_y[idx] = refi as i32;
self.coded_y[idx] = true;
}
}
}
pub fn set_deblock_params(&mut self, ena: bool, oa: i32, ob: i32) {
self.db_ena = ena && (self.flt_rows == 0 || self.db_ena);
self.db_oa = oa;
self.db_ob = ob;
}
fn derive_bs_row(&mut self, r: usize) {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DebDerive);
use rusty_h264_common::deblock::{derive_mb_records, pack_mb, BlockInfo, MbBs};
let (mb_w, w4) = (self.mb_w, self.mb_w * 4);
for br in r * 4..r * 4 + 4 {
let a = br * w4;
self.nnz_dbr[a..a + w4].copy_from_slice(&self.nnz_y[a..a + w4]);
}
for mb_x in 0..mb_w {
if !self.mb_t8x8[r * mb_w + mb_x] {
continue;
}
for b8 in 0..4usize {
let (bx, by) = (mb_x * 4 + (b8 % 2) * 2, r * 4 + (b8 / 2) * 2);
let any = (0..2).any(|sy| (0..2).any(|sx| self.nnz_y[(by + sy) * w4 + bx + sx] > 0));
for sy in 0..2 {
for sx in 0..2 {
self.nnz_dbr[(by + sy) * w4 + bx + sx] = any as u8;
}
}
}
}
let poc0: Vec<i32> = self.refs.iter().map(|f| f.poc).collect();
let poc1: Vec<i32> = self.refs1.iter().map(|f| f.poc).collect();
let info = BlockInfo {
inter: &self.inter_y,
nnz: &self.nnz_dbr,
mv: &self.mv_y,
ref_id: &self.ref_idx_y,
mv1: &self.mv1,
ref_id1: if poc1.is_empty() { &[] } else { &self.ref_idx1 },
w4,
t8x8: &self.mb_t8x8,
bs: &[],
poc0: &poc0,
poc1: &poc1,
kind: &[],
};
let has1 = !info.ref_id1.is_empty();
std::mem::swap(&mut self.pk_prev, &mut self.pk_cur);
self.pk_cur.clear();
for mb_x in 0..mb_w {
self.pk_cur.push(pack_mb(&info, has1, mb_x, r));
let cur = &self.pk_cur[mb_x];
let left = if mb_x > 0 { Some(&self.pk_cur[mb_x - 1]) } else { None };
let top = if r > 0 { Some(&self.pk_prev[mb_x]) } else { None };
let mb_t8 = self.mb_t8x8[r * mb_w + mb_x];
let (mut bv, mut bh) = ([[0i32; 4]; 4], [[0i32; 4]; 4]);
derive_mb_records(cur, left, top, mb_t8, &mut bv, &mut bh);
let mut m = MbBs::default();
for e in 0..4 {
for sg in 0..4 {
m.v[e][sg] = bv[e][sg] as u8;
m.h[e][sg] = bh[e][sg] as u8;
}
}
self.bs_frame[r * mb_w + mb_x] = m;
}
}
#[inline]
fn row_hook(&mut self, addr: usize) {
let _rh = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecRowHook);
edcstat::bump(&edcstat::MBS, 1); if !rowdb_on() {
self.edc_flush();
return;
}
let done = addr / self.mb_w;
if self.edc_tx.is_some() {
if self.bs_rows < done {
self.edc_giveback();
}
while self.bs_rows < done {
let r = self.bs_rows;
self.derive_bs_row(r);
self.bs_rows += 1;
let base = r * self.mb_w;
self.edc_flush_batch();
edcstat::bump(&edcstat::ROWS, 1);
edcstat::bump(
&edcstat::ROWBYTES,
(self.mb_w
* (std::mem::size_of::<rusty_h264_common::deblock::MbBs>() + 2))
as u64,
);
let msg = EdcMsg::Row {
r,
bs: self.bs_frame[base..base + self.mb_w].to_vec(),
qp: self.mb_qp[base..base + self.mb_w].to_vec(),
t8: self.mb_t8x8[base..base + self.mb_w].to_vec(),
};
self.edc_tx.as_ref().unwrap().send(msg).expect("worker alive");
}
return;
}
if self.bs_rows < done {
self.edc_flush();
}
while self.bs_rows < done {
let r = self.bs_rows;
self.derive_bs_row(r);
self.bs_rows += 1;
if self.db_ena {
self.save_bak(r);
self.filter_row(r);
self.flt_rows = r + 1;
}
}
}
fn save_bak(&mut self, r: usize) {
let y0 = (r * 16 + 15) * self.cw;
self.bak_y.copy_from_slice(&self.rec_y[y0..y0 + self.cw]);
let c0 = (r * 8 + 7) * self.ccw;
self.bak_u.copy_from_slice(&self.rec_u[c0..c0 + self.ccw]);
self.bak_v.copy_from_slice(&self.rec_v[c0..c0 + self.ccw]);
}
fn filter_row(&mut self, r: usize) {
let info = rusty_h264_common::deblock::BlockInfo {
inter: &self.inter_y,
nnz: &self.nnz_dbr,
mv: &self.mv_y,
ref_id: &self.ref_idx_y,
mv1: &self.mv1,
ref_id1: &self.ref_idx1,
w4: self.mb_w * 4,
t8x8: &self.mb_t8x8,
bs: &self.bs_frame,
poc0: &[],
poc1: &[],
kind: &self.mb_kind,
};
rusty_h264_common::deblock::filter_frame_rows(
&mut self.rec_y,
&mut self.rec_u,
&mut self.rec_v,
self.mb_w,
self.mb_h,
r..r + 1,
&self.mb_qp,
self.chroma_qp_offset,
self.db_oa,
self.db_ob,
&info,
);
}
#[inline]
fn top_y_px(&self, py: usize, x: usize) -> u8 {
if py % 16 == 0 && self.flt_rows * 16 >= py {
self.bak_y[x]
} else {
self.rec_y[(py - 1) * self.cw + x]
}
}
#[inline]
fn top_y_row(&self, py: usize, x: usize, n: usize) -> &[u8] {
if py % 16 == 0 && self.flt_rows * 16 >= py {
&self.bak_y[x..x + n]
} else {
&self.rec_y[(py - 1) * self.cw + x..][..n]
}
}
#[inline]
fn top_c_px(&self, c: usize, cy: usize, x: usize) -> u8 {
if cy % 8 == 0 && self.flt_rows * 8 >= cy {
if c == 0 { self.bak_u[x] } else { self.bak_v[x] }
} else {
let rec = if c == 0 { &self.rec_u } else { &self.rec_v };
rec[(cy - 1) * self.ccw + x]
}
}
#[inline]
fn top_c_row(&self, c: usize, cy: usize, x: usize, n: usize) -> &[u8] {
if cy % 8 == 0 && self.flt_rows * 8 >= cy {
if c == 0 { &self.bak_u[x..x + n] } else { &self.bak_v[x..x + n] }
} else {
let rec = if c == 0 { &self.rec_u } else { &self.rec_v };
&rec[(cy - 1) * self.ccw + x..][..n]
}
}
pub fn as_reference(&self) -> crate::RefFrame {
self.as_reference_pooled(&mut Vec::new())
}
pub fn as_reference_pooled(&self, pool: &mut Vec<Vec<u8>>) -> crate::RefFrame {
if mv_dump_on() {
MV_DUMP.lock().unwrap().push(MvField {
mb_w: self.mb_w,
mb_h: self.mb_h,
mv: self.mv_y.clone(),
ref_idx: self.ref_idx_y.clone(),
inter: self.inter_y.clone(),
});
}
let (mv, ref_idx, mv1, ref_idx1, ref_poc, w4) = if self.b_possible {
(
self.mv_y.clone(),
self.ref_idx_y.clone(),
self.mv1.clone(),
self.ref_idx1.clone(),
self.ref_idx_y
.iter()
.map(|&r| {
if r >= 0 {
self.refs.get(r as usize).map_or(i32::MIN, |f| f.poc)
} else {
i32::MIN
}
})
.collect(),
self.mb_w * 4,
)
} else {
(Vec::new(), Vec::new(), Vec::new(), Vec::new(), Vec::new(), 0)
};
let mut take = |len: usize| -> Vec<u8> {
match pool.iter().position(|v| v.len() == len) {
Some(i) => pool.swap_remove(i),
None => Vec::new(),
}
};
let (lpw, lph) = (self.cw + 2 * crate::LPAD, self.ch + 2 * crate::LPAD);
let (cpw, cph) = (self.ccw + 2 * crate::CPAD, self.ch / 2 + 2 * crate::CPAD);
crate::RefFrame {
py: rusty_h264_common::inter::pad_plane_into(take(lpw * lph), &self.rec_y, self.cw, self.ch, crate::LPAD),
pu: rusty_h264_common::inter::pad_plane_into(take(cpw * cph), &self.rec_u, self.ccw, self.ch / 2, crate::CPAD),
pv: rusty_h264_common::inter::pad_plane_into(take(cpw * cph), &self.rec_v, self.ccw, self.ch / 2, crate::CPAD),
cw: self.cw,
ch: self.ch,
frame_num: 0, poc: 0, mv,
ref_idx,
mv1,
ref_idx1,
ref_poc,
w4,
long_term: false,
long_term_idx: 0,
}
}
fn nnz_cache_load(&mut self, mb_x: usize, mb_y: usize) {
let w4 = self.mb_w * 4;
let top_unavail = mb_y == 0 || !self.nbr_in_slice(mb_x, mb_y - 1);
let left_unavail = mb_x == 0 || !self.nbr_in_slice(mb_x - 1, mb_y);
for lbx in 0..4 {
self.nnz_l_cache[1 + lbx] =
if top_unavail { 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 left_unavail { 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;
let top_unavail = mb_y == 0 || !self.nbr_in_slice(mb_x, mb_y - 1);
let left_unavail = mb_x == 0 || !self.nbr_in_slice(mb_x - 1, mb_y);
for c in 0..2 {
for bx in 0..2 {
self.nnz_c_cache[c][1 + bx] =
if top_unavail { 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 left_unavail { 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;
}
#[allow(clippy::too_many_arguments)]
pub fn decode_slice_data_cabac(
&mut self,
rbsp: &[u8],
start_byte: usize,
slice_qp: u8,
cabac_init_idc: u32,
is_i: bool,
is_p: bool,
first_mb: usize,
) -> Result<usize, MbError> {
let eligible = edc_on() && rowdb_on() && !is_i && (is_p || self.is_b);
let threaded = eligible
&& edc_mt()
.unwrap_or_else(|| edc_dispatch(self.mb_w, self.mb_h, self.bits_per_mb, true));
edcstat::bump(&edcstat::DISPATCH_ON, threaded as u64);
edcstat::bump(&edcstat::DISPATCH_SEEN, eligible as u64);
if !threaded {
let r = self.decode_slice_cabac_inner(rbsp, start_byte, slice_qp, cabac_init_idc, is_i, is_p, first_mb);
self.note_slice_density(rbsp.len().saturating_sub(start_byte), first_mb, &r);
return r;
}
let ctx = self.edc_take_ctx();
let (tx, rx) = std::sync::mpsc::sync_channel::<EdcMsg>(edc_bound());
let (ctx_tx, ctx_rx) = std::sync::mpsc::channel::<PixelCtx>();
let (back_tx, back_rx) = std::sync::mpsc::channel::<PixelCtx>();
let (res, ctx, panicked) = std::thread::scope(|sc| {
let h = sc.spawn(move || edc_worker(ctx, rx, ctx_tx, back_rx));
self.edc_tx = Some(tx);
self.edc_ctx_rx = Some(ctx_rx);
self.edc_back_tx = Some(back_tx);
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
self.decode_slice_cabac_inner(rbsp, start_byte, slice_qp, cabac_init_idc, is_i, is_p, first_mb)
}));
self.edc_flush_batch(); self.edc_giveback(); self.edc_tx = None; self.edc_ctx_rx = None;
self.edc_back_tx = None;
match (r, h.join()) {
(Ok(res), Ok(ctx)) => (res, Some(ctx), None),
(Err(p), Ok(ctx)) => (Err(MbError::Truncated), Some(ctx), Some(p)),
(Ok(_), Err(p)) | (Err(_), Err(p)) => (Err(MbError::Truncated), None, Some(p)),
}
});
if let Some(ctx) = ctx {
self.edc_restore_ctx(ctx);
}
if let Some(p) = panicked {
std::panic::resume_unwind(p);
}
self.note_slice_density(rbsp.len().saturating_sub(start_byte), first_mb, &res);
res
}
fn note_slice_density(&mut self, bytes: usize, first_mb: usize, r: &Result<usize, MbError>) {
let Ok(end) = r else { return };
let mbs = end.saturating_sub(first_mb);
if mbs == 0 {
return;
}
let bpm = (bytes * 8) as f64 / mbs as f64;
self.bits_per_mb = if self.bits_per_mb == 0.0 {
bpm
} else {
0.75 * self.bits_per_mb + 0.25 * bpm
};
}
fn decode_slice_cabac_inner(
&mut self,
rbsp: &[u8],
start_byte: usize,
slice_qp: u8,
cabac_init_idc: u32,
is_i: bool,
is_p: bool,
first_mb: usize,
) -> Result<usize, MbError> {
self.edc_active = edc_on();
let mut cab = crate::cabac::Cabac::new(rbsp, start_byte, slice_qp as i32, cabac_init_idc, is_i);
let (range, _offset) = cab.dbg_state();
let trace = std::env::var_os("RH_CABAC_TRACE").is_some();
debug_assert_eq!(range, 510, "CABAC init range must be 510");
const I16_CBP: [u32; 6] = [0, 16, 32, 15, 31, 47];
let mbw = self.mb_w;
let total = self.mb_w * self.mb_h;
let _alloc_g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecSliceAlloc);
let mut cat = vec![255u8; total]; let mut mb_cbp = vec![0u8; total];
let mut cmode = vec![-1i32; total]; let mut mb_nzc = vec![[0u8; 24]; total]; let mut cbf_dc = vec![0u16; total];
let mut mb_skip = vec![false; total];
let mut mb_ref = vec![[-1i8; 16]; total]; let mut mb_mvd = vec![[[0i16; 2]; 16]; total]; let mut mb_ref1 = vec![[-1i8; 16]; total]; let mut mb_mvd1 = vec![[[0i16; 2]; 16]; total]; let mut mb_direct = vec![false; total]; drop(_alloc_g);
let mut last_delta_qp = 0i32;
let mut addr = first_mb;
let _mbloop_g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbLoop);
loop {
if addr >= total {
return Err(MbError::Truncated);
}
self.row_hook(addr);
let (mbx, mby) = (addr % mbw, addr / mbw);
let left = (mbx > 0).then(|| addr - 1);
let top = (mby > 0).then(|| addr - mbw);
let mb_type;
if is_p {
let sctx = 11
+ left.map_or(0, |a| (!mb_skip[a]) as usize)
+ top.map_or(0, |a| (!mb_skip[a]) as usize);
if parse_mb_skip_cabac(&mut cab, sctx) {
mb_skip[addr] = true;
cat[addr] = 100; last_delta_qp = 0; self.decode_p_skip(mbx, mby)?;
self.mb_qp[addr] = self.cur_qp; let eos = cab.decode_terminate();
addr += 1;
if eos || addr >= total {
break;
}
continue;
}
let mbt = parse_mb_type_p_cabac(&mut cab);
if mbt == 30 {
return Err(MbError::Unsupported("CABAC I_PCM (WIP)"));
}
if mbt <= 3 {
let _gb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbP);
let mut allow8 = true;
let mut mvdc = [[0i16; 2]; 30];
let mut refc = [-1i8; 30];
if let Some(l) = left {
for (ci, bi) in [(6usize, 3usize), (12, 7), (18, 11), (24, 15)] {
refc[ci] = mb_ref[l][bi];
mvdc[ci] = mb_mvd[l][bi];
}
}
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];
}
}
if mbx > 0 && mby > 0 {
let a = addr - mbw - 1;
(refc[0], mvdc[0]) = (mb_ref[a][15], mb_mvd[a][15]);
}
if mby > 0 && mbx + 1 < mbw {
let a = addr - mbw + 1;
(refc[5], mvdc[5]) = (mb_ref[a][12], mb_mvd[a][12]);
}
let mut mmvd = [[0i16; 2]; 16];
let mut mref = [0i8; 16];
macro_rules! refidx {
($pi:expr, $zb:expr) => {{
if self.num_ref_active > 1 {
let s = CACHE30[$pi];
let c0 = (refc[s - 1] > 0) as usize + 2 * (refc[s - 6] > 0) as usize;
let r = parse_ref_idx_cabac(&mut cab, c0);
for &zb in $zb.iter() {
refc[CACHE30[zb]] = r;
}
r
} else {
0i8
}
}};
}
macro_rules! part {
($pi:expr, $zb:expr, $pred:expr, $rx:expr, $ry:expr, $rw:expr, $rh:expr, $refi:expr) => {{
let (mvx, mvy) = parse_mvd_partition(&mut cab, $pi, $zb, &mut mvdc, &mut refc, &mut mmvd, &mut mref, $refi);
let [na, nb, nc] = self.mv_neighbors_block(
(mbx * 4 + $rx / 4) as isize,
(mby * 4 + $ry / 4) as isize,
($rw / 4) as isize,
);
let pmv = $pred(na, nb, nc);
self.commit_inter_grid(mbx, mby, $rx, $ry, $rw, $rh, (pmv.0 + mvx, pmv.1 + mvy), $refi);
}};
}
match mbt {
0 => {
let r0 = refidx!(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
part!(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], |a, b, c| predict_partition_mv(0, 0, a, b, c, r0 as i32), 0, 0, 16, 16, r0);
}
1 => {
let r0 = refidx!(0, &[0, 1, 2, 3, 4, 5, 6, 7]);
let r1 = refidx!(8, &[8, 9, 10, 11, 12, 13, 14, 15]);
part!(0, &[0, 1, 2, 3, 4, 5, 6, 7], |a, b, c| predict_partition_mv(1, 0, a, b, c, r0 as i32), 0, 0, 16, 8, r0);
part!(8, &[8, 9, 10, 11, 12, 13, 14, 15], |a, b, c| predict_partition_mv(1, 1, a, b, c, r1 as i32), 0, 8, 16, 8, r1);
}
2 => {
let r0 = refidx!(0, &[0, 1, 2, 3, 8, 9, 10, 11]);
let r1 = refidx!(4, &[4, 5, 6, 7, 12, 13, 14, 15]);
part!(0, &[0, 1, 2, 3, 8, 9, 10, 11], |a, b, c| predict_partition_mv(2, 0, a, b, c, r0 as i32), 0, 0, 8, 16, r0);
part!(4, &[4, 5, 6, 7, 12, 13, 14, 15], |a, b, c| predict_partition_mv(2, 1, a, b, c, r1 as i32), 8, 0, 8, 16, r1);
}
_ => {
let mut subt = [0u32; 4];
for st in &mut subt {
*st = parse_sub_mb_type_p_cabac(&mut cab);
}
allow8 = subt.iter().all(|&t| t == 0);
let mut pr = [0i8; 4];
for (i, r) in pr.iter_mut().enumerate() {
let b = i * 4;
*r = refidx!(b, &[b, b + 1, b + 2, b + 3]);
}
for i in 0..4usize {
let b = i * 4;
let (ox, oy) = ((i % 2) * 8, (i / 2) * 8); let ri = pr[i];
match subt[i] {
0 => part!(b, &[b, b + 1, b + 2, b + 3], |a, b, c| predict_mv(a, b, c, ri as i32), ox, oy, 8, 8, ri),
1 => {
part!(b, &[b, b + 1], |a, b, c| predict_mv(a, b, c, ri as i32), ox, oy, 8, 4, ri);
part!(b + 2, &[b + 2, b + 3], |a, b, c| predict_mv(a, b, c, ri as i32), ox, oy + 4, 8, 4, ri);
}
2 => {
part!(b, &[b, b + 2], |a, b, c| predict_mv(a, b, c, ri as i32), ox, oy, 4, 8, ri);
part!(b + 1, &[b + 1, b + 3], |a, b, c| predict_mv(a, b, c, ri as i32), ox + 4, oy, 4, 8, ri);
}
_ => {
for j in 0..4usize {
let (sx, sy) = ((j % 2) * 4, (j / 2) * 4);
part!(b + j, &[b + j], |a, b, c| predict_mv(a, b, c, ri as i32), ox + sx, oy + sy, 4, 4, ri);
}
}
}
}
}
}
mb_ref[addr] = mref;
mb_mvd[addr] = mmvd;
cat[addr] = 100;
let cbp = parse_cbp_cabac(&mut cab, top.map(|a| mb_cbp[a]), left.map(|a| mb_cbp[a]));
mb_cbp[addr] = cbp as u8;
let t8 = self.transform_8x8_mode && (cbp & 15) != 0 && allow8 && {
let a = left.map_or(0, |x| self.mb_t8x8[x] as usize);
let b = top.map_or(0, |x| self.mb_t8x8[x] as usize);
cab.decode_decision(399 + a + b) != 0
};
self.mb_t8x8[addr] = t8;
let mut luma8 = [[0i32; 64]; 4]; let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
let mut nzc = [0xffu8; 48];
if let Some(t) = top {
let tnz = mb_nzc[t];
nzc[1..5].copy_from_slice(&tnz[12..16]);
(nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
(nzc[6], nzc[7], nzc[30], nzc[31]) = (tnz[20], tnz[21], tnz[22], tnz[23]);
}
if let Some(l) = left {
let lnz = mb_nzc[l];
(nzc[8], nzc[16], nzc[24], nzc[32]) = (lnz[3], lnz[7], lnz[11], lnz[15]);
(nzc[13], nzc[21], nzc[37], nzc[45]) = (lnz[17], lnz[21], lnz[19], lnz[23]);
}
let mut cbfdc = 0u16;
let mut nnzs = [0u8; 24]; let mut luma_scan = [[0i32; 16]; 16]; let mut cdc = [[0i32; 4]; 2]; let mut cac = [[[0i32; 16]; 4]; 2]; if cbp == 0 {
last_delta_qp = 0;
}
if cbp != 0 {
let ndc = (top.map(|a| cbf_dc[a]), left.map(|a| cbf_dc[a]));
let qpd = parse_mb_qp_delta_cabac(&mut cab, &mut last_delta_qp);
self.step_qp(qpd);
for id8 in 0..4usize {
if cbp_luma & (1 << id8) != 0 {
if t8 {
let n8 = parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, id8 * 4, RP_LUMA_8X8, false, ndc, &mut luma8[id8]) as u8;
for k in 0..4 {
nnzs[id8 * 4 + k] = n8;
}
} else {
for id4 in 0..4usize {
let iz = id8 * 4 + id4;
nnzs[iz] = parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, iz, RP_LUMA_4X4, false, ndc, &mut luma_scan[iz]) as u8;
}
}
} else {
for k in 0..4 {
nzc[NZC_CACHE[id8 * 4 + k]] = 0;
}
}
}
if cbp_chroma >= 1 {
for i in 0..2usize {
parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, 16 + i * 4, RP_CHROMA_DC + i, false, ndc, &mut cdc[i]);
}
}
if cbp_chroma == 2 {
for i in 0..2usize {
for id4 in 0..4usize {
nnzs[16 + i * 4 + id4] = parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, 16 + i * 4 + id4, RP_CHROMA_AC + i, false, ndc, &mut cac[i][id4]) as u8;
}
}
}
}
self.mb_qp[addr] = self.cur_qp;
cbf_dc[addr] = cbfdc;
let _sc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecStateCache);
let mut mn = [0u8; 24];
for k in 0..4 {
mn[k] = nzc[9 + k];
mn[4 + k] = nzc[17 + k];
mn[8 + k] = nzc[25 + k];
mn[12 + k] = nzc[33 + k];
}
(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;
}
}
mb_nzc[addr] = mn;
drop(_sc);
if self.refs.is_empty() {
return Err(MbError::Unsupported("inter without reference"));
}
let (mut jgmv, mut jgref) = ([(0i32, 0i32); 16], [0u8; 16]);
{
let w4r = self.mb_w * 4;
for by in 0..4usize {
for bx in 0..4usize {
let bidx = (mby * 4 + by) * w4r + (mbx * 4 + bx);
jgmv[by * 4 + bx] = self.mv_y[bidx];
jgref[by * 4 + bx] =
self.ref_idx_y[bidx].clamp(0, 15) as u8;
}
}
}
let job = PInterJob {
mbx,
mby,
t8,
qp: self.cur_qp,
cbp_chroma,
gmv: jgmv,
gref: jgref,
luma_scan,
luma8,
cdc,
cac,
nnzs,
};
let nores = cbp == 0 && nores_on();
if self.edc_tx.is_some() {
self.edc_giveback();
self.edc_commit_nnz(mbx, mby, t8, &nnzs, cbp_chroma);
if edcstat::on() {
edcstat::bump(&edcstat::J_INTER, 1);
let nores = job.cbp_chroma == 0
&& job.luma_scan.iter().all(|b| b.iter().all(|&c| c == 0))
&& job.cdc.iter().all(|p| p.iter().all(|&c| c == 0))
&& job.cac.iter().all(|p| p.iter().all(|b| b.iter().all(|&c| c == 0)));
if nores {
edcstat::bump(&edcstat::J_INTER_NORES, 1);
}
}
let job_msg = if nores {
edcstat::bump(&edcstat::J_NORES_SENT, 1);
EdcJob::InterNoRes(Box::new(PInterNoResJob {
mbx: job.mbx,
mby: job.mby,
t8: job.t8,
qp: job.qp,
gmv: job.gmv,
gref: job.gref,
}))
} else {
EdcJob::Inter(Box::new(job))
};
self.edc_send_job(job_msg);
} else if self.edc_active {
if nores {
edcstat::bump(&edcstat::J_NORES_SENT, 1);
self.edc_jobs.push(EdcJob::InterNoRes(Box::new(PInterNoResJob {
mbx: job.mbx,
mby: job.mby,
t8: job.t8,
qp: job.qp,
gmv: job.gmv,
gref: job.gref,
})));
} else {
self.edc_jobs.push(EdcJob::Inter(Box::new(job)));
}
} else {
self.recon_p_inter(&job);
if double_recon() {
self.recon_p_inter(&job);
}
}
let eos = cab.decode_terminate();
addr += 1;
if eos || addr >= total {
break;
}
continue;
}
mb_type = mbt - 5; } else if self.is_b {
self.edc_flush(); if self.edc_tx.is_some() {
self.edc_regions = Some(Vec::with_capacity(8));
}
let _gb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbB);
let mut allow8 = true;
let sctx = 24
+ left.map_or(0, |a| (!mb_skip[a]) as usize)
+ top.map_or(0, |a| (!mb_skip[a]) as usize);
if parse_mb_skip_cabac(&mut cab, sctx) {
mb_skip[addr] = true;
cat[addr] = 100;
mb_direct[addr] = true;
last_delta_qp = 0; self.decode_b_skip(mbx, mby)?;
self.mb_qp[addr] = self.cur_qp;
mb_ref[addr] = [0i8; 16];
mb_ref1[addr] = [0i8; 16];
let eos = cab.decode_terminate();
addr += 1;
if eos || addr >= total {
break;
}
continue;
}
let bci = left.map_or(0, |a| (!mb_direct[a]) as usize)
+ top.map_or(0, |a| (!mb_direct[a]) as usize);
let bmt = parse_mb_type_b_cabac(&mut cab, bci);
if bmt < 23 {
let mut mvdc0 = [[0i16; 2]; 30];
let mut refc0 = [-1i8; 30];
let mut mvdc1 = [[0i16; 2]; 30];
let mut refc1 = [-1i8; 30];
macro_rules! fill {
($mrf:expr, $mmv:expr, $rc:expr, $mc:expr) => {{
if let Some(l) = left {
for (ci, bi) in [(6usize, 3usize), (12, 7), (18, 11), (24, 15)] {
$rc[ci] = $mrf[l][bi];
$mc[ci] = $mmv[l][bi];
}
}
if let Some(t) = top {
for (ci, bi) in [(1usize, 12usize), (2, 13), (3, 14), (4, 15)] {
$rc[ci] = $mrf[t][bi];
$mc[ci] = $mmv[t][bi];
}
}
if mbx > 0 && mby > 0 {
let a = addr - mbw - 1;
($rc[0], $mc[0]) = ($mrf[a][15], $mmv[a][15]);
}
if mby > 0 && mbx + 1 < mbw {
let a = addr - mbw + 1;
($rc[5], $mc[5]) = ($mrf[a][12], $mmv[a][12]);
}
}};
}
fill!(mb_ref, mb_mvd, refc0, mvdc0);
fill!(mb_ref1, mb_mvd1, refc1, mvdc1);
let mut mmvd0 = [[0i16; 2]; 16];
let mut mref0 = [-1i8; 16];
let mut mmvd1 = [[0i16; 2]; 16];
let mut mref1 = [-1i8; 16];
if self.refs.is_empty() || self.refs1.is_empty() {
return Err(MbError::Unsupported("B without references"));
}
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
if bmt == 0 {
mb_direct[addr] = true;
allow8 = self.direct_8x8_inference;
(mref0, mref1) = ([0i8; 16], [0i8; 16]);
self.decode_b_direct(mbx, mby, 0, 0, 16, 16, &mut pred_y, &mut c_pred);
} else if bmt == 22 {
let mut subt = [0u32; 4];
for s in &mut subt {
*s = parse_sub_mb_type_b_cabac(&mut cab);
}
allow8 = subt.iter().all(|&t| if t == 0 { self.direct_8x8_inference } else { (1..=3).contains(&t) });
for i in 0..4usize {
if subt[i] == 0 {
let b = i * 4;
for &zb in &[b, b + 1, b + 2, b + 3] {
(mref0[G_SCAN4[zb]], mref1[G_SCAN4[zb]]) = (0, 0);
(refc0[CACHE30[zb]], refc1[CACHE30[zb]]) = (0, 0);
}
}
}
let mut sref = [[0i8; 2]; 4]; for list in 0..2usize {
let active = if list == 0 { self.num_ref_active } else { self.num_ref_active1 };
if active <= 1 {
continue;
}
let rc = if list == 0 { &mut refc0 } else { &mut refc1 };
for i in 0..4usize {
let st = subt[i];
if st == 0 || !b_sub_uses(st, list) {
continue;
}
let b = i * 4;
let s = CACHE30[b];
let c0 = (rc[s - 1] > 0) as usize + 2 * (rc[s - 6] > 0) as usize;
let r = parse_ref_idx_cabac(&mut cab, c0);
for &zb in &[b, b + 1, b + 2, b + 3] {
rc[CACHE30[zb]] = r;
}
sref[i][list] = r;
}
}
for list in 0..2usize {
let (mmv, mrf, mc, rc) = if list == 0 {
(&mut mmvd0, &mut mref0, &mut mvdc0, &mut refc0)
} else {
(&mut mmvd1, &mut mref1, &mut mvdc1, &mut refc1)
};
for i in 0..4usize {
let st = subt[i];
if st == 0 || !b_sub_uses(st, list) {
continue;
}
let b = i * 4;
for &(sx, sy, sw, sh) in b_sub_parts(st) {
let mut zb = [0usize; 4];
let mut n = 0;
for ly in sy / 4..sy / 4 + sh / 4 {
for lx in sx / 4..sx / 4 + sw / 4 {
zb[n] = b + ly * 2 + lx;
n += 1;
}
}
parse_mvd_partition(&mut cab, zb[0], &zb[..n], mc, rc, mmv, mrf, sref[i][list]);
}
}
}
for (p, &st) in subt.iter().enumerate() {
let (b8x, b8y) = ((p % 2) * 8, (p / 2) * 8);
if st == 0 {
self.decode_b_direct(mbx, mby, b8x, b8y, 8, 8, &mut pred_y, &mut c_pred);
continue;
}
for &(sx, sy, sw, sh) in b_sub_parts(st) {
let (px, py) = (b8x + sx, b8y + sy);
let mut mv = [(0i32, 0i32); 2];
for list in 0..2usize {
if b_sub_uses(st, list) {
let d = if list == 0 { mmvd0 } else { mmvd1 }[(py / 4) * 4 + px / 4];
let n = self.mv_neighbors_list((mbx * 4 + px / 4) as isize, (mby * 4 + py / 4) as isize, (sw / 4) as isize, list);
let pmv = predict_mv(n[0], n[1], n[2], sref[p][list] as i32);
mv[list] = (pmv.0 + d[0] as i32, pmv.1 + d[1] as i32);
}
}
let refi0 = if b_sub_uses(st, 0) { sref[p][0] as i32 } else { -1 };
let refi1 = if b_sub_uses(st, 1) { sref[p][1] as i32 } else { -1 };
self.b_set_motion(mbx, mby, px, py, sw, sh, refi0, mv[0], refi1, mv[1]);
self.b_mc_or_record(mbx, mby, px, py, sw, sh, refi0, mv[0], refi1, mv[1], &mut pred_y, &mut c_pred);
}
}
} else {
let (layout, mvmode, preds) = b_inter_layout(bmt);
let parts: &[(usize, &[usize])] = match mvmode {
0 => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])],
1 => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7]), (8, &[8, 9, 10, 11, 12, 13, 14, 15])],
_ => &[(0, &[0, 1, 2, 3, 8, 9, 10, 11]), (4, &[4, 5, 6, 7, 12, 13, 14, 15])],
};
let mut pref = [[0i8; 2]; 2]; for list in 0..2usize {
let active = if list == 0 { self.num_ref_active } else { self.num_ref_active1 };
if active <= 1 {
continue;
}
let rc = if list == 0 { &mut refc0 } else { &mut refc1 };
for (p, &(pidx, zb)) in parts.iter().enumerate() {
if !preds[p].uses(list) {
continue;
}
let s = CACHE30[pidx];
let c0 = (rc[s - 1] > 0) as usize + 2 * (rc[s - 6] > 0) as usize;
let r = parse_ref_idx_cabac(&mut cab, c0);
for &zbi in zb.iter() {
rc[CACHE30[zbi]] = r;
}
pref[p][list] = r;
}
}
for list in 0..2usize {
let (mmv, mrf, mc, rc) = if list == 0 {
(&mut mmvd0, &mut mref0, &mut mvdc0, &mut refc0)
} else {
(&mut mmvd1, &mut mref1, &mut mvdc1, &mut refc1)
};
for (p, &(pidx, zb)) in parts.iter().enumerate() {
if preds[p].uses(list) {
parse_mvd_partition(&mut cab, pidx, zb, mc, rc, mmv, mrf, pref[p][list]);
}
}
}
for (p, &(rx, ry, rw, rh)) in layout.iter().enumerate() {
let mut mv = [(0i32, 0i32); 2];
for list in 0..2usize {
if preds[p].uses(list) {
let d = if list == 0 { mmvd0 } else { mmvd1 }[(ry / 4) * 4 + rx / 4];
let n = self.mv_neighbors_list((mbx * 4 + rx / 4) as isize, (mby * 4 + ry / 4) as isize, (rw / 4) as isize, list);
let pmv = predict_partition_mv(mvmode, p, n[0], n[1], n[2], pref[p][list] as i32);
mv[list] = (pmv.0 + d[0] as i32, pmv.1 + d[1] as i32);
}
}
let refi0 = if preds[p].uses(0) { pref[p][0] as i32 } else { -1 };
let refi1 = if preds[p].uses(1) { pref[p][1] as i32 } else { -1 };
self.b_set_motion(mbx, mby, rx, ry, rw, rh, refi0, mv[0], refi1, mv[1]);
self.b_mc_or_record(mbx, mby, rx, ry, rw, rh, refi0, mv[0], refi1, mv[1], &mut pred_y, &mut c_pred);
}
}
mb_ref[addr] = mref0;
mb_mvd[addr] = mmvd0;
mb_ref1[addr] = mref1;
mb_mvd1[addr] = mmvd1;
cat[addr] = 100;
let cbp = parse_cbp_cabac(&mut cab, top.map(|a| mb_cbp[a]), left.map(|a| mb_cbp[a]));
mb_cbp[addr] = cbp as u8;
let t8 = self.transform_8x8_mode && (cbp & 15) != 0 && allow8 && {
let a = left.map_or(0, |x| self.mb_t8x8[x] as usize);
let b = top.map_or(0, |x| self.mb_t8x8[x] as usize);
cab.decode_decision(399 + a + b) != 0
};
self.mb_t8x8[addr] = t8;
let mut luma8 = [[0i32; 64]; 4]; let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
let mut nzc = [0xffu8; 48];
if let Some(t) = top {
let tnz = mb_nzc[t];
nzc[1..5].copy_from_slice(&tnz[12..16]);
(nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
(nzc[6], nzc[7], nzc[30], nzc[31]) = (tnz[20], tnz[21], tnz[22], tnz[23]);
}
if let Some(l) = left {
let lnz = mb_nzc[l];
(nzc[8], nzc[16], nzc[24], nzc[32]) = (lnz[3], lnz[7], lnz[11], lnz[15]);
(nzc[13], nzc[21], nzc[37], nzc[45]) = (lnz[17], lnz[21], lnz[19], lnz[23]);
}
let mut cbfdc = 0u16;
let mut nnzs = [0u8; 24]; let mut luma_scan = [[0i32; 16]; 16];
let mut cdc = [[0i32; 4]; 2];
let mut cac = [[[0i32; 16]; 4]; 2];
if cbp == 0 {
last_delta_qp = 0;
}
if cbp != 0 {
let ndc = (top.map(|a| cbf_dc[a]), left.map(|a| cbf_dc[a]));
let qpd = parse_mb_qp_delta_cabac(&mut cab, &mut last_delta_qp);
self.step_qp(qpd);
for id8 in 0..4usize {
if cbp_luma & (1 << id8) != 0 {
if t8 {
let n8 = parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, id8 * 4, RP_LUMA_8X8, false, ndc, &mut luma8[id8]) as u8;
for k in 0..4 {
nnzs[id8 * 4 + k] = n8;
}
} else {
for id4 in 0..4usize {
let iz = id8 * 4 + id4;
nnzs[iz] = parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, iz, RP_LUMA_4X4, false, ndc, &mut luma_scan[iz]) as u8;
}
}
} else {
for k in 0..4 {
nzc[NZC_CACHE[id8 * 4 + k]] = 0;
}
}
}
if cbp_chroma >= 1 {
for i in 0..2usize {
parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, 16 + i * 4, RP_CHROMA_DC + i, false, ndc, &mut cdc[i]);
}
}
if cbp_chroma == 2 {
for i in 0..2usize {
for id4 in 0..4usize {
nnzs[16 + i * 4 + id4] = parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, 16 + i * 4 + id4, RP_CHROMA_AC + i, false, ndc, &mut cac[i][id4]) as u8;
}
}
}
}
self.mb_qp[addr] = self.cur_qp;
cbf_dc[addr] = cbfdc;
let _sc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecStateCache);
let mut mn = [0u8; 24];
for k in 0..4 {
mn[k] = nzc[9 + k];
mn[4 + k] = nzc[17 + k];
mn[8 + k] = nzc[25 + k];
mn[12 + k] = nzc[33 + k];
}
(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;
}
}
mb_nzc[addr] = mn;
drop(_sc);
if let Some(regions) = self.edc_regions.take() {
self.edc_giveback();
self.edc_commit_nnz(mbx, mby, t8, &nnzs, cbp_chroma);
let job = BJob {
mbx,
mby,
t8,
qp: self.cur_qp,
cbp_chroma,
skip: false,
regions,
luma_scan,
luma8,
cdc,
cac,
nnzs,
};
self.edc_send_job(EdcJob::B(Box::new(job)));
} else {
self.add_inter_residual(mbx, mby, &pred_y, &c_pred, &luma_scan, if t8 { Some(&luma8) } else { None }, &cdc, &cac, cbp_chroma, &nnzs);
}
let eos = cab.decode_terminate();
addr += 1;
if eos || addr >= total {
break;
}
continue;
}
mb_type = bmt - 23; if mb_type == 25 {
return Err(MbError::Unsupported("CABAC I_PCM (WIP)"));
}
} else {
let li = left.map_or(0, |a| (cat[a] >= 2) as usize);
let ti = top.map_or(0, |a| (cat[a] >= 2) as usize);
mb_type = parse_mb_type_i_cabac(&mut cab, li + ti);
if mb_type == 25 {
return Err(MbError::Unsupported("CABAC I_PCM (WIP)"));
}
}
self.edc_intra_sync(); let _gi = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbI);
let cci = left.map_or(0, |a| (1..=3).contains(&cmode[a]) as usize)
+ top.map_or(0, |a| (1..=3).contains(&cmode[a]) as usize);
if mb_type != 0 {
let mt = mb_type - 1;
let pred_mode = I16Mode::from_id(mt % 4);
let cbp_chroma = (mt % 12) / 4;
let cbp_luma_15 = mt / 12 == 1;
let chroma_mode = parse_intra_chroma_pred_mode_cabac(&mut cab, cci) as u8;
cmode[addr] = chroma_mode as i32;
cat[addr] = 2;
mb_cbp[addr] = ((cbp_chroma as u8) << 4) | if cbp_luma_15 { 15 } else { 0 };
let w4 = self.mb_w * 4;
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]);
}
let ndc = (top.map(|a| cbf_dc[a]), left.map(|a| cbf_dc[a]));
let qpd = parse_mb_qp_delta_cabac(&mut cab, &mut last_delta_qp);
self.step_qp(qpd);
let qp = self.cur_qp;
let mut cbfdc = 0u16;
let mut dc_scan = [0i32; 16];
parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, 0, RP_I16_DC, true, ndc, &mut dc_scan);
let recon_dc = self.dequant_luma_dc(&un_scan_4x4_dcac(&dc_scan), qp, 0);
let mut q_blocks = [[0i32; 16]; 16];
for (iz, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
let total = if cbp_luma_15 {
let mut ac = [0i32; 16];
let t = parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, iz, RP_I16_AC, true, ndc, &mut ac);
un_scan_4x4_ac_into(&ac, &mut q_blocks[lby * 4 + lbx]);
t as u8
} else {
nzc[NZC_CACHE[iz]] = 0;
0
};
self.nnz_y[(mby * 4 + lby) * w4 + (mbx * 4 + lbx)] = total;
}
let mut cdc = [[0i32; 4]; 2];
let mut cac = [[[0i32; 16]; 4]; 2];
if cbp_chroma >= 1 {
for i in 0..2usize {
parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, 16 + i * 4, RP_CHROMA_DC + i, true, ndc, &mut cdc[i]);
}
}
if cbp_chroma == 2 {
for i in 0..2usize {
for id4 in 0..4usize {
parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, 16 + i * 4 + id4, RP_CHROMA_AC + i, true, ndc, &mut cac[i][id4]);
}
}
}
let top_ok = mby > 0 && self.nbr_in_slice(mbx, mby - 1) && self.intra_nbr_ok(mbx * 4, mby * 4 - 1);
let left_ok = mbx > 0 && self.nbr_in_slice(mbx - 1, mby) && self.intra_nbr_ok(mbx * 4 - 1, mby * 4);
let (lx, ly) = (mbx * 16, mby * 16);
let mut t16 = [0u8; 16];
let mut l16 = [0u8; 16];
if top_ok {
t16.copy_from_slice(self.top_y_row(ly, lx, 16));
}
if left_ok {
for i in 0..16 {
l16[i] = self.rec_y[(ly + i) * self.cw + lx - 1];
}
}
let corner = if top_ok && left_ok { self.top_y_px(ly, lx - 1) } else { 0 };
let pred_l = luma16x16_pred(pred_mode, top_ok, left_ok, &t16, &l16, corner);
for by in 0..4 {
for bx in 0..4 {
let mut deq = self.dequant(&q_blocks[by * 4 + bx], qp, 0);
deq[0] = recon_dc[by * 4 + bx];
let predb: [i32; 16] = std::array::from_fn(|i| pred_l[(by * 4 + i / 4) * 16 + (bx * 4 + i % 4)] as i32);
let s = reconstruct_4x4(&deq, &predb);
store(&mut self.rec_y, self.cw, lx + bx * 4, ly + by * 4, &s);
self.modes_y[(mby * 4 + by) * w4 + (mbx * 4 + bx)] = 2;
self.coded_y[(mby * 4 + by) * w4 + (mbx * 4 + bx)] = true;
}
}
self.recon_chroma_cabac(mbx, mby, chroma_mode, &cdc, &cac, cbp_chroma, top_ok, left_ok);
self.mb_qp[addr] = self.cur_qp;
cbf_dc[addr] = cbfdc;
let mut mn = [0u8; 24];
for k in 0..4 {
mn[k] = nzc[9 + k];
mn[4 + k] = nzc[17 + k];
mn[8 + k] = nzc[25 + k];
mn[12 + k] = nzc[33 + k];
}
(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;
}
}
mb_nzc[addr] = mn;
let eos = cab.decode_terminate();
addr += 1;
if eos || addr >= total {
break;
}
continue;
}
cat[addr] = 0;
let w4 = self.mb_w * 4;
let t8 = self.transform_8x8_mode && {
let a = left.map_or(0, |x| self.mb_t8x8[x] as usize);
let b = top.map_or(0, |x| self.mb_t8x8[x] as usize);
cab.decode_decision(399 + a + b) != 0
};
self.mb_t8x8[addr] = t8;
let mut modes = [2u8; 16]; let mut modes8 = [2u8; 4]; if t8 {
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let (bx, by) = (mbx * 4 + b8x * 2, mby * 4 + b8y * 2);
let predicted = self.predict_i4_mode(bx, by);
let rr = parse_intra4x4_pred_mode_cabac(&mut cab);
let actual = if rr < 0 {
predicted
} else {
let rem = rr as u8;
if rem < predicted { rem } else { rem + 1 }
};
modes8[b8] = actual;
for dy in 0..2 {
for dx in 0..2 {
self.modes_y[(by + dy) * w4 + (bx + dx)] = actual;
modes[(b8y * 2 + dy) * 4 + (b8x * 2 + dx)] = actual;
}
}
}
} else {
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
let (bx, by) = (mbx * 4 + lbx, mby * 4 + lby);
let predicted = self.predict_i4_mode(bx, by);
let rr = parse_intra4x4_pred_mode_cabac(&mut cab);
let actual = if rr < 0 {
predicted
} else {
let rem = rr as u8;
if rem < predicted { rem } else { rem + 1 }
};
self.modes_y[by * w4 + bx] = actual;
modes[lby * 4 + lbx] = actual;
}
}
let chroma_mode = parse_intra_chroma_pred_mode_cabac(&mut cab, cci) as u8;
cmode[addr] = chroma_mode as i32;
let cbp = parse_cbp_cabac(&mut cab, top.map(|a| mb_cbp[a]), left.map(|a| mb_cbp[a]));
mb_cbp[addr] = cbp as u8;
let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
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]);
}
let mut cbfdc = 0u16;
let mut luma_scan = [[0i32; 16]; 16]; let mut luma8 = [[0i32; 64]; 4]; let mut cdc = [[0i32; 4]; 2]; let mut cac = [[[0i32; 16]; 4]; 2]; if cbp == 0 {
last_delta_qp = 0;
}
if cbp != 0 {
let ndc = (top.map(|a| cbf_dc[a]), left.map(|a| cbf_dc[a]));
let qpd = parse_mb_qp_delta_cabac(&mut cab, &mut last_delta_qp);
self.step_qp(qpd);
for id8 in 0..4usize {
if cbp_luma & (1 << id8) != 0 {
if t8 {
let n = parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, id8 * 4, RP_LUMA_8X8, true, ndc, &mut luma8[id8]);
let (b8x, b8y) = (id8 % 2, id8 / 2);
for sy in 0..2 {
for sx in 0..2 {
self.nnz_y[(mby * 4 + b8y * 2 + sy) * w4 + (mbx * 4 + b8x * 2 + sx)] = n as u8;
}
}
} else {
for id4 in 0..4usize {
let iz = id8 * 4 + id4;
parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, iz, RP_LUMA_4X4, true, ndc, &mut luma_scan[iz]);
}
}
} else {
for k in 0..4 {
nzc[NZC_CACHE[id8 * 4 + k]] = 0;
}
if t8 {
let (b8x, b8y) = (id8 % 2, id8 / 2);
for sy in 0..2 {
for sx in 0..2 {
self.nnz_y[(mby * 4 + b8y * 2 + sy) * w4 + (mbx * 4 + b8x * 2 + sx)] = 0;
}
}
}
}
}
if cbp_chroma >= 1 {
for i in 0..2usize {
parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, 16 + i * 4, RP_CHROMA_DC + i, true, ndc, &mut cdc[i]);
}
}
if cbp_chroma == 2 {
for i in 0..2usize {
for id4 in 0..4usize {
parse_residual_cabac(&mut cab, &mut nzc, &mut cbfdc, 16 + i * 4 + id4, RP_CHROMA_AC + i, true, ndc, &mut cac[i][id4]);
}
}
}
}
self.mb_qp[addr] = self.cur_qp;
cbf_dc[addr] = cbfdc;
let mut mn = [0u8; 24];
for k in 0..4 {
mn[k] = nzc[9 + k];
mn[4 + k] = nzc[17 + k];
mn[8 + k] = nzc[25 + k];
mn[12 + k] = nzc[33 + k];
}
(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;
}
}
mb_nzc[addr] = mn;
let qp = self.cur_qp;
let top_ok = mby > 0 && self.nbr_in_slice(mbx, mby - 1) && self.intra_nbr_ok(mbx * 4, mby * 4 - 1);
let left_ok = mbx > 0 && self.nbr_in_slice(mbx - 1, mby) && self.intra_nbr_ok(mbx * 4 - 1, mby * 4);
if t8 {
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let (bx, by) = (mbx * 4 + b8x * 2, mby * 4 + b8y * 2);
let (px, py) = (bx * 4, by * 4);
let res8 = if cbp_luma & (1 << b8) != 0 {
let raster = un_scan_8x8(&luma8[b8]);
self.inv_quant8(&raster, qp, 0)
} else {
[0i32; 64]
};
let avail_top = b8y > 0 || top_ok;
let avail_left = b8x > 0 || left_ok;
let (t, l, corner, avail_corner) =
self.gather_i8(px, py, avail_top, avail_left, bx, by);
let pred =
intra8x8_pred(modes8[b8], avail_top, avail_left, avail_corner, &t, &l, corner);
let mut predb = [0i32; 64];
for i in 0..64 {
predb[i] = pred[i] as i32;
}
let recon = add_residual_8x8(&res8, &predb);
for dy in 0..8 {
for dx in 0..8 {
self.rec_y[(py + dy) * self.cw + (px + dx)] = recon[dy * 8 + dx];
}
}
for sy in 0..2 {
for sx in 0..2 {
self.coded_y[(by + sy) * w4 + (bx + sx)] = true;
}
}
}
}
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
if t8 {
break;
}
let (bx, by) = (mbx * 4 + lbx, mby * 4 + lby);
let (px, py) = (bx * 4, by * 4);
let at = lby > 0 || top_ok;
let al = lbx > 0 || left_ok;
let qb = un_scan_4x4_dcac(&luma_scan[blk]);
self.nnz_y[by * w4 + bx] = luma_scan[blk].iter().filter(|&&v| v != 0).count() as u8;
let (t, l, corner) = self.gather_i4(px, py, at, al, bx, by);
let pred = intra4x4_pred(modes[lby * 4 + lbx], at, al, &t, &l, corner);
let predb = std::array::from_fn(|i| pred[i] as i32);
let s = reconstruct_4x4(&self.dequant(&qb, qp, 0), &predb);
store(&mut self.rec_y, self.cw, px, py, &s);
self.coded_y[by * w4 + bx] = true;
}
self.recon_chroma_cabac(mbx, mby, chroma_mode, &cdc, &cac, cbp_chroma, top_ok, left_ok);
let eos = cab.decode_terminate();
addr += 1;
if eos || addr >= total {
break;
}
}
if trace {
eprintln!("# CABAC decoded {} MBs (of {total})", addr - first_mb);
}
self.edc_flush(); Ok(addr)
}
#[allow(clippy::too_many_arguments)]
#[allow(clippy::too_many_arguments)]
fn add_inter_residual(
&mut self,
mb_x: usize,
mb_y: usize,
pred_y: &[u8; 256],
c_pred: &[[u8; 64]; 2],
luma_scan: &[[i32; 16]; 16],
luma8: Option<&[[i32; 64]; 4]>,
cdc: &[[i32; 4]; 2],
cac: &[[[i32; 16]; 4]; 2],
cbp_chroma: u32,
nnzs: &[u8; 24],
) {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecResidAdd);
let qp = self.cur_qp;
let qpc = self.chroma_qp_for(qp);
let (w4r, w2r) = (self.mb_w * 4, self.mb_w * 2);
if let Some(l8) = luma8 {
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let nnz: u32 = (0..4).map(|k| nnzs[b8 * 4 + k] as u32).sum();
for sy in 0..2 {
for sx in 0..2 {
self.nnz_y[(mb_y * 4 + b8y * 2 + sy) * w4r + (mb_x * 4 + b8x * 2 + sx)] =
nnzs[b8 * 4 + sy * 2 + sx];
}
}
let res8 = if nnz == 0 {
[0i32; 64]
} else {
let raster = un_scan_8x8(&l8[b8]);
self.inv_quant8(&raster, qp, 1)
};
for sy in 0..2 {
for sx in 0..2 {
self.coded_y[(mb_y * 4 + b8y * 2 + sy) * w4r + (mb_x * 4 + b8x * 2 + sx)] = true;
}
}
let predb: [i32; 64] =
std::array::from_fn(|i| pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32);
let recon = add_residual_8x8(&res8, &predb);
let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
for dy in 0..8 {
for dx in 0..8 {
self.rec_y[(py + dy) * self.cw + (px + dx)] = recon[dy * 8 + dx];
}
}
}
}
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
if luma8.is_some() {
break;
}
let nnz = nnzs[blk];
self.nnz_y[(mb_y * 4 + lby) * w4r + (mb_x * 4 + lbx)] = nnz;
let cw = self.cw;
let p_off = (lby * 4) * 16 + lbx * 4;
let r_off = (mb_y * 4 + lby) * 4 * cw + (mb_x * 4 + lbx) * 4;
if nnz == 0 {
for r in 0..4 {
self.rec_y[r_off + r * cw..r_off + r * cw + 4]
.copy_from_slice(&pred_y[p_off + r * 16..p_off + r * 16 + 4]);
}
continue;
}
if nnz == 1 && luma_scan[blk][0] != 0 {
let f = self.dequant_dc4(luma_scan[blk][0], qp, 3);
reconstruct_4x4_dc_into((f + 32) >> 6, pred_y, p_off, 16, &mut self.rec_y, r_off, cw);
} else {
let deq = if nnz <= 6 {
dequant_scatter_4x4(&luma_scan[blk], nnz, 0, qp, self.scaling.as_ref().map(|sc| &sc[3]))
} else {
self.dequant(&un_scan_4x4_dcac(&luma_scan[blk]), qp, 3)
};
reconstruct_4x4_into(&deq, pred_y, p_off, 16, &mut self.rec_y, r_off, cw);
}
}
let mut c_dc = [[0i32; 4]; 2];
if cbp_chroma != 0 {
for c in 0..2 {
c_dc[c] = self.dequant_chroma_dc(&cdc[c], qpc, 4 + c);
}
}
let ccw = self.ccw;
for c in 0..2 {
for &(bx, by) in &CHROMA_4X4_SCAN_XY {
let mut ac_nz = false;
if cbp_chroma == 2 {
let n = nnzs[16 + c * 4 + by * 2 + bx];
self.nnz_c[c][(mb_y * 2 + by) * w2r + (mb_x * 2 + bx)] = n;
ac_nz = n != 0;
}
let dc = c_dc[c][by * 2 + bx];
let p_off = (by * 4) * 8 + bx * 4;
let r_off = (mb_y * 2 + by) * 4 * ccw + (mb_x * 2 + bx) * 4;
let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
if dc == 0 && !ac_nz {
for r in 0..4 {
plane[r_off + r * ccw..r_off + r * ccw + 4]
.copy_from_slice(&c_pred[c][p_off + r * 8..p_off + r * 8 + 4]);
}
continue;
}
if !ac_nz {
reconstruct_4x4_dc_into((dc + 32) >> 6, &c_pred[c], p_off, 8, plane, r_off, ccw);
continue;
}
let n = nnzs[16 + c * 4 + by * 2 + bx];
let mut deq = if n <= 6 {
dequant_scatter_4x4(&cac[c][by * 2 + bx], n, 1, qpc, self.scaling.as_ref().map(|sc| &sc[4 + c]))
} else {
let mut ac = [0i32; 16];
un_scan_4x4_ac_into(&cac[c][by * 2 + bx], &mut ac);
match &self.scaling {
Some(sc) => dequantize_weighted(&ac, qpc, &sc[4 + c]),
None => dequantize(&ac, qpc),
}
};
deq[0] = dc;
reconstruct_4x4_into(&deq, &c_pred[c], p_off, 8, plane, r_off, ccw);
}
}
}
fn recon_chroma_cabac(
&mut self,
mb_x: usize,
mb_y: usize,
chroma_mode: u8,
cdc: &[[i32; 4]; 2],
cac: &[[[i32; 16]; 4]; 2],
cbp_chroma: u32,
avail_top: bool,
avail_left: bool,
) {
let qpc = self.chroma_qp_for(self.cur_qp);
let (cx, cy) = (mb_x * 8, mb_y * 8);
let mut c_dc = [[0i32; 4]; 2];
if cbp_chroma != 0 {
for c in 0..2 {
c_dc[c] = self.dequant_chroma_dc(&cdc[c], qpc, 1 + c);
}
}
let w2 = self.mb_w * 2;
for c in 0..2 {
let mut ctop = [0u8; 8];
let mut cleft = [0u8; 8];
let mut ccorner = 0u8;
{
let rec_c = if c == 0 { &self.rec_u } else { &self.rec_v };
if avail_top {
ctop.copy_from_slice(self.top_c_row(c, cy, cx, 8));
}
if avail_left {
for i in 0..8 {
cleft[i] = rec_c[(cy + i) * self.ccw + cx - 1];
}
}
if avail_top && avail_left {
ccorner = self.top_c_px(c, cy, cx - 1);
}
}
let pred8 = chroma8x8_pred(chroma_mode, avail_top, avail_left, &ctop, &cleft, ccorner);
for &(bx, by) in &CHROMA_4X4_SCAN_XY {
let mut ac = [0i32; 16];
if cbp_chroma == 2 {
un_scan_4x4_ac_into(&cac[c][by * 2 + bx], &mut ac);
self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] =
cac[c][by * 2 + bx].iter().filter(|&&v| v != 0).count() as u8;
}
let mut deq = self.dequant(&ac, qpc, 1 + c);
deq[0] = c_dc[c][by * 2 + bx];
let predb: [i32; 16] =
std::array::from_fn(|i| pred8[(by * 4 + i / 4) * 8 + (bx * 4 + i % 4)] as i32);
let s = reconstruct_4x4(&deq, &predb);
let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
store(plane, self.ccw, cx + bx * 4, cy + by * 4, &s);
}
}
}
pub fn decode_slice_data(
&mut self,
r: &mut BitReader,
is_p: bool,
first_mb: usize,
) -> Result<usize, MbError> {
let eligible = edc_on() && rowdb_on() && (is_p || self.is_b);
let threaded = eligible
&& edc_mt().unwrap_or_else(|| edc_dispatch(self.mb_w, self.mb_h, self.bits_per_mb, false));
edcstat::bump(&edcstat::DISPATCH_ON, threaded as u64);
edcstat::bump(&edcstat::DISPATCH_SEEN, eligible as u64);
if !threaded {
return self.decode_slice_cavlc_inner(r, is_p, first_mb);
}
let ctx = self.edc_take_ctx();
let (tx, rx) = std::sync::mpsc::sync_channel::<EdcMsg>(edc_bound());
let (ctx_tx, ctx_rx) = std::sync::mpsc::channel::<PixelCtx>();
let (back_tx, back_rx) = std::sync::mpsc::channel::<PixelCtx>();
let (res, ctx, panicked) = std::thread::scope(|sc| {
let h = sc.spawn(move || edc_worker(ctx, rx, ctx_tx, back_rx));
self.edc_tx = Some(tx);
self.edc_ctx_rx = Some(ctx_rx);
self.edc_back_tx = Some(back_tx);
let r2 = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
self.decode_slice_cavlc_inner(r, is_p, first_mb)
}));
self.edc_flush_batch();
self.edc_giveback();
self.edc_tx = None;
self.edc_ctx_rx = None;
self.edc_back_tx = None;
match (r2, h.join()) {
(Ok(res), Ok(ctx)) => (res, Some(ctx), None),
(Err(pn), Ok(ctx)) => (Err(MbError::Truncated), Some(ctx), Some(pn)),
(Ok(_), Err(pn)) | (Err(_), Err(pn)) => (Err(MbError::Truncated), None, Some(pn)),
}
});
if let Some(ctx) = ctx {
self.edc_restore_ctx(ctx);
}
if let Some(pn) = panicked {
std::panic::resume_unwind(pn);
}
res
}
fn decode_slice_cavlc_inner(
&mut self,
r: &mut BitReader,
is_p: bool,
first_mb: usize,
) -> Result<usize, MbError> {
let total = self.mb_w * self.mb_h;
self.slice_first_mb = first_mb;
self.edc_active = edc_on();
let mut addr = first_mb;
while addr < total {
self.row_hook(addr);
if is_p || self.is_b {
let skip_run = {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
r.read_ue()?
} as usize;
for _ in 0..skip_run {
if addr >= total {
break;
}
if self.is_b {
self.decode_b_skip(addr % self.mb_w, addr / self.mb_w)?;
} else {
self.decode_p_skip(addr % self.mb_w, addr / self.mb_w)?;
}
self.mb_qp[addr] = self.cur_qp; addr += 1;
}
if addr >= total {
break;
}
if skip_run > 0 && !r.more_rbsp_data() {
break;
}
}
if self.is_b {
self.edc_intra_sync();
self.decode_b_mb(r, addr % self.mb_w, addr / self.mb_w)?;
} else {
self.decode_mb(r, addr % self.mb_w, addr / self.mb_w, is_p)?;
}
self.mb_qp[addr] = self.cur_qp;
addr += 1;
if !r.more_rbsp_data() {
break;
}
}
self.edc_flush(); Ok(addr)
}
fn decode_mb(
&mut self,
r: &mut BitReader,
mb_x: usize,
mb_y: usize,
is_p: bool,
) -> Result<(), MbError> {
let mut mb_type = {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
r.read_ue()?
};
if is_p {
if mb_type <= 2 {
return self.decode_inter(r, mb_x, mb_y, mb_type as u8);
}
if mb_type == 3 || mb_type == 4 {
return self.decode_p8x8(r, mb_x, mb_y, mb_type == 4);
}
mb_type -= 5;
}
self.edc_intra_sync();
self.decode_intra_mb(r, mb_x, mb_y, mb_type)
}
fn decode_intra_mb(
&mut self,
r: &mut BitReader,
mb_x: usize,
mb_y: usize,
mb_type: u32,
) -> Result<(), MbError> {
let _gi = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMbI);
if mb_type == 0 {
if self.transform_8x8_mode && r.read_bit()? {
self.decode_i8x8(r, mb_x, mb_y)?;
} else {
self.decode_i4x4(r, mb_x, mb_y)?;
}
} else if (1..=24).contains(&mb_type) {
self.decode_i16(r, mb_x, mb_y, mb_type - 1)?;
} else if mb_type == 25 {
self.edc_intra_sync();
self.decode_ipcm(r, mb_x, mb_y)?;
} else {
return Err(MbError::Unsupported("only I_4x4 / I_16x16 / I_PCM macroblocks"));
}
let w4 = self.mb_w * 4;
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
self.coded_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = true;
}
Ok(())
}
fn decode_inter(
&mut self,
r: &mut BitReader,
mb_x: usize,
mb_y: usize,
mode: u8,
) -> Result<(), MbError> {
if self.refs.is_empty() {
return Err(MbError::Unsupported("inter without reference"));
}
if mode == 0 {
self.mb_kind[mb_y * self.mb_w + mb_x] =
rusty_h264_common::deblock::MB_KIND_INTER_UNIFORM;
}
let w4 = self.mb_w * 4;
let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
let num_refs = self.refs.len();
let layout = inter_partitions(mode);
let nparts = layout.len();
let mut ref_idxs = [0i32; 4];
if self.num_ref_active > 1 {
for ri in ref_idxs[..nparts].iter_mut() {
*ri = read_ref_idx(r, self.num_ref_active)?;
if *ri as usize >= num_refs {
return Err(MbError::Truncated); }
}
}
let mut part_mv = [(0i32, (0i32, 0i32)); 4];
{
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MvGrid);
for (part, &(rx, ry, rw, rh)) in layout.iter().enumerate() {
let refi = ref_idxs[part];
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);
let mvd_x = r.read_se()?;
let mvd_y = r.read_se()?;
let mv = (pmv.0 + mvd_x, pmv.1 + mvd_y);
part_mv[part] = (refi, mv);
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] = mv;
self.inter_y[idx] = true;
self.ref_idx_y[idx] = refi;
self.coded_y[idx] = true;
}
}
}
}
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
let defer = self.edc_tx.is_some() || self.edc_active;
let mc_passes = if defer { 0 } else if double_recon() { 2 } else { 1 };
for _pass in 0..mc_passes {
if _pass > 0 {
edcstat::bump(&edcstat::DOUBLED, 1);
}
for (part, &(rx, ry, rw, rh)) in layout.iter().enumerate() {
let (refi, mv) = part_mv[part];
let reference = &self.refs[refi as usize];
let mut tmp = [0u8; 256];
mc_luma_padded(&reference.py, reference.lstride(), crate::LPAD, self.cw, ch, mb_x * 16 + rx, mb_y * 16 + ry, rw, rh, mv.0, mv.1, &mut tmp);
{
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
restride(&mut pred_y, 16, rx, ry, &tmp, 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.pu } else { &reference.pv };
let mut tc = [0u8; 64];
mc_chroma_padded(rc, reference.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv.0, mv.1, &mut tc);
{
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
restride(&mut c_pred[cc], 8, crx, cry, &tc, crw, crh);
}
}
self.weight_partition(&mut pred_y, &mut c_pred, 0, refi as usize, rx, ry, rw, rh);
}
}
self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, true, defer)
}
fn inter_finish(
&mut self,
r: &mut BitReader,
mb_x: usize,
mb_y: usize,
pred_y: &[u8; 256],
c_pred: &[[u8; 64]; 2],
allow_8x8: bool,
defer: bool,
) -> Result<(), MbError> {
let w4 = self.mb_w * 4;
let cbp = {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
read_cbp_inter(r)?
};
let cbp_luma = cbp & 15;
let cbp_chroma = cbp >> 4;
let t8x8 = cbp_luma > 0 && self.transform_8x8_mode && allow_8x8 && r.read_bit()?;
if t8x8 {
self.mb_t8x8[mb_y * self.mb_w + mb_x] = true;
}
if cbp != 0 {
self.step_qp(r.read_se()?);
}
let (qp, qpc) = (self.cur_qp, self.chroma_qp_for(self.cur_qp));
self.nnz_cache_load(mb_x, mb_y);
let mut luma_scan = [[0i32; 16]; 16];
let mut nnzs = [0u8; 24];
let mut luma8 = [[0i32; 64]; 4]; if t8x8 {
for b8 in 0..4 {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2);
if cbp_luma & (1 << b8) != 0 {
let mut scan8 = [0i32; 64];
for sub in 0..4 {
let (sx, sy) = (sub % 2, sub / 2);
let (cx, cy) = (b8x * 2 + sx, b8y * 2 + sy);
let nc = self.nc_pred(cx, cy);
let blk = decode_residual_block(r, 16, nc)?;
let total = blk.iter().filter(|&&v| v != 0).count() as u8;
self.nnz_cache_set(cx, cy, total);
self.nnz_y[(by + sy) * w4 + (bx + sx)] = total;
nnzs[b8 * 4 + sub] = total;
for k in 0..16 {
scan8[4 * k + sub] = blk[k];
}
}
luma8[b8] = scan8;
} else {
for sub in 0..4 {
let (sx, sy) = (sub % 2, sub / 2);
self.nnz_cache_set(b8x * 2 + sx, b8y * 2 + sy, 0);
self.nnz_y[(by + sy) * w4 + (bx + sx)] = 0;
}
}
}
} 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 = decode_residual_block(r, 16, nc)?;
luma_scan[blk] = scan16; scan16.iter().filter(|&&v| v != 0).count() as u8
} else {
0
};
self.nnz_cache_set(lbx, lby, total);
self.nnz_y[by * w4 + bx] = total;
nnzs[blk] = total;
}
}
let mut c_recon_dc = [[0i32; 4]; 2];
if cbp_chroma != 0 {
for (c, slot) in c_recon_dc.iter_mut().enumerate() {
let dc = decode_residual_block(r, 4, -1)?;
*slot = [dc[0], dc[1], dc[2], dc[3]]; }
}
let mut c_q = [[[0i32; 16]; 4]; 2];
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 = decode_residual_block(r, 15, nc)?;
let total = ac.iter().filter(|&&v| v != 0).count() 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;
c_q[c][by * 2 + bx] = ac; nnzs[16 + c * 4 + by * 2 + bx] = total;
}
}
}
if defer {
let (mut gmv, mut gref) = ([(0i32, 0i32); 16], [0u8; 16]);
let w4r = self.mb_w * 4;
for by in 0..4usize {
for bx in 0..4usize {
let bi = (mb_y * 4 + by) * w4r + (mb_x * 4 + bx);
gmv[by * 4 + bx] = self.mv_y[bi];
gref[by * 4 + bx] = self.ref_idx_y[bi].clamp(0, 15) as u8;
}
}
let ej = if cbp == 0 && nores_on() {
edcstat::bump(&edcstat::J_NORES_SENT, 1);
EdcJob::InterNoRes(Box::new(PInterNoResJob {
mbx: mb_x, mby: mb_y, t8: t8x8, qp, gmv, gref,
}))
} else {
EdcJob::Inter(Box::new(PInterJob {
mbx: mb_x, mby: mb_y, t8: t8x8, qp,
cbp_chroma, gmv, gref,
luma_scan, luma8, cdc: c_recon_dc, cac: c_q, nnzs,
}))
};
if self.edc_tx.is_some() {
self.edc_giveback();
self.edc_send_job(ej);
} else {
self.edc_jobs.push(ej);
}
} else {
self.add_inter_residual(
mb_x, mb_y, pred_y, c_pred, &luma_scan,
if t8x8 { Some(&luma8) } else { None },
&c_recon_dc, &c_q, cbp_chroma, &nnzs,
);
}
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
}
Ok(())
}
fn mv_neighbors_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) = if list == 0 {
(&self.mv_y, &self.ref_idx_y)
} else {
(&self.mv1, &self.ref_idx1)
};
let get = |bx: isize, by: isize| -> MvNeighbor {
if bx < 0
|| by < 0
|| bx >= w4
|| by >= h4
|| !self.coded_y[(by * w4 + bx) as usize]
|| !self.nbr_in_slice(bx as usize / 4, by as usize / 4)
{
MvNeighbor::NONE
} else {
let idx = (by * w4 + bx) as usize;
MvNeighbor { available: true, mv: mvg[idx], ref_idx: 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 col_block(&self, bx4: usize, by4: usize) -> (usize, usize) {
if self.direct_8x8_inference {
((bx4 / 2) * 3, (by4 / 2) * 3)
} else {
(bx4, by4)
}
}
fn col_zero(&self, bx: usize, by: usize) -> bool {
let Some(col) = self.refs1.first() else { return false };
if col.long_term || col.w4 == 0 {
return false;
}
let idx = by * col.w4 + bx;
if idx >= col.ref_idx.len() {
return false;
}
let (cref, cmv) = if col.ref_idx[idx] >= 0 {
(col.ref_idx[idx], col.mv[idx])
} else if idx < col.ref_idx1.len() && col.ref_idx1[idx] >= 0 {
(col.ref_idx1[idx], col.mv1[idx])
} else {
return false;
};
cref == 0 && cmv.0.abs() <= 1 && cmv.1.abs() <= 1
}
fn implicit_weights(&self, refi0: i32, refi1: i32) -> Option<(i32, i32)> {
if self.weighted_bipred_idc != 2 || refi0 < 0 || refi1 < 0 {
return None;
}
let r0 = &self.refs[refi0 as usize];
let r1 = &self.refs1[refi1 as usize];
let td = (r1.poc - r0.poc).clamp(-128, 127);
let tb = (self.cur_poc - r0.poc).clamp(-128, 127);
if td == 0 || r0.long_term || r1.long_term {
return None; }
let tx = (16384 + td.abs() / 2) / td;
let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
let w1 = dsf >> 2;
if !(-64..=128).contains(&w1) {
return None; }
Some((64 - w1, w1))
}
#[allow(clippy::too_many_arguments)]
fn b_mc(
&self,
mb_x: usize,
mb_y: usize,
px: usize,
py: usize,
rw: usize,
rh: usize,
refi0: i32,
mv0: (i32, i32),
refi1: i32,
mv1: (i32, i32),
pred_y: &mut [u8; 256],
c_pred: &mut [[u8; 64]; 2],
) {
let _gb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBMc);
let refi0 = if refi0 >= 0 { (refi0 as usize).min(self.refs.len().saturating_sub(1)) as i32 } else { -1 };
let refi1 = if refi1 >= 0 { (refi1 as usize).min(self.refs1.len().saturating_sub(1)) as i32 } else { -1 };
if (refi0 >= 0 && self.refs.is_empty()) || (refi1 >= 0 && self.refs1.is_empty()) {
return;
}
let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
let weights = {
let _gw = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBWeights);
self.implicit_weights(refi0, refi1)
};
let full = px == 0 && rw == 16;
let _gl = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBLuma);
let chroma_done = rusty_h264_common::inter::with_mc_scratch(|scr| match (refi0 >= 0, refi1 >= 0, full) {
(true, false, true) => {
let rf = &self.refs[refi0 as usize];
rusty_h264_common::inter::mc_luma_padded_pre(scr, &rf.py, rf.lstride(), crate::LPAD, self.cw, ch, mb_x * 16, mb_y * 16 + py, rw, rh, mv0.0, mv0.1, &mut pred_y[py * 16..py * 16 + rw * rh]);
false
}
(false, true, true) => {
let rf = &self.refs1[refi1 as usize];
rusty_h264_common::inter::mc_luma_padded_pre(scr, &rf.py, rf.lstride(), crate::LPAD, self.cw, ch, mb_x * 16, mb_y * 16 + py, rw, rh, mv1.0, mv1.1, &mut pred_y[py * 16..py * 16 + rw * rh]);
false
}
(true, true, true) => {
let rf = &self.refs[refi0 as usize];
rusty_h264_common::inter::mc_luma_padded_pre(scr, &rf.py, rf.lstride(), crate::LPAD, self.cw, ch, mb_x * 16, mb_y * 16 + py, rw, rh, mv0.0, mv0.1, &mut pred_y[py * 16..py * 16 + rw * rh]);
let mut b = [0u8; 256];
let rf = &self.refs1[refi1 as usize];
rusty_h264_common::inter::mc_luma_padded_pre(scr, &rf.py, rf.lstride(), crate::LPAD, self.cw, ch, mb_x * 16, mb_y * 16 + py, rw, rh, mv1.0, mv1.1, &mut b[..rw * rh]);
drop(_gl);
let _gbl = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBBlend);
let dst = &mut pred_y[py * 16..py * 16 + rw * rh];
match weights {
None => {
for (d, s) in dst.iter_mut().zip(&b[..rw * rh]) {
*d = ((*d as u16 + *s as u16 + 1) >> 1) as u8;
}
}
Some((w0, w1)) => {
for (d, s) in dst.iter_mut().zip(&b[..rw * rh]) {
*d = ((*d as i32 * w0 + *s as i32 * w1 + 32) >> 6).clamp(0, 255) as u8;
}
}
}
let _gc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBChroma);
self.b_mc_chroma(mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, c_pred, weights, cch);
true
}
_ => {
let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
if refi0 >= 0 {
let rf = &self.refs[refi0 as usize];
rusty_h264_common::inter::mc_luma_padded_pre(scr, &rf.py, rf.lstride(), crate::LPAD, self.cw, ch, mb_x * 16 + px, mb_y * 16 + py, rw, rh, mv0.0, mv0.1, &mut a[..rw * rh]);
}
if refi1 >= 0 {
let rf = &self.refs1[refi1 as usize];
rusty_h264_common::inter::mc_luma_padded_pre(scr, &rf.py, rf.lstride(), crate::LPAD, self.cw, ch, mb_x * 16 + px, mb_y * 16 + py, rw, rh, mv1.0, mv1.1, &mut b[..rw * rh]);
}
drop(_gl);
let _gbl = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBBlend);
match (refi0 >= 0, refi1 >= 0) {
(true, true) => {
for dy in 0..rh {
let (ar, br) = (&a[dy * rw..dy * rw + rw], &b[dy * rw..dy * rw + rw]);
let base = (py + dy) * 16 + px;
let dst = &mut pred_y[base..base + rw];
match weights {
None => {
for ((d, p), q) in dst.iter_mut().zip(ar).zip(br) {
*d = ((*p as u16 + *q as u16 + 1) >> 1) as u8;
}
}
Some((w0, w1)) => {
for ((d, p), q) in dst.iter_mut().zip(ar).zip(br) {
*d = ((*p as i32 * w0 + *q as i32 * w1 + 32) >> 6).clamp(0, 255) as u8;
}
}
}
}
}
(true, false) => {
for dy in 0..rh {
let d = (py + dy) * 16 + px;
pred_y[d..d + rw].copy_from_slice(&a[dy * rw..dy * rw + rw]);
}
}
_ => {
for dy in 0..rh {
let d = (py + dy) * 16 + px;
pred_y[d..d + rw].copy_from_slice(&b[dy * rw..dy * rw + rw]);
}
}
}
false
}
});
if chroma_done {
return;
}
let _gc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBChroma);
self.b_mc_chroma(mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, c_pred, weights, cch);
}
#[allow(clippy::too_many_arguments)]
fn b_mc_chroma(
&self,
mb_x: usize,
mb_y: usize,
px: usize,
py: usize,
rw: usize,
rh: usize,
refi0: i32,
mv0: (i32, i32),
refi1: i32,
mv1: (i32, i32),
c_pred: &mut [[u8; 64]; 2],
weights: Option<(i32, i32)>,
cch: usize,
) {
let (crx, cry, crw, crh) = (px / 2, py / 2, rw / 2, rh / 2);
let full = crx == 0 && crw == 8;
for c in 0..2 {
match (refi0 >= 0, refi1 >= 0, full) {
(true, false, true) => {
let rf = &self.refs[refi0 as usize];
let pl = if c == 0 { &rf.pu } else { &rf.pv };
mc_chroma_padded(pl, rf.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8, mb_y * 8 + cry, crw, crh, mv0.0, mv0.1, &mut c_pred[c][cry * 8..cry * 8 + crw * crh]);
}
(false, true, true) => {
let rf = &self.refs1[refi1 as usize];
let pl = if c == 0 { &rf.pu } else { &rf.pv };
mc_chroma_padded(pl, rf.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8, mb_y * 8 + cry, crw, crh, mv1.0, mv1.1, &mut c_pred[c][cry * 8..cry * 8 + crw * crh]);
}
(true, true, true) => {
let rf = &self.refs[refi0 as usize];
let pl = if c == 0 { &rf.pu } else { &rf.pv };
mc_chroma_padded(pl, rf.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8, mb_y * 8 + cry, crw, crh, mv0.0, mv0.1, &mut c_pred[c][cry * 8..cry * 8 + crw * crh]);
let mut cb = [0u8; 64];
let rf = &self.refs1[refi1 as usize];
let pl = if c == 0 { &rf.pu } else { &rf.pv };
mc_chroma_padded(pl, rf.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8, mb_y * 8 + cry, crw, crh, mv1.0, mv1.1, &mut cb[..crw * crh]);
let dst = &mut c_pred[c][cry * 8..cry * 8 + crw * crh];
match weights {
None => {
for (d, s) in dst.iter_mut().zip(&cb[..crw * crh]) {
*d = ((*d as u16 + *s as u16 + 1) >> 1) as u8;
}
}
Some((w0, w1)) => {
for (d, s) in dst.iter_mut().zip(&cb[..crw * crh]) {
*d = ((*d as i32 * w0 + *s as i32 * w1 + 32) >> 6).clamp(0, 255) as u8;
}
}
}
}
_ => {
let (mut ca, mut cb) = ([0u8; 64], [0u8; 64]);
if refi0 >= 0 {
let rf = &self.refs[refi0 as usize];
let pl = if c == 0 { &rf.pu } else { &rf.pv };
mc_chroma_padded(pl, rf.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv0.0, mv0.1, &mut ca[..crw * crh]);
}
if refi1 >= 0 {
let rf = &self.refs1[refi1 as usize];
let pl = if c == 0 { &rf.pu } else { &rf.pv };
mc_chroma_padded(pl, rf.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv1.0, mv1.1, &mut cb[..crw * crh]);
}
match (refi0 >= 0, refi1 >= 0) {
(true, true) => {
for dy in 0..crh {
let (pr, qr) = (&ca[dy * crw..dy * crw + crw], &cb[dy * crw..dy * crw + crw]);
let base = (cry + dy) * 8 + crx;
let dst = &mut c_pred[c][base..base + crw];
match weights {
None => {
for ((d, p), q) in dst.iter_mut().zip(pr).zip(qr) {
*d = ((*p as u16 + *q as u16 + 1) >> 1) as u8;
}
}
Some((w0, w1)) => {
for ((d, p), q) in dst.iter_mut().zip(pr).zip(qr) {
*d = ((*p as i32 * w0 + *q as i32 * w1 + 32) >> 6).clamp(0, 255) as u8;
}
}
}
}
}
(true, false) => {
for dy in 0..crh {
let d = (cry + dy) * 8 + crx;
c_pred[c][d..d + crw].copy_from_slice(&ca[dy * crw..dy * crw + crw]);
}
}
_ => {
for dy in 0..crh {
let d = (cry + dy) * 8 + crx;
c_pred[c][d..d + crw].copy_from_slice(&cb[dy * crw..dy * crw + crw]);
}
}
}
}
}
}
}
#[allow(clippy::too_many_arguments)]
fn b_set_motion(&mut self, mb_x: usize, mb_y: usize, px: usize, py: usize, rw: usize, rh: usize, refi0: i32, mv0: (i32, i32), refi1: i32, mv1: (i32, i32)) {
let _gs = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBSet);
let w4 = self.mb_w * 4;
for by in py / 4..(py + rh) / 4 {
for bx in px / 4..(px + rw) / 4 {
let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
self.ref_idx_y[idx] = refi0;
self.mv_y[idx] = if refi0 >= 0 { mv0 } else { (0, 0) };
self.ref_idx1[idx] = refi1;
self.mv1[idx] = if refi1 >= 0 { mv1 } else { (0, 0) };
self.inter_y[idx] = true;
self.coded_y[idx] = true;
self.modes_y[idx] = 2;
}
}
}
#[allow(clippy::too_many_arguments)]
fn coalesce_region(
x: usize,
y: usize,
w: usize,
h: usize,
uniform: &dyn Fn(usize, usize, usize, usize) -> bool,
emit: &mut dyn FnMut(usize, usize, usize, usize),
) {
if uniform(x, y, w, h) {
emit(x, y, w, h);
return;
}
if h > 1 && uniform(x, y, w, h / 2) && uniform(x, y + h / 2, w, h / 2) {
emit(x, y, w, h / 2);
emit(x, y + h / 2, w, h / 2);
return;
}
if w > 1 && uniform(x, y, w / 2, h) && uniform(x + w / 2, y, w / 2, h) {
emit(x, y, w / 2, h);
emit(x + w / 2, y, w / 2, h);
return;
}
match (w > 1, h > 1) {
(true, true) => {
for q in 0..4usize {
Self::coalesce_region(x + (q % 2) * (w / 2), y + (q / 2) * (h / 2), w / 2, h / 2, uniform, emit);
}
}
(true, false) => {
Self::coalesce_region(x, y, w / 2, h, uniform, emit);
Self::coalesce_region(x + w / 2, y, w / 2, h, uniform, emit);
}
(false, true) => {
Self::coalesce_region(x, y, w, h / 2, uniform, emit);
Self::coalesce_region(x, y + h / 2, w, h / 2, uniform, emit);
}
(false, false) => emit(x, y, 1, 1),
}
}
fn decode_b_direct(&mut self, mb_x: usize, mb_y: usize, px: usize, py: usize, rw: usize, rh: usize, pred_y: &mut [u8; 256], c_pred: &mut [[u8; 64]; 2]) {
let _gb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBDirect);
if !self.direct_spatial {
return self.decode_b_direct_temporal(mb_x, mb_y, px, py, rw, rh, pred_y, c_pred);
}
let gd = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBDeriv);
let (nbx, nby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
let n0 = self.mv_neighbors_list(nbx, nby, 4, 0);
let n1 = self.mv_neighbors_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 (bx0, by0, bw, bh) = (px / 4, py / 4, rw / 4, rh / 4);
let mut czg = [[false; 4]; 4]; for dy in 0..bh {
for dx in 0..bw {
let (colx, coly) = self.col_block(bx0 + dx, by0 + dy);
czg[dy][dx] = !direct_zero && self.col_zero(mb_x * 4 + colx, mb_y * 4 + coly);
}
}
let uniform = |x: usize, y: usize, w: usize, h: usize| -> bool {
let t = czg[y][x];
(y..y + h).all(|dy| (x..x + w).all(|dx| czg[dy][dx] == t))
};
let mut rects: [(usize, usize, usize, usize); 16] = [(0, 0, 0, 0); 16];
let mut n = 0usize;
Self::coalesce_region(0, 0, bw, bh, &uniform, &mut |x, y, w, h| {
rects[n] = (x, y, w, h);
n += 1;
});
drop(gd); for &(x, y, w, h) in &rects[..n] {
let cz = czg[y][x];
let m0 = if refi0 == 0 && cz { (0, 0) } else { mv0 };
let m1 = if refi1 == 0 && cz { (0, 0) } else { mv1 };
let (lx, ly, lw, lh) = ((bx0 + x) * 4, (by0 + y) * 4, w * 4, h * 4);
self.b_mc_or_record(mb_x, mb_y, lx, ly, lw, lh, refi0, m0, refi1, m1, pred_y, c_pred);
self.b_set_motion(mb_x, mb_y, lx, ly, lw, lh, refi0, m0, refi1, m1);
}
}
#[allow(clippy::too_many_arguments)]
fn decode_b_direct_temporal(&mut self, mb_x: usize, mb_y: usize, px: usize, py: usize, rw: usize, rh: usize, pred_y: &mut [u8; 256], c_pred: &mut [[u8; 64]; 2]) {
let poc1 = self.refs1.first().map_or(0, |f| f.poc);
let infer = self.direct_8x8_inference;
let step = if infer { 8 } else { 4 };
let mut sy = py;
while sy < py + rh {
let mut sx = px;
while sx < px + rw {
let (colx, coly) = self.col_block(sx / 4, sy / 4);
let (mvcol, refpoc) = {
let col = &self.refs1[0];
let idx = (mb_y * 4 + coly) * col.w4 + (mb_x * 4 + colx);
if col.w4 != 0 && idx < col.mv.len() && col.ref_poc[idx] != i32::MIN {
(col.mv[idx], col.ref_poc[idx])
} else {
((0, 0), i32::MIN) }
};
let (refi0, mvc) = if refpoc == i32::MIN {
(0, (0, 0))
} else {
let r = self.refs.iter().position(|f| f.poc == refpoc).unwrap_or(0) as i32;
(r, mvcol)
};
let poc0 = self.refs[refi0 as usize].poc;
let td = (poc1 - poc0).clamp(-128, 127);
let tb = (self.cur_poc - poc0).clamp(-128, 127);
let (mv0, mv1) = if td == 0 || self.refs[refi0 as usize].long_term {
(mvc, (0, 0))
} else {
let tx = (16384 + td.abs() / 2) / td;
let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
let m0 = ((dsf * mvc.0 + 128) >> 8, (dsf * mvc.1 + 128) >> 8);
(m0, (m0.0 - mvc.0, m0.1 - mvc.1))
};
self.b_mc_or_record(mb_x, mb_y, sx, sy, step, step, refi0, mv0, 0, mv1, pred_y, c_pred);
self.b_set_motion(mb_x, mb_y, sx, sy, step, step, refi0, mv0, 0, mv1);
sx += step;
}
sy += step;
}
}
fn read_b_ref(&self, r: &mut BitReader, list: usize) -> Result<i32, MbError> {
let (active, avail) = if list == 0 {
(self.num_ref_active, self.refs.len())
} else {
(self.num_ref_active1, self.refs1.len())
};
let v = if active > 1 { read_ref_idx(r, active)? } else { 0 };
if v as usize >= avail {
return Err(MbError::Truncated);
}
Ok(v)
}
fn decode_b_skip(&mut self, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
if self.refs.is_empty() || self.refs1.is_empty() {
return Err(MbError::Unsupported("B without references"));
}
if self.edc_tx.is_some() {
self.edc_regions = Some(Vec::with_capacity(4));
}
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
self.decode_b_direct(mb_x, mb_y, 0, 0, 16, 16, &mut pred_y, &mut c_pred);
if let Some(regions) = self.edc_regions.take() {
let w4 = self.mb_w * 4;
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
self.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
}
self.edc_giveback();
self.edc_send_job(EdcJob::BSkip { mbx: mb_x, mby: mb_y, regions });
return Ok(());
}
for dy in 0..16 {
let d = (mb_y * 16 + dy) * self.cw + mb_x * 16;
self.rec_y[d..d + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
}
for c in 0..2 {
let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
for dy in 0..8 {
let d = (mb_y * 8 + dy) * self.ccw + mb_x * 8;
plane[d..d + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
}
}
let w4 = self.mb_w * 4;
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
self.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
}
Ok(())
}
fn decode_b_mb(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
let mb_type = r.read_ue()?;
if mb_type >= 23 {
return self.decode_intra_mb(r, mb_x, mb_y, mb_type - 23);
}
if self.refs.is_empty() || self.refs1.is_empty() {
return Err(MbError::Unsupported("B without references"));
}
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
if mb_type == 0 {
self.decode_b_direct(mb_x, mb_y, 0, 0, 16, 16, &mut pred_y, &mut c_pred);
return self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, self.direct_8x8_inference, false);
}
if mb_type == 22 {
return self.decode_b_8x8(r, mb_x, mb_y);
}
let (layout, mvmode, preds) = b_inter_layout(mb_type);
let mut refi = [[-1i32; 2]; 2]; for (p, &(_, _, _, _)) in layout.iter().enumerate() {
if preds[p].uses(0) {
refi[p][0] = self.read_b_ref(r, 0)?;
}
}
for (p, _) in layout.iter().enumerate() {
if preds[p].uses(1) {
refi[p][1] = self.read_b_ref(r, 1)?;
}
}
let mut mvd = [[(0i32, 0i32); 2]; 2];
for (p, _) in layout.iter().enumerate() {
if preds[p].uses(0) {
mvd[p][0] = (r.read_se()?, r.read_se()?);
}
}
for (p, _) in layout.iter().enumerate() {
if preds[p].uses(1) {
mvd[p][1] = (r.read_se()?, r.read_se()?);
}
}
for (p, &(rx, ry, rw, rh)) in layout.iter().enumerate() {
let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
let pwb = (rw / 4) as isize;
let mut mv = [(0i32, 0i32); 2];
for list in 0..2 {
if refi[p][list] >= 0 {
let n = self.mv_neighbors_list(pbx, pby, pwb, list);
let pmv = predict_partition_mv(mvmode, p, n[0], n[1], n[2], refi[p][list]);
mv[list] = (pmv.0 + mvd[p][list].0, pmv.1 + mvd[p][list].1);
}
}
self.b_set_motion(mb_x, mb_y, rx, ry, rw, rh, refi[p][0], mv[0], refi[p][1], mv[1]);
self.b_mc_or_record(mb_x, mb_y, rx, ry, rw, rh, refi[p][0], mv[0], refi[p][1], mv[1], &mut pred_y, &mut c_pred);
}
self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, true, false)
}
fn decode_b_8x8(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
let mut sub = [0u32; 4];
for s in sub.iter_mut() {
let v = r.read_ue()?;
if v > 12 {
return Err(MbError::Unsupported("invalid B sub_mb_type"));
}
*s = v;
}
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
let mut refi = [[-1i32; 2]; 4];
for (p, &st) in sub.iter().enumerate() {
if st != 0 && b_sub_uses(st, 0) {
refi[p][0] = self.read_b_ref(r, 0)?;
}
}
for (p, &st) in sub.iter().enumerate() {
if st != 0 && b_sub_uses(st, 1) {
refi[p][1] = self.read_b_ref(r, 1)?;
}
}
const MAX_MVD: usize = 16;
let mut mvd0 = [(0i32, 0i32); MAX_MVD];
let mut mvd1 = [(0i32, 0i32); MAX_MVD];
let (mut n0, mut n1) = (0usize, 0usize);
for &st in &sub {
if st != 0 && b_sub_uses(st, 0) {
for _ in b_sub_parts(st) {
mvd0[n0] = (r.read_se()?, r.read_se()?);
n0 += 1;
}
}
}
for &st in &sub {
if st != 0 && b_sub_uses(st, 1) {
for _ in b_sub_parts(st) {
mvd1[n1] = (r.read_se()?, r.read_se()?);
n1 += 1;
}
}
}
let (mut i0, mut i1) = (0usize, 0usize);
for (p, &st) in sub.iter().enumerate() {
let (b8x, b8y) = ((p % 2) * 8, (p / 2) * 8);
if st == 0 {
self.decode_b_direct(mb_x, mb_y, b8x, b8y, 8, 8, &mut pred_y, &mut c_pred);
continue;
}
for &(sx, sy, sw, sh) in b_sub_parts(st) {
let (px, py) = (b8x + sx, b8y + sy);
let (pbx, pby) = ((mb_x * 4 + px / 4) as isize, (mb_y * 4 + py / 4) as isize);
let pwb = (sw / 4) as isize;
let mut mv = [(0i32, 0i32); 2];
if b_sub_uses(st, 0) {
let n = self.mv_neighbors_list(pbx, pby, pwb, 0);
let pmv = predict_mv(n[0], n[1], n[2], refi[p][0]);
let d = mvd0[i0];
i0 += 1;
mv[0] = (pmv.0 + d.0, pmv.1 + d.1);
}
if b_sub_uses(st, 1) {
let n = self.mv_neighbors_list(pbx, pby, pwb, 1);
let pmv = predict_mv(n[0], n[1], n[2], refi[p][1]);
let d = mvd1[i1];
i1 += 1;
mv[1] = (pmv.0 + d.0, pmv.1 + d.1);
}
self.b_set_motion(mb_x, mb_y, px, py, sw, sh, refi[p][0], mv[0], refi[p][1], mv[1]);
self.b_mc_or_record(mb_x, mb_y, px, py, sw, sh, refi[p][0], mv[0], refi[p][1], mv[1], &mut pred_y, &mut c_pred);
}
}
let allow_8x8 = sub
.iter()
.all(|&st| if st == 0 { self.direct_8x8_inference } else { st <= 3 });
self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, allow_8x8, false)
}
fn decode_p8x8(
&mut self,
r: &mut BitReader,
mb_x: usize,
mb_y: usize,
ref0: bool,
) -> Result<(), MbError> {
if self.refs.is_empty() {
return Err(MbError::Unsupported("inter without reference"));
}
let w4 = self.mb_w * 4;
let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
let num_refs = self.refs.len();
let mut sub_types = [0u32; 4];
for st in sub_types.iter_mut() {
let v = r.read_ue()?;
if v > 3 {
return Err(MbError::Unsupported("B-slice / invalid sub_mb_type"));
}
*st = v;
}
let mut ref_idxs = [0i32; 4];
if self.num_ref_active > 1 && !ref0 {
for ri in ref_idxs.iter_mut() {
*ri = read_ref_idx(r, self.num_ref_active)?;
if *ri as usize >= num_refs {
return Err(MbError::Truncated); }
}
}
let defer = self.edc_tx.is_some() || self.edc_active;
let mut regions: [(usize, usize, usize, usize, i32, (i32, i32)); 16] =
[(0, 0, 0, 0, 0, (0, 0)); 16];
let mut nreg = 0usize;
for part in 0..4usize {
let refi = ref_idxs[part];
let (b8x, b8y) = ((part % 2) * 8, (part / 2) * 8);
for &(srx, sry, srw, srh) in sub_mb_partitions(sub_types[part]) {
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);
let mvd_x = r.read_se()?;
let mvd_y = r.read_se()?;
let mv = (pmv.0 + mvd_x, pmv.1 + mvd_y);
for by in py / 4..py / 4 + srh / 4 {
for bx in px / 4..px / 4 + srw / 4 {
let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
self.mv_y[idx] = mv;
self.inter_y[idx] = true;
self.ref_idx_y[idx] = refi;
self.coded_y[idx] = true;
}
}
regions[nreg] = (px, py, srw, srh, refi, mv);
nreg += 1;
}
}
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
if !defer {
for &(px, py, srw, srh, refi, mv) in ®ions[..nreg] {
let reference = &self.refs[refi as usize];
let mut tmp = [0u8; 256];
mc_luma_padded(&reference.py, reference.lstride(), crate::LPAD, self.cw, ch, mb_x * 16 + px, mb_y * 16 + py, srw, srh, mv.0, mv.1, &mut tmp);
restride(&mut pred_y, 16, px, py, &tmp, 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.pu } else { &reference.pv };
let mut tc = [0u8; 64];
mc_chroma_padded(rc, reference.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv.0, mv.1, &mut tc);
restride(&mut c_pred[cc], 8, crx, cry, &tc, crw, crh);
}
self.weight_partition(
&mut pred_y, &mut c_pred, 0, refi as usize, px, py, srw, srh,
);
}
}
let allow_8x8 = sub_types.iter().all(|&t| t == 0);
self.inter_finish(r, mb_x, mb_y, &pred_y, &c_pred, allow_8x8, defer)
}
#[allow(clippy::too_many_arguments)]
fn b_mc_or_record(&mut self, mb_x: usize, mb_y: usize, px: usize, py: usize, rw: usize, rh: usize, refi0: i32, mv0: (i32, i32), refi1: i32, mv1: (i32, i32), pred_y: &mut [u8; 256], c_pred: &mut [[u8; 64]; 2]) {
if self.edc_regions.is_some() {
let cr0 = if refi0 >= 0 { (refi0 as usize).min(self.refs.len().saturating_sub(1)) as i32 } else { -1 };
let cr1 = if refi1 >= 0 { (refi1 as usize).min(self.refs1.len().saturating_sub(1)) as i32 } else { -1 };
let w = if (cr0 >= 0 && self.refs.is_empty()) || (cr1 >= 0 && self.refs1.is_empty()) {
None } else {
self.implicit_weights(cr0, cr1)
};
self.edc_regions.as_mut().unwrap().push(BRegion { px, py, rw, rh, refi0, refi1, mv0, mv1, w });
return;
}
self.b_mc(mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, pred_y, c_pred);
}
fn edc_take_ctx(&mut self) -> PixelCtx {
PixelCtx {
rec_y: std::mem::take(&mut self.rec_y),
rec_u: std::mem::take(&mut self.rec_u),
rec_v: std::mem::take(&mut self.rec_v),
bak_y: std::mem::take(&mut self.bak_y),
bak_u: std::mem::take(&mut self.bak_u),
bak_v: std::mem::take(&mut self.bak_v),
refs: self.refs.clone(),
refs1: self.refs1.clone(),
weights: self.weights.clone(),
scaling: self.scaling,
scaling8: self.scaling8,
cw: self.cw,
ccw: self.ccw,
mb_w: self.mb_w,
mb_h: self.mb_h,
chroma_qp_offset: self.chroma_qp_offset,
flt_rows: self.flt_rows,
db_ena: self.db_ena,
db_oa: self.db_oa,
db_ob: self.db_ob,
cur_qp: self.cur_qp,
qp_grid: self.mb_qp.clone(),
t8_grid: self.mb_t8x8.clone(),
bs_store: self.bs_frame.clone(),
}
}
fn edc_restore_ctx(&mut self, ctx: PixelCtx) {
self.rec_y = ctx.rec_y;
self.rec_u = ctx.rec_u;
self.rec_v = ctx.rec_v;
self.bak_y = ctx.bak_y;
self.bak_u = ctx.bak_u;
self.bak_v = ctx.bak_v;
self.flt_rows = ctx.flt_rows;
}
#[inline]
fn edc_send_job(&mut self, job: EdcJob) {
edcstat::bump(&edcstat::JOBS, 1);
if !batch_on() {
self.edc_tx
.as_ref()
.unwrap()
.send(EdcMsg::Job(job))
.expect("worker alive");
return;
}
self.edc_batch.push(job);
}
fn edc_flush_batch(&mut self) {
if self.edc_batch.is_empty() {
return;
}
let cap = self.edc_batch.capacity().max(self.mb_w);
let jobs = std::mem::replace(&mut self.edc_batch, Vec::with_capacity(cap));
edcstat::bump(&edcstat::BATCHES, 1);
self.edc_tx
.as_ref()
.unwrap()
.send(EdcMsg::Batch(jobs))
.expect("worker alive");
}
fn edc_intra_sync(&mut self) {
if self.edc_tx.is_none() {
self.edc_flush();
return;
}
if self.edc_parked.is_some() {
return; }
self.edc_flush_batch();
edcstat::bump(&edcstat::NEEDCTX, 1);
self.edc_tx.as_ref().unwrap().send(EdcMsg::NeedCtx).expect("worker alive");
let mut ctx = self.edc_ctx_rx.as_ref().unwrap().recv().expect("worker ctx");
self.rec_y = std::mem::take(&mut ctx.rec_y);
self.rec_u = std::mem::take(&mut ctx.rec_u);
self.rec_v = std::mem::take(&mut ctx.rec_v);
self.bak_y = std::mem::take(&mut ctx.bak_y);
self.bak_u = std::mem::take(&mut ctx.bak_u);
self.bak_v = std::mem::take(&mut ctx.bak_v);
self.flt_rows = ctx.flt_rows;
self.edc_parked = Some(ctx);
}
fn edc_giveback(&mut self) {
if let Some(mut parked) = self.edc_parked.take() {
parked.rec_y = std::mem::take(&mut self.rec_y);
parked.rec_u = std::mem::take(&mut self.rec_u);
parked.rec_v = std::mem::take(&mut self.rec_v);
parked.bak_y = std::mem::take(&mut self.bak_y);
parked.bak_u = std::mem::take(&mut self.bak_u);
parked.bak_v = std::mem::take(&mut self.bak_v);
parked.flt_rows = self.flt_rows;
let _ = self.edc_back_tx.as_ref().unwrap().send(parked);
}
}
fn edc_commit_nnz(&mut self, mbx: usize, mby: usize, t8: bool, nnzs: &[u8; 24], cbp_chroma: u32) {
let (w4r, w2r) = (self.mb_w * 4, self.mb_w * 2);
if t8 {
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let n = nnzs[b8 * 4];
for sy in 0..2 {
for sx in 0..2 {
self.nnz_y[(mby * 4 + b8y * 2 + sy) * w4r + (mbx * 4 + b8x * 2 + sx)] = n;
}
}
}
} else {
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
self.nnz_y[(mby * 4 + lby) * w4r + (mbx * 4 + lbx)] = nnzs[blk];
}
}
if cbp_chroma == 2 {
for c in 0..2usize {
for &(bx, by) in &CHROMA_4X4_SCAN_XY {
self.nnz_c[c][(mby * 2 + by) * w2r + (mbx * 2 + bx)] = nnzs[16 + c * 4 + by * 2 + bx];
}
}
}
}
fn edc_flush(&mut self) {
if self.edc_jobs.is_empty() {
return;
}
let jobs = std::mem::take(&mut self.edc_jobs);
for j in &jobs {
match j {
EdcJob::Skip { mbx, mby, mv } => {
self.recon_p_skip(*mbx, *mby, *mv);
if double_recon() {
edcstat::bump(&edcstat::DOUBLED, 1);
self.recon_p_skip(*mbx, *mby, *mv);
}
}
EdcJob::Inter(job) => {
self.recon_p_inter(job);
if double_recon() {
edcstat::bump(&edcstat::DOUBLED, 1);
self.recon_p_inter(job);
}
}
EdcJob::InterNoRes(job) => {
let full = job.to_full();
self.recon_p_inter(&full);
if double_recon() {
edcstat::bump(&edcstat::DOUBLED, 1);
self.recon_p_inter(&full);
}
}
EdcJob::B(_) | EdcJob::BSkip { .. } => unreachable!("B jobs are worker-only"),
}
}
self.edc_jobs = jobs;
self.edc_jobs.clear();
}
fn recon_p_inter(&mut self, j: &PInterJob) {
let mbw = self.mb_w;
let saved_qp = self.cur_qp;
self.cur_qp = j.qp;
let qp = j.qp;
let qpc = self.chroma_qp_for(qp);
let (w4r, w2r) = (mbw * 4, mbw * 2);
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
{
let _ms = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMcStage);
let (rh16, cch) = (self.mb_h * 16, self.mb_h * 8);
let mut gmv = [(0i32, 0i32); 16];
let mut gref = [0usize; 16];
let _gg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MvGrid);
for by in 0..4usize {
for bx in 0..4usize {
let bidx = (j.mby * 4 + by) * w4r + (j.mbx * 4 + bx);
gmv[by * 4 + bx] = self.mv_y[bidx];
gref[by * 4 + bx] =
(self.ref_idx_y[bidx].max(0) as usize).min(self.refs.len() - 1);
}
}
drop(_gg);
let rect_eq = |x4: usize, y4: usize, w4: usize, h4: usize| -> bool {
let t = y4 * 4 + x4;
(0..h4).all(|dy| {
(0..w4).all(|dx| {
let b = (y4 + dy) * 4 + (x4 + dx);
gmv[b] == gmv[t] && gref[b] == gref[t]
})
})
};
let refs = &self.refs;
let (cw, ccw) = (self.cw, self.ccw);
let mut mc_rect = |x4: usize,
y4: usize,
w4: usize,
h4: usize,
pred_y: &mut [u8; 256],
c_pred: &mut [[u8; 64]; 2]| {
let b = y4 * 4 + x4;
let (mv, reference) = (gmv[b], &refs[gref[b]]);
let (w, h) = (w4 * 4, h4 * 4);
if w == 16 {
rusty_h264_common::inter::with_mc_scratch(|scr| rusty_h264_common::inter::mc_luma_padded_pre(scr, &reference.py, reference.lstride(), crate::LPAD, cw, rh16, j.mbx * 16, j.mby * 16 + y4 * 4, w, h, mv.0, mv.1, &mut pred_y[y4 * 64..y4 * 64 + w * h]));
} else {
let mut t = [0u8; 256];
rusty_h264_common::inter::with_mc_scratch(|scr| rusty_h264_common::inter::mc_luma_padded_pre(scr, &reference.py, reference.lstride(), crate::LPAD, cw, rh16, j.mbx * 16 + x4 * 4, j.mby * 16 + y4 * 4, w, h, mv.0, mv.1, &mut t[..w * h]));
let _pb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
for dy in 0..h {
pred_y[(y4 * 4 + dy) * 16 + x4 * 4..][..w]
.copy_from_slice(&t[dy * w..dy * w + w]);
}
}
let (cw4, ch4) = (w4 * 2, h4 * 2);
for cc in 0..2 {
let rc = if cc == 0 { &reference.pu } else { &reference.pv };
if cw4 == 8 {
mc_chroma_padded(rc, reference.cstride(), crate::CPAD, ccw, cch, j.mbx * 8, j.mby * 8 + y4 * 2, cw4, ch4, mv.0, mv.1, &mut c_pred[cc][y4 * 16..y4 * 16 + cw4 * ch4]);
continue;
}
let mut tc = [0u8; 64];
mc_chroma_padded(rc, reference.cstride(), crate::CPAD, ccw, cch, j.mbx * 8 + x4 * 2, j.mby * 8 + y4 * 2, cw4, ch4, mv.0, mv.1, &mut tc[..cw4 * ch4]);
let _pb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
for dy in 0..ch4 {
c_pred[cc][(y4 * 2 + dy) * 8 + x4 * 2..][..cw4]
.copy_from_slice(&tc[dy * cw4..dy * cw4 + cw4]);
}
}
};
if rect_eq(0, 0, 4, 4) {
mc_rect(0, 0, 4, 4, &mut pred_y, &mut c_pred);
} else if rect_eq(0, 0, 4, 2) && rect_eq(0, 2, 4, 2) {
mc_rect(0, 0, 4, 2, &mut pred_y, &mut c_pred);
mc_rect(0, 2, 4, 2, &mut pred_y, &mut c_pred);
} else if rect_eq(0, 0, 2, 4) && rect_eq(2, 0, 2, 4) {
mc_rect(0, 0, 2, 4, &mut pred_y, &mut c_pred);
mc_rect(2, 0, 2, 4, &mut pred_y, &mut c_pred);
} else {
for q in 0..4usize {
let (qx, qy) = ((q % 2) * 2, (q / 2) * 2);
if rect_eq(qx, qy, 2, 2) {
mc_rect(qx, qy, 2, 2, &mut pred_y, &mut c_pred);
} else if rect_eq(qx, qy, 2, 1) && rect_eq(qx, qy + 1, 2, 1) {
mc_rect(qx, qy, 2, 1, &mut pred_y, &mut c_pred);
mc_rect(qx, qy + 1, 2, 1, &mut pred_y, &mut c_pred);
} else if rect_eq(qx, qy, 1, 2) && rect_eq(qx + 1, qy, 1, 2) {
mc_rect(qx, qy, 1, 2, &mut pred_y, &mut c_pred);
mc_rect(qx + 1, qy, 1, 2, &mut pred_y, &mut c_pred);
} else {
for j in 0..4usize {
mc_rect(qx + (j % 2), qy + (j / 2), 1, 1, &mut pred_y, &mut c_pred);
}
}
}
}
if self.weights.is_some() {
for by in 0..4usize {
for bx in 0..4usize {
let refi = gref[by * 4 + bx];
self.weight_partition(
&mut pred_y, &mut c_pred, 0, refi, bx * 4, by * 4, 4, 4,
);
}
}
}
}
self.add_inter_residual(j.mbx, j.mby, &pred_y, &c_pred, &j.luma_scan, if j.t8 { Some(&j.luma8) } else { None }, &j.cdc, &j.cac, j.cbp_chroma, &j.nnzs);
self.cur_qp = saved_qp;
}
fn decode_p_skip(&mut self, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
self.mb_kind[mb_y * self.mb_w + mb_x] = rusty_h264_common::deblock::MB_KIND_SKIP;
if self.refs.is_empty() {
return Err(MbError::Unsupported("P_Skip without reference"));
}
let mv = self.skip_mv(mb_x, mb_y);
{
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
self.set_mb_mv(mb_x, mb_y, mv, true, 0);
let w4 = self.mb_w * 4;
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
self.coded_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = true;
self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
}
}
if self.edc_tx.is_some() {
self.edc_giveback();
self.edc_send_job(EdcJob::Skip { mbx: mb_x, mby: mb_y, mv });
return Ok(());
}
if self.edc_active {
self.edc_jobs.push(EdcJob::Skip { mbx: mb_x, mby: mb_y, mv });
return Ok(());
}
self.recon_p_skip(mb_x, mb_y, mv);
if double_recon() {
self.recon_p_skip(mb_x, mb_y, mv);
}
Ok(())
}
fn recon_p_skip(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32)) {
let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
let mut pred = [0u8; 256];
let rf0 = &self.refs[0];
mc_luma_padded(&rf0.py, rf0.lstride(), crate::LPAD, self.cw, ch, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred);
if let Some(wt) = &self.weights {
for p in pred.iter_mut() {
*p = wt.apply_luma(*p, 0, 0);
}
}
{
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
for dy in 0..16 {
let d = (mb_y * 16 + dy) * self.cw + mb_x * 16;
self.rec_y[d..d + 16].copy_from_slice(&pred[dy * 16..dy * 16 + 16]);
}
}
for c in 0..2 {
let mut pc = [0u8; 64];
let rf0 = &self.refs[0];
let rc = if c == 0 { &rf0.pu } else { &rf0.pv };
mc_chroma_padded(rc, rf0.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut pc);
if let Some(wt) = &self.weights {
for p in pc.iter_mut() {
*p = wt.apply_chroma(*p, 0, 0, c);
}
}
let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
for dy in 0..8 {
let d = (mb_y * 8 + dy) * self.ccw + mb_x * 8;
plane[d..d + 8].copy_from_slice(&pc[dy * 8..dy * 8 + 8]);
}
}
}
fn predict_i4_mode(&self, bx: usize, by: usize) -> u8 {
if bx == 0 || by == 0 {
return 2;
}
if !self.nbr_in_slice((bx - 1) / 4, by / 4)
|| !self.nbr_in_slice(bx / 4, (by - 1) / 4)
|| !self.intra_nbr_ok(bx - 1, by)
|| !self.intra_nbr_ok(bx, by - 1)
{
return 2;
}
let w4 = self.mb_w * 4;
self.modes_y[by * w4 + (bx - 1)].min(self.modes_y[(by - 1) * w4 + bx])
}
fn gather_i4(
&self,
px: usize,
py: usize,
avail_top: bool,
avail_left: bool,
bx: usize,
by: usize,
) -> ([u8; 8], [u8; 4], u8) {
let (cw, w4) = (self.cw, self.mb_w * 4);
let mut top = [0u8; 8];
let mut left = [0u8; 4];
let mut corner = 0;
if avail_top {
for i in 0..4 {
top[i] = self.top_y_px(py, px + i);
}
let tr_avail = bx + 1 < w4
&& self.coded_y[(by - 1) * w4 + (bx + 1)]
&& self.nbr_in_slice((bx + 1) / 4, (by - 1) / 4)
&& self.intra_nbr_ok(bx + 1, by - 1);
for i in 0..4 {
top[4 + i] = if tr_avail {
self.top_y_px(py, px + 4 + i)
} else {
top[3]
};
}
}
if avail_left {
for i in 0..4 {
left[i] = self.rec_y[(py + i) * cw + px - 1];
}
}
if avail_top && avail_left && self.intra_nbr_ok(bx - 1, by - 1) {
corner = self.top_y_px(py, px - 1);
}
(top, left, corner)
}
fn decode_ipcm(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
r.align_to_byte()?;
let (lx, ly) = (mb_x * 16, mb_y * 16);
for dy in 0..16 {
for dx in 0..16 {
self.rec_y[(ly + dy) * self.cw + (lx + dx)] = r.read_bits(8)? as u8;
}
}
let (cx, cy) = (mb_x * 8, mb_y * 8);
for plane in [&mut self.rec_u, &mut self.rec_v] {
for dy in 0..8 {
for dx in 0..8 {
plane[(cy + dy) * self.ccw + (cx + dx)] = r.read_bits(8)? as u8;
}
}
}
let (w4, w2) = (self.mb_w * 4, self.mb_w * 2);
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
let idx = (mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx);
self.nnz_y[idx] = 16;
self.modes_y[idx] = 2;
self.inter_y[idx] = false;
self.ref_idx_y[idx] = -1;
self.mv_y[idx] = (0, 0);
}
for c in 0..2 {
for by in 0..2 {
for bx in 0..2 {
self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = 16;
}
}
}
Ok(())
}
fn decode_i4x4(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
let w4 = self.mb_w * 4;
let mut modes = [2u8; 16]; for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
let predicted = self.predict_i4_mode(bx, by);
let actual = if r.read_bit()? {
predicted
} else {
let rem = r.read_bits(3)? as u8;
if rem < predicted {
rem
} else {
rem + 1
}
};
self.modes_y[by * w4 + bx] = actual;
modes[lby * 4 + lbx] = actual;
}
let chroma_mode = r.read_ue()? as u8;
let cbp = read_cbp_intra(r)?;
let cbp_luma = cbp & 15;
let cbp_chroma = cbp >> 4;
if cbp != 0 {
self.step_qp(r.read_se()?);
}
let qp = self.cur_qp;
let top_mb_avail = mb_y > 0
&& self.nbr_in_slice(mb_x, mb_y - 1)
&& self.intra_nbr_ok(mb_x * 4, mb_y * 4 - 1);
let left_mb_avail = mb_x > 0
&& self.nbr_in_slice(mb_x - 1, mb_y)
&& self.intra_nbr_ok(mb_x * 4 - 1, mb_y * 4);
self.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 (px, py) = (bx * 4, by * 4);
let avail_top = lby > 0 || top_mb_avail;
let avail_left = lbx > 0 || left_mb_avail;
let mut qb = [0i32; 16];
let total = if cbp_luma & (1 << (blk / 4)) != 0 {
let nc = self.nc_pred(lbx, lby);
let scan16 = decode_residual_block(r, 16, nc)?;
qb = un_scan_4x4_dcac(&scan16);
scan16.iter().filter(|&&v| v != 0).count() as u8
} else {
0
};
self.nnz_cache_set(lbx, lby, total);
self.nnz_y[by * w4 + bx] = total;
let (top, left, corner) = self.gather_i4(px, py, avail_top, avail_left, bx, by);
let pred = intra4x4_pred(modes[lby * 4 + lbx], avail_top, avail_left, &top, &left, corner);
let mut predb = [0i32; 16];
for i in 0..16 {
predb[i] = pred[i] as i32;
}
let s = reconstruct_4x4(&self.dequant(&qb, qp, 0), &predb);
store(&mut self.rec_y, self.cw, px, py, &s);
self.coded_y[by * w4 + bx] = true;
}
self.decode_chroma(r, mb_x, mb_y, cbp_chroma, chroma_mode)
}
fn decode_i8x8(&mut self, r: &mut BitReader, mb_x: usize, mb_y: usize) -> Result<(), MbError> {
let w4 = self.mb_w * 4;
self.mb_t8x8[mb_y * self.mb_w + mb_x] = true;
let mut modes8 = [2u8; 4];
for (b8, mode) in modes8.iter_mut().enumerate() {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2);
let predicted = self.predict_i4_mode(bx, by);
let actual = if r.read_bit()? {
predicted
} else {
let rem = r.read_bits(3)? as u8;
if rem < predicted { rem } else { rem + 1 }
};
*mode = actual;
for sy in 0..2 {
for sx in 0..2 {
self.modes_y[(by + sy) * w4 + (bx + sx)] = actual;
}
}
}
let chroma_mode = r.read_ue()? as u8;
let cbp = read_cbp_intra(r)?;
let cbp_luma = cbp & 15;
let cbp_chroma = cbp >> 4;
if cbp != 0 {
self.step_qp(r.read_se()?);
}
let qp = self.cur_qp;
let top_mb_avail = mb_y > 0
&& self.nbr_in_slice(mb_x, mb_y - 1)
&& self.intra_nbr_ok(mb_x * 4, mb_y * 4 - 1);
let left_mb_avail = mb_x > 0
&& self.nbr_in_slice(mb_x - 1, mb_y)
&& self.intra_nbr_ok(mb_x * 4 - 1, mb_y * 4);
self.nnz_cache_load(mb_x, mb_y);
for b8 in 0..4 {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2);
let (px, py) = (bx * 4, by * 4);
let mut res8 = [0i32; 64];
if cbp_luma & (1 << b8) != 0 {
let mut scan8 = [0i32; 64];
for sub in 0..4 {
let (sx, sy) = (sub % 2, sub / 2);
let (cx, cy) = (b8x * 2 + sx, b8y * 2 + sy);
let nc = self.nc_pred(cx, cy);
let blk = decode_residual_block(r, 16, nc)?;
let total = blk.iter().filter(|&&v| v != 0).count() as u8;
self.nnz_cache_set(cx, cy, total);
self.nnz_y[(by + sy) * w4 + (bx + sx)] = total;
for k in 0..16 {
scan8[4 * k + sub] = blk[k];
}
}
let raster = un_scan_8x8(&scan8);
res8 = self.inv_quant8(&raster, qp, 0);
} else {
for sub in 0..4 {
let (sx, sy) = (sub % 2, sub / 2);
self.nnz_cache_set(b8x * 2 + sx, b8y * 2 + sy, 0);
self.nnz_y[(by + sy) * w4 + (bx + sx)] = 0;
}
}
let avail_top = b8y > 0 || top_mb_avail;
let avail_left = b8x > 0 || left_mb_avail;
let (top, left, corner, avail_corner) =
self.gather_i8(px, py, avail_top, avail_left, bx, by);
let pred = intra8x8_pred(
modes8[b8], avail_top, avail_left, avail_corner, &top, &left, corner,
);
let mut predb = [0i32; 64];
for i in 0..64 {
predb[i] = pred[i] as i32;
}
let recon = add_residual_8x8(&res8, &predb);
for dy in 0..8 {
for dx in 0..8 {
self.rec_y[(py + dy) * self.cw + (px + dx)] = recon[dy * 8 + dx];
}
}
for sy in 0..2 {
for sx in 0..2 {
self.coded_y[(by + sy) * w4 + (bx + sx)] = true;
}
}
}
self.decode_chroma(r, mb_x, mb_y, cbp_chroma, chroma_mode)
}
fn inv_quant8(&self, raster: &[i32; 64], qp: u8, list: usize) -> [i32; 64] {
match &self.scaling8 {
Some(s) => inverse_quant_8x8(raster, qp, &s[list]),
None => inverse_quant_8x8(raster, qp, &[16i32; 64]),
}
}
#[allow(clippy::too_many_arguments)]
fn gather_i8(
&self,
px: usize,
py: usize,
avail_top: bool,
avail_left: bool,
bx: usize,
by: usize,
) -> ([u8; 16], [u8; 8], u8, bool) {
let (cw, w4) = (self.cw, self.mb_w * 4);
let mut top = [0u8; 16];
let mut left = [0u8; 8];
let mut corner = 0;
if avail_top {
for i in 0..8 {
top[i] = self.top_y_px(py, px + i);
}
let tr_avail = bx + 2 < w4
&& self.coded_y[(by - 1) * w4 + (bx + 2)]
&& self.nbr_in_slice((bx + 2) / 4, (by - 1) / 4)
&& self.intra_nbr_ok(bx + 2, by - 1);
for i in 0..8 {
top[8 + i] = if tr_avail {
self.top_y_px(py, px + 8 + i)
} else {
top[7]
};
}
}
if avail_left {
for i in 0..8 {
left[i] = self.rec_y[(py + i) * cw + px - 1];
}
}
let avail_corner = avail_top && avail_left && self.intra_nbr_ok(bx - 1, by - 1);
if avail_corner {
corner = self.top_y_px(py, px - 1);
}
(top, left, corner, avail_corner)
}
fn decode_i16(
&mut self,
r: &mut BitReader,
mb_x: usize,
mb_y: usize,
mt: u32,
) -> Result<(), MbError> {
let pred_mode = I16Mode::from_id(mt % 4);
let cbp_chroma = (mt % 12) / 4;
let cbp_luma_15 = mt / 12 == 1;
let chroma_mode = r.read_ue()? as u8;
self.step_qp(r.read_se()?);
let qp = self.cur_qp;
let w4 = self.mb_w * 4;
self.nnz_cache_load(mb_x, mb_y);
let nc_dc = self.nc_pred(0, 0);
let dc_scan = decode_residual_block(r, 16, nc_dc)?;
let dc_levels = un_scan_4x4_dcac(&dc_scan);
let recon_dc = self.dequant_luma_dc(&dc_levels, qp, 0);
let mut q_blocks = [[0i32; 16]; 16];
for &(bx, by) in &LUMA_4X4_SCAN_XY {
let total = if cbp_luma_15 {
let nc = self.nc_pred(bx, by);
let ac = decode_residual_block(r, 15, nc)?;
un_scan_4x4_ac_into(&ac, &mut q_blocks[by * 4 + bx]);
ac.iter().filter(|&&v| v != 0).count() as u8
} else {
0
};
self.nnz_cache_set(bx, by, total);
self.nnz_y[(mb_y * 4 + by) * w4 + (mb_x * 4 + bx)] = total;
}
let avail_top = mb_y > 0
&& self.nbr_in_slice(mb_x, mb_y - 1)
&& self.intra_nbr_ok(mb_x * 4, mb_y * 4 - 1);
let avail_left = mb_x > 0
&& self.nbr_in_slice(mb_x - 1, mb_y)
&& self.intra_nbr_ok(mb_x * 4 - 1, mb_y * 4);
let (lx, ly) = (mb_x * 16, mb_y * 16);
let mut top = [0u8; 16];
let mut left = [0u8; 16];
if avail_top {
for i in 0..16 {
top[i] = self.top_y_px(ly, lx + i);
}
}
if avail_left {
for i in 0..16 {
left[i] = self.rec_y[(ly + i) * self.cw + lx - 1];
}
}
let corner = if avail_top && avail_left {
self.top_y_px(ly, lx - 1)
} else {
0
};
let pred_l = luma16x16_pred(pred_mode, avail_top, avail_left, &top, &left, corner);
for by in 0..4 {
for bx in 0..4 {
let mut deq = self.dequant(&q_blocks[by * 4 + bx], qp, 0);
deq[0] = recon_dc[by * 4 + bx];
let mut predb = [0i32; 16];
for dy in 0..4 {
for dx in 0..4 {
predb[dy * 4 + dx] = pred_l[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
}
}
let s = reconstruct_4x4(&deq, &predb);
store(&mut self.rec_y, self.cw, lx + bx * 4, ly + by * 4, &s);
}
}
for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
}
self.decode_chroma(r, mb_x, mb_y, cbp_chroma, chroma_mode)
}
fn decode_chroma(
&mut self,
r: &mut BitReader,
mb_x: usize,
mb_y: usize,
cbp_chroma: u32,
chroma_mode: u8,
) -> Result<(), MbError> {
let qpc = self.chroma_qp_for(self.cur_qp);
let (cx, cy) = (mb_x * 8, mb_y * 8);
let avail_top = mb_y > 0
&& self.nbr_in_slice(mb_x, mb_y - 1)
&& self.intra_nbr_ok(mb_x * 4, mb_y * 4 - 1);
let avail_left = mb_x > 0
&& self.nbr_in_slice(mb_x - 1, mb_y)
&& self.intra_nbr_ok(mb_x * 4 - 1, mb_y * 4);
let mut c_recon_dc = [[0i32; 4]; 2];
if cbp_chroma != 0 {
for (c, slot) in c_recon_dc.iter_mut().enumerate() {
let dc = decode_residual_block(r, 4, -1)?;
*slot = self.dequant_chroma_dc(&[dc[0], dc[1], dc[2], dc[3]], qpc, 1 + c);
}
}
let mut c_q_blocks = [[[0i32; 16]; 4]; 2];
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 = decode_residual_block(r, 15, nc)?;
let total = ac.iter().filter(|&&v| v != 0).count() 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;
un_scan_4x4_ac_into(&ac, &mut c_q_blocks[c][by * 2 + bx]);
}
}
}
for c in 0..2 {
let mut ctop = [0u8; 8];
let mut cleft = [0u8; 8];
let mut ccorner = 0u8;
{
let rec_c = if c == 0 { &self.rec_u } else { &self.rec_v };
if avail_top {
for i in 0..8 {
ctop[i] = self.top_c_px(c, cy, cx + i);
}
}
if avail_left {
for i in 0..8 {
cleft[i] = rec_c[(cy + i) * self.ccw + cx - 1];
}
}
if avail_top && avail_left {
ccorner = self.top_c_px(c, cy, cx - 1);
}
}
let pred8 = chroma8x8_pred(chroma_mode, avail_top, avail_left, &ctop, &cleft, ccorner);
for &(bx, by) in &CHROMA_4X4_SCAN_XY {
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;
}
}
let mut deq = self.dequant(&c_q_blocks[c][by * 2 + bx], qpc, 1 + c);
deq[0] = c_recon_dc[c][by * 2 + bx];
let s = reconstruct_4x4(&deq, &predb);
let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
store(plane, self.ccw, cx + bx * 4, cy + by * 4, &s);
}
}
Ok(())
}
fn dump_mb_map(&self) {
if std::env::var_os("RH264_DUMP_MB").is_none() {
return;
}
let w4 = self.mb_w * 4;
let mut hist = [0usize; 4];
eprintln!("--- frame poc {} ---", self.cur_poc);
for mb_y in 0..self.mb_h {
let mut row = String::new();
for mb_x in 0..self.mb_w {
let b = (mb_y * 4) * w4 + mb_x * 4;
let r = self.ref_idx_y[b];
if r < 0 {
row.push('i');
} else {
if (r as usize) < 4 {
hist[r as usize] += 1;
}
row.push((b'0' + (r as u8).min(9)) as char);
}
}
eprintln!("{row}");
}
eprintln!(
"ref histogram: {hist:?} num_ref_active={} refs.len()={} OUT-OF-RANGE={}",
self.num_ref_active,
self.refs.len(),
hist.iter().skip(self.refs.len()).sum::<usize>()
);
let list: Vec<String> = self
.refs
.iter()
.enumerate()
.map(|(i, f)| {
let synth = if f.w4 == 0 { " SYNTH-GREY" } else { "" };
format!("[{i}] poc={} fn={}{synth}", f.poc, f.frame_num)
})
.collect();
eprintln!(" RefPicList0: {}", list.join(" "));
}
pub fn deblock(&mut self, offset_a: i32, offset_b: i32) {
self.edc_flush(); self.dump_mb_map();
if rowdb_on() {
while self.bs_rows < self.mb_h {
let r = self.bs_rows;
self.derive_bs_row(r);
self.bs_rows += 1;
}
}
let nnz_db_storage;
let nnz_db: &[u8] = if self.mb_t8x8.iter().any(|&t| t) {
let mut n = self.nnz_y.clone();
let w4 = self.mb_w * 4;
for mb_y in 0..self.mb_h {
for mb_x in 0..self.mb_w {
if !self.mb_t8x8[mb_y * self.mb_w + mb_x] {
continue;
}
for b8 in 0..4 {
let (bx, by) = (mb_x * 4 + (b8 % 2) * 2, mb_y * 4 + (b8 / 2) * 2);
let any = (0..2).any(|sy| (0..2).any(|sx| self.nnz_y[(by + sy) * w4 + (bx + sx)] > 0));
for sy in 0..2 {
for sx in 0..2 {
n[(by + sy) * w4 + (bx + sx)] = u8::from(any);
}
}
}
}
}
nnz_db_storage = n;
&nnz_db_storage
} else {
&self.nnz_y
};
let poc0: Vec<i32> = self.refs.iter().map(|f| f.poc).collect();
let poc1: Vec<i32> = self.refs1.iter().map(|f| f.poc).collect();
let mut info = rusty_h264_common::deblock::BlockInfo {
inter: &self.inter_y,
nnz: nnz_db,
mv: &self.mv_y,
ref_id: &self.ref_idx_y,
mv1: &self.mv1,
ref_id1: if poc1.is_empty() { &[] } else { &self.ref_idx1 },
w4: self.mb_w * 4,
t8x8: &self.mb_t8x8,
bs: &[],
poc0: &poc0,
poc1: &poc1,
kind: &self.mb_kind,
};
let bs_store;
if rowdb_on() {
bs_store = std::mem::take(&mut self.bs_frame);
info.bs = &bs_store;
} else if !std::env::var_os("RS_H264_BS_PRE").is_some_and(|v| v == "0") {
let mut buf = Vec::new();
rusty_h264_common::deblock::precompute_bs_frame(&info, self.mb_w, self.mb_h, &mut buf);
bs_store = buf;
info.bs = &bs_store;
} else {
bs_store = Vec::new();
}
let first_row = if rowdb_on() { self.flt_rows } else { 0 };
rusty_h264_common::deblock::filter_frame_rows(
&mut self.rec_y,
&mut self.rec_u,
&mut self.rec_v,
self.mb_w,
self.mb_h,
first_row..self.mb_h,
&self.mb_qp,
self.chroma_qp_offset,
offset_a,
offset_b,
&info,
);
drop(info);
if rowdb_on() {
self.bs_frame = bs_store;
}
}
pub fn into_frame_recycle(mut self, crop_r: usize, crop_b: usize) -> (YuvFrame, GridPool) {
let [c0, c1] = std::mem::take(&mut self.nnz_c);
let pool = GridPool {
bits_per_mb: self.bits_per_mb,
mb_qp: std::mem::take(&mut self.mb_qp),
bs_frame: std::mem::take(&mut self.bs_frame),
pk_prev: std::mem::take(&mut self.pk_prev),
pk_cur: std::mem::take(&mut self.pk_cur),
nnz_dbr: std::mem::take(&mut self.nnz_dbr),
bak_y: std::mem::take(&mut self.bak_y),
bak_u: std::mem::take(&mut self.bak_u),
bak_v: std::mem::take(&mut self.bak_v),
nnz_y: std::mem::take(&mut self.nnz_y),
nnz_c0: c0,
nnz_c1: c1,
modes_y: std::mem::take(&mut self.modes_y),
coded_y: std::mem::take(&mut self.coded_y),
mv_y: std::mem::take(&mut self.mv_y),
inter_y: std::mem::take(&mut self.inter_y),
ref_idx_y: std::mem::take(&mut self.ref_idx_y),
mv1: std::mem::take(&mut self.mv1),
ref_idx1: std::mem::take(&mut self.ref_idx1),
mb_t8x8: std::mem::take(&mut self.mb_t8x8),
mb_kind: std::mem::take(&mut self.mb_kind),
};
(self.into_frame(crop_r, crop_b), pool)
}
pub fn into_frame(self, crop_r: usize, crop_b: usize) -> YuvFrame {
if crop_r == 0 && crop_b == 0 {
return YuvFrame {
width: self.cw,
height: self.ch,
y: self.rec_y,
u: self.rec_u,
v: self.rec_v,
};
}
let dw = self.cw - 2 * crop_r;
let dh = self.ch - 2 * crop_b;
let mut y = vec![0u8; dw * dh];
for row in 0..dh {
y[row * dw..row * dw + dw].copy_from_slice(&self.rec_y[row * self.cw..row * self.cw + dw]);
}
let (cdw, cdh) = (dw / 2, dh / 2);
let mut u = vec![0u8; cdw * cdh];
let mut v = vec![0u8; cdw * cdh];
for row in 0..cdh {
u[row * cdw..row * cdw + cdw]
.copy_from_slice(&self.rec_u[row * self.ccw..row * self.ccw + cdw]);
v[row * cdw..row * cdw + cdw]
.copy_from_slice(&self.rec_v[row * self.ccw..row * self.ccw + cdw]);
}
let _ = self.cch;
YuvFrame {
width: dw,
height: dh,
y,
u,
v,
}
}
}
fn cabac_unary(cab: &mut crate::cabac::Cabac, ctx: usize, off: usize) -> u32 {
if cab.decode_decision(ctx) == 0 {
return 0;
}
let mut sym = 0;
loop {
let bin = cab.decode_decision(ctx + off);
sym += 1;
if bin == 0 || sym >= 512 {
break;
}
}
sym
}
fn cabac_exp_bypass(cab: &mut crate::cabac::Cabac, mut count: i32) -> u32 {
let mut sym = 0u32;
loop {
let c = cab.decode_bypass();
if c == 1 {
sym += 1 << count;
count += 1;
}
if c == 0 || count == 16 {
break;
}
}
let mut sym2 = 0u32;
while count > 0 {
count -= 1;
if cab.decode_bypass() != 0 {
sym2 |= 1 << count;
}
}
sym + sym2
}
fn cabac_ueg_level(cab: &mut crate::cabac::Cabac, ctx: usize) -> u32 {
if cab.decode_decision(ctx) == 0 {
return 0;
}
let mut code = 0u32;
let mut count = 1;
let mut tmp;
loop {
tmp = cab.decode_decision(ctx);
code += 1;
count += 1;
if tmp == 0 || count == 13 {
break;
}
}
if tmp != 0 {
code += cabac_exp_bypass(cab, 0) + 1;
}
code
}
fn parse_mb_qp_delta_cabac(cab: &mut crate::cabac::Cabac, last_delta_qp: &mut i32) -> i32 {
const O: usize = 60;
let ctx_inc = (*last_delta_qp != 0) as usize;
let mut qp_delta = 0;
if cab.decode_decision(O + ctx_inc) != 0 {
let code = cabac_unary(cab, O + 2, 1) + 1;
qp_delta = ((code + 1) >> 1) as i32;
if code & 1 == 0 {
qp_delta = -qp_delta;
}
}
*last_delta_qp = qp_delta;
qp_delta
}
const NZC_CACHE: [usize; 24] = [
9, 10, 17, 18, 11, 12, 19, 20, 25, 26, 33, 34, 27, 28, 35, 36, 14, 15, 22, 23, 38, 39, 46, 47, ];
const RES_MAXPOS: [i32; 11] = [0, 15, 14, 15, 3, 14, 63, 3, 3, 14, 14];
const RES_MAXC2: [i32; 11] = [0, 4, 4, 4, 3, 4, 4, 3, 3, 4, 4];
const RES_CBF: [usize; 11] = [0, 0, 4, 8, 12, 16, 0, 12, 12, 16, 16];
const RES_MAP: [usize; 11] = [0, 0, 15, 29, 44, 47, 0, 44, 44, 47, 47];
const RES_ONE: [usize; 11] = [0, 0, 10, 20, 30, 39, 199, 30, 30, 39, 39];
const RP_I16_DC: usize = 1;
const RP_I16_AC: usize = 2;
const RP_LUMA_4X4: usize = 3;
const RP_CHROMA_DC: usize = 7; const RP_CHROMA_AC: usize = 9; const RP_LUMA_8X8: usize = 6;
use rusty_h264_common::cabac_tables::{LAST8X8, SIG8X8};
#[allow(clippy::too_many_arguments)]
fn parse_residual_cabac(
cab: &mut crate::cabac::Cabac,
nzc: &mut [u8; 48],
cbf_dc: &mut u16,
iz: usize,
rp: usize,
is_intra: bool,
ndc: (Option<u16>, Option<u16>), out: &mut [i32], ) -> u32 {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Entropy);
let is8 = rp == RP_LUMA_8X8;
let is_dc = rp == RP_I16_DC || rp == RP_CHROMA_DC || rp == RP_CHROMA_DC + 1;
let (mut na, mut nb) = (is_intra as u8, is_intra as u8);
let scan = 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;
}
}
if !is8 {
let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EntCbf);
let cbf = cab.decode_decision(85 + RES_CBF[rp] + (na + (nb << 1)) as usize);
if cbf == 0 {
if !is_dc {
nzc[scan] = 0;
}
return 0;
}
if is_dc {
*cbf_dc |= 1 << rp;
}
}
let maxpos = RES_MAXPOS[rp] as usize;
let (map, last) = if is8 { (402, 417) } else { (105 + RES_MAP[rp], 166 + RES_MAP[rp]) };
let mut pos = [0u8; 64];
let mut n = 0usize;
let mut last_hit = false;
let _sg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EntSig);
for i in 0..maxpos {
let (mi, li) = if is8 { (SIG8X8[i] as usize, LAST8X8[i] as usize) } else { (i, i) };
if cab.decode_decision(map + mi) != 0 {
pos[n] = i as u8;
n += 1;
if cab.decode_decision(last + li) != 0 {
last_hit = true;
break;
}
}
}
if !last_hit {
pos[n] = maxpos as u8;
n += 1;
}
let coeff_num = n as u32;
let one = 227 + RES_ONE[rp];
let abs = 232 + RES_ONE[rp];
let maxc2 = RES_MAXC2[rp];
let (mut c1, mut c2) = (1i32, 0i32);
drop(_sg);
let _lg = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EntLvl);
for k in (0..n).rev() {
let mut level = 1 + cab.decode_decision(one + c1 as usize) as i32;
if level == 2 {
level += cabac_ueg_level(cab, abs + c2 as usize) as i32;
c2 = (c2 + 1).min(maxc2);
c1 = 0;
} else if c1 != 0 {
c1 = (c1 + 1).min(4);
}
if cab.decode_bypass() != 0 {
level = -level;
}
out[pos[k] as usize] = level;
}
if is8 {
for k in 0..4 {
nzc[NZC_CACHE[(iz + k).min(23)]] = coeff_num as u8;
}
} else if !is_dc {
nzc[scan] = coeff_num as u8;
}
coeff_num
}
enum EdcMsg {
Job(EdcJob),
Batch(Vec<EdcJob>),
Row {
r: usize,
bs: Vec<rusty_h264_common::deblock::MbBs>,
qp: Vec<u8>,
t8: Vec<bool>,
},
NeedCtx,
}
pub(crate) struct PixelCtx {
rec_y: Vec<u8>,
rec_u: Vec<u8>,
rec_v: Vec<u8>,
bak_y: Vec<u8>,
bak_u: Vec<u8>,
bak_v: Vec<u8>,
refs: Vec<crate::Ref>,
refs1: Vec<crate::Ref>,
weights: Option<WeightTable>,
scaling: Option<[[i32; 16]; 6]>,
scaling8: Option<[[i32; 64]; 2]>,
cw: usize,
ccw: usize,
mb_w: usize,
mb_h: usize,
chroma_qp_offset: i32,
flt_rows: usize,
db_ena: bool,
db_oa: i32,
db_ob: i32,
cur_qp: u8,
qp_grid: Vec<u8>,
t8_grid: Vec<bool>,
bs_store: Vec<rusty_h264_common::deblock::MbBs>,
}
impl PixelCtx {
fn chroma_qp_for(&self, qp: u8) -> u8 {
rusty_h264_common::predict::chroma_qp(
((qp as i32 + self.chroma_qp_offset).clamp(0, 51)) as u8,
)
}
fn dequant(&self, levels: &[i32; 16], qp: u8, list: usize) -> [i32; 16] {
match &self.scaling {
Some(sc) => dequantize_weighted(levels, qp, &sc[list]),
None => dequantize(levels, qp),
}
}
fn dequant_dc4(&self, level: i32, qp: u8, list: usize) -> i32 {
rusty_h264_common::transform::dequantize_dc4(
level,
qp,
self.scaling.as_ref().map(|sc| sc[list][0]),
)
}
fn inv_quant8(&self, raster: &[i32; 64], qp: u8, list: usize) -> [i32; 64] {
match &self.scaling8 {
Some(sc) => inverse_quant_8x8(raster, qp, &sc[list]),
None => inverse_quant_8x8(raster, qp, &[16i32; 64]),
}
}
fn dequant_chroma_dc(&self, levels: &[i32; 4], qp: u8, list: usize) -> [i32; 4] {
match &self.scaling {
Some(sc) => inverse_quant_chroma_dc_weighted(levels, qp, sc[list][0]),
None => inverse_quant_chroma_dc(levels, qp),
}
}
fn weight_partition(
&self,
pred_y: &mut [u8; 256],
c_pred: &mut [[u8; 64]; 2],
list: usize,
refi: usize,
rx: usize,
ry: usize,
rw: usize,
rh: usize,
) {
let Some(wt) = &self.weights else { return };
for dy in 0..rh {
for dx in 0..rw {
let i = (ry + dy) * 16 + (rx + dx);
pred_y[i] = wt.apply_luma(pred_y[i], list, refi);
}
}
let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
for cc in 0..2 {
for dy in 0..crh {
for dx in 0..crw {
let i = (cry + dy) * 8 + (crx + dx);
c_pred[cc][i] = wt.apply_chroma(c_pred[cc][i], list, refi, cc);
}
}
}
}
fn recon_p_inter(&mut self, j: &PInterJob) {
let mbw = self.mb_w;
let saved_qp = self.cur_qp;
self.cur_qp = j.qp;
let qp = j.qp;
let qpc = self.chroma_qp_for(qp);
let (w4r, w2r) = (mbw * 4, mbw * 2);
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
{
let _ms = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecMcStage);
let (rh16, cch) = (self.mb_h * 16, self.mb_h * 8);
let gmv = j.gmv;
let mut gref = [0usize; 16];
for k in 0..16 {
gref[k] = (j.gref[k] as usize).min(self.refs.len() - 1);
}
let rect_eq = |x4: usize, y4: usize, w4: usize, h4: usize| -> bool {
let t = y4 * 4 + x4;
(0..h4).all(|dy| {
(0..w4).all(|dx| {
let b = (y4 + dy) * 4 + (x4 + dx);
gmv[b] == gmv[t] && gref[b] == gref[t]
})
})
};
let refs = &self.refs;
let (cw, ccw) = (self.cw, self.ccw);
let mut mc_rect = |x4: usize,
y4: usize,
w4: usize,
h4: usize,
pred_y: &mut [u8; 256],
c_pred: &mut [[u8; 64]; 2]| {
let b = y4 * 4 + x4;
let (mv, reference) = (gmv[b], &refs[gref[b]]);
let (w, h) = (w4 * 4, h4 * 4);
if w == 16 {
rusty_h264_common::inter::with_mc_scratch(|scr| rusty_h264_common::inter::mc_luma_padded_pre(scr, &reference.py, reference.lstride(), crate::LPAD, cw, rh16, j.mbx * 16, j.mby * 16 + y4 * 4, w, h, mv.0, mv.1, &mut pred_y[y4 * 64..y4 * 64 + w * h]));
} else {
let mut t = [0u8; 256];
rusty_h264_common::inter::with_mc_scratch(|scr| rusty_h264_common::inter::mc_luma_padded_pre(scr, &reference.py, reference.lstride(), crate::LPAD, cw, rh16, j.mbx * 16 + x4 * 4, j.mby * 16 + y4 * 4, w, h, mv.0, mv.1, &mut t[..w * h]));
let _pb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
for dy in 0..h {
pred_y[(y4 * 4 + dy) * 16 + x4 * 4..][..w]
.copy_from_slice(&t[dy * w..dy * w + w]);
}
}
let (cw4, ch4) = (w4 * 2, h4 * 2);
for cc in 0..2 {
let rc = if cc == 0 { &reference.pu } else { &reference.pv };
if cw4 == 8 {
mc_chroma_padded(rc, reference.cstride(), crate::CPAD, ccw, cch, j.mbx * 8, j.mby * 8 + y4 * 2, cw4, ch4, mv.0, mv.1, &mut c_pred[cc][y4 * 16..y4 * 16 + cw4 * ch4]);
continue;
}
let mut tc = [0u8; 64];
mc_chroma_padded(rc, reference.cstride(), crate::CPAD, ccw, cch, j.mbx * 8 + x4 * 2, j.mby * 8 + y4 * 2, cw4, ch4, mv.0, mv.1, &mut tc[..cw4 * ch4]);
let _pb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
for dy in 0..ch4 {
c_pred[cc][(y4 * 2 + dy) * 8 + x4 * 2..][..cw4]
.copy_from_slice(&tc[dy * cw4..dy * cw4 + cw4]);
}
}
};
if rect_eq(0, 0, 4, 4) {
mc_rect(0, 0, 4, 4, &mut pred_y, &mut c_pred);
} else if rect_eq(0, 0, 4, 2) && rect_eq(0, 2, 4, 2) {
mc_rect(0, 0, 4, 2, &mut pred_y, &mut c_pred);
mc_rect(0, 2, 4, 2, &mut pred_y, &mut c_pred);
} else if rect_eq(0, 0, 2, 4) && rect_eq(2, 0, 2, 4) {
mc_rect(0, 0, 2, 4, &mut pred_y, &mut c_pred);
mc_rect(2, 0, 2, 4, &mut pred_y, &mut c_pred);
} else {
for q in 0..4usize {
let (qx, qy) = ((q % 2) * 2, (q / 2) * 2);
if rect_eq(qx, qy, 2, 2) {
mc_rect(qx, qy, 2, 2, &mut pred_y, &mut c_pred);
} else if rect_eq(qx, qy, 2, 1) && rect_eq(qx, qy + 1, 2, 1) {
mc_rect(qx, qy, 2, 1, &mut pred_y, &mut c_pred);
mc_rect(qx, qy + 1, 2, 1, &mut pred_y, &mut c_pred);
} else if rect_eq(qx, qy, 1, 2) && rect_eq(qx + 1, qy, 1, 2) {
mc_rect(qx, qy, 1, 2, &mut pred_y, &mut c_pred);
mc_rect(qx + 1, qy, 1, 2, &mut pred_y, &mut c_pred);
} else {
for j in 0..4usize {
mc_rect(qx + (j % 2), qy + (j / 2), 1, 1, &mut pred_y, &mut c_pred);
}
}
}
}
if self.weights.is_some() {
for by in 0..4usize {
for bx in 0..4usize {
let refi = gref[by * 4 + bx];
self.weight_partition(
&mut pred_y, &mut c_pred, 0, refi, bx * 4, by * 4, 4, 4,
);
}
}
}
}
self.add_inter_residual(j.mbx, j.mby, &pred_y, &c_pred, &j.luma_scan, if j.t8 { Some(&j.luma8) } else { None }, &j.cdc, &j.cac, j.cbp_chroma, &j.nnzs);
}
fn recon_p_skip(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32)) {
let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
let mut pred = [0u8; 256];
let rf0 = &self.refs[0];
mc_luma_padded(&rf0.py, rf0.lstride(), crate::LPAD, self.cw, ch, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred);
if let Some(wt) = &self.weights {
for p in pred.iter_mut() {
*p = wt.apply_luma(*p, 0, 0);
}
}
{
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
for dy in 0..16 {
let d = (mb_y * 16 + dy) * self.cw + mb_x * 16;
self.rec_y[d..d + 16].copy_from_slice(&pred[dy * 16..dy * 16 + 16]);
}
}
for c in 0..2 {
let mut pc = [0u8; 64];
let rf0 = &self.refs[0];
let rc = if c == 0 { &rf0.pu } else { &rf0.pv };
mc_chroma_padded(rc, rf0.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut pc);
if let Some(wt) = &self.weights {
for p in pc.iter_mut() {
*p = wt.apply_chroma(*p, 0, 0, c);
}
}
let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
for dy in 0..8 {
let d = (mb_y * 8 + dy) * self.ccw + mb_x * 8;
plane[d..d + 8].copy_from_slice(&pc[dy * 8..dy * 8 + 8]);
}
}
}
fn add_inter_residual(
&mut self,
mb_x: usize,
mb_y: usize,
pred_y: &[u8; 256],
c_pred: &[[u8; 64]; 2],
luma_scan: &[[i32; 16]; 16],
luma8: Option<&[[i32; 64]; 4]>,
cdc: &[[i32; 4]; 2],
cac: &[[[i32; 16]; 4]; 2],
cbp_chroma: u32,
nnzs: &[u8; 24],
) {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecResidAdd);
let qp = self.cur_qp;
let qpc = self.chroma_qp_for(qp);
let (w4r, w2r) = (self.mb_w * 4, self.mb_w * 2);
if let Some(l8) = luma8 {
for b8 in 0..4usize {
let (b8x, b8y) = (b8 % 2, b8 / 2);
let nnz: u32 = (0..4).map(|k| nnzs[b8 * 4 + k] as u32).sum();
let res8 = if nnz == 0 {
[0i32; 64]
} else {
let raster = un_scan_8x8(&l8[b8]);
self.inv_quant8(&raster, qp, 1)
};
let predb: [i32; 64] =
std::array::from_fn(|i| pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32);
let recon = add_residual_8x8(&res8, &predb);
let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
for dy in 0..8 {
for dx in 0..8 {
self.rec_y[(py + dy) * self.cw + (px + dx)] = recon[dy * 8 + dx];
}
}
}
}
for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
if luma8.is_some() {
break;
}
let nnz = nnzs[blk];
let cw = self.cw;
let p_off = (lby * 4) * 16 + lbx * 4;
let r_off = (mb_y * 4 + lby) * 4 * cw + (mb_x * 4 + lbx) * 4;
if nnz == 0 {
for r in 0..4 {
self.rec_y[r_off + r * cw..r_off + r * cw + 4]
.copy_from_slice(&pred_y[p_off + r * 16..p_off + r * 16 + 4]);
}
continue;
}
if nnz == 1 && luma_scan[blk][0] != 0 {
let f = self.dequant_dc4(luma_scan[blk][0], qp, 3);
reconstruct_4x4_dc_into((f + 32) >> 6, pred_y, p_off, 16, &mut self.rec_y, r_off, cw);
} else {
let deq = if nnz <= 6 {
dequant_scatter_4x4(&luma_scan[blk], nnz, 0, qp, self.scaling.as_ref().map(|sc| &sc[3]))
} else {
self.dequant(&un_scan_4x4_dcac(&luma_scan[blk]), qp, 3)
};
reconstruct_4x4_into(&deq, pred_y, p_off, 16, &mut self.rec_y, r_off, cw);
}
}
let mut c_dc = [[0i32; 4]; 2];
if cbp_chroma != 0 {
for c in 0..2 {
c_dc[c] = self.dequant_chroma_dc(&cdc[c], qpc, 4 + c);
}
}
let ccw = self.ccw;
for c in 0..2 {
for &(bx, by) in &CHROMA_4X4_SCAN_XY {
let mut ac_nz = false;
if cbp_chroma == 2 {
let n = nnzs[16 + c * 4 + by * 2 + bx];
ac_nz = n != 0;
}
let dc = c_dc[c][by * 2 + bx];
let p_off = (by * 4) * 8 + bx * 4;
let r_off = (mb_y * 2 + by) * 4 * ccw + (mb_x * 2 + bx) * 4;
let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
if dc == 0 && !ac_nz {
for r in 0..4 {
plane[r_off + r * ccw..r_off + r * ccw + 4]
.copy_from_slice(&c_pred[c][p_off + r * 8..p_off + r * 8 + 4]);
}
continue;
}
if !ac_nz {
reconstruct_4x4_dc_into((dc + 32) >> 6, &c_pred[c], p_off, 8, plane, r_off, ccw);
continue;
}
let n = nnzs[16 + c * 4 + by * 2 + bx];
let mut deq = if n <= 6 {
dequant_scatter_4x4(&cac[c][by * 2 + bx], n, 1, qpc, self.scaling.as_ref().map(|sc| &sc[4 + c]))
} else {
let mut ac = [0i32; 16];
un_scan_4x4_ac_into(&cac[c][by * 2 + bx], &mut ac);
match &self.scaling {
Some(sc) => dequantize_weighted(&ac, qpc, &sc[4 + c]),
None => dequantize(&ac, qpc),
}
};
deq[0] = dc;
reconstruct_4x4_into(&deq, &c_pred[c], p_off, 8, plane, r_off, ccw);
}
}
}
fn filter_row(&mut self, r: usize) {
let info = rusty_h264_common::deblock::BlockInfo {
inter: &[],
nnz: &[],
mv: &[],
ref_id: &[],
mv1: &[],
ref_id1: &[],
w4: self.mb_w * 4,
t8x8: &self.t8_grid,
bs: &self.bs_store,
poc0: &[],
poc1: &[],
kind: &[],
};
rusty_h264_common::deblock::filter_frame_rows(
&mut self.rec_y,
&mut self.rec_u,
&mut self.rec_v,
self.mb_w,
self.mb_h,
r..r + 1,
&self.qp_grid,
self.chroma_qp_offset,
self.db_oa,
self.db_ob,
&info,
);
}
fn save_bak(&mut self, r: usize) {
let y0 = (r * 16 + 15) * self.cw;
self.bak_y.copy_from_slice(&self.rec_y[y0..y0 + self.cw]);
let c0 = (r * 8 + 7) * self.ccw;
self.bak_u.copy_from_slice(&self.rec_u[c0..c0 + self.ccw]);
self.bak_v.copy_from_slice(&self.rec_v[c0..c0 + self.ccw]);
}
}
pub(crate) mod edcstat {
use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
pub static NEEDCTX: AtomicU64 = AtomicU64::new(0);
pub static JOBS: AtomicU64 = AtomicU64::new(0);
pub static ROWS: AtomicU64 = AtomicU64::new(0);
pub static ROWBYTES: AtomicU64 = AtomicU64::new(0);
pub static MBS: AtomicU64 = AtomicU64::new(0);
pub static J_INTER: AtomicU64 = AtomicU64::new(0);
pub static DOUBLED: AtomicU64 = AtomicU64::new(0);
pub static J_NORES_SENT: AtomicU64 = AtomicU64::new(0);
pub static BATCHES: AtomicU64 = AtomicU64::new(0);
pub static DISPATCH_ON: AtomicU64 = AtomicU64::new(0);
pub static DISPATCH_SEEN: AtomicU64 = AtomicU64::new(0);
pub static J_INTER_NORES: AtomicU64 = AtomicU64::new(0);
#[inline]
pub fn bump(c: &AtomicU64, n: u64) {
if on() {
c.fetch_add(n, Relaxed);
}
}
pub fn on() -> bool {
static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*V.get_or_init(|| std::env::var_os("RS_H264_EDC_STATS").is_some())
}
pub fn report() {
if !on() {
return;
}
eprintln!(
"EDCDISPATCH threaded_slices={} eligible_slices={}",
DISPATCH_ON.load(Relaxed), DISPATCH_SEEN.load(Relaxed)
);
eprintln!(
"EDCSIZE EdcMsg={} EdcJob={} PInterJob={} BJob={}",
std::mem::size_of::<super::EdcMsg>(),
std::mem::size_of::<super::EdcJob>(),
std::mem::size_of::<super::PInterJob>(),
std::mem::size_of::<super::BJob>(),
);
let (n, j, r, b, m) = (
NEEDCTX.load(Relaxed), JOBS.load(Relaxed), ROWS.load(Relaxed),
ROWBYTES.load(Relaxed), MBS.load(Relaxed),
);
eprintln!(
"EDCSTAT needctx={n} jobs={j} rows={r} rowbytes={b} mbs={m} batches={} jobs_per_batch={:.1} needctx_per_1k_mb={:.1} jobs_per_needctx={:.1}",
BATCHES.load(Relaxed),
j as f64 / BATCHES.load(Relaxed).max(1) as f64,
1000.0 * n as f64 / m.max(1) as f64,
j as f64 / n.max(1) as f64
);
let (ji, jn) = (J_INTER.load(Relaxed), J_INTER_NORES.load(Relaxed));
eprintln!(
"EDCMIX doubled={} nores_sent={} inter={ji} inter_no_residual={jn} ({:.1}% of inter) wasted_bytes={:.1} MB of {:.1} MB total inter payload",
DOUBLED.load(Relaxed),
J_NORES_SENT.load(Relaxed),
100.0 * jn as f64 / ji.max(1) as f64,
(jn * 2784) as f64 / 1.048576e6,
(ji * 2784) as f64 / 1.048576e6,
);
}
}
fn edc_worker(
mut ctx: PixelCtx,
rx: std::sync::mpsc::Receiver<EdcMsg>,
ctx_tx: std::sync::mpsc::Sender<PixelCtx>,
back_rx: std::sync::mpsc::Receiver<PixelCtx>,
) -> PixelCtx {
while let Ok(msg) = rx.recv() {
match msg {
EdcMsg::Batch(jobs) => {
for j in jobs {
match j {
EdcJob::Skip { mbx, mby, mv } => ctx.recon_p_skip(mbx, mby, mv),
EdcJob::Inter(j) => ctx.recon_p_inter(&j),
EdcJob::InterNoRes(j) => ctx.recon_p_inter(&j.to_full()),
EdcJob::B(j) => ctx.recon_b(&j),
EdcJob::BSkip { mbx, mby, regions } => ctx.recon_b_skip(mbx, mby, ®ions),
}
}
}
EdcMsg::Job(EdcJob::Skip { mbx, mby, mv }) => ctx.recon_p_skip(mbx, mby, mv),
EdcMsg::Job(EdcJob::Inter(j)) => ctx.recon_p_inter(&j),
EdcMsg::Job(EdcJob::InterNoRes(j)) => ctx.recon_p_inter(&j.to_full()),
EdcMsg::Job(EdcJob::B(j)) => ctx.recon_b(&j),
EdcMsg::Job(EdcJob::BSkip { mbx, mby, regions }) => ctx.recon_b_skip(mbx, mby, ®ions),
EdcMsg::Row { r, bs, qp, t8 } => {
let (w, base) = (ctx.mb_w, r * ctx.mb_w);
ctx.bs_store[base..base + w].copy_from_slice(&bs);
ctx.qp_grid[base..base + w].copy_from_slice(&qp);
ctx.t8_grid[base..base + w].copy_from_slice(&t8);
if ctx.db_ena {
ctx.save_bak(r);
ctx.filter_row(r);
ctx.flt_rows = r + 1;
}
}
EdcMsg::NeedCtx => {
ctx_tx.send(ctx).expect("parse thread alive");
ctx = back_rx.recv().expect("ctx returned after intra");
}
}
}
ctx
}
pub(crate) struct BRegion {
px: usize,
py: usize,
rw: usize,
rh: usize,
refi0: i32,
refi1: i32,
mv0: (i32, i32),
mv1: (i32, i32),
w: Option<(i32, i32)>,
}
pub(crate) struct BJob {
mbx: usize,
mby: usize,
t8: bool,
qp: u8,
cbp_chroma: u32,
skip: bool,
regions: Vec<BRegion>,
luma_scan: [[i32; 16]; 16],
luma8: [[i32; 64]; 4],
cdc: [[i32; 4]; 2],
cac: [[[i32; 16]; 4]; 2],
nnzs: [u8; 24],
}
impl PixelCtx {
fn b_mc(
&self,
mb_x: usize,
mb_y: usize,
px: usize,
py: usize,
rw: usize,
rh: usize,
refi0: i32,
mv0: (i32, i32),
refi1: i32,
mv1: (i32, i32),
pred_y: &mut [u8; 256],
c_pred: &mut [[u8; 64]; 2],
wparam: Option<(i32, i32)>,
) {
let _gb = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBMc);
let refi0 = if refi0 >= 0 { (refi0 as usize).min(self.refs.len().saturating_sub(1)) as i32 } else { -1 };
let refi1 = if refi1 >= 0 { (refi1 as usize).min(self.refs1.len().saturating_sub(1)) as i32 } else { -1 };
if (refi0 >= 0 && self.refs.is_empty()) || (refi1 >= 0 && self.refs1.is_empty()) {
return;
}
let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
let weights = wparam;
let full = px == 0 && rw == 16;
let _gl = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBLuma);
let chroma_done = rusty_h264_common::inter::with_mc_scratch(|scr| match (refi0 >= 0, refi1 >= 0, full) {
(true, false, true) => {
let rf = &self.refs[refi0 as usize];
rusty_h264_common::inter::mc_luma_padded_pre(scr, &rf.py, rf.lstride(), crate::LPAD, self.cw, ch, mb_x * 16, mb_y * 16 + py, rw, rh, mv0.0, mv0.1, &mut pred_y[py * 16..py * 16 + rw * rh]);
false
}
(false, true, true) => {
let rf = &self.refs1[refi1 as usize];
rusty_h264_common::inter::mc_luma_padded_pre(scr, &rf.py, rf.lstride(), crate::LPAD, self.cw, ch, mb_x * 16, mb_y * 16 + py, rw, rh, mv1.0, mv1.1, &mut pred_y[py * 16..py * 16 + rw * rh]);
false
}
(true, true, true) => {
let rf = &self.refs[refi0 as usize];
rusty_h264_common::inter::mc_luma_padded_pre(scr, &rf.py, rf.lstride(), crate::LPAD, self.cw, ch, mb_x * 16, mb_y * 16 + py, rw, rh, mv0.0, mv0.1, &mut pred_y[py * 16..py * 16 + rw * rh]);
let mut b = [0u8; 256];
let rf = &self.refs1[refi1 as usize];
rusty_h264_common::inter::mc_luma_padded_pre(scr, &rf.py, rf.lstride(), crate::LPAD, self.cw, ch, mb_x * 16, mb_y * 16 + py, rw, rh, mv1.0, mv1.1, &mut b[..rw * rh]);
drop(_gl);
let _gbl = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBBlend);
let dst = &mut pred_y[py * 16..py * 16 + rw * rh];
match weights {
None => {
for (d, s) in dst.iter_mut().zip(&b[..rw * rh]) {
*d = ((*d as u16 + *s as u16 + 1) >> 1) as u8;
}
}
Some((w0, w1)) => {
for (d, s) in dst.iter_mut().zip(&b[..rw * rh]) {
*d = ((*d as i32 * w0 + *s as i32 * w1 + 32) >> 6).clamp(0, 255) as u8;
}
}
}
let _gc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBChroma);
self.b_mc_chroma(mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, c_pred, weights, cch);
true
}
_ => {
let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
if refi0 >= 0 {
let rf = &self.refs[refi0 as usize];
rusty_h264_common::inter::mc_luma_padded_pre(scr, &rf.py, rf.lstride(), crate::LPAD, self.cw, ch, mb_x * 16 + px, mb_y * 16 + py, rw, rh, mv0.0, mv0.1, &mut a[..rw * rh]);
}
if refi1 >= 0 {
let rf = &self.refs1[refi1 as usize];
rusty_h264_common::inter::mc_luma_padded_pre(scr, &rf.py, rf.lstride(), crate::LPAD, self.cw, ch, mb_x * 16 + px, mb_y * 16 + py, rw, rh, mv1.0, mv1.1, &mut b[..rw * rh]);
}
drop(_gl);
let _gbl = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBBlend);
match (refi0 >= 0, refi1 >= 0) {
(true, true) => {
for dy in 0..rh {
let (ar, br) = (&a[dy * rw..dy * rw + rw], &b[dy * rw..dy * rw + rw]);
let base = (py + dy) * 16 + px;
let dst = &mut pred_y[base..base + rw];
match weights {
None => {
for ((d, p), q) in dst.iter_mut().zip(ar).zip(br) {
*d = ((*p as u16 + *q as u16 + 1) >> 1) as u8;
}
}
Some((w0, w1)) => {
for ((d, p), q) in dst.iter_mut().zip(ar).zip(br) {
*d = ((*p as i32 * w0 + *q as i32 * w1 + 32) >> 6).clamp(0, 255) as u8;
}
}
}
}
}
(true, false) => {
for dy in 0..rh {
let d = (py + dy) * 16 + px;
pred_y[d..d + rw].copy_from_slice(&a[dy * rw..dy * rw + rw]);
}
}
_ => {
for dy in 0..rh {
let d = (py + dy) * 16 + px;
pred_y[d..d + rw].copy_from_slice(&b[dy * rw..dy * rw + rw]);
}
}
}
false
}
});
if chroma_done {
return;
}
let _gc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::DecBChroma);
self.b_mc_chroma(mb_x, mb_y, px, py, rw, rh, refi0, mv0, refi1, mv1, c_pred, weights, cch);
}
fn b_mc_chroma(
&self,
mb_x: usize,
mb_y: usize,
px: usize,
py: usize,
rw: usize,
rh: usize,
refi0: i32,
mv0: (i32, i32),
refi1: i32,
mv1: (i32, i32),
c_pred: &mut [[u8; 64]; 2],
weights: Option<(i32, i32)>,
cch: usize,
) {
let (crx, cry, crw, crh) = (px / 2, py / 2, rw / 2, rh / 2);
let full = crx == 0 && crw == 8;
for c in 0..2 {
match (refi0 >= 0, refi1 >= 0, full) {
(true, false, true) => {
let rf = &self.refs[refi0 as usize];
let pl = if c == 0 { &rf.pu } else { &rf.pv };
mc_chroma_padded(pl, rf.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8, mb_y * 8 + cry, crw, crh, mv0.0, mv0.1, &mut c_pred[c][cry * 8..cry * 8 + crw * crh]);
}
(false, true, true) => {
let rf = &self.refs1[refi1 as usize];
let pl = if c == 0 { &rf.pu } else { &rf.pv };
mc_chroma_padded(pl, rf.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8, mb_y * 8 + cry, crw, crh, mv1.0, mv1.1, &mut c_pred[c][cry * 8..cry * 8 + crw * crh]);
}
(true, true, true) => {
let rf = &self.refs[refi0 as usize];
let pl = if c == 0 { &rf.pu } else { &rf.pv };
mc_chroma_padded(pl, rf.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8, mb_y * 8 + cry, crw, crh, mv0.0, mv0.1, &mut c_pred[c][cry * 8..cry * 8 + crw * crh]);
let mut cb = [0u8; 64];
let rf = &self.refs1[refi1 as usize];
let pl = if c == 0 { &rf.pu } else { &rf.pv };
mc_chroma_padded(pl, rf.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8, mb_y * 8 + cry, crw, crh, mv1.0, mv1.1, &mut cb[..crw * crh]);
let dst = &mut c_pred[c][cry * 8..cry * 8 + crw * crh];
match weights {
None => {
for (d, s) in dst.iter_mut().zip(&cb[..crw * crh]) {
*d = ((*d as u16 + *s as u16 + 1) >> 1) as u8;
}
}
Some((w0, w1)) => {
for (d, s) in dst.iter_mut().zip(&cb[..crw * crh]) {
*d = ((*d as i32 * w0 + *s as i32 * w1 + 32) >> 6).clamp(0, 255) as u8;
}
}
}
}
_ => {
let (mut ca, mut cb) = ([0u8; 64], [0u8; 64]);
if refi0 >= 0 {
let rf = &self.refs[refi0 as usize];
let pl = if c == 0 { &rf.pu } else { &rf.pv };
mc_chroma_padded(pl, rf.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv0.0, mv0.1, &mut ca[..crw * crh]);
}
if refi1 >= 0 {
let rf = &self.refs1[refi1 as usize];
let pl = if c == 0 { &rf.pu } else { &rf.pv };
mc_chroma_padded(pl, rf.cstride(), crate::CPAD, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv1.0, mv1.1, &mut cb[..crw * crh]);
}
match (refi0 >= 0, refi1 >= 0) {
(true, true) => {
for dy in 0..crh {
let (pr, qr) = (&ca[dy * crw..dy * crw + crw], &cb[dy * crw..dy * crw + crw]);
let base = (cry + dy) * 8 + crx;
let dst = &mut c_pred[c][base..base + crw];
match weights {
None => {
for ((d, p), q) in dst.iter_mut().zip(pr).zip(qr) {
*d = ((*p as u16 + *q as u16 + 1) >> 1) as u8;
}
}
Some((w0, w1)) => {
for ((d, p), q) in dst.iter_mut().zip(pr).zip(qr) {
*d = ((*p as i32 * w0 + *q as i32 * w1 + 32) >> 6).clamp(0, 255) as u8;
}
}
}
}
}
(true, false) => {
for dy in 0..crh {
let d = (cry + dy) * 8 + crx;
c_pred[c][d..d + crw].copy_from_slice(&ca[dy * crw..dy * crw + crw]);
}
}
_ => {
for dy in 0..crh {
let d = (cry + dy) * 8 + crx;
c_pred[c][d..d + crw].copy_from_slice(&cb[dy * crw..dy * crw + crw]);
}
}
}
}
}
}
}
fn recon_b_skip(&mut self, mbx: usize, mby: usize, regions: &[BRegion]) {
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
for r in regions {
self.b_mc(mbx, mby, r.px, r.py, r.rw, r.rh, r.refi0, r.mv0, r.refi1, r.mv1, &mut pred_y, &mut c_pred, r.w);
}
for dy in 0..16 {
let d = (mby * 16 + dy) * self.cw + mbx * 16;
self.rec_y[d..d + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
}
for c in 0..2 {
let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
for dy in 0..8 {
let d = (mby * 8 + dy) * self.ccw + mbx * 8;
plane[d..d + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
}
}
}
fn recon_b(&mut self, j: &BJob) {
let mut pred_y = [0u8; 256];
let mut c_pred = [[0u8; 64]; 2];
for r in &j.regions {
self.b_mc(j.mbx, j.mby, r.px, r.py, r.rw, r.rh, r.refi0, r.mv0, r.refi1, r.mv1, &mut pred_y, &mut c_pred, r.w);
}
if j.skip {
for dy in 0..16 {
let d = (j.mby * 16 + dy) * self.cw + j.mbx * 16;
self.rec_y[d..d + 16].copy_from_slice(&pred_y[dy * 16..dy * 16 + 16]);
}
for c in 0..2 {
let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
for dy in 0..8 {
let d = (j.mby * 8 + dy) * self.ccw + j.mbx * 8;
plane[d..d + 8].copy_from_slice(&c_pred[c][dy * 8..dy * 8 + 8]);
}
}
} else {
self.cur_qp = j.qp;
self.add_inter_residual(j.mbx, j.mby, &pred_y, &c_pred, &j.luma_scan, if j.t8 { Some(&j.luma8) } else { None }, &j.cdc, &j.cac, j.cbp_chroma, &j.nnzs);
}
}
}
enum EdcJob {
Skip { mbx: usize, mby: usize, mv: (i32, i32) },
Inter(Box<PInterJob>),
B(Box<BJob>),
BSkip { mbx: usize, mby: usize, regions: Vec<BRegion> },
InterNoRes(Box<PInterNoResJob>),
}
struct PInterNoResJob {
mbx: usize,
mby: usize,
t8: bool,
qp: u8,
gmv: [(i32, i32); 16],
gref: [u8; 16],
}
impl PInterNoResJob {
#[inline]
fn to_full(&self) -> PInterJob {
PInterJob {
mbx: self.mbx,
mby: self.mby,
t8: self.t8,
qp: self.qp,
cbp_chroma: 0,
gmv: self.gmv,
gref: self.gref,
luma_scan: [[0i32; 16]; 16],
luma8: [[0i32; 64]; 4],
cdc: [[0i32; 4]; 2],
cac: [[[0i32; 16]; 4]; 2],
nnzs: [0u8; 24],
}
}
}
struct PInterJob {
mbx: usize,
mby: usize,
t8: bool,
qp: u8,
cbp_chroma: u32,
gmv: [(i32, i32); 16],
gref: [u8; 16],
luma_scan: [[i32; 16]; 16],
luma8: [[i32; 64]; 4],
cdc: [[i32; 4]; 2],
cac: [[[i32; 16]; 4]; 2],
nnzs: [u8; 24],
}
fn edc_on() -> bool {
use std::sync::atomic::{AtomicU8, Ordering};
static ON: AtomicU8 = AtomicU8::new(0);
match ON.load(Ordering::Relaxed) {
0 => {
let v = !std::env::var_os("RS_H264_EDC").is_some_and(|v| v == "0");
ON.store(if v { 1 } else { 2 }, Ordering::Relaxed);
v
}
n => n == 1,
}
}
fn batch_on() -> bool {
static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*V.get_or_init(|| !std::env::var_os("RS_H264_BATCH").is_some_and(|v| v == "0"))
}
fn nores_on() -> bool {
static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*V.get_or_init(|| !std::env::var_os("RS_H264_NORES").is_some_and(|v| v == "0"))
}
fn double_recon() -> bool {
static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*V.get_or_init(|| std::env::var_os("RS_H264_DOUBLE_RECON").is_some())
}
fn edc_bound() -> usize {
static V: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
*V.get_or_init(|| {
std::env::var("RS_H264_EDC_BOUND")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(256)
})
}
fn edc_dispatch(mb_w: usize, mb_h: usize, bits_per_mb: f64, cabac: bool) -> bool {
const BITS_MIN: f64 = 38.4;
const MAX_MBS: usize = 5000; cabac && bits_per_mb > BITS_MIN && mb_w * mb_h <= MAX_MBS
}
fn edc_mt() -> Option<bool> {
static V: std::sync::OnceLock<Option<bool>> = std::sync::OnceLock::new();
*V.get_or_init(|| match std::env::var("RS_H264_EDC_MT").ok().as_deref() {
Some("0") => Some(false),
Some("1") => Some(true),
_ => None,
})
}
fn rowdb_on() -> bool {
use std::sync::atomic::{AtomicU8, Ordering};
static ON: AtomicU8 = AtomicU8::new(0);
match ON.load(Ordering::Relaxed) {
0 => {
let v = !std::env::var_os("RS_H264_ROWDB").is_some_and(|v| v == "0");
ON.store(if v { 1 } else { 2 }, Ordering::Relaxed);
v
}
n => n == 1,
}
}
const CACHE30: [usize; 16] = [7, 8, 13, 14, 9, 10, 15, 16, 19, 20, 25, 26, 21, 22, 27, 28];
const G_SCAN4: [usize; 16] = [0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15];
fn parse_sub_mb_type_p_cabac(cab: &mut crate::cabac::Cabac) -> u32 {
const S: usize = 21;
if cab.decode_decision(S) != 0 {
return 0;
}
if cab.decode_decision(S + 1) != 0 {
3 - cab.decode_decision(S + 2)
} else {
1
}
}
fn parse_intra_mb_type_cabac(cab: &mut crate::cabac::Cabac, base: usize) -> u32 {
if cab.decode_decision(base) == 0 {
return 0; }
if cab.decode_terminate() {
return 25; }
let mut t = 1 + 12 * cab.decode_decision(base + 1) as u32; if cab.decode_decision(base + 2) != 0 {
t += 4 + 4 * cab.decode_decision(base + 2) as u32;
}
t += 2 * cab.decode_decision(base + 3) as u32;
t += cab.decode_decision(base + 3) as u32;
t
}
#[doc(hidden)]
pub fn parse_mb_type_b(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> u32 {
parse_mb_type_b_cabac(cab, ctx_inc)
}
fn parse_mb_type_b_cabac(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> u32 {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
const B: usize = 27;
if cab.decode_decision(B + ctx_inc) == 0 {
return 0; }
if cab.decode_decision(B + 3) == 0 {
return 1 + cab.decode_decision(B + 5) as u32; }
let mut m = (cab.decode_decision(B + 4) as u32) << 3;
m |= (cab.decode_decision(B + 5) as u32) << 2;
m |= (cab.decode_decision(B + 5) as u32) << 1;
m |= cab.decode_decision(B + 5) as u32;
if m < 8 {
return m + 3;
}
if m == 13 {
return parse_intra_mb_type_cabac(cab, 32) + 23;
}
if m == 14 {
return 11; }
if m == 15 {
return 22; }
m = (m << 1) | cab.decode_decision(B + 5) as u32;
m - 4
}
fn parse_sub_mb_type_b_cabac(cab: &mut crate::cabac::Cabac) -> u32 {
const B: usize = 36;
if cab.decode_decision(B) == 0 {
return 0; }
if cab.decode_decision(B + 1) == 0 {
return 1 + cab.decode_decision(B + 3) as u32; }
let mut st = 3u32;
if cab.decode_decision(B + 2) != 0 {
if cab.decode_decision(B + 3) != 0 {
return 11 + cab.decode_decision(B + 3) as u32; }
st += 4;
}
st += 2 * cab.decode_decision(B + 3) as u32;
st += cab.decode_decision(B + 3) as u32;
st
}
fn parse_mvd_partition(
cab: &mut crate::cabac::Cabac,
part_idx: usize,
zblocks: &[usize],
mvdc: &mut [[i16; 2]; 30],
refc: &mut [i8; 30],
mmvd: &mut [[i16; 2]; 16],
mref: &mut [i8; 16],
ref_idx: i8,
) -> (i32, i32) {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
let s = 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
}
};
let (cx, cy) = (ctx(0), ctx(1));
let mvx = parse_mvd_cabac(cab, 0, cx);
let mvy = parse_mvd_cabac(cab, 1, cy);
for &zb in zblocks {
mvdc[CACHE30[zb]] = [mvx, mvy];
refc[CACHE30[zb]] = ref_idx;
mmvd[G_SCAN4[zb]] = [mvx, mvy];
mref[G_SCAN4[zb]] = ref_idx;
}
(mvx as i32, mvy as i32)
}
fn parse_ref_idx_cabac(cab: &mut crate::cabac::Cabac, ctx0: usize) -> i8 {
const B: usize = 54;
let mut r = 0i8;
let mut bin_idx = 0u32;
while bin_idx < 32 {
let ctx = match bin_idx {
0 => ctx0,
1 => 4,
_ => 5,
};
if cab.decode_decision(B + ctx) == 0 {
break;
}
r += 1;
bin_idx += 1;
}
r
}
fn decode_ueg_mv(cab: &mut crate::cabac::Cabac, base: usize) -> u32 {
const P2C: [usize; 8] = [0, 1, 2, 3, 3, 3, 3, 3];
if cab.decode_decision(base) == 0 {
return 0;
}
let mut code = 0u32;
let mut count = 1usize;
let mut tmp;
loop {
tmp = cab.decode_decision(base + P2C[count]);
code += 1;
count += 1;
if tmp == 0 || count == 8 {
break;
}
}
if tmp != 0 {
code += cabac_exp_bypass(cab, 3) + 1;
}
code
}
fn parse_mvd_cabac(cab: &mut crate::cabac::Cabac, comp: usize, ctx_inc: usize) -> i16 {
let base = 40 + comp * 7; if cab.decode_decision(base + ctx_inc) == 0 {
return 0;
}
let mag = (decode_ueg_mv(cab, base + 3) + 1) as i16;
if cab.decode_bypass() != 0 {
-mag
} else {
mag
}
}
fn parse_mb_skip_cabac(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> bool {
cab.decode_decision(ctx_inc) != 0
}
fn parse_mb_type_p_cabac(cab: &mut crate::cabac::Cabac) -> u32 {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
const S: usize = 11; if cab.decode_decision(S + 3) == 0 {
return if cab.decode_decision(S + 4) != 0 {
if cab.decode_decision(S + 6) != 0 { 1 } else { 2 }
} else if cab.decode_decision(S + 5) != 0 {
3
} else {
0
};
}
if cab.decode_decision(S + 6) == 0 {
return 5; }
if cab.decode_terminate() {
return 30; }
let mut t = 6 + cab.decode_decision(S + 7) * 12;
if cab.decode_decision(S + 8) != 0 {
t += 4;
if cab.decode_decision(S + 8) != 0 {
t += 4;
}
}
t += cab.decode_decision(S + 9) << 1;
t += cab.decode_decision(S + 9);
t
}
fn parse_mb_type_i_cabac(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> u32 {
const O: usize = 3; if cab.decode_decision(O + ctx_inc) == 0 {
return 0; }
if cab.decode_terminate() {
return 25; }
let mut t = 1 + cab.decode_decision(O + 3) * 12; if cab.decode_decision(O + 4) != 0 {
t += 4; if cab.decode_decision(O + 5) != 0 {
t += 4;
}
}
t += cab.decode_decision(O + 6) << 1; t += cab.decode_decision(O + 7);
t
}
fn parse_intra4x4_pred_mode_cabac(cab: &mut crate::cabac::Cabac) -> i32 {
const IPR: usize = 68;
if cab.decode_decision(IPR) == 1 {
return -1; }
let mut m = cab.decode_decision(IPR + 1) as i32;
m |= (cab.decode_decision(IPR + 1) as i32) << 1;
m |= (cab.decode_decision(IPR + 1) as i32) << 2;
m
}
fn parse_intra_chroma_pred_mode_cabac(cab: &mut crate::cabac::Cabac, ctx_inc: usize) -> u32 {
const CIPR: usize = 64;
if cab.decode_decision(CIPR + ctx_inc) == 0 {
return 0;
}
if cab.decode_decision(CIPR + 3) == 0 {
return 1;
}
if cab.decode_decision(CIPR + 3) == 0 {
return 2;
}
3
}
fn parse_cbp_cabac(cab: &mut crate::cabac::Cabac, top: Option<u8>, left: Option<u8>) -> u32 {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
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 = cab.decode_decision(CBP + (l(1 << 1) + (t(1 << 2) << 1)) as usize);
let b1 = cab.decode_decision(CBP + (nb(b0) + (t(1 << 3) << 1)) as usize);
let b2 = cab.decode_decision(CBP + (l(1 << 3) + (nb(b0) << 1)) as usize);
let b3 = cab.decode_decision(CBP + (nb(b2) + (nb(b1) << 1)) as usize);
let mut cbp = b0 | (b1 << 1) | (b2 << 2) | (b3 << 3);
let ct = top.map_or(0u32, |c| ((c >> 4) != 0) as u32);
let cl = left.map_or(0u32, |c| ((c >> 4) != 0) as u32);
if cab.decode_decision(CBP + 4 + (cl + (ct << 1)) as usize) != 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);
let c1 = cab.decode_decision(CBP + 8 + (cl2 + (ct2 << 1)) as usize);
cbp |= 1 << (4 + c1);
}
cbp
}
fn read_ref_idx(r: &mut BitReader, num_ref_active: usize) -> Result<i32, OutOfData> {
if num_ref_active == 2 {
Ok(if r.read_bit()? { 0 } else { 1 }) } else {
Ok(r.read_ue()? as i32)
}
}
#[derive(Clone, Copy, PartialEq)]
enum BPred {
L0,
L1,
Bi,
}
impl BPred {
fn uses(self, list: usize) -> bool {
matches!(
(self, list),
(BPred::L0, 0) | (BPred::L1, 1) | (BPred::Bi, 0) | (BPred::Bi, 1)
)
}
}
const B16X16: &[(usize, usize, usize, usize)] = &[(0, 0, 16, 16)];
const B16X8: &[(usize, usize, usize, usize)] = &[(0, 0, 16, 8), (0, 8, 16, 8)];
const B8X16: &[(usize, usize, usize, usize)] = &[(0, 0, 8, 16), (8, 0, 8, 16)];
type Region = (usize, usize, usize, usize);
pub fn b_inter_shape(mb_type: u32) -> (u8, u8, u8) {
let (_, mvmode, preds) = b_inter_layout(mb_type);
let code = |p: BPred| match (p.uses(0), p.uses(1)) {
(true, true) => 3,
(true, false) => 1,
_ => 2,
};
(mvmode, code(preds[0]), code(preds[1]))
}
fn b_inter_layout(mb_type: u32) -> (&'static [Region], u8, [BPred; 2]) {
use BPred::*;
match mb_type {
1 => (B16X16, 0, [L0, L0]),
2 => (B16X16, 0, [L1, L1]),
3 => (B16X16, 0, [Bi, Bi]),
4 => (B16X8, 1, [L0, L0]),
5 => (B8X16, 2, [L0, L0]),
6 => (B16X8, 1, [L1, L1]),
7 => (B8X16, 2, [L1, L1]),
8 => (B16X8, 1, [L0, L1]),
9 => (B8X16, 2, [L0, L1]),
10 => (B16X8, 1, [L1, L0]),
11 => (B8X16, 2, [L1, L0]),
12 => (B16X8, 1, [L0, Bi]),
13 => (B8X16, 2, [L0, Bi]),
14 => (B16X8, 1, [L1, Bi]),
15 => (B8X16, 2, [L1, Bi]),
16 => (B16X8, 1, [Bi, L0]),
17 => (B8X16, 2, [Bi, L0]),
18 => (B16X8, 1, [Bi, L1]),
19 => (B8X16, 2, [Bi, L1]),
20 => (B16X8, 1, [Bi, Bi]),
_ => (B8X16, 2, [Bi, Bi]), }
}
fn b_sub_uses(st: u32, list: usize) -> bool {
let pred = match st {
1 | 4 | 5 | 10 => 0, 2 | 6 | 7 | 11 => 1, _ => 2, };
(list == 0 && pred != 1) || (list == 1 && pred != 0)
}
fn b_sub_parts(st: u32) -> &'static [(usize, usize, usize, usize)] {
match st {
1..=3 => &[(0, 0, 8, 8)],
4 | 6 | 8 => &[(0, 0, 8, 4), (0, 4, 8, 4)],
5 | 7 | 9 => &[(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 sub_mb_partitions(sub_type: u32) -> &'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)],
}
}
#[inline]
fn restride(dst: &mut [u8], dst_stride: usize, x0: usize, y0: usize, src: &[u8], w: usize, h: usize) {
macro_rules! rows {
($n:expr) => {{
for dy in 0..h {
dst[(y0 + dy) * dst_stride + x0..][..$n].copy_from_slice(&src[dy * $n..][..$n]);
}
}};
}
match w {
16 => rows!(16),
8 => rows!(8),
4 => rows!(4),
2 => rows!(2),
_ => {
for dy in 0..h {
dst[(y0 + dy) * dst_stride + x0..][..w].copy_from_slice(&src[dy * w..][..w]);
}
}
}
}
fn store(plane: &mut [u8], stride: usize, x0: usize, y0: usize, s: &[u8; 16]) {
let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Scatter);
for dy in 0..4 {
for dx in 0..4 {
plane[(y0 + dy) * stride + (x0 + dx)] = s[dy * 4 + dx];
}
}
}
fn un_scan_8x8(scan: &[i32; 64]) -> [i32; 64] {
const ZZ8: [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,
];
let mut out = [0i32; 64];
for k in 0..64 {
out[ZZ8[k]] = scan[k];
}
out
}
#[cfg(test)]
mod tests {
use super::*;
fn fd(qp: u8, offset: i32) -> FrameDecoder {
FrameDecoder::new(1, 1, qp, offset, Vec::new(), 1, false, false, true)
}
#[test]
fn mb_qp_delta_accumulates_mod_52() {
let mut d = fd(26, 0);
assert_eq!(d.cur_qp, 26, "QPy starts at the slice QP");
d.step_qp(4);
assert_eq!(d.cur_qp, 30); d.step_qp(-10);
assert_eq!(d.cur_qp, 20); d.step_qp(40);
assert_eq!(d.cur_qp, 8);
d.step_qp(-20);
assert_eq!(d.cur_qp, 40);
}
#[test]
fn chroma_qp_index_offset_applied_and_clamped() {
assert_eq!(fd(0, 0).chroma_qp_for(30), 29);
assert_eq!(fd(0, 2).chroma_qp_for(30), 31);
assert_eq!(fd(0, -12).chroma_qp_for(5), chroma_qp(0));
assert_eq!(fd(0, 99).chroma_qp_for(40), chroma_qp(51));
}
}