1#![allow(clippy::needless_range_loop)]
6use crate::cabac::CabacEncoder;
14use crate::config::EncoderConfig;
15use rusty_h264_common::cavlc::{
16 encode_residual_block, scan_4x4_ac, scan_4x4_dcac, write_cbp_inter, write_cbp_intra,
17};
18use rusty_h264_common::inter::{
19 inter_partitions, mc_chroma, mc_luma, predict_mv, predict_partition_mv, MvNeighbor,
20};
21use rusty_h264_common::predict::{
22 add_residual_4x4, add_residual_8x8, chroma8x8_pred, chroma_mode_available, chroma_qp,
23 intra4x4_pred, intra8x8_pred, luma16x16_pred, reconstruct_4x4, I16Mode, CHROMA_4X4_SCAN_XY,
24 LUMA_4X4_SCAN_XY,
25};
26use rusty_h264_common::transform::{
27 dequantize, forward_core, forward_core_8x8, forward_dct_blocks, forward_quant_chroma_dc,
28 forward_quant_luma_dc, inverse_dct_blocks, inverse_quant_8x8, inverse_quant_chroma_dc,
29 inverse_quant_luma_dc, quantize, quantize_8x8, satd_4x4_sum,
30};
31use rusty_h264_common::aligned::AlignedBytes;
32use rusty_h264_common::{BitWriter, YuvFrame};
33
34pub(crate) static DEFER_SUBPEL: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
54
55pub(crate) static SPLIT_T: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
56
57fn split_t() -> f64 {
58 let v = SPLIT_T.load(std::sync::atomic::Ordering::Relaxed);
59 if v != u32::MAX {
60 return v as f64;
61 }
62 let d: u32 = std::env::var("RFF_SPLIT_T").ok().and_then(|s| s.parse().ok()).unwrap_or(0);
63 SPLIT_T.store(d, std::sync::atomic::Ordering::Relaxed);
64 d as f64
65}
66
67mod split_harvest {
75 use std::fs::File;
76 use std::io::Write;
77 use std::sync::{Mutex, OnceLock};
78
79 fn sink() -> &'static Option<Mutex<File>> {
80 static S: OnceLock<Option<Mutex<File>>> = OnceLock::new();
81 S.get_or_init(|| {
82 std::env::var("RFF_SPLIT_HARVEST").ok().and_then(|p| {
83 let mut f = File::create(p).ok()?;
84 let _ = writeln!(f, "c16,best,lambda,gate,won");
85 Some(Mutex::new(f))
86 })
87 })
88 }
89
90 #[inline]
91 pub fn enabled() -> bool {
92 sink().is_some()
93 }
94
95 pub fn record(c16: i64, best: i64, lambda: f64, gate: i64, won: u8) {
96 if let Some(m) = sink() {
97 if let Ok(mut f) = m.lock() {
98 let _ = writeln!(f, "{c16},{best},{lambda:.4},{gate},{won}");
99 }
100 }
101 }
102}
103
104fn hpel_ref_enabled() -> bool {
107 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
108 *E.get_or_init(|| std::env::var("RFF_HPEL_REF").map(|v| v != "0").unwrap_or(true))
109}
110
111fn satd_avg_enabled() -> bool {
115 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
116 *E.get_or_init(|| std::env::var("RFF_SATD_AVG").map(|v| v != "0").unwrap_or(true))
117}
118
119static ME_SADFP: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
129pub fn set_me_sadfp(on: bool) {
130 ME_SADFP.store(if on { 2 } else { 0 }, core::sync::atomic::Ordering::Relaxed)
132}
133pub fn set_me_sadfp_mode(m: u32) {
134 ME_SADFP.store(m.min(2), core::sync::atomic::Ordering::Relaxed)
135}
136fn me_sadfp_mode() -> u32 {
137 match ME_SADFP.load(core::sync::atomic::Ordering::Relaxed) {
138 u32::MAX => {
139 static INIT: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
140 *INIT.get_or_init(|| {
147 std::env::var("RFF_ME_SADFP").ok().and_then(|v| v.parse().ok()).unwrap_or(1)
148 })
149 }
150 m => m,
151 }
152}
153
154fn me_sadt() -> f64 {
158 static T: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
159 *T.get_or_init(|| std::env::var("RFF_ME_SADT").ok().and_then(|s| s.parse().ok()).unwrap_or(0.13))
160}
161fn me_sadt_dbg() -> bool {
162 static D: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
163 *D.get_or_init(|| std::env::var_os("RFF_ME_SADT_DBG").is_some())
164}
165
166static SP_FC: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
176pub fn set_sp_fc(on: bool) {
177 SP_FC.store(on as u32, core::sync::atomic::Ordering::Relaxed)
178}
179fn sp_fc_enabled() -> bool {
180 match SP_FC.load(core::sync::atomic::Ordering::Relaxed) {
181 0 => false,
182 1 => true,
183 _ => {
184 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
185 *E.get_or_init(|| std::env::var("RFF_SP_FC").map(|v| v != "0").unwrap_or(false))
186 }
187 }
188}
189
190static ME_FC: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
191pub fn set_me_fc(on: bool) {
192 ME_FC.store(on as u32, core::sync::atomic::Ordering::Relaxed)
193}
194fn me_fc_enabled() -> bool {
195 match ME_FC.load(core::sync::atomic::Ordering::Relaxed) {
196 0 => false,
197 1 => true,
198 _ => {
199 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
200 *E.get_or_init(|| std::env::var("RFF_ME_FC").map(|v| v != "0").unwrap_or(true))
201 }
202 }
203}
204
205fn me_sad_dcmax() -> f64 {
211 static T: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
212 *T.get_or_init(|| std::env::var("RFF_ME_SADDC").ok().and_then(|s| s.parse().ok()).unwrap_or(0.6))
213}
214
215fn b2_mgain(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> (f64, f64) {
229 const WIDE: isize = 8;
230 const STEP: isize = 4;
231 const TARGET: usize = 24;
232 let sad16 = |bx: usize, by: usize, rx: isize, ry: isize| -> Option<u32> {
233 if rx < 0 || ry < 0 || rx as usize + 16 > cw || ry as usize + 16 > ch {
234 return None;
235 }
236 let (rx, ry) = (rx as usize, ry as usize);
237 let mut s = 0u32;
238 for dy in 0..16 {
239 let a = &sy[(by + dy) * cw + bx..][..16];
240 let b = &ref_y[(ry + dy) * cw + rx..][..16];
241 s += a.iter().zip(b).map(|(&p, &q)| p.abs_diff(q) as u32).sum::<u32>();
242 }
243 Some(s)
244 };
245 let (mbw, mbh) = (cw / 16, ch / 16);
246 if mbw < 6 || mbh < 6 {
247 return (0.0, 0.0);
248 }
249 let inner = (mbw - 4) * (mbh - 4);
250 let stride = (inner / TARGET).max(1);
251 let (mut acc, mut dc, mut n) = (0.0f64, 0.0f64, 0u32);
252 let mut i = 0usize;
253 while i < inner {
254 let (mx, my) = (2 + i % (mbw - 4), 2 + i / (mbw - 4));
255 let (bx, by) = (mx * 16, my * 16);
256 if let Some(s0) = sad16(bx, by, bx as isize, by as isize) {
257 let (mut ms, mut mr) = (0u32, 0u32);
258 for dy in 0..16 {
259 ms += sy[(by + dy) * cw + bx..][..16].iter().map(|&v| v as u32).sum::<u32>();
260 mr += ref_y[(by + dy) * cw + bx..][..16].iter().map(|&v| v as u32).sum::<u32>();
261 }
262 dc += ms.abs_diff(mr) as f64 / (s0 + 1) as f64;
263 let mut best = s0;
264 let mut dy = -WIDE;
265 while dy <= WIDE {
266 let mut dx = -WIDE;
267 while dx <= WIDE {
268 if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
269 best = best.min(s);
270 }
271 dx += STEP;
272 }
273 dy += STEP;
274 }
275 acc += (s0 - best) as f64 / (s0 + 1) as f64;
276 n += 1;
277 }
278 i += stride;
279 }
280 if n == 0 { (0.0, 0.0) } else { (acc / n as f64, dc / n as f64) }
281}
282
283static SP_MAXIT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
288pub fn set_sp_maxit(n: u32) {
289 SP_MAXIT.store(n, core::sync::atomic::Ordering::Relaxed)
290}
291fn sp_maxit() -> u32 {
292 match SP_MAXIT.load(core::sync::atomic::Ordering::Relaxed) {
293 u32::MAX => {
294 static INIT: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
295 *INIT.get_or_init(|| {
296 std::env::var("RFF_SP_MAXIT").ok().and_then(|v| v.parse().ok()).unwrap_or(0)
297 })
298 }
299 n => n,
300 }
301}
302
303fn me_sadfp_lambda() -> f64 {
308 static E: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
309 *E.get_or_init(|| {
312 std::env::var("RFF_ME_SADL").ok().and_then(|v| v.parse().ok()).unwrap_or(0.5)
313 })
314}
315
316#[cfg(feature = "profile")]
320pub mod spstats {
321 use core::sync::atomic::{AtomicU64, Ordering};
322 pub static POS: [AtomicU64; 2 * 8 * 2] = [const { AtomicU64::new(0) }; 32];
324 pub static IT: [AtomicU64; 2 * 6 * 2] = [const { AtomicU64::new(0) }; 24];
326 #[inline]
327 pub fn ev(st: usize, pos: usize, it: u32) {
328 POS[(st * 8 + pos.min(7)) * 2].fetch_add(1, Ordering::Relaxed);
329 IT[(st * 6 + (it.max(1) as usize - 1).min(5)) * 2].fetch_add(1, Ordering::Relaxed);
330 }
331 #[inline]
332 pub fn imp(st: usize, pos: usize, it: u32) {
333 POS[(st * 8 + pos.min(7)) * 2 + 1].fetch_add(1, Ordering::Relaxed);
334 IT[(st * 6 + (it.max(1) as usize - 1).min(5)) * 2 + 1].fetch_add(1, Ordering::Relaxed);
335 }
336 pub static REDUNDANT: AtomicU64 = AtomicU64::new(0);
338 #[inline]
339 pub fn redundant() { REDUNDANT.fetch_add(1, Ordering::Relaxed); }
340 pub fn reset() {
341 for c in POS.iter() { c.store(0, Ordering::Relaxed); }
342 for c in IT.iter() { c.store(0, Ordering::Relaxed); }
343 REDUNDANT.store(0, Ordering::Relaxed);
344 }
345 pub fn snapshot() -> (Vec<u64>, Vec<u64>) {
346 (POS.iter().map(|c| c.load(Ordering::Relaxed)).collect(),
347 IT.iter().map(|c| c.load(Ordering::Relaxed)).collect())
348 }
349 pub fn redundant_count() -> u64 { REDUNDANT.load(Ordering::Relaxed) }
350}
351
352#[cfg(feature = "profile")]
354pub mod satdpath {
355 use core::sync::atomic::{AtomicU64, Ordering};
356 pub static C: [AtomicU64; 3] = [const { AtomicU64::new(0) }; 3];
357 #[inline]
358 pub fn bump(i: usize) { C[i].fetch_add(1, Ordering::Relaxed); }
359 pub fn reset() { for c in C.iter() { c.store(0, Ordering::Relaxed); } }
360 pub fn snapshot() -> Vec<u64> { C.iter().map(|c| c.load(Ordering::Relaxed)).collect() }
361}
362
363pub const DIA_RUNGS: [i32; 5] = [64, 32, 16, 8, 4];
380pub const DIA_DEFAULT: u32 = 0b11100;
382pub static DIA_MASK: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(u32::MAX);
383pub fn set_dia_mask(m: u32) { DIA_MASK.store(m, core::sync::atomic::Ordering::Relaxed) }
384fn dia_mask() -> u32 {
385 let m = DIA_MASK.load(core::sync::atomic::Ordering::Relaxed);
386 if m != u32::MAX { return m; }
387 static INIT: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
388 *INIT.get_or_init(|| match std::env::var("RFF_DIA_LADDER") {
389 Ok(v) => {
390 let want: Vec<i32> = v.split(',').filter_map(|t| t.trim().parse().ok()).collect();
391 let mut m = 0u32;
392 for (i, r) in DIA_RUNGS.iter().enumerate() {
393 if want.contains(r) { m |= 1 << i; }
394 }
395 if m == 0 { DIA_DEFAULT } else { m }
396 }
397 Err(_) => DIA_DEFAULT,
398 })
399}
400
401#[cfg(feature = "profile")]
406pub mod diastats {
407 use core::sync::atomic::{AtomicU64, Ordering};
408 pub static C: [AtomicU64; 12] = [const { AtomicU64::new(0) }; 12];
410 #[inline]
411 pub fn ev(i: usize) { C[i * 2].fetch_add(1, Ordering::Relaxed); }
412 #[inline]
413 pub fn imp(i: usize) { C[i * 2 + 1].fetch_add(1, Ordering::Relaxed); }
414 pub fn reset() { for c in C.iter() { c.store(0, Ordering::Relaxed); } }
415 pub fn snapshot() -> Vec<(u64, u64)> {
416 (0..6).map(|i| (C[i * 2].load(Ordering::Relaxed), C[i * 2 + 1].load(Ordering::Relaxed))).collect()
417 }
418}
419
420pub(crate) static SUBPEL_PAT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
432
433pub(crate) static SP_DISPATCH: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
436
437fn sp_dispatch_cfg() -> (u32, i64) {
438 use std::sync::OnceLock;
439 let forced = SP_DISPATCH.load(std::sync::atomic::Ordering::Relaxed);
440 if forced == 0 {
441 return (0, 0);
442 }
443 static C: OnceLock<(u32, i64)> = OnceLock::new();
444 *C.get_or_init(|| {
445 let on = std::env::var("RFF_SUBPEL_DISPATCH").map(|s| s != "0").unwrap_or(false);
453 if !on {
454 return (0, 0);
455 }
456 let k = std::env::var("RFF_SUBPEL_LEARN").ok().and_then(|s| s.parse().ok()).unwrap_or(200);
457 let t = std::env::var("RFF_SUBPEL_T").ok().and_then(|s| s.parse().ok()).unwrap_or(67);
458 (k, t)
459 })
460}
461
462fn subpel_pattern_override() -> Option<u32> {
464 let v = SUBPEL_PAT.load(std::sync::atomic::Ordering::Relaxed);
465 if v != u32::MAX {
466 return Some(v);
467 }
468 if let Some(e) = std::env::var("RFF_SUBPEL_PAT").ok().and_then(|s| s.parse::<u32>().ok()) {
469 SUBPEL_PAT.store(e, std::sync::atomic::Ordering::Relaxed);
470 return Some(e);
471 }
472 None
473}
474
475fn subpel_pattern() -> u32 {
476 let v = SUBPEL_PAT.load(std::sync::atomic::Ordering::Relaxed);
477 if v != u32::MAX {
478 return v;
479 }
480 let d = std::env::var("RFF_SUBPEL_PAT").ok().and_then(|s| s.parse().ok()).unwrap_or(0);
483 SUBPEL_PAT.store(d, std::sync::atomic::Ordering::Relaxed);
484 d
485}
486
487mod subpel_harvest {
496 use std::fs::File;
497 use std::io::Write;
498 use std::sync::{Mutex, OnceLock};
499
500 fn sink() -> &'static Option<Mutex<File>> {
501 static S: OnceLock<Option<Mutex<File>>> = OnceLock::new();
502 S.get_or_init(|| {
503 std::env::var("RFF_SUBPEL_HARVEST").ok().and_then(|p| {
504 let mut f = File::create(p).ok()?;
505 let _ = writeln!(f, "pre,post,lambda,w,h,evals,to_best,ring1");
506 Some(Mutex::new(f))
507 })
508 })
509 }
510
511 #[inline]
512 pub fn enabled() -> bool {
513 sink().is_some()
514 }
515
516 #[allow(clippy::too_many_arguments)]
517 pub fn record(pre: i64, post: i64, lambda: f64, w: usize, h: usize, evals: u32, to_best: u32, ring1: i64) {
518 if let Some(m) = sink() {
519 if let Ok(mut f) = m.lock() {
520 let _ = writeln!(f, "{pre},{post},{lambda:.4},{w},{h},{evals},{to_best},{ring1}");
521 }
522 }
523 }
524}
525
526fn bdirect_planes_enabled() -> bool {
530 use std::sync::OnceLock;
531 static ON: OnceLock<bool> = OnceLock::new();
532 *ON.get_or_init(|| std::env::var("RFF_BDIRECT_PLANES").map(|s| s != "0").unwrap_or(true))
533}
534
535fn me_batch_enabled() -> bool {
536 use std::sync::OnceLock;
537 static ON: OnceLock<bool> = OnceLock::new();
538 *ON.get_or_init(|| std::env::var("RFF_ME_BATCH").map(|s| s != "0").unwrap_or(true))
539}
540
541#[cfg(accel)]
545#[repr(align(16))]
546struct AlignedMb([u8; 256]);
547
548#[derive(Clone, Copy)]
553struct BInter<'a> {
554 dir: u8,
555 l1: &'a crate::RefFrame,
556 mv0: (i32, i32),
557 mv1: (i32, i32),
558}
559
560#[cfg(accel)]
563#[repr(align(16))]
564struct AlignedDct([i16; 256]);
565
566fn mb_variance(sy: &[u8], cw: usize, mb_x: usize, mb_y: usize) -> i64 {
571 let base = mb_y * 16 * cw + mb_x * 16;
572 let (mut s, mut ss) = (0u32, 0u32);
577 for r in 0..16 {
578 let row = &sy[base + r * cw..base + r * cw + 16];
579 for &p in row {
580 let v = p as u32;
581 s += v;
582 ss += v * v;
583 }
584 }
585 ss as i64 - (s as i64) * (s as i64) / 256 }
588
589fn aq_qp_map(sy: &[u8], cw: usize, mb_w: usize, mb_h: usize, base_qp: u8, strength: f64) -> Vec<u8> {
596 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncAq);
597 const AQ_DQP_MAX: i32 = 4;
598 let n = mb_w * mb_h;
599 if strength == 0.0 || n == 0 {
600 return vec![base_qp; n];
601 }
602 let mut var = Vec::with_capacity(n);
605 let mut lv = Vec::with_capacity(n);
606 for my in 0..mb_h {
607 for mx in 0..mb_w {
608 let v = (mb_variance(sy, cw, mx, my) + 1) as f64;
609 var.push(v);
610 lv.push(v.log2());
611 }
612 }
613 let mean_lv = lv.iter().sum::<f64>() / n as f64;
614 const AQ_SPREAD_LO: f64 = 1.5;
621 const AQ_SPREAD_HI: f64 = 5.0;
622 const AQ_SPREAD_MIN: f64 = 0.0; let std_lv = (lv.iter().map(|&l| (l - mean_lv).powi(2)).sum::<f64>() / n as f64).sqrt();
624 let factor = (1.0 - (std_lv - AQ_SPREAD_LO) / (AQ_SPREAD_HI - AQ_SPREAD_LO)).clamp(AQ_SPREAD_MIN, 1.0);
625 let eff_strength = strength * factor;
626 let dqp: Vec<i32> = lv
628 .iter()
629 .map(|&l| (eff_strength * (l - mean_lv)).round() as i32)
630 .map(|d| d.clamp(-AQ_DQP_MAX, AQ_DQP_MAX))
631 .collect();
632 let sum_v: f64 = var.iter().sum();
637 let qstep: [f64; (2 * AQ_DQP_MAX + 1) as usize] =
642 std::array::from_fn(|i| 2f64.powf(-((i as i32 - AQ_DQP_MAX) as f64) / 6.0));
643 let sum_vs: f64 = var
644 .iter()
645 .zip(&dqp)
646 .map(|(&v, &d)| v * qstep[(d + AQ_DQP_MAX) as usize])
647 .sum();
648 let c = (6.0 * (sum_vs / sum_v).log2()).round() as i32;
649 dqp.iter()
650 .map(|&d| (base_qp as i32 + c + d).clamp(0, 51) as u8)
651 .collect()
652}
653
654fn me_wide_hr_thresh() -> f64 {
699 use std::sync::OnceLock;
700 static T: OnceLock<f64> = OnceLock::new();
701 *T.get_or_init(|| std::env::var("RFF_ME_HR").ok().and_then(|s| s.parse().ok()).unwrap_or(16.0))
702}
703
704fn me_wide_hr_dbg() -> bool {
706 use std::sync::OnceLock;
707 static D: OnceLock<bool> = OnceLock::new();
708 *D.get_or_init(|| std::env::var_os("RFF_ME_HR_DBG").is_some())
709}
710
711fn me_wide_headroom(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
712 const LOCAL: isize = 2; const WIDE: isize = 24; const STEP: isize = 4; const TARGET: usize = 24; let sad16 = |bx: usize, by: usize, rx: isize, ry: isize| -> Option<u32> {
717 if rx < 0 || ry < 0 || rx as usize + 16 > cw || ry as usize + 16 > ch {
718 return None;
719 }
720 let (rx, ry) = (rx as usize, ry as usize);
721 let mut s = 0u32;
722 for dy in 0..16 {
723 let a = &sy[(by + dy) * cw + bx..][..16];
724 let b = &ref_y[(ry + dy) * cw + rx..][..16];
725 s += a.iter().zip(b).map(|(&p, &q)| p.abs_diff(q) as u32).sum::<u32>();
726 }
727 Some(s)
728 };
729 let (mbw, mbh) = (cw / 16, ch / 16);
732 if mbw < 6 || mbh < 6 {
733 return 0.0;
734 }
735 let inner = (mbw - 4) * (mbh - 4);
736 let stride = (inner / TARGET).max(1);
737 let (mut acc, mut n) = (0.0f64, 0u32);
738 let mut i = 0usize;
739 while i < inner {
740 let (mx, my) = (2 + i % (mbw - 4), 2 + i / (mbw - 4));
741 let (bx, by) = (mx * 16, my * 16);
742 let mut best_local = u32::MAX;
743 for dy in -LOCAL..=LOCAL {
744 for dx in -LOCAL..=LOCAL {
745 if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
746 best_local = best_local.min(s);
747 }
748 }
749 }
750 let mut best_wide = best_local;
751 let mut dy = -WIDE;
752 while dy <= WIDE {
753 let mut dx = -WIDE;
754 while dx <= WIDE {
755 if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
756 best_wide = best_wide.min(s);
757 }
758 dx += STEP;
759 }
760 dy += STEP;
761 }
762 if best_local > 0 {
763 acc += (best_local - best_wide) as f64 / best_local as f64;
764 n += 1;
765 }
766 i += stride;
767 }
768 if n == 0 {
769 0.0
770 } else {
771 100.0 * acc / n as f64
772 }
773}
774
775fn global_mc_residual(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
776 if cw < 48 || ch < 48 {
777 return f64::INFINITY;
778 }
779 let sad = |dx: isize, dy: isize| -> u64 {
780 let mut s = 0u64;
781 let mut y = 16;
782 while y < ch - 16 {
783 let cbase = (y * cw) as isize;
784 let rbase = (y as isize + dy) * cw as isize + dx;
785 let mut x = 16isize;
786 while x < (cw - 16) as isize {
787 let c = sy[(cbase + x) as usize] as i32;
788 let r = ref_y[(rbase + x) as usize] as i32;
789 s += (c - r).unsigned_abs() as u64;
790 x += 8;
791 }
792 y += 8;
793 }
794 s
795 };
796 let (mut best, mut bc) = ((0isize, 0isize), u64::MAX);
797 let mut dy = -12;
798 while dy <= 12 {
799 let mut dx = -12;
800 while dx <= 12 {
801 let c = sad(dx, dy);
802 if c < bc {
803 bc = c;
804 best = (dx, dy);
805 }
806 dx += 4;
807 }
808 dy += 4;
809 }
810 for dy in best.1 - 3..=best.1 + 3 {
811 for dx in best.0 - 3..=best.0 + 3 {
812 let c = sad(dx, dy);
813 if c < bc {
814 bc = c;
815 }
816 }
817 }
818 let nx = (16..cw - 16).step_by(8).count();
819 let ny = (16..ch - 16).step_by(8).count();
820 bc as f64 / (nx * ny).max(1) as f64
821}
822
823fn apply_mbtree_qpo(aq_qp: &mut [u8], qpo: &[i32]) {
827 if qpo.len() == aq_qp.len() {
828 for (q, &o) in aq_qp.iter_mut().zip(qpo) {
829 *q = (*q as i32 + o).clamp(0, 51) as u8;
830 }
831 }
832}
833
834fn implicit_bi_weights(cur_poc: i32, l0_poc: i32, l1_poc: i32) -> (i32, i32) {
840 let td = (l1_poc - l0_poc).clamp(-128, 127);
841 let tb = (cur_poc - l0_poc).clamp(-128, 127);
842 if td == 0 {
843 return (32, 32);
844 }
845 let tx = (16384 + td.abs() / 2) / td;
846 let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
847 let w1 = dsf >> 2;
848 if !(-64..=128).contains(&w1) {
849 return (32, 32);
850 }
851 (64 - w1, w1)
852}
853
854#[inline(always)]
858fn bi_blend(p: i32, q: i32, w: (i32, i32)) -> u8 {
859 ((p * w.0 + q * w.1 + 32) >> 6).clamp(0, 255) as u8
860}
861
862#[cfg(accel)]
867#[inline]
868fn scan_4x4_dcac_i16(d: &[i16]) -> [i32; 16] {
869 [
870 d[0] as i32, d[1] as i32, d[4] as i32, d[8] as i32, d[5] as i32, d[2] as i32,
871 d[3] as i32, d[6] as i32, d[9] as i32, d[12] as i32, d[13] as i32, d[10] as i32,
872 d[7] as i32, d[11] as i32, d[14] as i32, d[15] as i32,
873 ]
874}
875
876
877pub struct FrameEncoder {
880 mb_w: usize,
881 mb_h: usize,
882 qp: u8, qpc: u8, cur_qp: u8,
887 bi_w: (i32, i32),
891 cw: usize, ccw: usize, rec_y: AlignedBytes,
895 rec_u: AlignedBytes,
896 rec_v: AlignedBytes,
897 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_y: Vec<(i32, i32)>,
908 ref_idx1_y: Vec<i32>,
909 idz: i64, rdoq_strength: f64, transform_8x8: bool, sub8x8: bool, me_wide: bool, sadfp: bool,
917 me_wide_var: u64, me_rescue: i64, me_wide_coh: f64, me_range: i32, me_fast: bool, me_learn: u32,
932 me_payoff_pct: u32,
933 sp_single_pass: bool,
944 sp_defer: std::cell::Cell<bool>,
950 sp_learn_n: std::cell::Cell<u32>,
951 sp_ring1: std::cell::Cell<i64>,
952 sp_total: std::cell::Cell<i64>,
953 sp_1pass: std::cell::Cell<bool>,
954 resc_n: std::cell::Cell<u32>, resc_big: std::cell::Cell<u32>, resc_off: std::cell::Cell<bool>, inter8x8: u8, inter8_pen: i64, fast: bool, skip_accel_check: bool, coded_path_v2: bool, tune_lambda_scale: f64, tune_intra_penalty: f64,
964 satd_q: f64, subpel_force: bool, me_snap: bool, me_subpel_iter: bool, greedy_skip: bool, greedy_min_free: u32, rd_skip: bool, rd_skip_min_free: u32, rd_skip_fast_t: f64, satd_var_thresh: i64, aq_strength: f64, mb_use_satd: bool, nnz_l_cache: [u8; 25],
980 nnz_c_cache: [[u8; 9]; 2],
982 mb_skip_sad: Vec<u32>,
988 mb_was_skip: Vec<bool>,
989}
990
991type InterChoice = (u8, Vec<(i32, (i32, i32))>);
994
995const SKIP_RATE_BITS: f64 = 1.0;
998
999
1000
1001pub static EXT_MV: std::sync::Mutex<Vec<Vec<(i32, i32)>>> = std::sync::Mutex::new(Vec::new());
1006pub static MVCMP: [std::sync::atomic::AtomicU64; 7] = {
1008 const Z: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1009 [Z; 7]
1010};
1011pub static MVCMP_FRAME: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1012fn mv_force_on() -> bool {
1018 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1019 *ON.get_or_init(|| std::env::var("RFF_MV_FORCE").map_or(false, |v| v != "0"))
1020}
1021fn mv_cmp_on() -> bool {
1022 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1023 *ON.get_or_init(|| std::env::var("RFF_MV_CMP").map_or(false, |v| v != "0"))
1024}
1025
1026pub static MC_COUNT: [std::sync::atomic::AtomicU64; 2] = [
1030 std::sync::atomic::AtomicU64::new(0),
1031 std::sync::atomic::AtomicU64::new(0),
1032];
1033fn mc_count_on() -> bool {
1034 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1035 *ON.get_or_init(|| std::env::var("RFF_MC_COUNT").map_or(false, |v| v != "0"))
1036}
1037
1038pub static ME_PROBE: [std::sync::atomic::AtomicU64; 7] = {
1040 const Z: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1041 [Z; 7]
1042};
1043
1044fn me_oracle_on() -> bool {
1046 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1047 *ON.get_or_init(|| std::env::var("RFF_ME_ORACLE").map_or(false, |v| v != "0"))
1048}
1049
1050const SPLIT_GATE_BITS: f64 = 60.0;
1056
1057const FAST_INTRA_PENALTY_BITS: f64 = 24.0;
1061
1062#[derive(Default)]
1070struct MbState {
1071 rec_y: Vec<u8>,
1072 rec_u: Vec<u8>,
1073 rec_v: Vec<u8>,
1074 nnz_y: Vec<u8>,
1075 nnz_c: [Vec<u8>; 2],
1076 mv_y: Vec<(i32, i32)>,
1077 inter_y: Vec<bool>,
1078 ref_idx_y: Vec<i32>,
1079 coded_y: Vec<bool>,
1080 modes_y: Vec<u8>,
1081 cur_qp: u8,
1087}
1088
1089fn fast_intra_enabled() -> bool {
1095 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1096 *ON.get_or_init(|| std::env::var("RUSTY_FAST_INTRA").map_or(true, |v| v != "0"))
1097}
1098
1099fn coded_source(cfg: &EncoderConfig, frame: &YuvFrame) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
1100 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncSource);
1101 let cw = cfg.mb_width() * 16;
1102 let ch = cfg.mb_height() * 16;
1103 if frame.width == cw && frame.height == ch {
1106 return (frame.y.clone(), frame.u.clone(), frame.v.clone());
1107 }
1108 let y = clamp_plane(&frame.y, frame.width, frame.height, cw, ch);
1109 let u = clamp_plane(&frame.u, frame.chroma_width(), frame.chroma_height(), cw / 2, ch / 2);
1110 let v = clamp_plane(&frame.v, frame.chroma_width(), frame.chroma_height(), cw / 2, ch / 2);
1111 (y, u, v)
1112}
1113
1114fn clamp_plane(plane: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
1128 let mut out = vec![0u8; ow * oh];
1129 for y in 0..oh {
1130 let sy = y.min(h - 1);
1131 let src = &plane[sy * w..sy * w + w];
1132 let dst = &mut out[y * ow..y * ow + ow];
1133 if ow <= w {
1134 dst.copy_from_slice(&src[..ow]);
1135 } else {
1136 dst[..w].copy_from_slice(src);
1137 dst[w..].fill(src[w - 1]);
1138 }
1139 }
1140 out
1141}
1142
1143#[cfg(test)]
1146fn clamp_plane_per_pixel(plane: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
1147 let mut out = vec![0u8; ow * oh];
1148 for y in 0..oh {
1149 for x in 0..ow {
1150 out[y * ow + x] = plane[y.min(h - 1) * w + x.min(w - 1)];
1151 }
1152 }
1153 out
1154}
1155
1156#[cfg(test)]
1157mod source_tests {
1158 use super::*;
1159
1160 #[test]
1161 fn clamp_plane_matches_per_pixel_oracle() {
1162 let mut s: u32 = 0xDEAD_BEEF;
1163 let mut rnd = || {
1164 s = s.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
1165 (s >> 24) as u8
1166 };
1167 let cases = [
1170 (1920usize, 1080usize, 1920usize, 1088usize), (960, 540, 960, 544), (352, 288, 352, 288), (100, 100, 112, 112), (37, 5, 48, 16), (16, 1, 16, 16), (1, 1, 16, 16), ];
1178 for (w, h, ow, oh) in cases {
1179 let plane: Vec<u8> = (0..w * h).map(|_| rnd()).collect();
1180 assert_eq!(
1181 clamp_plane(&plane, w, h, ow, oh),
1182 clamp_plane_per_pixel(&plane, w, h, ow, oh),
1183 "clamp mismatch for {w}x{h} -> {ow}x{oh}"
1184 );
1185 }
1186 }
1187}
1188
1189impl FrameEncoder {
1190 fn new(cfg: &EncoderConfig) -> Self {
1191 let (mb_w, mb_h) = (cfg.mb_width(), cfg.mb_height());
1192 let (cw, ch) = (mb_w * 16, mb_h * 16);
1193 let (ccw, cch) = (cw / 2, ch / 2);
1194 Self {
1195 mb_w,
1196 mb_h,
1197 qp: cfg.qp,
1198 qpc: chroma_qp(cfg.qp),
1199 cur_qp: cfg.qp,
1200 bi_w: (32, 32),
1201 cw,
1202 ccw,
1203 rec_y: AlignedBytes::zeroed(cw * ch),
1204 rec_u: AlignedBytes::zeroed(ccw * cch),
1205 rec_v: AlignedBytes::zeroed(ccw * cch),
1206 nnz_y: vec![0; (mb_w * 4) * (mb_h * 4)],
1207 nnz_c: [vec![0; (mb_w * 2) * (mb_h * 2)], vec![0; (mb_w * 2) * (mb_h * 2)]],
1208 modes_y: vec![2; (mb_w * 4) * (mb_h * 4)],
1209 coded_y: vec![false; (mb_w * 4) * (mb_h * 4)],
1210 mv_y: vec![(0, 0); (mb_w * 4) * (mb_h * 4)],
1211 inter_y: vec![false; (mb_w * 4) * (mb_h * 4)],
1212 ref_idx_y: vec![-1; (mb_w * 4) * (mb_h * 4)],
1213 mv1_y: vec![(0, 0); (mb_w * 4) * (mb_h * 4)],
1214 ref_idx1_y: vec![-1; (mb_w * 4) * (mb_h * 4)],
1215 idz: if cfg.gop_size <= 1 { 2 } else { 3 },
1218 rdoq_strength: 0.0, transform_8x8: cfg.transform_8x8,
1220 sub8x8: std::env::var("RFF_SUB8X8").ok().map(|s| s == "1")
1229 .or(cfg.sub_8x8)
1230 .unwrap_or(cfg.preset == crate::config::Preset::Quality),
1231 sadfp: me_sadfp_mode() == 2,
1251 me_wide: std::env::var("RFF_ME_WIDE").ok().map(|s| s == "1")
1252 .or(cfg.me_wide)
1253 .unwrap_or(cfg.preset == crate::config::Preset::Quality),
1254 me_wide_var: std::env::var("RFF_ME_WIDE_VAR").ok().and_then(|s| s.parse().ok()).unwrap_or(800),
1255 me_rescue: std::env::var("RFF_ME_RESCUE").ok().and_then(|s| s.parse().ok()).unwrap_or(3),
1256 me_wide_coh: std::env::var("RFF_ME_COH").ok().and_then(|s| s.parse().ok()).unwrap_or(4.0),
1257 me_range: std::env::var("RFF_ME_RANGE").ok().and_then(|s| s.parse().ok()).unwrap_or(24),
1258 me_fast: std::env::var("RFF_ME_FASTMO").map(|s| s != "0").unwrap_or(true),
1259 me_learn: std::env::var("RFF_ME_LEARN").ok().and_then(|s| s.parse().ok()).unwrap_or(40),
1260 me_payoff_pct: std::env::var("RFF_ME_PAYOFF").ok().and_then(|s| s.parse().ok()).unwrap_or(15),
1261 sp_single_pass: cfg.preset == crate::config::Preset::Balanced,
1267 sp_defer: std::cell::Cell::new({
1268 let a = DEFER_SUBPEL.load(std::sync::atomic::Ordering::Relaxed) != 0
1269 || std::env::var("RFF_DEFER_SUBPEL").map(|v| v != "0").unwrap_or(false);
1270 a && cfg.preset == crate::config::Preset::Quality
1275 }),
1276 sp_learn_n: std::cell::Cell::new(0),
1277 sp_ring1: std::cell::Cell::new(0),
1278 sp_total: std::cell::Cell::new(0),
1279 sp_1pass: std::cell::Cell::new(false),
1280 resc_n: std::cell::Cell::new(0),
1281 resc_big: std::cell::Cell::new(0),
1282 resc_off: std::cell::Cell::new(false),
1283 inter8x8: std::env::var("RFF_INTER8")
1284 .ok()
1285 .and_then(|s| s.parse().ok())
1286 .unwrap_or(1),
1287 inter8_pen: std::env::var("RFF_INTER8_PEN")
1292 .ok()
1293 .and_then(|s| s.parse().ok())
1294 .unwrap_or(8),
1295 fast: cfg.preset != crate::config::Preset::Quality,
1297 skip_accel_check: cfg.tune_skip_accel_check,
1298 coded_path_v2: cfg.coded_path_v2,
1299 aq_strength: cfg.aq_strength,
1300 tune_lambda_scale: cfg.tune_lambda_scale,
1301 tune_intra_penalty: cfg.tune_intra_penalty,
1302 satd_q: cfg.tune_satd_q,
1303 subpel_force: cfg.tune_subpel || cfg.preset == crate::config::Preset::Balanced,
1304 me_snap: cfg.tune_me_snap,
1305 me_subpel_iter: cfg.tune_me_subpel_iter,
1306 greedy_skip: cfg.tune_greedy_skip,
1307 greedy_min_free: cfg.tune_greedy_skip_min_free.unwrap_or(85),
1308 rd_skip: cfg.tune_rd_skip,
1309 rd_skip_fast_t: cfg.tune_rd_skip_fast_t.unwrap_or(0.0),
1310 rd_skip_min_free: cfg.tune_rd_skip_min_free.unwrap_or(
1311 if cfg.preset == crate::config::Preset::Fast { 60 } else { 90 },
1312 ),
1313 satd_var_thresh: i64::MAX,
1314 mb_use_satd: false,
1315 nnz_l_cache: [0x80; 25],
1316 nnz_c_cache: [[0x80; 9]; 2],
1317 mb_skip_sad: vec![0; mb_w * mb_h],
1318 mb_was_skip: vec![false; mb_w * mb_h],
1319 }
1320 }
1321
1322 fn pred_skip_sad(&self, mb_x: usize, mb_y: usize) -> u32 {
1328 let mbw = self.mb_w;
1329 let at = |x: isize, y: isize| -> Option<(bool, u32)> {
1330 if x < 0 || y < 0 || x >= mbw as isize {
1331 return None;
1332 }
1333 let i = y as usize * mbw + x as usize;
1334 Some((self.mb_was_skip[i], self.mb_skip_sad[i]))
1335 };
1336 let a = at(mb_x as isize - 1, mb_y as isize); let b = at(mb_x as isize, mb_y as isize - 1); let c = at(mb_x as isize + 1, mb_y as isize - 1) .or_else(|| at(mb_x as isize - 1, mb_y as isize - 1)); let sad = |n: Option<(bool, u32)>| n.filter(|&(s, _)| s).map_or(0, |(_, v)| v);
1341 let (sa, sb, sc) = (sad(a), sad(b), sad(c));
1342 if b.is_none() && c.is_none() && a.is_some() {
1344 return sa;
1345 }
1346 match (
1347 a.is_some_and(|(s, _)| s),
1348 b.is_some_and(|(s, _)| s),
1349 c.is_some_and(|(s, _)| s),
1350 ) {
1351 (true, false, false) => sa,
1352 (false, true, false) => sb,
1353 (false, false, true) => sc,
1354 _ => sb.max(sa.min(sc)).min(sa.max(sc)), }
1356 }
1357
1358 fn qp_delta(&mut self) -> i32 {
1363 let d = self.qp as i32 - self.cur_qp as i32;
1364 self.cur_qp = self.qp;
1365 d
1366 }
1367
1368 fn mv_neighbors(&self, mb_x: usize, mb_y: usize) -> [MvNeighbor; 3] {
1371 let w4 = self.mb_w * 4;
1372 let get = |avail: bool, bx: isize, by: isize| {
1373 if avail {
1374 let idx = by as usize * w4 + bx as usize;
1375 MvNeighbor {
1376 available: true,
1377 mv: self.mv_y[idx],
1378 ref_idx: self.ref_idx_y[idx],
1379 }
1380 } else {
1381 MvNeighbor::NONE
1382 }
1383 };
1384 let (bx, by) = (mb_x as isize * 4, mb_y as isize * 4);
1385 let a = get(mb_x > 0, bx - 1, by);
1386 let b = get(mb_y > 0, bx, by - 1);
1387 let c = if mb_y > 0 && mb_x + 1 < self.mb_w {
1389 get(true, bx + 4, by - 1)
1390 } else {
1391 get(mb_x > 0 && mb_y > 0, bx - 1, by - 1)
1392 };
1393 [a, b, c]
1394 }
1395
1396 fn skip_mv(&self, mb_x: usize, mb_y: usize) -> (i32, i32) {
1399 let [a, b, c] = self.mv_neighbors(mb_x, mb_y);
1400 if !a.available
1401 || !b.available
1402 || (a.ref_idx == 0 && a.mv == (0, 0))
1403 || (b.ref_idx == 0 && b.mv == (0, 0))
1404 {
1405 (0, 0)
1406 } else {
1407 predict_mv(a, b, c, 0)
1408 }
1409 }
1410
1411 fn set_mb_mv(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32), inter: bool, refi: i32) {
1414 let w4 = self.mb_w * 4;
1415 for dy in 0..4 {
1416 for dx in 0..4 {
1417 let idx = (mb_y * 4 + dy) * w4 + (mb_x * 4 + dx);
1418 self.mv_y[idx] = mv;
1419 self.inter_y[idx] = inter;
1420 self.ref_idx_y[idx] = if inter { refi } else { -1 };
1421 }
1422 }
1423 }
1424
1425 fn mv_neighbors_block(&self, pbx: isize, pby: isize, pwb: isize) -> [MvNeighbor; 3] {
1429 let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
1430 let get = |bx: isize, by: isize| -> MvNeighbor {
1431 if bx < 0 || by < 0 || bx >= w4 || by >= h4 || !self.coded_y[(by * w4 + bx) as usize] {
1432 MvNeighbor::NONE
1433 } else {
1434 let idx = (by * w4 + bx) as usize;
1435 MvNeighbor { available: true, mv: self.mv_y[idx], ref_idx: self.ref_idx_y[idx] }
1436 }
1437 };
1438 let a = get(pbx - 1, pby);
1439 let b = get(pbx, pby - 1);
1440 let mut c = get(pbx + pwb, pby - 1);
1441 if !c.available {
1442 c = get(pbx - 1, pby - 1); }
1444 [a, b, c]
1445 }
1446
1447 fn mv_neighbors_block_list(&self, pbx: isize, pby: isize, pwb: isize, list: usize) -> [MvNeighbor; 3] {
1453 let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
1454 let (mvg, refg): (&[(i32, i32)], &[i32]) = if list == 0 {
1455 (&self.mv_y, &self.ref_idx_y)
1456 } else {
1457 (&self.mv1_y, &self.ref_idx1_y)
1458 };
1459 let get = |bx: isize, by: isize| -> MvNeighbor {
1460 if bx < 0 || by < 0 || bx >= w4 || by >= h4 || !self.coded_y[(by * w4 + bx) as usize] {
1461 MvNeighbor::NONE
1462 } else {
1463 let idx = (by * w4 + bx) as usize;
1464 MvNeighbor { available: true, mv: mvg[idx], ref_idx: refg[idx] }
1465 }
1466 };
1467 let a = get(pbx - 1, pby);
1468 let b = get(pbx, pby - 1);
1469 let mut c = get(pbx + pwb, pby - 1);
1470 if !c.available {
1471 c = get(pbx - 1, pby - 1); }
1473 [a, b, c]
1474 }
1475
1476 #[allow(clippy::too_many_arguments)]
1486 #[inline]
1487 fn mc_satd_hp(
1488 &self,
1489 reference: &crate::RefFrame,
1490 hp: Option<&rusty_h264_common::inter::HpelPlanes>,
1491 hr_on: bool,
1492 sa_on: bool,
1496 src_row: &[u8],
1497 lx: usize,
1498 ly: usize,
1499 rw: usize,
1500 rh: usize,
1501 mv: (i32, i32),
1502 ) -> i64 {
1503 #[cfg(not(accel))]
1504 let _ = sa_on;
1505 #[cfg(feature = "profile")]
1506 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1507 let ch = self.mb_h * 16;
1508 let cw = self.cw;
1509 let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1510 let interior_fullpel = mv.0 & 3 == 0
1511 && mv.1 & 3 == 0
1512 && ix0 >= 0
1513 && iy0 >= 0
1514 && ix0 + rw as isize <= cw as isize
1515 && iy0 + rh as isize <= ch as isize;
1516 #[cfg(feature = "profile")]
1517 {
1518 let fullpel = mv.0 & 3 == 0 && mv.1 & 3 == 0;
1519 satdpath::bump(if interior_fullpel { 0 } else if fullpel { 1 } else { 2 });
1520 }
1521 if interior_fullpel {
1522 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1523 let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1524 return satd_px(src_row, cw, &reference.y[ry0 * cw + rx0..], cw, rw, rh);
1525 }
1526 if let Some(hp) = hp {
1527 if hr_on {
1528 if let Some((plane, base, stride)) =
1529 rusty_h264_common::inter::hpel_ref(hp, lx, ly, rw, rh, mv.0, mv.1)
1530 {
1531 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1532 return satd_px(src_row, cw, &plane[base..], stride, rw, rh);
1533 }
1534 }
1535 #[cfg(accel)]
1542 if sa_on {
1543 if let Some((pa, ba, pb, bb, stride)) =
1544 rusty_h264_common::inter::hpel_qpel_refs(hp, lx, ly, rw, rh, mv.0, mv.1)
1545 {
1546 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1547 if let Some(v) = rusty_h264_accel::satd_avg(
1548 src_row, cw, &pa[ba..], &pb[bb..], stride, rw, rh,
1549 ) {
1550 return v as i64;
1551 }
1552 }
1553 }
1554 let mut pred = [0u8; 256];
1555 if rusty_h264_common::inter::hpel_block(hp, lx, ly, rw, rh, mv.0, mv.1, &mut pred) {
1556 return satd_px(src_row, cw, &pred, rw, rw, rh);
1557 }
1558 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1559 return satd_px(src_row, cw, &pred, rw, rw, rh);
1560 }
1561 let mut pred = [0u8; 256];
1562 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1563 satd_px(src_row, cw, &pred, rw, rw, rh)
1564 }
1565
1566 #[allow(clippy::too_many_arguments)]
1574 #[inline]
1575 fn mc_sad_hp(
1576 &self,
1577 reference: &crate::RefFrame,
1578 hp: Option<&rusty_h264_common::inter::HpelPlanes>,
1579 hr_on: bool,
1580 src_row: &[u8],
1581 lx: usize,
1582 ly: usize,
1583 rw: usize,
1584 rh: usize,
1585 mv: (i32, i32),
1586 _asrc: Option<&[u8; 256]>,
1587 ) -> i64 {
1588 #[cfg(feature = "profile")]
1589 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1590 let ch = self.mb_h * 16;
1591 let cw = self.cw;
1592 let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1593 let interior_fullpel = mv.0 & 3 == 0
1594 && mv.1 & 3 == 0
1595 && ix0 >= 0
1596 && iy0 >= 0
1597 && ix0 + rw as isize <= cw as isize
1598 && iy0 + rh as isize <= ch as isize;
1599 if interior_fullpel {
1600 let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1601 #[cfg(accel)]
1602 if rw == 16 && rh == 16 {
1603 if let Some(src) = _asrc {
1604 return rusty_h264_accel::sad_16x16(src, 16, &reference.y[ry0 * cw + rx0..], cw)
1605 as i64;
1606 }
1607 }
1608 return sad_strided(src_row, cw, &reference.y[ry0 * cw + rx0..], cw, rw, rh);
1609 }
1610 if let Some(hp) = hp {
1611 if hr_on {
1612 if let Some((plane, base, stride)) =
1615 rusty_h264_common::inter::hpel_ref(hp, lx, ly, rw, rh, mv.0, mv.1)
1616 {
1617 return sad_strided(src_row, cw, &plane[base..], stride, rw, rh);
1618 }
1619 if let Some((pa, ba, pb, bb, stride)) =
1621 rusty_h264_common::inter::hpel_qpel_refs(hp, lx, ly, rw, rh, mv.0, mv.1)
1622 {
1623 return sad_avg_strided(src_row, cw, &pa[ba..], &pb[bb..], stride, rw, rh);
1624 }
1625 }
1626 let mut pred = [0u8; 256];
1627 if rusty_h264_common::inter::hpel_block(hp, lx, ly, rw, rh, mv.0, mv.1, &mut pred) {
1628 return sad_strided(src_row, cw, &pred, rw, rw, rh);
1629 }
1630 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1631 return sad_strided(src_row, cw, &pred, rw, rw, rh);
1632 }
1633 let mut pred = [0u8; 256];
1634 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1635 sad_strided(src_row, cw, &pred, rw, rw, rh)
1636 }
1637
1638 #[allow(clippy::too_many_arguments)]
1647 fn mc_sad(
1648 &self,
1649 reference: &crate::RefFrame,
1650 sy: &[u8],
1651 lx: usize,
1652 ly: usize,
1653 rw: usize,
1654 rh: usize,
1655 mv: (i32, i32),
1656 _asrc: Option<&[u8; 256]>,
1659 ) -> i64 {
1660 #[cfg(feature = "profile")]
1664 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1665 let ch = self.mb_h * 16;
1666 let cw = self.cw;
1667 let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1668 let interior_fullpel = mv.0 & 3 == 0
1669 && mv.1 & 3 == 0
1670 && ix0 >= 0
1671 && iy0 >= 0
1672 && ix0 + rw as isize <= cw as isize
1673 && iy0 + rh as isize <= ch as isize;
1674 #[cfg(accel)]
1678 if interior_fullpel && rw == 16 && rh == 16 {
1679 if let Some(src) = _asrc {
1680 let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1681 return rusty_h264_accel::sad_16x16(src, 16, &reference.y[ry0 * cw + rx0..], cw)
1682 as i64;
1683 }
1684 }
1685 let mut sad = 0u32;
1686 if interior_fullpel {
1687 let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1689 let refy = &reference.y;
1690 for dy in 0..rh {
1691 let s = &sy[(ly + dy) * cw + lx..][..rw];
1692 let r = &refy[(ry0 + dy) * cw + rx0..][..rw];
1693 sad += s.iter().zip(r).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1694 }
1695 } else {
1696 let mut pred = [0u8; 256];
1697 let from_planes = !self.fast
1699 && rusty_h264_common::inter::hpel_block(
1700 reference.hpel(cw, ch),
1701 lx,
1702 ly,
1703 rw,
1704 rh,
1705 mv.0,
1706 mv.1,
1707 &mut pred,
1708 );
1709 if !from_planes {
1710 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1711 }
1712 for dy in 0..rh {
1713 let s = &sy[(ly + dy) * cw + lx..][..rw];
1714 let p = &pred[dy * rw..][..rw];
1715 sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1716 }
1717 }
1718 sad as i64
1719 }
1720
1721 fn bi_dist(
1727 &self,
1728 l0: &crate::RefFrame,
1729 l1: &crate::RefFrame,
1730 sy: &[u8],
1731 lx: usize,
1732 ly: usize,
1733 mv0: (i32, i32),
1734 mv1: (i32, i32),
1735 ) -> i64 {
1736 #[cfg(feature = "profile")]
1738 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(4);
1739 let ch = self.mb_h * 16;
1740 let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
1741 mc_luma(&l0.y, self.cw, ch, lx, ly, 16, 16, mv0.0, mv0.1, &mut a);
1742 mc_luma(&l1.y, self.cw, ch, lx, ly, 16, 16, mv1.0, mv1.1, &mut b);
1743 let mut avg = [0u8; 256];
1744 for i in 0..256 {
1745 avg[i] = bi_blend(a[i] as i32, b[i] as i32, self.bi_w);
1746 }
1747 if self.fast && !self.mb_use_satd {
1748 let mut sad = 0u32;
1749 for dy in 0..16 {
1750 let s = &sy[(ly + dy) * self.cw + lx..][..16];
1751 let p = &avg[dy * 16..][..16];
1752 sad += s.iter().zip(p).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
1753 }
1754 sad as i64
1755 } else {
1756 satd_px(&sy[ly * self.cw + lx..], self.cw, &avg, 16, 16, 16)
1757 }
1758 }
1759
1760 fn pred_dist(&self, sy: &[u8], lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
1764 if self.fast && !self.mb_use_satd {
1765 let mut sad = 0u32;
1766 for dy in 0..16 {
1767 let s = &sy[(ly + dy) * self.cw + lx..][..16];
1768 let p = &pred[dy * 16..][..16];
1769 sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1770 }
1771 sad as i64
1772 } else {
1773 satd_px(&sy[ly * self.cw + lx..], self.cw, pred, 16, 16, 16)
1774 }
1775 }
1776
1777 fn col_zero(&self, l1: &crate::RefFrame, bx: usize, by: usize) -> bool {
1782 if l1.w4 == 0 {
1783 return false;
1784 }
1785 let idx = by * l1.w4 + bx;
1786 if idx >= l1.ref_idx.len() {
1787 return false;
1788 }
1789 l1.ref_idx[idx] == 0 && l1.mv[idx].0.abs() <= 1 && l1.mv[idx].1.abs() <= 1
1790 }
1791
1792 #[allow(clippy::too_many_arguments)]
1796 fn b_mc_block(
1797 &self,
1798 l0: &crate::RefFrame,
1799 l1: &crate::RefFrame,
1800 mb_x: usize,
1801 mb_y: usize,
1802 dx: usize,
1803 dy: usize,
1804 refi0: i32,
1805 m0: (i32, i32),
1806 refi1: i32,
1807 m1: (i32, i32),
1808 pred_y: &mut [u8; 256],
1809 c_pred: &mut [[u8; 64]; 2],
1810 ) {
1811 #[cfg(feature = "profile")]
1813 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(4);
1814 let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
1815 let (px, py) = (mb_x * 16 + dx, mb_y * 16 + dy);
1816 let (mut a, mut b) = ([0u8; 16], [0u8; 16]);
1817 let mc4 = |r: &crate::RefFrame, mv: (i32, i32), out: &mut [u8; 16]| {
1825 if !self.fast
1826 && bdirect_planes_enabled()
1827 && rusty_h264_common::inter::hpel_block(
1828 r.hpel(self.cw, ch), px, py, 4, 4, mv.0, mv.1, out,
1829 )
1830 {
1831 return;
1832 }
1833 mc_luma(&r.y, self.cw, ch, px, py, 4, 4, mv.0, mv.1, out);
1834 };
1835 if refi0 >= 0 {
1836 mc4(l0, m0, &mut a);
1837 }
1838 if refi1 >= 0 {
1839 mc4(l1, m1, &mut b);
1840 }
1841 for yy in 0..4 {
1842 for xx in 0..4 {
1843 let i = yy * 4 + xx;
1844 let v = match (refi0 >= 0, refi1 >= 0) {
1845 (true, true) => bi_blend(a[i] as i32, b[i] as i32, self.bi_w),
1846 (true, false) => a[i],
1847 _ => b[i],
1848 };
1849 pred_y[(dy + yy) * 16 + (dx + xx)] = v;
1850 }
1851 }
1852 let (cpx, cpy) = (mb_x * 8 + dx / 2, mb_y * 8 + dy / 2);
1854 for c in 0..2 {
1855 let (r0, r1) = if c == 0 { (&l0.u, &l1.u) } else { (&l0.v, &l1.v) };
1856 let (mut ca, mut cb) = ([0u8; 4], [0u8; 4]);
1857 if refi0 >= 0 {
1858 mc_chroma(r0, self.ccw, cch, cpx, cpy, 2, 2, m0.0, m0.1, &mut ca);
1859 }
1860 if refi1 >= 0 {
1861 mc_chroma(r1, self.ccw, cch, cpx, cpy, 2, 2, m1.0, m1.1, &mut cb);
1862 }
1863 for yy in 0..2 {
1864 for xx in 0..2 {
1865 let i = yy * 2 + xx;
1866 let v = match (refi0 >= 0, refi1 >= 0) {
1867 (true, true) => bi_blend(ca[i] as i32, cb[i] as i32, self.bi_w),
1868 (true, false) => ca[i],
1869 _ => cb[i],
1870 };
1871 c_pred[c][(dy / 2 + yy) * 8 + (dx / 2 + xx)] = v;
1872 }
1873 }
1874 }
1875 }
1876
1877 fn b_direct(
1882 &self,
1883 l0: &crate::RefFrame,
1884 l1: &crate::RefFrame,
1885 mb_x: usize,
1886 mb_y: usize,
1887 ) -> ([u8; 256], [[u8; 64]; 2], [(i32, (i32, i32), i32, (i32, i32)); 16]) {
1888 let (nbx, nby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
1889 let n0 = self.mv_neighbors_block_list(nbx, nby, 4, 0);
1890 let n1 = self.mv_neighbors_block_list(nbx, nby, 4, 1);
1891 let min_pos = |a: i32, b: i32| if a < 0 { b } else if b < 0 { a } else { a.min(b) };
1892 let rid = |n: &[MvNeighbor; 3]| min_pos(min_pos(n[0].ref_idx, n[1].ref_idx), n[2].ref_idx);
1893 let (mut refi0, mut refi1) = (rid(&n0), rid(&n1));
1894 let direct_zero = refi0 < 0 && refi1 < 0;
1895 if direct_zero {
1896 refi0 = 0;
1897 refi1 = 0;
1898 }
1899 let mv0 = if refi0 >= 0 && !direct_zero { predict_mv(n0[0], n0[1], n0[2], refi0) } else { (0, 0) };
1900 let mv1 = if refi1 >= 0 && !direct_zero { predict_mv(n1[0], n1[1], n1[2], refi1) } else { (0, 0) };
1901 let mut pred_y = [0u8; 256];
1902 let mut c_pred = [[0u8; 64]; 2];
1903 let mut motion = [(0i32, (0i32, 0i32), 0i32, (0i32, 0i32)); 16];
1904 for sby in 0..4 {
1905 for sbx in 0..4 {
1906 let cz = !direct_zero && self.col_zero(l1, mb_x * 4 + sbx, mb_y * 4 + sby);
1907 let m0 = if refi0 == 0 && cz { (0, 0) } else { mv0 };
1908 let m1 = if refi1 == 0 && cz { (0, 0) } else { mv1 };
1909 motion[sby * 4 + sbx] = (refi0, m0, refi1, m1);
1910 self.b_mc_block(l0, l1, mb_x, mb_y, sbx * 4, sby * 4, refi0, m0, refi1, m1, &mut pred_y, &mut c_pred);
1911 }
1912 }
1913 (pred_y, c_pred, motion)
1914 }
1915
1916 fn commit_direct_motion(&mut self, mb_x: usize, mb_y: usize, motion: &[(i32, (i32, i32), i32, (i32, i32)); 16]) {
1919 let w4 = self.mb_w * 4;
1920 for sby in 0..4 {
1921 for sbx in 0..4 {
1922 let (refi0, m0, refi1, m1) = motion[sby * 4 + sbx];
1923 let idx = (mb_y * 4 + sby) * w4 + (mb_x * 4 + sbx);
1924 self.inter_y[idx] = true;
1925 self.coded_y[idx] = true;
1926 self.mv_y[idx] = m0;
1927 self.ref_idx_y[idx] = refi0;
1928 self.mv1_y[idx] = m1;
1929 self.ref_idx1_y[idx] = refi1;
1930 }
1931 }
1932 }
1933
1934 #[allow(clippy::too_many_arguments)]
1943 fn motion_search(
1949 &self,
1950 reference: &crate::RefFrame,
1951 sy: &[u8],
1952 lx: usize,
1953 ly: usize,
1954 rw: usize,
1955 rh: usize,
1956 predictors: &[(i32, i32)],
1957 lambda_me: f64,
1958 start: Option<(i32, i32)>,
1962 ) -> ((i32, i32), i64) {
1963 #[inline(always)]
1969 fn mvbits(d: i32) -> u32 {
1970 let codenum = if d > 0 { (2 * d - 1) as u32 } else { (-2 * d) as u32 };
1971 1 + 2 * (31 - (codenum + 1).leading_zeros())
1972 }
1973 let center = predictors[0];
1974 let probe = me_oracle_on();
1975 let sadfp = !self.fast && start.is_none() && self.sadfp;
1981 #[cfg(accel)]
1986 let asrc_buf = if (self.fast || sadfp) && rw == 16 && rh == 16 {
1987 let mut a = AlignedMb([0u8; 256]);
1988 for dy in 0..16 {
1989 a.0[dy * 16..dy * 16 + 16].copy_from_slice(&sy[(ly + dy) * self.cw + lx..][..16]);
1990 }
1991 Some(a)
1992 } else {
1993 None
1994 };
1995 #[cfg(accel)]
1996 let asrc: Option<&[u8; 256]> = asrc_buf.as_ref().map(|a| &a.0);
1997 #[cfg(not(accel))]
1998 let asrc: Option<&[u8; 256]> = None;
1999 let use_sad = self.fast && !self.mb_use_satd;
2007 let cw = self.cw;
2008 let hp: Option<&rusty_h264_common::inter::HpelPlanes> =
2012 if !self.fast { Some(reference.hpel(cw, self.mb_h * 16)) } else { None };
2013 let hr_on = hpel_ref_enabled();
2014 let sa_on = cfg!(accel) && hr_on && satd_avg_enabled();
2017 let src_row = &sy[ly * cw + lx..];
2018 let cost = |mv: (i32, i32)| -> i64 {
2019 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2020 let dist = if use_sad {
2023 self.mc_sad(reference, sy, lx, ly, rw, rh, mv, asrc)
2024 } else {
2025 self.mc_satd_hp(reference, hp, hr_on, sa_on, src_row, lx, ly, rw, rh, mv)
2026 };
2027 dist + (lambda_me * rate as f64) as i64
2028 };
2029 let lam_fp = lambda_me * if sadfp { me_sadfp_lambda() } else { 1.0 };
2033 let cost_fp = |mv: (i32, i32)| -> i64 {
2034 if !sadfp {
2035 return cost(mv);
2036 }
2037 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2038 self.mc_sad_hp(reference, hp, hr_on, src_row, lx, ly, rw, rh, mv, asrc)
2039 + (lam_fp * rate as f64) as i64
2040 };
2041 let refine_only = start.is_some();
2043 let (mut best, mut best_c) = match start {
2044 Some(mv) => (mv, cost(mv)),
2045 None => {
2046 let mut b = (0, 0);
2047 let mut bc = cost_fp(b);
2048 for &p in predictors {
2049 let pc = cost_fp(p);
2050 if pc < bc {
2051 bc = pc;
2052 b = p;
2053 }
2054 }
2055 (b, bc)
2056 }
2057 };
2058 let (seed_mv, mut seed_c) = (best, best_c);
2066 if !refine_only && self.me_snap && (best.0 & 3 != 0 || best.1 & 3 != 0) {
2067 let snapped = ((best.0 + 2).div_euclid(4) * 4, (best.1 + 2).div_euclid(4) * 4);
2068 best_c = cost_fp(snapped);
2069 best = snapped;
2070 }
2071 let mut ladder = [0i32; 5];
2085 let mut nladder = 0usize;
2086 let steps: &[i32] = if self.fast {
2087 &[16, 4]
2088 } else {
2089 let m = dia_mask();
2090 for (i, r) in DIA_RUNGS.iter().enumerate() {
2091 if m & (1 << i) != 0 {
2092 ladder[nladder] = *r;
2093 nladder += 1;
2094 }
2095 }
2096 &ladder[..nladder]
2097 };
2098 let _gd = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeDiamond);
2099 let fc = !self.fast && rw == 16 && rh == 16 && cfg!(accel) && me_fc_enabled();
2107 let ch_px = self.mb_h as isize * 16;
2108 for (_si, &step) in steps.iter().enumerate() {
2109 if refine_only {
2110 break;
2111 }
2112 loop {
2113 #[cfg(accel)]
2114 if fc && best.0 & 3 == 0 && best.1 & 3 == 0 {
2115 let s = (step >> 2) as isize;
2117 let (bx, by) = (lx as isize + (best.0 >> 2) as isize, ly as isize + (best.1 >> 2) as isize);
2118 if bx - s >= 0 && by - s >= 0 && bx + s + 16 <= cw as isize && by + s + 16 <= ch_px {
2119 let offs = [
2120 (by * cw as isize + bx + s) as usize,
2121 (by * cw as isize + bx - s) as usize,
2122 ((by + s) * cw as isize + bx) as usize,
2123 ((by - s) * cw as isize + bx) as usize,
2124 ];
2125 let batch = if sadfp {
2126 rusty_h264_accel::sad_16x16_x4(src_row, cw, &reference.y, offs, cw)
2127 } else {
2128 rusty_h264_accel::satd_16x16_x4(src_row, cw, &reference.y, offs, cw)
2129 };
2130 if let Some(sads) = batch {
2131 let ring = [(step, 0), (-step, 0), (0, step), (0, -step)];
2132 let (mut bi, mut bc) = (usize::MAX, best_c);
2133 for (i, &(dx, dy)) in ring.iter().enumerate() {
2134 let mv = (best.0 + dx, best.1 + dy);
2135 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2136 let cc = sads[i] as i64 + (lam_fp * rate as f64) as i64;
2137 #[cfg(feature = "profile")]
2138 diastats::ev(_si);
2139 if cc < bc {
2140 bc = cc;
2141 bi = i;
2142 }
2143 }
2144 if bi == usize::MAX {
2145 break;
2146 }
2147 best_c = bc;
2148 best = (best.0 + ring[bi].0, best.1 + ring[bi].1);
2149 #[cfg(feature = "profile")]
2150 diastats::imp(_si);
2151 continue;
2152 }
2153 }
2154 }
2155 let mut improved = false;
2156 for &(dx, dy) in &[(step, 0), (-step, 0), (0, step), (0, -step)] {
2157 let c = (best.0 + dx, best.1 + dy);
2158 let cc = cost_fp(c);
2159 #[cfg(feature = "profile")]
2160 diastats::ev(_si);
2161 if cc < best_c {
2162 best_c = cc;
2163 best = c;
2164 improved = true;
2165 #[cfg(feature = "profile")]
2166 diastats::imp(_si);
2167 }
2168 }
2169 if !improved {
2170 break;
2171 }
2172 }
2173 }
2174 drop(_gd);
2187 if sadfp {
2192 best_c = cost(best);
2193 seed_c = cost(seed_mv);
2194 }
2195 let _gr = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeRescue);
2196 let flat = !refine_only && self.me_wide && !self.fast && {
2197 let (mut s, mut ss) = (0u64, 0u64);
2198 for dy in 0..rh {
2199 for dx in 0..rw {
2200 let v = sy[(ly + dy) * self.cw + lx + dx] as u64;
2201 s += v;
2202 ss += v * v;
2203 }
2204 }
2205 let n = (rw * rh) as u64;
2206 (ss - s * s / n) / n < self.me_wide_var
2207 };
2208 if self.me_wide && !self.fast && (flat || self.me_fast) && !self.resc_off.get() {
2219 let dist = self.mc_satd_hp(reference, hp, hr_on, sa_on, src_row, lx, ly, rw, rh, best);
2220 if dist / (rw * rh).max(1) as i64 > self.me_rescue {
2221 let pre_c = best_c;
2231 let (cx, cy) = ((best.0 + 2).div_euclid(4) * 4, (best.1 + 2).div_euclid(4) * 4);
2232 let mut gb = best;
2233 let cw = self.cw;
2242 let r = self.me_range;
2243 let batched = rw == 16 && rh == 16 && cfg!(accel) && {
2244 let (icdx, icdy) = (cx >> 2, cy >> 2);
2245 lx as i32 + icdx >= r
2246 && lx as i32 + icdx + r + 16 <= cw as i32
2247 && ly as i32 + icdy >= r
2248 && ly as i32 + icdy + r + 16 <= (self.mb_h * 16) as i32
2249 && me_batch_enabled()
2250 };
2251 #[cfg(accel)]
2252 if batched {
2253 let (icdx, icdy) = ((cx >> 2), (cy >> 2));
2254 let src = &sy[ly * cw + lx..];
2255 let mut dy = -r;
2256 while dy <= r {
2257 let rby = (ly as i32 + icdy + dy) as usize;
2258 let mut dx = -r;
2259 while dx <= r {
2260 let rbx = (lx as i32 + icdx + dx) as usize;
2261 let satd =
2262 2 * rusty_h264_accel::satd_16x16(src, cw, &reference.y[rby * cw + rbx..], cw) as i64;
2263 let mv = (cx + dx * 4, cy + dy * 4);
2264 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2265 let cc = satd + (lambda_me * rate as f64) as i64;
2266 if cc < best_c {
2267 best_c = cc;
2268 gb = mv;
2269 }
2270 dx += 2;
2271 }
2272 dy += 2;
2273 }
2274 }
2275 if !batched {
2276 let mut dy = -r;
2277 while dy <= r {
2278 let mut dx = -r;
2279 while dx <= r {
2280 let cc = cost((cx + dx * 4, cy + dy * 4));
2281 if cc < best_c {
2282 best_c = cc;
2283 gb = (cx + dx * 4, cy + dy * 4);
2284 }
2285 dx += 2;
2286 }
2287 dy += 2;
2288 }
2289 }
2290 best = gb;
2291 for dy in -1..=1 {
2292 for dx in -1..=1 {
2293 let c = (best.0 + dx * 4, best.1 + dy * 4);
2294 let cc = cost(c);
2295 if cc < best_c {
2296 best_c = cc;
2297 best = c;
2298 }
2299 }
2300 }
2301 let n = self.resc_n.get();
2310 if n < self.me_learn {
2311 self.resc_n.set(n + 1);
2312 if best_c * 16 <= pre_c * 15 {
2313 self.resc_big.set(self.resc_big.get() + 1);
2314 }
2315 if n + 1 == self.me_learn
2316 && self.resc_big.get() * 100 < self.me_learn * self.me_payoff_pct
2317 {
2318 self.resc_off.set(true);
2319 }
2320 }
2321 }
2322 }
2323 drop(_gr);
2324 let _gs = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeSubpel);
2325 if probe {
2331 let mut ob = center;
2334 let mut oc = i64::MAX;
2335 for gy in -24i32..=24 {
2336 for gx in -24i32..=24 {
2337 let c = (center.0 + gx * 4, center.1 + gy * 4);
2338 let cc = cost(c);
2339 if cc < oc {
2340 oc = cc;
2341 ob = c;
2342 }
2343 }
2344 }
2345 let fullpel_best = ob;
2346 for &st in &[2i32, 1] {
2347 for &(dx, dy) in &[(st, 0), (-st, 0), (0, st), (0, -st)] {
2348 let c = (ob.0 + dx, ob.1 + dy);
2349 let cc = cost(c);
2350 if cc < oc {
2351 oc = cc;
2352 ob = c;
2353 }
2354 }
2355 }
2356 let mut oc_sp = oc;
2360 for dy in -3i32..=3 {
2361 for dx in -3i32..=3 {
2362 let c = (fullpel_best.0 + dx, fullpel_best.1 + dy);
2363 let cc = cost(c);
2364 if cc < oc_sp {
2365 oc_sp = cc;
2366 }
2367 }
2368 }
2369 let (mut mb_, mut mc_) = (best, best_c);
2371 for &st in &[2i32, 1] {
2372 for &(dx, dy) in &[(st, 0), (-st, 0), (0, st), (0, -st)] {
2373 let c = (mb_.0 + dx, mb_.1 + dy);
2374 let cc = cost(c);
2375 if cc < mc_ {
2376 mc_ = cc;
2377 mb_ = c;
2378 }
2379 }
2380 }
2381 use std::sync::atomic::Ordering::Relaxed;
2382 ME_PROBE[0].fetch_add(1, Relaxed);
2383 ME_PROBE[1].fetch_add(mc_.max(0) as u64, Relaxed);
2384 ME_PROBE[2].fetch_add(oc.max(0) as u64, Relaxed);
2385 ME_PROBE[3].fetch_add((mc_ > oc) as u64, Relaxed);
2386 ME_PROBE[5].fetch_add(oc_sp.max(0) as u64, Relaxed);
2387 ME_PROBE[6].fetch_add((mc_ > oc_sp) as u64, Relaxed);
2388 }
2389 let subpel: &[i32] = if (self.fast && !self.subpel_force) || (self.sp_defer.get() && !refine_only) {
2390 &[]
2391 } else {
2392 &[2, 1]
2393 };
2394 let (hv_pre, mut hv_evals) = (best_c, 0u32);
2396 let (mut hv_to_best, mut hv_ring1) = (0u32, i64::MIN);
2400 let mut pat = subpel_pattern_override()
2401 .unwrap_or(if self.sp_single_pass { 2 } else { 0 });
2402 let (sp_learn, sp_t) = sp_dispatch_cfg();
2403 let sp_dispatching = sp_learn > 0 && pat == 0 && !subpel.is_empty();
2405 if sp_dispatching && self.sp_learn_n.get() >= sp_learn && self.sp_1pass.get() {
2406 pat = 2;
2407 }
2408 const SP_MEMO_N: usize = 64;
2421 #[inline(always)]
2422 fn sp_slot(mv: (i32, i32)) -> usize {
2423 ((mv.0 & 7) as usize) | (((mv.1 & 7) as usize) << 3)
2424 }
2425 let mut memo_mv = [(i32::MIN, i32::MIN); SP_MEMO_N];
2426 let mut memo_c = [0i64; SP_MEMO_N];
2427 if !subpel.is_empty() {
2428 let s0 = sp_slot(best);
2429 memo_mv[s0] = best;
2430 memo_c[s0] = best_c;
2431 }
2432 #[cfg(feature = "profile")]
2437 let mut seen: Vec<(i32, i32)> = Vec::with_capacity(64);
2438 #[cfg(feature = "profile")]
2439 {
2440 seen.push(best);
2441 }
2442 let sp_cap = sp_maxit();
2450 let sp_fc = sp_fc_enabled() && !self.fast && rw == 16 && rh == 16 && cfg!(accel);
2452 for &step in subpel {
2453 let ring8 = [
2458 (step, 0), (-step, 0), (0, step), (0, -step),
2459 (step, step), (-step, -step), (step, -step), (-step, step),
2460 ];
2461 let ring4 = [(step, 0), (-step, 0), (0, step), (0, -step)];
2462 let ring: &[(i32, i32)] = if pat & 1 != 0 { &ring4 } else { &ring8 };
2463 let mut _iter = 0u32;
2464 loop {
2465 #[cfg(accel)]
2474 if sp_fc && step == 1 && pat & 1 == 0 {
2475 _iter += 1;
2476 let hp8 = hp.expect("sp_fc implies non-fast, which resolves hp");
2477 let ring8 = [
2478 (1, 0), (-1, 0), (0, 1), (0, -1),
2479 (1, 1), (-1, -1), (1, -1), (-1, 1),
2480 ];
2481 let mut prs: [Option<(&[u8], usize, &[u8], usize, usize)>; 8] = [None; 8];
2482 let mut all = true;
2483 for (i, &(dx, dy)) in ring8.iter().enumerate() {
2484 prs[i] = rusty_h264_common::inter::hpel_qpel_refs(
2485 hp8, lx, ly, rw, rh, best.0 + dx, best.1 + dy,
2486 );
2487 all &= prs[i].is_some();
2488 }
2489 if all {
2490 let stride = prs[0].unwrap().4;
2491 let pack = |a: usize, b: usize, c2: usize, d: usize| {
2492 let g = |i: usize| {
2493 let (pa, oa, pb, ob, _) = prs[i].unwrap();
2494 (pa, oa, pb, ob)
2495 };
2496 rusty_h264_accel::satd_avg_16x16_x4(
2497 src_row, cw, [g(a), g(b), g(c2), g(d)], stride,
2498 )
2499 };
2500 if let (Some(ax), Some(di)) = (pack(0, 1, 2, 3), pack(4, 5, 6, 7)) {
2501 let (mut bi, mut bc) = (usize::MAX, best_c);
2502 for i in 0..8 {
2503 let (dx, dy) = ring8[i];
2504 let mv = (best.0 + dx, best.1 + dy);
2505 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2506 let d = if i < 4 { ax[i] } else { di[i - 4] } as i64;
2507 let cc = d + (lambda_me * rate as f64) as i64;
2508 hv_evals += 1;
2509 if cc < bc {
2510 bc = cc;
2511 bi = i;
2512 }
2513 }
2514 if hv_ring1 == i64::MIN {
2515 hv_ring1 = if bi == usize::MAX { best_c } else { bc };
2516 }
2517 if bi == usize::MAX
2518 || !self.me_subpel_iter
2519 || pat & 2 != 0
2520 || (sp_cap != 0 && _iter >= sp_cap)
2521 {
2522 if bi != usize::MAX {
2523 best_c = bc;
2524 best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2525 hv_to_best = hv_evals;
2526 }
2527 break;
2528 }
2529 best_c = bc;
2530 best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2531 hv_to_best = hv_evals;
2532 continue;
2533 }
2534 }
2535 _iter -= 1;
2536 }
2537 #[cfg(accel)]
2538 if sp_fc && step == 2 && best.0 & 3 == 0 && best.1 & 3 == 0 && pat & 1 == 0 {
2539 _iter += 1;
2540 let hp8 = hp.expect("sp_fc implies non-fast, which resolves hp");
2541 let ring8 = [
2542 (step, 0), (-step, 0), (0, step), (0, -step),
2543 (step, step), (-step, -step), (step, -step), (-step, step),
2544 ];
2545 let mut refs8: [Option<(&[u8], usize, usize)>; 8] = [None; 8];
2546 let mut all = true;
2547 for (i, &(dx, dy)) in ring8.iter().enumerate() {
2548 refs8[i] = rusty_h264_common::inter::hpel_ref(
2549 hp8, lx, ly, rw, rh, best.0 + dx, best.1 + dy,
2550 );
2551 all &= refs8[i].is_some();
2552 }
2553 if all {
2554 let stride = refs8[0].unwrap().2;
2555 let pack = |a: usize, b: usize, c2: usize, d: usize| {
2556 let g = |i: usize| {
2557 let (p, o, _) = refs8[i].unwrap();
2558 (p, o)
2559 };
2560 rusty_h264_accel::satd_16x16_x4p(
2561 src_row, cw, [g(a), g(b), g(c2), g(d)], stride,
2562 )
2563 };
2564 if let (Some(ax), Some(di)) = (pack(0, 1, 2, 3), pack(4, 5, 6, 7)) {
2565 let (mut bi, mut bc) = (usize::MAX, best_c);
2566 for i in 0..8 {
2567 let (dx, dy) = ring8[i];
2568 let mv = (best.0 + dx, best.1 + dy);
2569 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2570 let d = if i < 4 { ax[i] } else { di[i - 4] } as i64;
2571 let cc = d + (lambda_me * rate as f64) as i64;
2572 hv_evals += 1;
2573 if cc < bc {
2574 bc = cc;
2575 bi = i;
2576 }
2577 }
2578 if hv_ring1 == i64::MIN {
2579 hv_ring1 = if bi == usize::MAX { best_c } else { bc };
2580 }
2581 if bi == usize::MAX
2582 || !self.me_subpel_iter
2583 || pat & 2 != 0
2584 || (sp_cap != 0 && _iter >= sp_cap)
2585 {
2586 if bi != usize::MAX {
2587 best_c = bc;
2588 best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2589 hv_to_best = hv_evals;
2590 }
2591 break;
2592 }
2593 best_c = bc;
2594 best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2595 hv_to_best = hv_evals;
2596 continue;
2597 }
2598 }
2599 _iter -= 1; }
2601 let mut improved = false;
2602 _iter += 1;
2603 for (_pi, &(dx, dy)) in ring.iter().enumerate() {
2604 let c = (best.0 + dx, best.1 + dy);
2605 let slot = sp_slot(c);
2606 let cc = if memo_mv[slot] == c {
2607 memo_c[slot]
2608 } else {
2609 let v = cost(c);
2610 memo_mv[slot] = c;
2611 memo_c[slot] = v;
2612 v
2613 };
2614 hv_evals += 1;
2615 #[cfg(feature = "profile")]
2619 {
2620 spstats::ev(if step == 2 { 0 } else { 1 }, _pi, _iter);
2621 if seen.contains(&c) {
2622 spstats::redundant();
2623 } else {
2624 seen.push(c);
2625 }
2626 }
2627 if cc < best_c {
2628 best_c = cc;
2629 best = c;
2630 improved = true;
2631 hv_to_best = hv_evals;
2632 #[cfg(feature = "profile")]
2633 spstats::imp(if step == 2 { 0 } else { 1 }, _pi, _iter);
2634 }
2635 }
2636 if hv_ring1 == i64::MIN {
2637 hv_ring1 = best_c;
2638 }
2639 if !improved
2640 || !self.me_subpel_iter
2641 || pat & 2 != 0
2642 || (sp_cap != 0 && _iter >= sp_cap)
2643 {
2644 break;
2645 }
2646 }
2647 }
2648 if sp_dispatching {
2649 let n = self.sp_learn_n.get();
2650 if n < sp_learn {
2651 self.sp_learn_n.set(n + 1);
2652 if hv_ring1 != i64::MIN {
2653 self.sp_ring1.set(self.sp_ring1.get() + (hv_pre - hv_ring1).max(0));
2654 self.sp_total.set(self.sp_total.get() + (hv_pre - best_c).max(0));
2655 }
2656 if n + 1 == sp_learn {
2657 let tot = self.sp_total.get();
2658 self.sp_1pass.set(tot > 0 && self.sp_ring1.get() * 100 >= tot * sp_t);
2660 }
2661 }
2662 }
2663 if !subpel.is_empty() && subpel_harvest::enabled() {
2664 subpel_harvest::record(hv_pre, best_c, lambda_me, rw, rh, hv_evals, hv_to_best, hv_ring1);
2665 }
2666 if self.me_snap && seed_c < best_c {
2669 best = seed_mv;
2670 best_c = seed_c;
2671 }
2672 (best, best_c)
2673 }
2674
2675 #[allow(clippy::too_many_arguments)]
2680 #[allow(clippy::too_many_arguments)]
2685 fn encode_inter_mb(
2686 &mut self,
2687 w: &mut BitWriter,
2688 refs: &[crate::RefFrame],
2689 sy: &[u8],
2690 su: &[u8],
2691 sv: &[u8],
2692 mb_x: usize,
2693 mb_y: usize,
2694 mode: u8,
2695 parts: &[(i32, (i32, i32))],
2696 ) {
2697 if self.coded_path_v2 {
2698 self.encode_inter_mb_v2(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2699 } else {
2700 self.encode_inter_mb_v1(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2701 }
2702 }
2703
2704 #[allow(clippy::too_many_arguments)]
2712 fn encode_inter_mb_v2(
2713 &mut self,
2714 w: &mut BitWriter,
2715 refs: &[crate::RefFrame],
2716 sy: &[u8],
2717 su: &[u8],
2718 sv: &[u8],
2719 mb_x: usize,
2720 mb_y: usize,
2721 mode: u8,
2722 parts: &[(i32, (i32, i32))],
2723 ) {
2724 #[cfg(feature = "profile")]
2726 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(1);
2727 #[cfg(not(accel))]
2728 {
2729 self.encode_inter_mb_v1(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2730 }
2731 #[cfg(accel)]
2732 {
2733 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncInterCode);
2734 let (qp, qpc) = (self.qp, self.qpc);
2735 let w4 = self.mb_w * 4;
2736 let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
2737
2738 let mut pred_y = [0u8; 256];
2740 let mut c_pred = [[0u8; 64]; 2];
2741 let mut mvds = [(0i32, 0i32); 4];
2742 let mut n_mvd = 0;
2743 let _g_mc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
2744 for (part, &(rx, ry, rw, rh)) in inter_partitions(mode).iter().enumerate() {
2745 let (refi, mv) = parts[part];
2746 let reference = &refs[refi as usize];
2747 let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
2748 let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
2749 let pmv = predict_partition_mv(mode, part, a, b, c, refi);
2750 mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
2751 n_mvd += 1;
2752 for by in ry / 4..ry / 4 + rh / 4 {
2753 for bx in rx / 4..rx / 4 + rw / 4 {
2754 let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
2755 self.mv_y[idx] = mv;
2756 self.inter_y[idx] = true;
2757 self.ref_idx_y[idx] = refi;
2758 self.coded_y[idx] = true;
2759 }
2760 }
2761 if rw == 16 && rh == 16 {
2762 self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
2763 } else {
2764 let mut tmp = [0u8; 256];
2765 self.mc_luma_cached(reference, mb_x * 16 + rx, mb_y * 16 + ry, rw, rh, mv.0, mv.1, &mut tmp);
2766 for dy in 0..rh {
2767 for dx in 0..rw {
2768 pred_y[(ry + dy) * 16 + (rx + dx)] = tmp[dy * rw + dx];
2769 }
2770 }
2771 }
2772 let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
2773 for cc in 0..2 {
2774 let rc = if cc == 0 { &reference.u } else { &reference.v };
2775 if crw == 8 && crh == 8 {
2776 mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut c_pred[cc]);
2777 } else {
2778 let mut tc = [0u8; 64];
2779 mc_chroma(rc, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv.0, mv.1, &mut tc);
2780 for dy in 0..crh {
2781 for dx in 0..crw {
2782 c_pred[cc][(cry + dy) * 8 + (crx + dx)] = tc[dy * crw + dx];
2783 }
2784 }
2785 }
2786 }
2787 }
2788
2789 let mut dctw = AlignedDct([0i16; 256]);
2791 let dct = &mut dctw.0;
2792 let mut cbp_luma = 0u32;
2793 drop(_g_mc);
2794 let _g_tq = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncTq);
2795 let base = mb_y * 16 * self.cw + mb_x * 16;
2796 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
2797 rusty_h264_accel::dct_four_t4(
2798 &mut dct[qi * 64..qi * 64 + 64],
2799 &sy[base + qy * self.cw + qx..],
2800 self.cw,
2801 &pred_y[qy * 16 + qx..],
2802 16,
2803 );
2804 }
2805 let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
2806 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
2807 for qi in 0..4 {
2808 rusty_h264_accel::quant_four_4x4(&mut dct[qi * 64..qi * 64 + 64], &ff, mf);
2809 }
2810 for blk in 0..16 {
2812 if dct[blk * 16..blk * 16 + 16].iter().any(|&v| v != 0) {
2813 cbp_luma |= 1 << (blk / 4);
2814 }
2815 }
2816
2817 let mut c_dc_levels = [[0i32; 4]; 2];
2819 let mut c_recon_dc = [[0i32; 4]; 2];
2820 let mut c_q = [[[0i32; 16]; 4]; 2];
2821 let (mut any_ac, mut any_dc) = (false, false);
2822 for c in 0..2 {
2823 let src = if c == 0 { su } else { sv };
2824 let dc2x2 = {
2825 #[repr(align(16))]
2826 struct A([i16; 64]);
2827 let mut cdct = A([0i16; 64]);
2828 rusty_h264_accel::dct_four_t4(
2829 &mut cdct.0,
2830 &src[(mb_y * 8) * self.ccw + mb_x * 8..],
2831 self.ccw,
2832 &c_pred[c],
2833 8,
2834 );
2835 let dc = [cdct.0[0] as i32, cdct.0[16] as i32, cdct.0[32] as i32, cdct.0[48] as i32];
2836 let ffc = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
2837 let mfc = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
2838 rusty_h264_accel::quant_four_4x4(&mut cdct.0, &ffc, mfc);
2839 for i in 0..4 {
2840 let q = &mut c_q[c][i];
2841 q[0] = 0;
2842 for j in 1..16 {
2843 let v = cdct.0[i * 16 + j] as i32;
2844 q[j] = v;
2845 if v != 0 {
2846 any_ac = true;
2847 }
2848 }
2849 }
2850 dc
2851 };
2852 let dl = forward_quant_chroma_dc(&dc2x2, qpc, false);
2853 if dl.iter().any(|&v| v != 0) {
2854 any_dc = true;
2855 }
2856 c_recon_dc[c] = inverse_quant_chroma_dc(&dl, qpc);
2857 c_dc_levels[c] = dl;
2858 }
2859 let cbp_chroma: u32 = if any_ac { 2 } else if any_dc { 1 } else { 0 };
2860 let cbp = cbp_luma | (cbp_chroma << 4);
2861
2862 drop(_g_tq);
2864 let _g_syn = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
2865 w.write_ue(mode as u32);
2866 let num_refs = refs.len();
2867 if num_refs > 1 {
2868 for &(refi, _) in parts {
2869 write_ref_idx(w, refi, num_refs);
2870 }
2871 }
2872 for &(mvdx, mvdy) in &mvds[..n_mvd] {
2873 w.write_se(mvdx);
2874 w.write_se(mvdy);
2875 }
2876 write_cbp_inter(w, cbp);
2877 if cbp != 0 {
2878 w.write_se(self.qp_delta()); }
2880 self.nnz_cache_load(mb_x, mb_y);
2881 drop(_g_syn);
2882
2883 let _g_scan = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Scatter);
2885 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
2886 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
2887 let total = if cbp_luma & (1 << (blk / 4)) != 0 {
2888 let nc = self.nc_pred(lbx, lby);
2889 let scan16 = scan_4x4_dcac_i16(&dct[blk * 16..blk * 16 + 16]);
2890 encode_residual_block(w, &scan16, 16, nc) as u8
2891 } else {
2892 0
2893 };
2894 self.nnz_cache_set(lbx, lby, total);
2895 self.nnz_y[by * w4 + bx] = total;
2896 }
2897 if cbp_chroma != 0 {
2898 for c in 0..2 {
2899 encode_residual_block(w, &c_dc_levels[c], 4, -1);
2900 }
2901 }
2902 if cbp_chroma == 2 {
2903 self.chroma_cache_load(mb_x, mb_y);
2904 let w2 = self.mb_w * 2;
2905 for c in 0..2 {
2906 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
2907 let nc = self.chroma_nc_pred(c, bx, by);
2908 let ac = scan_4x4_ac(&c_q[c][by * 2 + bx]);
2909 let total = encode_residual_block(w, &ac, 15, nc) as u8;
2910 self.chroma_nnz_cache_set(c, bx, by, total);
2911 self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
2912 }
2913 }
2914 }
2915 drop(_g_scan);
2916
2917 let _g_rec = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
2919 #[repr(align(16))]
2920 struct Align16([i16; 64]);
2921 let mut dct_in = Align16([0i16; 64]);
2922 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
2923 let rec_off = base + qy * self.cw + qx;
2924 if cbp_luma & (1 << qi) == 0 {
2925 for r in 0..8 {
2926 let (dsti, srci) = (rec_off + r * self.cw, (qy + r) * 16 + qx);
2927 self.rec_y[dsti..dsti + 8].copy_from_slice(&pred_y[srci..srci + 8]);
2928 }
2929 continue;
2930 }
2931 for k in 0..4 {
2932 let blk = qi * 4 + k;
2933 let mut lvl = [0i32; 16];
2934 for i in 0..16 {
2935 lvl[i] = dct[blk * 16 + i] as i32;
2936 }
2937 let deq = dequantize(&lvl, qp);
2938 for i in 0..16 {
2939 dct_in.0[k * 16 + i] = deq[i] as i16;
2940 }
2941 }
2942 rusty_h264_accel::idct_four_t4_rec(
2943 &mut self.rec_y[rec_off..],
2944 self.cw,
2945 &pred_y[qy * 16 + qx..],
2946 16,
2947 &dct_in.0,
2948 );
2949 }
2950 for c in 0..2 {
2952 let base_c = (mb_y * 8) * self.ccw + mb_x * 8;
2953 let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
2954 if cbp_chroma == 0 {
2955 for r in 0..8 {
2956 let dsti = base_c + r * self.ccw;
2957 plane[dsti..dsti + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
2958 }
2959 } else {
2960 #[repr(align(16))]
2961 struct A([i16; 64]);
2962 let mut d = A([0i16; 64]);
2963 for i in 0..4 {
2964 let deq = dequantize(&c_q[c][i], qpc);
2965 for j in 0..16 {
2966 d.0[i * 16 + j] = deq[j] as i16;
2967 }
2968 d.0[i * 16] = c_recon_dc[c][i] as i16;
2969 }
2970 rusty_h264_accel::idct_four_t4_rec(&mut plane[base_c..], self.ccw, &c_pred[c], 8, &d.0);
2971 }
2972 }
2973 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
2974 self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
2975 }
2976 }
2977 }
2978
2979 #[allow(clippy::too_many_arguments)]
2980 fn encode_inter_mb_v1(
2981 &mut self,
2982 w: &mut BitWriter,
2983 refs: &[crate::RefFrame],
2984 sy: &[u8],
2985 su: &[u8],
2986 sv: &[u8],
2987 mb_x: usize,
2988 mb_y: usize,
2989 mode: u8,
2990 parts: &[(i32, (i32, i32))],
2991 ) {
2992 self.encode_inter_mb_v1_b(w, refs, sy, su, sv, mb_x, mb_y, mode, parts, None);
2993 }
2994
2995 #[allow(clippy::too_many_arguments)]
3006 fn plan_inter_mb(
3007 &mut self,
3008 refs: &[crate::RefFrame],
3009 sy: &[u8],
3010 su: &[u8],
3011 sv: &[u8],
3012 mb_x: usize,
3013 mb_y: usize,
3014 mode: u8,
3015 parts: &[(i32, (i32, i32))],
3016 bspec: Option<BInter>,
3017 ) -> InterPlan {
3018 #[cfg(feature = "profile")]
3020 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(1);
3021 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncInterCode);
3022 let (qp, qpc) = (self.qp, self.qpc);
3023 let w4 = self.mb_w * 4;
3024 let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
3025
3026 let mut pred_y = [0u8; 256];
3028 let mut c_pred = [[0u8; 64]; 2];
3029 let mut mvds = [(0i32, 0i32); 4]; let mut plan_refs = [0i32; 4]; let mut n_mvd = 0;
3032 let _g_mc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
3033 if let Some(b) = bspec.filter(|b| b.dir == 0) {
3034 let (dp, dc, motion) = self.b_direct(&refs[0], b.l1, mb_x, mb_y);
3036 pred_y = dp;
3037 c_pred = dc;
3038 self.commit_direct_motion(mb_x, mb_y, &motion);
3039 } else if let Some(b) = bspec {
3040 let use0 = b.dir == 1 || b.dir == 3;
3042 let use1 = b.dir == 2 || b.dir == 3;
3043 let (lx, ly) = (mb_x * 16, mb_y * 16);
3044 let (cx, cy) = (mb_x * 8, mb_y * 8);
3045 let (pbx, pby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
3046 if use0 {
3048 let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, 4, 0);
3049 let p = predict_partition_mv(0, 0, a, c0, c1, 0);
3050 mvds[n_mvd] = (b.mv0.0 - p.0, b.mv0.1 - p.1);
3051 n_mvd += 1;
3052 }
3053 if use1 {
3054 let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, 4, 1);
3055 let p = predict_partition_mv(0, 0, a, c0, c1, 0);
3056 mvds[n_mvd] = (b.mv1.0 - p.0, b.mv1.1 - p.1);
3057 n_mvd += 1;
3058 }
3059 let mut a_y = [0u8; 256];
3062 let mut b_y = [0u8; 256];
3063 let mut a_c = [[0u8; 64]; 2];
3064 let mut b_c = [[0u8; 64]; 2];
3065 if use0 {
3066 mc_luma(&refs[0].y, self.cw, ch, lx, ly, 16, 16, b.mv0.0, b.mv0.1, &mut a_y);
3067 mc_chroma(&refs[0].u, self.ccw, cch, cx, cy, 8, 8, b.mv0.0, b.mv0.1, &mut a_c[0]);
3068 mc_chroma(&refs[0].v, self.ccw, cch, cx, cy, 8, 8, b.mv0.0, b.mv0.1, &mut a_c[1]);
3069 }
3070 if use1 {
3071 mc_luma(&b.l1.y, self.cw, ch, lx, ly, 16, 16, b.mv1.0, b.mv1.1, &mut b_y);
3072 mc_chroma(&b.l1.u, self.ccw, cch, cx, cy, 8, 8, b.mv1.0, b.mv1.1, &mut b_c[0]);
3073 mc_chroma(&b.l1.v, self.ccw, cch, cx, cy, 8, 8, b.mv1.0, b.mv1.1, &mut b_c[1]);
3074 }
3075 match (use0, use1) {
3076 (true, true) => {
3077 for i in 0..256 {
3078 pred_y[i] = bi_blend(a_y[i] as i32, b_y[i] as i32, self.bi_w);
3079 }
3080 for c in 0..2 {
3081 for i in 0..64 {
3082 c_pred[c][i] = bi_blend(a_c[c][i] as i32, b_c[c][i] as i32, self.bi_w);
3083 }
3084 }
3085 }
3086 (true, false) => {
3087 pred_y = a_y;
3088 c_pred = a_c;
3089 }
3090 _ => {
3091 pred_y = b_y;
3092 c_pred = b_c;
3093 }
3094 }
3095 for by in 0..4 {
3097 for bx in 0..4 {
3098 let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
3099 self.inter_y[idx] = true;
3100 self.coded_y[idx] = true;
3101 self.mv_y[idx] = if use0 { b.mv0 } else { (0, 0) };
3102 self.ref_idx_y[idx] = if use0 { 0 } else { -1 };
3103 self.mv1_y[idx] = if use1 { b.mv1 } else { (0, 0) };
3104 self.ref_idx1_y[idx] = if use1 { 0 } else { -1 };
3105 }
3106 }
3107 } else {
3108 for (part, &(rx, ry, rw, rh)) in inter_partitions(mode).iter().enumerate() {
3109 let (refi, mv) = parts[part];
3110 plan_refs[part] = refi; let reference = &refs[refi as usize];
3112 let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
3113 let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
3114 let pmv = predict_partition_mv(mode, part, a, b, c, refi);
3115 mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
3116 n_mvd += 1;
3117 for by in ry / 4..ry / 4 + rh / 4 {
3119 for bx in rx / 4..rx / 4 + rw / 4 {
3120 let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
3121 self.mv_y[idx] = mv;
3122 self.inter_y[idx] = true;
3123 self.ref_idx_y[idx] = refi;
3124 self.coded_y[idx] = true;
3125 }
3126 }
3127 if rw == 16 && rh == 16 {
3130 self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
3131 } else {
3132 let mut tmp = [0u8; 256];
3133 self.mc_luma_cached(reference, mb_x * 16 + rx, mb_y * 16 + ry, rw, rh, mv.0, mv.1, &mut tmp);
3134 for dy in 0..rh {
3135 for dx in 0..rw {
3136 pred_y[(ry + dy) * 16 + (rx + dx)] = tmp[dy * rw + dx];
3137 }
3138 }
3139 }
3140 let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
3142 for cc in 0..2 {
3143 let rc = if cc == 0 { &reference.u } else { &reference.v };
3144 if crw == 8 && crh == 8 {
3145 mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut c_pred[cc]);
3146 } else {
3147 let mut tc = [0u8; 64];
3148 mc_chroma(rc, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv.0, mv.1, &mut tc);
3149 for dy in 0..crh {
3150 for dx in 0..crw {
3151 c_pred[cc][(cry + dy) * 8 + (crx + dx)] = tc[dy * crw + dx];
3152 }
3153 }
3154 }
3155 }
3156 }
3157 } let mut q_blocks = [[0i32; 16]; 16]; let mut cbp_luma = 0u32;
3162 #[allow(unused_mut)]
3165 let mut t8x8 = false;
3166 #[allow(unused_mut)]
3167 let mut q8 = [[0i32; 64]; 4];
3168 drop(_g_mc);
3169 let _g_tq = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncTq);
3170 #[cfg(accel)]
3171 {
3172 let mut dctw = AlignedDct([0i16; 256]);
3177 let dct = &mut dctw.0;
3178 let base = mb_y * 16 * self.cw + mb_x * 16;
3179 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3180 rusty_h264_accel::dct_four_t4(
3181 &mut dct[qi * 64..qi * 64 + 64],
3182 &sy[base + qy * self.cw + qx..],
3183 self.cw,
3184 &pred_y[qy * 16 + qx..],
3185 16,
3186 );
3187 }
3188 let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
3189 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
3190 for qi in 0..4 {
3191 rusty_h264_accel::quant_four_4x4(&mut dct[qi * 64..qi * 64 + 64], &ff, mf);
3192 }
3193 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3194 let mut nz = false;
3195 for i in 0..16 {
3196 let v = dct[blk * 16 + i] as i32;
3197 q_blocks[lby * 4 + lbx][i] = v;
3198 nz |= v != 0;
3199 }
3200 if nz {
3201 cbp_luma |= 1 << (blk / 4);
3202 }
3203 }
3204 }
3205 #[cfg(not(accel))]
3206 {
3207 let mut res_blocks = [[0i32; 16]; 16]; for lby in 0..4 {
3210 for lbx in 0..4 {
3211 let b = &mut res_blocks[lby * 4 + lbx];
3212 for dy in 0..4 {
3213 for dx in 0..4 {
3214 let sx = mb_x * 16 + lbx * 4 + dx;
3215 let syy = mb_y * 16 + lby * 4 + dy;
3216 b[dy * 4 + dx] = sy[syy * self.cw + sx] as i32
3217 - pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3218 }
3219 }
3220 }
3221 }
3222 let mut coeffs = [[0i32; 16]; 16];
3223 forward_dct_blocks(&res_blocks, &mut coeffs);
3224 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3225 let q = rdoq(&coeffs[lby * 4 + lbx], qp, 6, self.rdoq_strength, 0);
3226 if q.iter().any(|&v| v != 0) {
3227 cbp_luma |= 1 << (blk / 4);
3228 }
3229 q_blocks[lby * 4 + lbx] = q;
3230 }
3231 }
3232
3233 {
3239 if self.transform_8x8 && self.inter8x8 != 0 {
3240 let lambda =
3241 0.85 * self.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
3242 let mut ssd4 = 0i64;
3243 let mut rate4 = 0f64;
3244 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3245 let mut predb = [0i32; 16];
3246 for dy in 0..4 {
3247 for dx in 0..4 {
3248 predb[dy * 4 + dx] =
3249 pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3250 }
3251 }
3252 let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3253 let s = reconstruct_4x4(&deq, &predb);
3254 for dy in 0..4 {
3255 for dx in 0..4 {
3256 let sx = mb_x * 16 + lbx * 4 + dx;
3257 let syy = mb_y * 16 + lby * 4 + dy;
3258 let d = s[dy * 4 + dx] as i64 - sy[syy * self.cw + sx] as i64;
3259 ssd4 += d * d;
3260 }
3261 }
3262 for &l in &q_blocks[lby * 4 + lbx] {
3263 if l != 0 {
3264 rate4 += rdoq_rate((l as i64).abs());
3265 }
3266 }
3267 }
3268 let (q8c, cbp8, rate8, _rec8, ssd8) =
3269 plan_inter8_luma(sy, self.cw, mb_x, mb_y, &pred_y, qp);
3270 let j4 = ssd4 as f64 + lambda * (rate4 + 16.0);
3273 let j8 = ssd8 as f64 + lambda * (rate8 + 16.0 + self.inter8_pen as f64);
3274 if cbp8 > 0 && j8 < j4 {
3275 t8x8 = true;
3276 cbp_luma = cbp8;
3277 q8 = q8c;
3278 }
3279 }
3280 }
3281
3282 let mut c_dc_levels = [[0i32; 4]; 2];
3284 let mut c_recon_dc = [[0i32; 4]; 2];
3285 let mut c_q = [[[0i32; 16]; 4]; 2];
3286 let (mut any_ac, mut any_dc) = (false, false);
3287 for c in 0..2 {
3288 let src = if c == 0 { su } else { sv };
3289 #[cfg(accel)]
3295 let (mut dc2x2, applied) = {
3296 #[repr(align(16))]
3297 struct A([i16; 64]);
3298 let mut dct = A([0i16; 64]);
3299 rusty_h264_accel::dct_four_t4(
3300 &mut dct.0,
3301 &src[(mb_y * 8) * self.ccw + mb_x * 8..],
3302 self.ccw,
3303 &c_pred[c],
3304 8,
3305 );
3306 let dc = [
3307 dct.0[0] as i32,
3308 dct.0[16] as i32,
3309 dct.0[32] as i32,
3310 dct.0[48] as i32,
3311 ];
3312 let ffc = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
3313 let mfc = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
3314 rusty_h264_accel::quant_four_4x4(&mut dct.0, &ffc, mfc);
3315 for i in 0..4 {
3316 let q = &mut c_q[c][i];
3317 q[0] = 0;
3318 for j in 1..16 {
3319 let v = dct.0[i * 16 + j] as i32;
3320 q[j] = v;
3321 if v != 0 {
3322 any_ac = true;
3323 }
3324 }
3325 }
3326 (dc, true)
3327 };
3328 #[cfg(not(accel))]
3329 let (mut dc2x2, applied) = ([0i32; 4], false);
3330 if !applied {
3331 let mut res_blocks = [[0i32; 16]; 4];
3333 for by in 0..2 {
3334 for bx in 0..2 {
3335 let b = &mut res_blocks[by * 2 + bx];
3336 for dy in 0..4 {
3337 for dx in 0..4 {
3338 let sx = mb_x * 8 + bx * 4 + dx;
3339 let syy = mb_y * 8 + by * 4 + dy;
3340 b[dy * 4 + dx] = src[syy * self.ccw + sx] as i32
3341 - c_pred[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
3342 }
3343 }
3344 }
3345 }
3346 let mut coeffs = [[0i32; 16]; 4];
3347 forward_dct_blocks(&res_blocks, &mut coeffs);
3348 for i in 0..4 {
3349 dc2x2[i] = coeffs[i][0];
3350 let mut q = rdoq(&coeffs[i], qpc, 6, self.rdoq_strength, 1);
3351 q[0] = 0;
3352 if q[1..].iter().any(|&v| v != 0) {
3353 any_ac = true;
3354 }
3355 c_q[c][i] = q;
3356 }
3357 }
3358 let dl = forward_quant_chroma_dc(&dc2x2, qpc, false);
3359 if dl.iter().any(|&v| v != 0) {
3360 any_dc = true;
3361 }
3362 c_recon_dc[c] = inverse_quant_chroma_dc(&dl, qpc);
3363 c_dc_levels[c] = dl;
3364 }
3365 let cbp_chroma: u32 = if any_ac { 2 } else if any_dc { 1 } else { 0 };
3366 let cbp = cbp_luma | (cbp_chroma << 4);
3367
3368 drop(_g_tq);
3369 let _g_rec = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
3370 #[cfg(accel)]
3372 if t8x8 {
3373 let weight = [16i32; 64];
3376 for b8 in 0..4usize {
3377 let (b8x, b8y) = (b8 % 2, b8 / 2);
3378 let res_r = inverse_quant_8x8(&q8[b8], qp, &weight);
3379 let predb: [i32; 64] = std::array::from_fn(|i| {
3380 pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
3381 });
3382 let recon = add_residual_8x8(&res_r, &predb);
3383 for dy in 0..8 {
3384 for dx in 0..8 {
3385 let px = mb_x * 16 + b8x * 8 + dx;
3386 let py = mb_y * 16 + b8y * 8 + dy;
3387 self.rec_y[py * self.cw + px] = recon[dy * 8 + dx];
3388 }
3389 }
3390 }
3391 } else {
3392 #[repr(align(16))]
3401 struct Align16([i16; 64]);
3402 let mut dct_in = Align16([0i16; 64]);
3403 let base = mb_y * 16 * self.cw + mb_x * 16;
3404 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3405 let rec_off = base + qy * self.cw + qx;
3406 if cbp_luma & (1 << qi) == 0 {
3407 for r in 0..8 {
3408 let (dsti, srci) = (rec_off + r * self.cw, (qy + r) * 16 + qx);
3409 self.rec_y[dsti..dsti + 8].copy_from_slice(&pred_y[srci..srci + 8]);
3410 }
3411 continue;
3412 }
3413 for k in 0..4 {
3414 let blk = qi * 4 + k;
3415 let (lbx, lby) = LUMA_4X4_SCAN_XY[blk];
3416 let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3417 for i in 0..16 {
3418 dct_in.0[k * 16 + i] = deq[i] as i16;
3419 }
3420 }
3421 rusty_h264_accel::idct_four_t4_rec(
3422 &mut self.rec_y[rec_off..],
3423 self.cw,
3424 &pred_y[qy * 16 + qx..],
3425 16,
3426 &dct_in.0,
3427 );
3428 }
3429 }
3430 #[cfg(not(accel))]
3431 if t8x8 {
3432 let weight = [16i32; 64];
3434 for b8 in 0..4usize {
3435 let (b8x, b8y) = (b8 % 2, b8 / 2);
3436 let res_r = inverse_quant_8x8(&q8[b8], qp, &weight);
3437 let predb: [i32; 64] = std::array::from_fn(|i| {
3438 pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
3439 });
3440 let recon = add_residual_8x8(&res_r, &predb);
3441 for dy in 0..8 {
3442 for dx in 0..8 {
3443 let px = mb_x * 16 + b8x * 8 + dx;
3444 let py = mb_y * 16 + b8y * 8 + dy;
3445 self.rec_y[py * self.cw + px] = recon[dy * 8 + dx];
3446 }
3447 }
3448 }
3449 } else {
3450 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3451 let mut predb = [0i32; 16];
3452 for dy in 0..4 {
3453 for dx in 0..4 {
3454 predb[dy * 4 + dx] = pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3455 }
3456 }
3457 let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3458 let s = reconstruct_4x4(&deq, &predb);
3459 store(&mut self.rec_y, self.cw, mb_x * 16 + lbx * 4, mb_y * 16 + lby * 4, &s);
3460 }
3461 }
3462 for c in 0..2 {
3463 #[cfg(accel)]
3468 {
3469 let base = (mb_y * 8) * self.ccw + mb_x * 8;
3470 let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3471 if cbp_chroma == 0 {
3472 for r in 0..8 {
3474 let dsti = base + r * self.ccw;
3475 plane[dsti..dsti + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
3476 }
3477 } else {
3478 #[repr(align(16))]
3479 struct A([i16; 64]);
3480 let mut d = A([0i16; 64]);
3481 for i in 0..4 {
3482 let deq = dequantize(&c_q[c][i], qpc);
3483 for j in 0..16 {
3484 d.0[i * 16 + j] = deq[j] as i16;
3485 }
3486 d.0[i * 16] = c_recon_dc[c][i] as i16;
3487 }
3488 rusty_h264_accel::idct_four_t4_rec(&mut plane[base..], self.ccw, &c_pred[c], 8, &d.0);
3489 }
3490 }
3491 #[cfg(not(accel))]
3492 {
3493 let mut deq_blocks = [[0i32; 16]; 4];
3496 for i in 0..4 {
3497 deq_blocks[i] = dequantize(&c_q[c][i], qpc);
3498 deq_blocks[i][0] = c_recon_dc[c][i];
3499 }
3500 let mut res = [[0i32; 16]; 4];
3501 inverse_dct_blocks(&deq_blocks, &mut res);
3502 let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3503 for by in 0..2 {
3504 for bx in 0..2 {
3505 let mut predb = [0i32; 16];
3506 for dy in 0..4 {
3507 for dx in 0..4 {
3508 predb[dy * 4 + dx] = c_pred[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
3509 }
3510 }
3511 let s = add_residual_4x4(&res[by * 2 + bx], &predb);
3512 store(plane, self.ccw, mb_x * 8 + bx * 4, mb_y * 8 + by * 4, &s);
3513 }
3514 }
3515 }
3516 }
3517 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3519 self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
3520 }
3521 InterPlan { mvds, plan_refs, n_mvd, cbp, q_blocks, c_dc_levels, c_q, t8x8, q8 }
3522 }
3523
3524 #[allow(clippy::too_many_arguments)]
3527 fn encode_inter_mb_v1_b(
3528 &mut self,
3529 w: &mut BitWriter,
3530 refs: &[crate::RefFrame],
3531 sy: &[u8],
3532 su: &[u8],
3533 sv: &[u8],
3534 mb_x: usize,
3535 mb_y: usize,
3536 mode: u8,
3537 parts: &[(i32, (i32, i32))],
3538 bspec: Option<BInter>,
3539 ) {
3540 let plan = self.plan_inter_mb(refs, sy, su, sv, mb_x, mb_y, mode, parts, bspec);
3541 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
3542 self.emit_inter_cavlc(w, refs.len(), mb_x, mb_y, mode, parts, bspec, &plan);
3543 }
3544
3545 #[allow(clippy::too_many_arguments)]
3547 fn emit_inter_cavlc(
3548 &mut self,
3549 w: &mut BitWriter,
3550 num_refs: usize,
3551 mb_x: usize,
3552 mb_y: usize,
3553 mode: u8,
3554 parts: &[(i32, (i32, i32))],
3555 bspec: Option<BInter>,
3556 plan: &InterPlan,
3557 ) {
3558 let w4 = self.mb_w * 4;
3559 let (cbp, cbp_luma, cbp_chroma) = (plan.cbp, plan.cbp & 15, plan.cbp >> 4);
3560 w.write_ue(bspec.map_or(mode as u32, |b| b.dir as u32)); if mode == 3 {
3567 for _ in 0..4 {
3568 w.write_ue(0);
3569 }
3570 }
3571 if num_refs > 1 {
3572 for &(refi, _) in parts {
3573 write_ref_idx(w, refi, num_refs);
3574 }
3575 }
3576 for &(mvdx, mvdy) in &plan.mvds[..plan.n_mvd] {
3577 w.write_se(mvdx);
3578 w.write_se(mvdy);
3579 }
3580 write_cbp_inter(w, cbp);
3581 if cbp_luma > 0 && self.transform_8x8 {
3585 w.write_bit(plan.t8x8);
3586 }
3587 if cbp != 0 {
3588 w.write_se(self.qp_delta()); }
3590 self.nnz_cache_load(mb_x, mb_y);
3591 if plan.t8x8 {
3592 for b8 in 0..4usize {
3596 let (b8x, b8y) = (b8 % 2, b8 / 2);
3597 let scan8 = scan_8x8_fwd(&plan.q8[b8]);
3598 for sub in 0..4usize {
3599 let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
3600 let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
3601 let total = if cbp_luma & (1 << b8) != 0 {
3602 let nc = self.nc_pred(cx, cy);
3603 let blk: [i32; 16] = std::array::from_fn(|k| scan8[4 * k + sub]);
3604 encode_residual_block(w, &blk, 16, nc) as u8
3605 } else {
3606 0
3607 };
3608 self.nnz_cache_set(cx, cy, total);
3609 self.nnz_y[by * w4 + bx] = total;
3610 }
3611 }
3612 } else {
3613 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3614 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
3615 let total = if cbp_luma & (1 << (blk / 4)) != 0 {
3616 let nc = self.nc_pred(lbx, lby);
3617 let scan16 = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
3618 encode_residual_block(w, &scan16, 16, nc) as u8
3619 } else {
3620 0
3621 };
3622 self.nnz_cache_set(lbx, lby, total);
3623 self.nnz_y[by * w4 + bx] = total;
3624 }
3625 }
3626 if cbp_chroma != 0 {
3627 for c in 0..2 {
3628 encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
3629 }
3630 }
3631 if cbp_chroma == 2 {
3632 self.chroma_cache_load(mb_x, mb_y);
3633 let w2 = self.mb_w * 2;
3634 for c in 0..2 {
3635 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
3636 let nc = self.chroma_nc_pred(c, bx, by);
3637 let ac = scan_4x4_ac(&plan.c_q[c][by * 2 + bx]);
3638 let total = encode_residual_block(w, &ac, 15, nc) as u8;
3639 self.chroma_nnz_cache_set(c, bx, by, total);
3640 self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
3641 }
3642 }
3643 }
3644 }
3645
3646 #[inline]
3656 fn mc_luma_cached(
3657 &self,
3658 reference: &crate::RefFrame,
3659 x0: usize,
3660 y0: usize,
3661 bw: usize,
3662 bh: usize,
3663 mvx: i32,
3664 mvy: i32,
3665 out: &mut [u8],
3666 ) {
3667 let ch = self.mb_h * 16;
3668 let cw = self.cw;
3669 if !self.fast {
3670 let p = reference.hpel(cw, ch);
3671 if rusty_h264_common::inter::hpel_block(p, x0, y0, bw, bh, mvx, mvy, out) {
3672 return;
3673 }
3674 if let Some((plane, base, stride)) =
3675 rusty_h264_common::inter::hpel_ref(p, x0, y0, bw, bh, mvx, mvy)
3676 {
3677 for r in 0..bh {
3678 out[r * bw..r * bw + bw].copy_from_slice(&plane[base + r * stride..][..bw]);
3679 }
3680 return;
3681 }
3682 }
3683 mc_luma(&reference.y, cw, ch, x0, y0, bw, bh, mvx, mvy, out);
3684 }
3685
3686 fn skip_predict_luma(
3692 &self,
3693 refs: &[crate::RefFrame],
3694 mb_x: usize,
3695 mb_y: usize,
3696 mv: (i32, i32),
3697 ) -> [u8; 256] {
3698 #[cfg(feature = "profile")]
3700 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(3);
3701 let reference = &refs[0]; let ch = self.mb_h * 16;
3703 let mut pred_y = [0u8; 256];
3704 self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
3705 pred_y
3706 }
3707
3708 fn skip_predict_chroma(
3710 &self,
3711 refs: &[crate::RefFrame],
3712 mb_x: usize,
3713 mb_y: usize,
3714 mv: (i32, i32),
3715 ) -> [[u8; 64]; 2] {
3716 let reference = &refs[0];
3717 let cch = self.mb_h * 8;
3718 let mut pred_c = [[0u8; 64]; 2];
3719 for c in 0..2 {
3720 let rc = if c == 0 { &reference.u } else { &reference.v };
3721 mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut pred_c[c]);
3722 }
3723 pred_c
3724 }
3725
3726 fn skip_luma_is_free(&self, sy: &[u8], mb_x: usize, mb_y: usize, pred_y: &[u8; 256]) -> bool {
3731 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFree);
3732 let qp = self.qp;
3733 #[cfg(accel)]
3740 if self.skip_accel_check {
3741 #[repr(align(16))]
3742 struct Align16([i16; 64]);
3743 let mut dct = Align16([0i16; 64]);
3744 let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
3745 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
3746 for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
3747 rusty_h264_accel::dct_four_t4(
3748 &mut dct.0,
3749 &sy[(mb_y * 16 + qy) * self.cw + mb_x * 16 + qx..],
3750 self.cw,
3751 &pred_y[qy * 16 + qx..],
3752 16,
3753 );
3754 rusty_h264_accel::quant_four_4x4(&mut dct.0, &ff, mf);
3755 if dct.0.iter().any(|&v| v != 0) {
3756 return false;
3757 }
3758 }
3759 return true;
3760 }
3761 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
3767 let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
3768 let mut t_min = i32::MAX;
3769 for p in 0..8 {
3770 let t = (65536 + mf[p] as i32 - 1) / mf[p] as i32 - ff[p] as i32;
3771 t_min = t_min.min(t);
3772 }
3773 let t_dc = (65536 + mf[0] as i32 - 1) / mf[0] as i32 - ff[0] as i32;
3774 for by in 0..4 {
3779 for bx in 0..4 {
3780 let mut res = [0i32; 16];
3781 let (mut sad, mut dc) = (0i32, 0i32);
3782 for dy in 0..4 {
3783 for dx in 0..4 {
3784 let sx = mb_x * 16 + bx * 4 + dx;
3785 let syy = mb_y * 16 + by * 4 + dy;
3786 let d = sy[syy * self.cw + sx] as i32
3787 - pred_y[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
3788 res[dy * 4 + dx] = d;
3789 sad += d.abs();
3790 dc += d;
3791 }
3792 }
3793 if 4 * sad < t_min {
3794 continue; }
3796 if dc.abs() >= t_dc {
3797 return false; }
3799 if quantize(&forward_core(&res), qp, 6).iter().any(|&v| v != 0) {
3800 return false;
3801 }
3802 }
3803 }
3804 true
3805 }
3806
3807 fn skip_chroma_is_free(
3809 &self,
3810 su: &[u8],
3811 sv: &[u8],
3812 mb_x: usize,
3813 mb_y: usize,
3814 pred_c: &[[u8; 64]; 2],
3815 ) -> bool {
3816 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFree);
3817 let qpc = self.qpc;
3818 #[cfg(accel)]
3824 if self.skip_accel_check {
3825 #[repr(align(16))]
3826 struct Align16C([i16; 64]);
3827 let mut dct = Align16C([0i16; 64]);
3828 let ff = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
3829 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
3830 for c in 0..2 {
3831 let src = if c == 0 { su } else { sv };
3832 rusty_h264_accel::dct_four_t4(
3833 &mut dct.0,
3834 &src[(mb_y * 8) * self.ccw + mb_x * 8..],
3835 self.ccw,
3836 &pred_c[c],
3837 8,
3838 );
3839 let dc2x2 = [
3840 dct.0[0] as i32,
3841 dct.0[16] as i32,
3842 dct.0[32] as i32,
3843 dct.0[48] as i32,
3844 ];
3845 rusty_h264_accel::quant_four_4x4(&mut dct.0, &ff, mf);
3846 for b in 0..4 {
3847 if dct.0[b * 16 + 1..b * 16 + 16].iter().any(|&v| v != 0) {
3848 return false;
3849 }
3850 }
3851 if forward_quant_chroma_dc(&dc2x2, qpc, false).iter().any(|&v| v != 0) {
3852 return false;
3853 }
3854 }
3855 return true;
3856 }
3857 for c in 0..2 {
3858 let src = if c == 0 { su } else { sv };
3859 let mut dc2x2 = [0i32; 4];
3860 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
3861 let mut res = [0i32; 16];
3862 for dy in 0..4 {
3863 for dx in 0..4 {
3864 let sx = mb_x * 8 + bx * 4 + dx;
3865 let syy = mb_y * 8 + by * 4 + dy;
3866 res[dy * 4 + dx] = src[syy * self.ccw + sx] as i32
3867 - pred_c[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
3868 }
3869 }
3870 let coeffs = forward_core(&res);
3871 dc2x2[by * 2 + bx] = coeffs[0];
3872 if quantize(&coeffs, qpc, 6)[1..].iter().any(|&v| v != 0) {
3873 return false;
3874 }
3875 }
3876 if forward_quant_chroma_dc(&dc2x2, qpc, false).iter().any(|&v| v != 0) {
3877 return false;
3878 }
3879 }
3880 true
3881 }
3882
3883 #[allow(clippy::too_many_arguments)]
3885 fn pred_ssd(
3886 &self,
3887 sy: &[u8],
3888 su: &[u8],
3889 sv: &[u8],
3890 mb_x: usize,
3891 mb_y: usize,
3892 pred_y: &[u8; 256],
3893 pred_c: &[[u8; 64]; 2],
3894 ) -> i64 {
3895 let mut ssd = 0i64;
3896 for dy in 0..16 {
3897 for dx in 0..16 {
3898 let d = sy[(mb_y * 16 + dy) * self.cw + mb_x * 16 + dx] as i64
3899 - pred_y[dy * 16 + dx] as i64;
3900 ssd += d * d;
3901 }
3902 }
3903 for c in 0..2 {
3904 let src = if c == 0 { su } else { sv };
3905 for dy in 0..8 {
3906 for dx in 0..8 {
3907 let d = src[(mb_y * 8 + dy) * self.ccw + mb_x * 8 + dx] as i64
3908 - pred_c[c][dy * 8 + dx] as i64;
3909 ssd += d * d;
3910 }
3911 }
3912 }
3913 ssd
3914 }
3915
3916 fn mb_ssd(&self, sy: &[u8], su: &[u8], sv: &[u8], mb_x: usize, mb_y: usize) -> i64 {
3918 let mut ssd = 0i64;
3919 for dy in 0..16 {
3920 for dx in 0..16 {
3921 let i = (mb_y * 16 + dy) * self.cw + mb_x * 16 + dx;
3922 let d = sy[i] as i64 - self.rec_y[i] as i64;
3923 ssd += d * d;
3924 }
3925 }
3926 for c in 0..2 {
3927 let (src, rec) = if c == 0 { (su, &self.rec_u) } else { (sv, &self.rec_v) };
3928 for dy in 0..8 {
3929 for dx in 0..8 {
3930 let i = (mb_y * 8 + dy) * self.ccw + mb_x * 8 + dx;
3931 let d = src[i] as i64 - rec[i] as i64;
3932 ssd += d * d;
3933 }
3934 }
3935 }
3936 ssd
3937 }
3938
3939 #[allow(clippy::too_many_arguments)]
3942 fn commit_skip_probe_marker(&self) {}
3943 fn commit_skip(
3944 &mut self,
3945 mb_x: usize,
3946 mb_y: usize,
3947 mv: (i32, i32),
3948 pred_y: &[u8; 256],
3949 pred_c: &[[u8; 64]; 2],
3950 ) {
3951 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MvGrid);
3952 let base = mb_y * 16 * self.cw + mb_x * 16;
3955 for r in 0..16 {
3956 let d = base + r * self.cw;
3957 self.rec_y[d..d + 16].copy_from_slice(&pred_y[r * 16..r * 16 + 16]);
3958 }
3959 let cbase = mb_y * 8 * self.ccw + mb_x * 8;
3960 for c in 0..2 {
3961 let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3962 for r in 0..8 {
3963 let d = cbase + r * self.ccw;
3964 plane[d..d + 8].copy_from_slice(&pred_c[c][r * 8..r * 8 + 8]);
3965 }
3966 }
3967 self.set_mb_mv(mb_x, mb_y, mv, true, 0);
3968 let w4 = self.mb_w * 4;
3969 for row in 0..4 {
3970 let st = (mb_y * 4 + row) * w4 + mb_x * 4;
3971 self.modes_y[st..st + 4].fill(2);
3972 self.coded_y[st..st + 4].fill(true);
3973 }
3974 }
3975
3976 #[allow(clippy::too_many_arguments)]
3982 fn trial_inter(
3983 &mut self,
3984 refs: &[crate::RefFrame],
3985 sy: &[u8],
3986 su: &[u8],
3987 sv: &[u8],
3988 mb_x: usize,
3989 mb_y: usize,
3990 mode: u8,
3991 parts: &[(i32, (i32, i32))],
3992 ) -> (i64, usize) {
3993 let snap = self.save_mb(mb_x, mb_y);
3994 let mut scratch = BitWriter::new();
3995 self.encode_inter_mb(&mut scratch, refs, sy, su, sv, mb_x, mb_y, mode, parts);
3996 let bits = scratch.bit_len();
3997 let ssd = self.mb_ssd(sy, su, sv, mb_x, mb_y);
3998 self.load_mb(mb_x, mb_y, &snap);
3999 (ssd, bits)
4000 }
4001
4002 fn trial_intra(
4006 &mut self,
4007 sy: &[u8],
4008 su: &[u8],
4009 sv: &[u8],
4010 mb_x: usize,
4011 mb_y: usize,
4012 is_p: bool,
4013 ) -> (i64, usize) {
4014 let snap = self.save_mb(mb_x, mb_y);
4015 let mut scratch = BitWriter::new();
4016 encode_mb(self, &mut scratch, mb_x, mb_y, sy, su, sv, is_p);
4017 let bits = scratch.bit_len();
4018 let ssd = self.mb_ssd(sy, su, sv, mb_x, mb_y);
4019 self.load_mb(mb_x, mb_y, &snap);
4020 (ssd, bits)
4021 }
4022
4023 #[allow(clippy::too_many_arguments)]
4028 fn best_part(
4029 &self,
4030 refs: &[crate::RefFrame],
4031 sy: &[u8],
4032 nb: &[MvNeighbor; 3],
4033 num_refs: usize,
4034 rx: usize,
4035 ry: usize,
4036 rw: usize,
4037 rh: usize,
4038 extra: &[(i32, i32)],
4039 lme: f64,
4040 ) -> (i32, (i32, i32), i64) {
4041 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMe);
4042 let [a, b, c] = *nb;
4043 let (mut br, mut bmv, mut bc) = (0i32, (0, 0), i64::MAX);
4044 for r in 0..num_refs {
4045 let mut seeds = vec![predict_mv(a, b, c, r as i32)];
4046 seeds.extend_from_slice(extra);
4047 let (mv, cost) = self.motion_search(&refs[r], sy, rx, ry, rw, rh, &seeds, lme, None);
4048 let cost = cost + (lme * ref_bits(r, num_refs) as f64) as i64;
4049 if cost < bc {
4050 bc = cost;
4051 br = r as i32;
4052 bmv = mv;
4053 }
4054 }
4055 (br, bmv, bc)
4056 }
4057
4058 #[allow(clippy::too_many_arguments)]
4062 fn refine_part(
4063 &self,
4064 refs: &[crate::RefFrame],
4065 sy: &[u8],
4066 nb: &[MvNeighbor; 3],
4067 num_refs: usize,
4068 rx: usize,
4069 ry: usize,
4070 rw: usize,
4071 rh: usize,
4072 lme: f64,
4073 r: i32,
4074 mv: (i32, i32),
4075 ) -> ((i32, i32), i64) {
4076 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMe);
4077 let [a, b, c] = *nb;
4078 let rb = (lme * ref_bits(r as usize, num_refs) as f64) as i64;
4079 let seeds = [predict_mv(a, b, c, r)];
4080 let (m, cc) = self.motion_search(&refs[r as usize], sy, rx, ry, rw, rh, &seeds, lme, Some(mv));
4081 (m, cc + rb)
4082 }
4083
4084 fn best_i16_sad(&self, sy: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4088 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCost);
4089 let (lx, ly) = (mb_x * 16, mb_y * 16);
4090 let (avail_top, avail_left) = (mb_y > 0, mb_x > 0);
4091 let mut top = [0u8; 16];
4092 let mut left = [0u8; 16];
4093 if avail_top {
4094 for i in 0..16 {
4095 top[i] = self.rec_y[(ly - 1) * self.cw + lx + i];
4096 }
4097 }
4098 if avail_left {
4099 for i in 0..16 {
4100 left[i] = self.rec_y[(ly + i) * self.cw + lx - 1];
4101 }
4102 }
4103 let corner = if avail_top && avail_left {
4104 self.rec_y[(ly - 1) * self.cw + lx - 1]
4105 } else {
4106 0
4107 };
4108 let mut best = i64::MAX;
4109 for mode in [I16Mode::Dc, I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
4110 if !mode.available(avail_top, avail_left) {
4111 continue;
4112 }
4113 let pred = i16_pred(self, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
4114 best = best.min(sad_16x16(sy, self.cw, lx, ly, &pred));
4115 }
4116 best
4117 }
4118
4119 fn best_i16_satd(&self, sy: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4122 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCost);
4123 let (lx, ly) = (mb_x * 16, mb_y * 16);
4124 let (avail_top, avail_left) = (mb_y > 0, mb_x > 0);
4125 let mut top = [0u8; 16];
4126 let mut left = [0u8; 16];
4127 if avail_top {
4128 for i in 0..16 {
4129 top[i] = self.rec_y[(ly - 1) * self.cw + lx + i];
4130 }
4131 }
4132 if avail_left {
4133 for i in 0..16 {
4134 left[i] = self.rec_y[(ly + i) * self.cw + lx - 1];
4135 }
4136 }
4137 let corner = if avail_top && avail_left {
4138 self.rec_y[(ly - 1) * self.cw + lx - 1]
4139 } else {
4140 0
4141 };
4142 let mut best = i64::MAX;
4143 for mode in [I16Mode::Dc, I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
4144 if !mode.available(avail_top, avail_left) {
4145 continue;
4146 }
4147 let pred = i16_pred(self, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
4148 best = best.min(satd_16x16(sy, self.cw, lx, ly, &pred));
4149 }
4150 best
4151 }
4152
4153 fn save_mb(&self, mb_x: usize, mb_y: usize) -> MbState {
4156 let mut d = MbState::default();
4157 self.save_mb_into(mb_x, mb_y, &mut d);
4158 d
4159 }
4160
4161 fn save_mb_into(&self, mb_x: usize, mb_y: usize, d: &mut MbState) {
4165 let w4 = self.mb_w * 4;
4166 let w2 = self.mb_w * 2;
4167 macro_rules! reg4 {
4168 ($v:expr, $o:expr) => {{
4169 $o.clear();
4170 for dy in 0..4 {
4171 for dx in 0..4 {
4172 $o.push($v[(mb_y * 4 + dy) * w4 + mb_x * 4 + dx]);
4173 }
4174 }
4175 }};
4176 }
4177 macro_rules! regn {
4178 ($v:expr, $o:expr, $n:expr, $ox:expr, $oy:expr, $stride:expr) => {{
4179 $o.clear();
4180 for dy in 0..$n {
4181 for dx in 0..$n {
4182 $o.push($v[($oy + dy) * $stride + $ox + dx]);
4183 }
4184 }
4185 }};
4186 }
4187 regn!(self.rec_y, d.rec_y, 16, mb_x * 16, mb_y * 16, self.cw);
4188 regn!(self.rec_u, d.rec_u, 8, mb_x * 8, mb_y * 8, self.ccw);
4189 regn!(self.rec_v, d.rec_v, 8, mb_x * 8, mb_y * 8, self.ccw);
4190 reg4!(self.nnz_y, d.nnz_y);
4191 regn!(self.nnz_c[0], d.nnz_c[0], 2, mb_x * 2, mb_y * 2, w2);
4192 regn!(self.nnz_c[1], d.nnz_c[1], 2, mb_x * 2, mb_y * 2, w2);
4193 reg4!(self.mv_y, d.mv_y);
4194 reg4!(self.inter_y, d.inter_y);
4195 reg4!(self.ref_idx_y, d.ref_idx_y);
4196 reg4!(self.coded_y, d.coded_y);
4197 reg4!(self.modes_y, d.modes_y);
4198 d.cur_qp = self.cur_qp;
4199 }
4200
4201 fn load_mb(&mut self, mb_x: usize, mb_y: usize, s: &MbState) {
4203 let w4 = self.mb_w * 4;
4204 let w2 = self.mb_w * 2;
4205 macro_rules! put4 {
4206 ($v:expr, $src:expr) => {
4207 for dy in 0..4 {
4208 for dx in 0..4 {
4209 $v[(mb_y * 4 + dy) * w4 + mb_x * 4 + dx] = $src[dy * 4 + dx];
4210 }
4211 }
4212 };
4213 }
4214 macro_rules! putn {
4215 ($v:expr, $src:expr, $n:expr, $ox:expr, $oy:expr, $stride:expr) => {
4216 for dy in 0..$n {
4217 for dx in 0..$n {
4218 $v[($oy + dy) * $stride + $ox + dx] = $src[dy * $n + dx];
4219 }
4220 }
4221 };
4222 }
4223 putn!(self.rec_y, s.rec_y, 16, mb_x * 16, mb_y * 16, self.cw);
4224 putn!(self.rec_u, s.rec_u, 8, mb_x * 8, mb_y * 8, self.ccw);
4225 putn!(self.rec_v, s.rec_v, 8, mb_x * 8, mb_y * 8, self.ccw);
4226 put4!(self.nnz_y, s.nnz_y);
4227 putn!(self.nnz_c[0], s.nnz_c[0], 2, mb_x * 2, mb_y * 2, w2);
4228 putn!(self.nnz_c[1], s.nnz_c[1], 2, mb_x * 2, mb_y * 2, w2);
4229 put4!(self.mv_y, s.mv_y);
4230 put4!(self.inter_y, s.inter_y);
4231 put4!(self.ref_idx_y, s.ref_idx_y);
4232 put4!(self.coded_y, s.coded_y);
4233 put4!(self.modes_y, s.modes_y);
4234 self.cur_qp = s.cur_qp;
4235 }
4236
4237 fn nnz_cache_load(&mut self, mb_x: usize, mb_y: usize) {
4242 let w4 = self.mb_w * 4;
4243 for lbx in 0..4 {
4244 self.nnz_l_cache[1 + lbx] = if mb_y == 0 {
4245 0x80
4246 } else {
4247 self.nnz_y[(mb_y * 4 - 1) * w4 + (mb_x * 4 + lbx)]
4248 };
4249 }
4250 for lby in 0..4 {
4251 self.nnz_l_cache[(lby + 1) * 5] = if mb_x == 0 {
4252 0x80
4253 } else {
4254 self.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 - 1)]
4255 };
4256 }
4257 }
4258
4259 #[inline]
4263 fn nc_pred(&self, lbx: usize, lby: usize) -> i32 {
4264 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;
4267 if r < 0x80 {
4268 (r + 1) >> 1
4269 } else {
4270 r & 0x7f
4271 }
4272 }
4273
4274 #[inline]
4276 fn nnz_cache_set(&mut self, lbx: usize, lby: usize, total: u8) {
4277 self.nnz_l_cache[(lby + 1) * 5 + (lbx + 1)] = total;
4278 }
4279
4280 fn chroma_cache_load(&mut self, mb_x: usize, mb_y: usize) {
4284 let w2 = self.mb_w * 2;
4285 for c in 0..2 {
4286 for bx in 0..2 {
4287 self.nnz_c_cache[c][1 + bx] = if mb_y == 0 {
4288 0x80
4289 } else {
4290 self.nnz_c[c][(mb_y * 2 - 1) * w2 + (mb_x * 2 + bx)]
4291 };
4292 }
4293 for by in 0..2 {
4294 self.nnz_c_cache[c][(by + 1) * 3] = if mb_x == 0 {
4295 0x80
4296 } else {
4297 self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 - 1)]
4298 };
4299 }
4300 }
4301 }
4302
4303 #[inline]
4305 fn chroma_nc_pred(&self, c: usize, bx: usize, by: usize) -> i32 {
4306 let left = self.nnz_c_cache[c][(by + 1) * 3 + bx] as i32;
4307 let top = self.nnz_c_cache[c][by * 3 + (bx + 1)] as i32;
4308 let r = left + top;
4309 if r < 0x80 {
4310 (r + 1) >> 1
4311 } else {
4312 r & 0x7f
4313 }
4314 }
4315
4316 #[inline]
4318 fn chroma_nnz_cache_set(&mut self, c: usize, bx: usize, by: usize, total: u8) {
4319 self.nnz_c_cache[c][(by + 1) * 3 + (bx + 1)] = total;
4320 }
4321}
4322
4323#[inline(never)]
4340fn derive_mb_bs_from(
4341 fe: &FrameEncoder,
4342 mb_x: usize,
4343 mb_y: usize,
4344 kind: rusty_h264_common::deblock::MbKind,
4345) -> rusty_h264_common::deblock::MbBs {
4346 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncBs);
4347 let view = rusty_h264_common::deblock::BlockInfo {
4348 inter: &fe.inter_y,
4349 nnz: &fe.nnz_y,
4350 mv: &fe.mv_y,
4351 ref_id: &fe.ref_idx_y,
4352 mv1: &[],
4353 ref_id1: &[],
4354 w4: fe.mb_w * 4,
4355 t8x8: &[],
4356 bs: &[],
4357 };
4358 rusty_h264_common::deblock::derive_mb_kind(&view, mb_x, mb_y, kind)
4359}
4360
4361pub fn encode_slice_data(
4362 w: &mut BitWriter,
4363 cfg: &EncoderConfig,
4364 frame: &YuvFrame,
4365 qp: u8,
4366 is_p: bool,
4367 refs: &[crate::RefFrame],
4368 qpo: &[i32],
4369) -> crate::RefFrame {
4370 let _g_prep = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncPrep);
4371 let mut fe = FrameEncoder::new(cfg);
4372 let precomp = rusty_h264_common::deblock::precomputed_bs_enabled();
4373 let mut bs_grid =
4374 vec![rusty_h264_common::deblock::MbBs::UNSET; if precomp { fe.mb_w * fe.mb_h } else { 0 }];
4375 fe.qp = qp;
4376 fe.qpc = chroma_qp(qp);
4377 fe.cur_qp = qp;
4378 if cfg.cabac_dz_div > 0 {
4379 fe.idz = cfg.cabac_dz_div; } let (sy, su, sv) = coded_source(cfg, frame);
4382 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
4383 let num_refs = refs.len();
4384 if is_p && fe.me_wide && !refs.is_empty()
4389 && global_mc_residual(&sy, fe.cw, fe.mb_h * 16, &refs[0].y) < fe.me_wide_coh
4390 {
4391 fe.me_wide = false;
4392 }
4393 if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
4400 let hr = me_wide_headroom(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
4401 if me_wide_hr_dbg() {
4402 eprintln!("ME_HR qp{qp} headroom={hr:.2}");
4403 }
4404 if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
4405 fe.me_wide = false;
4406 }
4407 }
4408 if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
4413 let (mg, dc) = b2_mgain(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
4414 if me_sadt_dbg() {
4415 eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
4416 }
4417 fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
4418 }
4419 if is_p && fe.satd_q > 0.0 {
4426 let mut vars: Vec<i64> = (0..fe.mb_h)
4427 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
4428 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
4429 .collect();
4430 vars.sort_unstable();
4431 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
4432 fe.satd_var_thresh = vars[idx];
4433 }
4434 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
4439 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
4441 let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
4442 let mut skip_run = 0u32;
4443 if is_p && mv_cmp_on() {
4452 MVCMP_FRAME.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
4453 }
4454 let mut rdskip_snap = MbState::default();
4456 let mut rdskip_free = 0usize;
4457 let mut rdskip_seen = 0usize;
4458 let mut rdskip_on = false;
4459 let mut greedy_on = fe.greedy_min_free == 0; let rdskip_learn = (fe.mb_w * fe.mb_h / 8).max(64);
4461 let rdskip_min_free = fe.rd_skip_min_free as usize;
4462
4463 drop(_g_prep);
4464 let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
4465 for mb_y in 0..fe.mb_h {
4466 for mb_x in 0..fe.mb_w {
4467 let mb_idx = mb_y * fe.mb_w + mb_x;
4468 fe.qp = aq_qp[mb_idx];
4469 fe.qpc = chroma_qp(aq_qp[mb_idx]);
4470 let mut inter: Option<InterChoice> = None;
4473 let mut coded: Option<BitWriter> = None;
4477 if is_p {
4478 if num_refs > 0 {
4479 let _g_skip = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncSkip);
4483 let _g_smc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Neighbors);
4484 rdskip_seen += 1;
4485 if rdskip_seen >= rdskip_learn {
4486 rdskip_on = rdskip_free * 100 >= rdskip_seen * rdskip_min_free;
4487 greedy_on = fe.greedy_min_free == 0
4488 || rdskip_free * 100 >= rdskip_seen * fe.greedy_min_free as usize;
4489 }
4490 let mv_skip = fe.skip_mv(mb_x, mb_y);
4491 let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
4492 drop(_g_smc);
4493 let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
4494 let skip_c = if luma_free || !fe.fast {
4497 fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
4498 } else {
4499 [[0u8; 64]; 2]
4500 };
4501 let is_free =
4502 luma_free && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &skip_c);
4503 let skip_sad = if fe.fast {
4505 0
4506 } else {
4507 let (lx, ly) = (mb_x * 16, mb_y * 16);
4508 let mut s = 0u32;
4509 for dy in 0..16 {
4510 let src = &sy[(ly + dy) * fe.cw + lx..][..16];
4511 let p = &skip_y[dy * 16..][..16];
4512 s += src.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
4513 }
4514 s
4515 };
4516 if is_free {
4517 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
4518 if !fe.fast {
4519 fe.mb_was_skip[mb_idx] = true;
4520 fe.mb_skip_sad[mb_idx] = skip_sad;
4521 }
4522 mb_qpy[mb_idx] = fe.cur_qp; rdskip_free += 1;
4524 if precomp {
4525 bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, rusty_h264_common::deblock::MbKind::Skip);
4526 }
4527 skip_run += 1;
4528 continue;
4529 }
4530 drop(_g_skip);
4531 let (lx, ly) = (mb_x * 16, mb_y * 16);
4532 let nb = {
4533 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMvPred);
4534 fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
4535 };
4536 let lme = lambda.sqrt();
4537
4538 if fe.fast {
4539 fe.mb_use_satd = fe.satd_q > 0.0
4551 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
4552 let (r16, mv16, cost_inter) =
4553 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
4554 let cost_intra = if fe.mb_use_satd {
4555 fe.best_i16_satd(&sy, mb_x, mb_y)
4556 } else {
4557 fe.best_i16_sad(&sy, mb_x, mb_y)
4558 } + (lme * fe.tune_intra_penalty) as i64;
4559 inter = if cost_intra < cost_inter {
4560 None } else {
4562 Some((0, vec![(r16, mv16)]))
4563 };
4564 } else {
4565 if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
4575 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
4576 fe.mb_was_skip[mb_idx] = true;
4577 fe.mb_skip_sad[mb_idx] = skip_sad;
4578 mb_qpy[mb_idx] = fe.cur_qp; if precomp {
4580 bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, rusty_h264_common::deblock::MbKind::Skip);
4581 }
4582 skip_run += 1;
4583 continue;
4584 }
4585
4586 let (r16, mv16, c16) =
4588 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
4589 let mut best_c = c16;
4590 let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
4591
4592 const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
4595 let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
4596 let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
4597 let split_t = split_t();
4598 if c16 > split_gate && (split_t <= 0.0 || (c16 as f64) >= split_t * lme) {
4599 let (rt, mvt, ct) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 8, &[mv16], lme);
4600 let (rb, mvb, cb) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly + 8, 16, 8, &[mv16], lme);
4601 let (rl, mvl, cl) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 8, 16, &[mv16], lme);
4602 let (rr, mvr, cr) = fe.best_part(refs, &sy, &nb, num_refs, lx + 8, ly, 8, 16, &[mv16], lme);
4603 if ct + cb < best_c {
4604 best_c = ct + cb;
4605 pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
4606 }
4607 if cl + cr < best_c {
4608 best_c = cl + cr;
4609 pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
4610 }
4611
4612 if fe.sub8x8 {
4617 let mut c8 = (lme * 4.0) as i64; let mut p8 = Vec::with_capacity(4);
4619 for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
4620 let (r, mv, c) = fe.best_part(
4621 refs, &sy, &nb, num_refs, lx + qx, ly + qy, 8, 8, &[mv16], lme,
4622 );
4623 c8 += c;
4624 p8.push((r, mv));
4625 }
4626 if c8 < best_c {
4627 best_c = c8;
4628 pick = Some((3u8, p8));
4629 }
4630 }
4631 }
4632
4633 if fe.sp_defer.get() {
4638 if let Some((mode, parts)) = pick.as_mut() {
4639 let regions: &[(usize, usize, usize, usize)] = match mode {
4640 1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
4641 2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
4642 3 => &[(0, 0, 8, 8), (8, 0, 8, 8), (0, 8, 8, 8), (8, 8, 8, 8)],
4643 _ => &[(0, 0, 16, 16)],
4644 };
4645 let mut tot = if *mode == 3 { (lme * 4.0) as i64 } else { 0 };
4646 for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
4647 let (r, mv) = parts[i];
4648 let (m2, c2) = fe.refine_part(
4649 refs, &sy, &nb, num_refs, lx + qx, ly + qy, pw, ph, lme, r, mv,
4650 );
4651 parts[i] = (r, m2);
4652 tot += c2;
4653 }
4654 best_c = tot;
4655 }
4656 }
4657 if split_harvest::enabled() {
4658 let won = match pick.as_ref().map(|p| p.0) {
4659 Some(0) | None => 0u8,
4660 Some(m) => m,
4661 };
4662 split_harvest::record(c16, best_c, lme, split_gate, won);
4663 }
4664 let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
4667 + (lme * fe.tune_intra_penalty) as i64;
4668 inter = if c_intra < best_c { None } else { pick };
4669 fe.mb_was_skip[mb_idx] = false;
4670 fe.mb_skip_sad[mb_idx] = skip_sad;
4671 }
4672
4673 if fe.rd_skip && rdskip_on && inter.is_some() {
4685 let skip_cp = fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip);
4686 let ssd_s = fe.pred_ssd(&sy, &su, &sv, mb_x, mb_y, &skip_y, &skip_cp);
4692 debug_assert_eq!(ssd_s, {
4693 let snap = fe.save_mb(mb_x, mb_y);
4694 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
4695 let v = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y);
4696 fe.load_mb(mb_x, mb_y, &snap);
4697 v
4698 }, "skip prediction SSD must equal the committed-skip reconstruction SSD");
4699 let j_skip = ssd_s as f64 + lambda;
4701 let take_skip = if fe.rd_skip_fast_t > 0.0
4707 && (ssd_s as f64) <= lambda * fe.rd_skip_fast_t
4708 {
4709 true
4710 } else {
4711 fe.save_mb_into(mb_x, mb_y, &mut rdskip_snap);
4717 let mut scratch = BitWriter::new();
4718 {
4719 let (m, p) = inter.as_ref().unwrap();
4720 fe.encode_inter_mb(
4721 &mut scratch, refs, &sy, &su, &sv, mb_x, mb_y, *m, p,
4722 );
4723 }
4724 let bits_c = scratch.bit_len();
4725 let ssd_c = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y);
4726 let won = j_skip <= ssd_c as f64 + lambda * bits_c as f64;
4727 if won {
4728 fe.load_mb(mb_x, mb_y, &rdskip_snap); true
4730 } else {
4731 coded = Some(scratch); false
4733 }
4734 };
4735 if take_skip {
4736 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
4737 if !fe.fast {
4738 fe.mb_was_skip[mb_idx] = true;
4739 fe.mb_skip_sad[mb_idx] = skip_sad;
4740 }
4741 mb_qpy[mb_idx] = fe.cur_qp;
4742 if precomp {
4743 bs_grid[mb_idx] = derive_mb_bs_from(
4744 &fe, mb_x, mb_y,
4745 rusty_h264_common::deblock::MbKind::Skip,
4746 );
4747 }
4748 skip_run += 1;
4749 continue;
4750 }
4751 }
4752 }
4753 w.write_ue(skip_run); skip_run = 0;
4755 }
4756 if mv_force_on() && is_p && inter.is_some() {
4757 let fi = MVCMP_FRAME.load(std::sync::atomic::Ordering::Relaxed);
4758 let ext = EXT_MV.lock().unwrap();
4759 if let Some(field) = ext.get(fi) {
4760 let w4 = fe.mb_w * 4;
4761 let b0 = (mb_y * 4) * w4 + mb_x * 4;
4762 let uniform = (0..4).all(|r| {
4765 (0..4).all(|c| field.get(b0 + r * w4 + c) == field.get(b0))
4766 });
4767 if uniform {
4768 if let Some(&emv) = field.get(b0) {
4769 inter = Some((0, vec![(0, emv)]));
4770 MVCMP[6].fetch_add(1, std::sync::atomic::Ordering::Relaxed);
4771 }
4772 }
4773 }
4774 }
4775 if mv_cmp_on() && is_p {
4776 if let Some((mode, parts)) = inter.as_ref() {
4777 let fi = MVCMP_FRAME.load(std::sync::atomic::Ordering::Relaxed);
4778 let ext = EXT_MV.lock().unwrap();
4779 if let Some(field) = ext.get(fi) {
4780 let bidx = (mb_y * 4) * (fe.mb_w * 4) + mb_x * 4;
4781 if let Some(&emv) = field.get(bidx) {
4782 let (mode, parts) = (*mode, parts.clone());
4783 drop(ext);
4784 let (so, bo) =
4787 fe.trial_inter(refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts);
4788 let (se, be) = fe.trial_inter(
4789 refs, &sy, &su, &sv, mb_x, mb_y, 0, &[(0, emv)],
4790 );
4791 let jo = so as f64 + lambda * bo as f64;
4792 let je = se as f64 + lambda * be as f64;
4793 use std::sync::atomic::Ordering::Relaxed;
4794 MVCMP[0].fetch_add(1, Relaxed);
4795 MVCMP[1].fetch_add(bo as u64, Relaxed);
4796 MVCMP[2].fetch_add(be as u64, Relaxed);
4797 MVCMP[3].fetch_add(so.max(0) as u64, Relaxed);
4798 MVCMP[4].fetch_add(se.max(0) as u64, Relaxed);
4799 MVCMP[5].fetch_add((je < jo) as u64, Relaxed);
4800 MVCMP[6].fetch_add((parts[0].1 != emv) as u64, Relaxed);
4801 }
4802 }
4803 }
4804 }
4805 let mb_kind = match &inter {
4808 Some((_, parts)) if parts.len() == 1 => {
4811 rusty_h264_common::deblock::MbKind::InterUniform
4812 }
4813 Some(_) => rusty_h264_common::deblock::MbKind::Inter,
4814 None => rusty_h264_common::deblock::MbKind::Intra,
4815 };
4816 match inter {
4817 Some((mode, parts)) => match coded {
4818 Some(sc) => w.append(&sc),
4821 None => {
4822 fe.encode_inter_mb(w, refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts)
4823 }
4824 },
4825 None => encode_mb(&mut fe, w, mb_x, mb_y, &sy, &su, &sv, is_p),
4826 }
4827 mb_qpy[mb_idx] = fe.cur_qp; if precomp {
4829 bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, mb_kind);
4830 }
4831 }
4832 }
4833 debug_assert!(
4834 !precomp || bs_grid.iter().all(|b| *b != rusty_h264_common::deblock::MbBs::UNSET),
4835 "a macroblock loop exit failed to store its boundary strengths"
4836 );
4837 if is_p && skip_run > 0 {
4838 w.write_ue(skip_run); }
4840 w.rbsp_trailing_bits();
4841
4842 drop(_g_loop);
4846 let _g_fin = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFinal);
4847 let info = rusty_h264_common::deblock::BlockInfo {
4852 inter: &fe.inter_y,
4853 nnz: &fe.nnz_y,
4854 mv: &fe.mv_y,
4855 ref_id: &fe.ref_idx_y,
4856 mv1: &[],
4857 ref_id1: &[],
4858 w4: fe.mb_w * 4,
4859 t8x8: &[],
4860 bs: &bs_grid,
4861 };
4862 drop(_g_fin);
4865 rusty_h264_common::deblock::filter_frame(
4866 &mut fe.rec_y,
4867 &mut fe.rec_u,
4868 &mut fe.rec_v,
4869 fe.mb_w,
4870 fe.mb_h,
4871 &mb_qpy,
4872 0, 0, 0, &info,
4876 );
4877 let w4 = fe.mb_w * 4;
4878 crate::RefFrame {
4879 y: fe.rec_y,
4880 u: fe.rec_u,
4881 v: fe.rec_v,
4882 poc: 0, frame_num: 0, mv: fe.mv_y,
4886 ref_idx: fe.ref_idx_y,
4887 w4,
4888 hpel: std::sync::OnceLock::new(),
4890 }
4891}
4892
4893#[allow(clippy::too_many_arguments)]
4904#[allow(clippy::too_many_arguments)]
4905pub fn encode_slice_data_b(
4906 w: &mut BitWriter,
4907 cfg: &EncoderConfig,
4908 frame: &YuvFrame,
4909 qp: u8,
4910 poc: i32,
4911 l0: &crate::RefFrame,
4912 l1: &crate::RefFrame,
4913 qpo: &[i32],
4914) {
4915 let mut fe = FrameEncoder::new(cfg);
4916 fe.qp = qp;
4917 fe.qpc = chroma_qp(qp);
4918 fe.cur_qp = qp;
4919 if cfg.cabac_dz_div > 0 {
4920 fe.idz = cfg.cabac_dz_div; } fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
4925 let (sy, su, sv) = coded_source(cfg, frame);
4926 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
4927 let lme = lambda.sqrt();
4928 let refs = std::slice::from_ref(l0); if fe.satd_q > 0.0 {
4932 let mut vars: Vec<i64> = (0..fe.mb_h)
4933 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
4934 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
4935 .collect();
4936 vars.sort_unstable();
4937 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
4938 fe.satd_var_thresh = vars[idx];
4939 }
4940 let mut skip_run = 0u32; for mb_y in 0..fe.mb_h {
4942 for mb_x in 0..fe.mb_w {
4943 let (lx, ly) = (mb_x * 16, mb_y * 16);
4944 let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
4945 fe.mb_use_satd =
4946 fe.satd_q > 0.0 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
4947 let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
4950 let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
4951 let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
4952 let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
4953 let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
4957 if fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
4965 && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc)
4966 {
4967 fe.commit_direct_motion(mb_x, mb_y, &dmotion);
4968 skip_run += 1;
4969 continue;
4970 }
4971 let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
4972 let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
4973 let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
4974 let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
4976 let r_bi = mvd_bits(mv0.0 - pmv0.0) + mvd_bits(mv0.1 - pmv0.1)
4977 + mvd_bits(mv1.0 - pmv1.0) + mvd_bits(mv1.1 - pmv1.1);
4978 let j_bi = d_bi + (lme * r_bi as f64) as i64;
4979 let (mut dir, mut best) = (0u8, d_direct);
4984 if j0 < best { dir = 1; best = j0; }
4985 if j1 < best { dir = 2; best = j1; }
4986 if j_bi < best { dir = 3; best = j_bi; }
4987 let _ = best;
4988 w.write_ue(skip_run); skip_run = 0;
4990 let bspec = BInter { dir, l1, mv0, mv1 };
4991 fe.encode_inter_mb_v1_b(w, refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
4992 }
4993 }
4994 if skip_run > 0 {
4995 w.write_ue(skip_run); }
4997 w.rbsp_trailing_bits();
4998}
4999
5000#[inline(always)]
5004fn mvd_bits(d: i32) -> u32 {
5005 let codenum = if d > 0 { (2 * d - 1) as u32 } else { (-2 * d) as u32 };
5006 1 + 2 * (31 - (codenum + 1).leading_zeros())
5007}
5008
5009fn write_ref_idx(w: &mut BitWriter, refi: i32, num_refs: usize) {
5013 if num_refs == 2 {
5014 w.write_bit(refi == 0); } else {
5016 w.write_ue(refi as u32);
5017 }
5018}
5019
5020fn ref_bits(r: usize, num_refs: usize) -> u32 {
5023 if num_refs <= 1 {
5024 0
5025 } else if num_refs == 2 {
5026 1
5027 } else {
5028 let mut n = r as u32 + 1;
5029 let mut len = 1;
5030 while n > 1 {
5031 n >>= 1;
5032 len += 2;
5033 }
5034 len
5035 }
5036}
5037
5038fn residual(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[i32; 16]) -> [i32; 16] {
5039 let mut r = [0i32; 16];
5040 for dy in 0..4 {
5041 for dx in 0..4 {
5042 r[dy * 4 + dx] = src[(y0 + dy) * stride + (x0 + dx)] as i32 - pred[dy * 4 + dx];
5043 }
5044 }
5045 r
5046}
5047
5048fn store(plane: &mut [u8], stride: usize, x0: usize, y0: usize, s: &[u8; 16]) {
5050 for dy in 0..4 {
5051 for dx in 0..4 {
5052 plane[(y0 + dy) * stride + (x0 + dx)] = s[dy * 4 + dx];
5053 }
5054 }
5055}
5056
5057fn pred_block(pred: &[u8; 256], bx: usize, by: usize) -> [i32; 16] {
5060 let mut p = [0i32; 16];
5061 for dy in 0..4 {
5062 for dx in 0..4 {
5063 p[dy * 4 + dx] = pred[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
5064 }
5065 }
5066 p
5067}
5068
5069#[inline]
5080fn satd_px(src: &[u8], ss: usize, pred: &[u8], ps: usize, w: usize, h: usize) -> i64 {
5081 #[cfg(accel)]
5082 {
5083 let asm = match (w, h) {
5084 (16, 16) => Some(rusty_h264_accel::satd_16x16(src, ss, pred, ps)),
5085 (16, 8) => Some(rusty_h264_accel::satd_16x8(src, ss, pred, ps)),
5086 (8, 16) => Some(rusty_h264_accel::satd_8x16(src, ss, pred, ps)),
5087 (8, 8) => Some(rusty_h264_accel::satd_8x8(src, ss, pred, ps)),
5088 (4, 4) => Some(rusty_h264_accel::satd_4x4(src, ss, pred, ps)),
5089 _ => None,
5090 };
5091 if let Some(v) = asm {
5092 return 2 * v as i64;
5093 }
5094 }
5095 let (nbx, nby) = (w / 4, h / 4);
5097 let mut blocks = [[0i32; 16]; 16];
5098 let mut bi = 0;
5099 for by in 0..nby {
5100 for bx in 0..nbx {
5101 let blk = &mut blocks[bi];
5102 for dy in 0..4 {
5103 for dx in 0..4 {
5104 blk[dy * 4 + dx] =
5105 src[(by * 4 + dy) * ss + bx * 4 + dx] as i32 - pred[(by * 4 + dy) * ps + bx * 4 + dx] as i32;
5106 }
5107 }
5108 bi += 1;
5109 }
5110 }
5111 satd_4x4_sum(&blocks[..nbx * nby])
5112}
5113
5114#[inline]
5119fn sad_strided(src: &[u8], ss: usize, r: &[u8], rs: usize, w: usize, h: usize) -> i64 {
5120 #[cfg(accel)]
5121 {
5122 match (w, h) {
5123 (16, 16) => return rusty_h264_accel::sad_16x16(src, ss, r, rs) as i64,
5124 (16, 8) => return rusty_h264_accel::sad_16x8(src, ss, r, rs) as i64,
5125 (8, 16) => return rusty_h264_accel::sad_8x16(src, ss, r, rs) as i64,
5126 _ => {}
5127 }
5128 }
5129 let mut sad = 0u32;
5130 for dy in 0..h {
5131 let a = &src[dy * ss..][..w];
5132 let b = &r[dy * rs..][..w];
5133 sad += a.iter().zip(b).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
5134 }
5135 sad as i64
5136}
5137
5138#[inline]
5142fn sad_avg_strided(src: &[u8], ss: usize, a: &[u8], b: &[u8], rs: usize, w: usize, h: usize) -> i64 {
5143 let mut sad = 0u32;
5144 for dy in 0..h {
5145 let s = &src[dy * ss..][..w];
5146 let pa = &a[dy * rs..][..w];
5147 let pb = &b[dy * rs..][..w];
5148 for i in 0..w {
5149 let p = ((pa[i] as u16 + pb[i] as u16 + 1) >> 1) as u8;
5150 sad += s[i].abs_diff(p) as u32;
5151 }
5152 }
5153 sad as i64
5154}
5155
5156fn satd_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
5157 satd_px(&src[ly * stride + lx..], stride, pred, 16, 16, 16)
5158}
5159
5160fn sad_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
5164 let mut sad = 0u32;
5165 for dy in 0..16 {
5166 let s = &src[(ly + dy) * stride + lx..][..16];
5167 let p = &pred[dy * 16..][..16];
5168 sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
5169 }
5170 sad as i64
5171}
5172
5173fn satd_8x8(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[u8; 64]) -> i64 {
5175 satd_px(&src[y0 * stride + x0..], stride, pred, 8, 8, 8)
5176}
5177
5178fn satd_4x4(src: &[u8], stride: usize, px: usize, py: usize, pred: &[u8; 16]) -> i64 {
5180 satd_px(&src[py * stride + px..], stride, pred, 4, 4, 4)
5181}
5182
5183fn i4_mode_available(mode: u8, top: bool, left: bool) -> bool {
5185 match mode {
5186 0 | 3 | 7 => top, 1 | 8 => left, 2 => true, _ => top && left, }
5191}
5192
5193struct I4Plan {
5196 modes: [u8; 16], q: [[i32; 16]; 16], cbp_luma: u32, nonzero: i64, }
5201
5202struct MbPlan {
5208 use_i4: bool,
5209 i16_mode: I16Mode,
5212 i16_cbp15: bool,
5213 i16_dc_levels: [i32; 16],
5214 i16_q: [[i32; 16]; 16],
5215 i4: Option<I4Plan>,
5217 i8: Option<I8Plan>,
5220 chroma_mode: u8,
5222 cbp_chroma: u32,
5223 c_dc_levels: [[i32; 4]; 2],
5224 c_q_blocks: [[[i32; 16]; 4]; 2],
5225}
5226
5227struct InterPlan {
5234 mvds: [(i32, i32); 4], plan_refs: [i32; 4], n_mvd: usize,
5237 cbp: u32,
5238 q_blocks: [[i32; 16]; 16], c_dc_levels: [[i32; 4]; 2],
5240 c_q: [[[i32; 16]; 4]; 2],
5241 t8x8: bool, q8: [[i32; 64]; 4], }
5244
5245fn gather_i4(
5247 fe: &FrameEncoder,
5248 px: usize,
5249 py: usize,
5250 avail_top: bool,
5251 avail_left: bool,
5252 bx: usize,
5253 by: usize,
5254) -> ([u8; 8], [u8; 4], u8) {
5255 let (cw, w4) = (fe.cw, fe.mb_w * 4);
5256 let mut top = [0u8; 8];
5257 let mut left = [0u8; 4];
5258 let mut corner = 0;
5259 if avail_top {
5260 for i in 0..4 {
5261 top[i] = fe.rec_y[(py - 1) * cw + px + i];
5262 }
5263 let tr_avail = bx + 1 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 1)];
5264 for i in 0..4 {
5265 top[4 + i] = if tr_avail {
5266 fe.rec_y[(py - 1) * cw + px + 4 + i]
5267 } else {
5268 top[3]
5269 };
5270 }
5271 }
5272 if avail_left {
5273 for i in 0..4 {
5274 left[i] = fe.rec_y[(py + i) * cw + px - 1];
5275 }
5276 }
5277 if avail_top && avail_left {
5278 corner = fe.rec_y[(py - 1) * cw + px - 1];
5279 }
5280 (top, left, corner)
5281}
5282
5283#[inline]
5290fn modes_at(fe: &FrameEncoder, modes: &[u8; 16], lbx: usize, lby: usize, dx: isize, dy: isize, bx: usize, by: usize) -> u8 {
5291 let (nx, ny) = (lbx as isize + dx, lby as isize + dy);
5292 if (0..4).contains(&nx) && (0..4).contains(&ny) {
5293 modes[ny as usize * 4 + nx as usize]
5294 } else {
5295 let w4 = fe.mb_w * 4;
5296 let gx = (bx as isize + dx) as usize;
5297 let gy = (by as isize + dy) as usize;
5298 fe.modes_y[gy * w4 + gx]
5299 }
5300}
5301
5302fn plan_i4x4(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I4Plan {
5303 let w4 = fe.mb_w * 4;
5304 let mut modes = [2u8; 16];
5305 let mut q = [[0i32; 16]; 16];
5306 let mut cbp_luma = 0u32;
5307 let mut nonzero = 0i64;
5308
5309 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
5310 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
5311 let (px, py) = (bx * 4, by * 4);
5312 let avail_top = by > 0;
5313 let avail_left = bx > 0;
5314 let (top, left, corner) = gather_i4(fe, px, py, avail_top, avail_left, bx, by);
5315
5316 let mut best_m = 2u8;
5321 let mut best_cost = i64::MAX;
5322 if fe.fast && fast_intra_enabled() {
5323 let lm = if bx > 0 { modes_at(fe, &modes, lbx, lby, -1, 0, bx, by) } else { 2 };
5324 let tm = if by > 0 { modes_at(fe, &modes, lbx, lby, 0, -1, bx, by) } else { 2 };
5325 let mpm = lm.min(tm);
5326 let mut cands = [mpm, 2u8, 0, 1];
5327 for i in 1..4 {
5328 for j in 0..i {
5329 if cands[i] == cands[j] {
5330 cands[i] = 255;
5331 }
5332 }
5333 }
5334 for &m in cands.iter() {
5335 if m == 255 || !i4_mode_available(m, avail_top, avail_left) {
5336 continue;
5337 }
5338 let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
5339 let cost = satd_4x4(sy, fe.cw, px, py, &pred);
5340 if cost < best_cost {
5341 best_cost = cost;
5342 best_m = m;
5343 }
5344 }
5345 } else {
5346 for m in 0..9u8 {
5347 if !i4_mode_available(m, avail_top, avail_left) {
5348 continue;
5349 }
5350 let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
5351 let cost = satd_4x4(sy, fe.cw, px, py, &pred);
5352 if cost < best_cost {
5353 best_cost = cost;
5354 best_m = m;
5355 }
5356 }
5357 }
5358
5359 let pred = intra4x4_pred(best_m, avail_top, avail_left, &top, &left, corner);
5361 let mut predb = [0i32; 16];
5362 for i in 0..16 {
5363 predb[i] = pred[i] as i32;
5364 }
5365 let res = residual(sy, fe.cw, px, py, &predb);
5366 let qb = rdoq(&forward_core(&res), qp, fe.idz, fe.rdoq_strength, 0); let s = reconstruct_4x4(&dequantize(&qb, qp), &predb);
5368 store(&mut fe.rec_y, fe.cw, px, py, &s);
5369 fe.coded_y[by * w4 + bx] = true;
5370
5371 let nz = qb.iter().filter(|&&v| v != 0).count();
5372 if nz > 0 {
5373 cbp_luma |= 1 << ((lby / 2) * 2 + (lbx / 2));
5374 }
5375 nonzero += nz as i64;
5376 modes[lby * 4 + lbx] = best_m;
5377 q[lby * 4 + lbx] = qb;
5378 }
5379 I4Plan {
5380 modes,
5381 q,
5382 cbp_luma,
5383 nonzero,
5384 }
5385}
5386
5387struct I8Plan {
5391 modes: [u8; 4], q: [[i32; 64]; 4], cbp_luma: u32, nonzero: i64, }
5396
5397const ZIGZAG_8X8: [usize; 64] = [
5400 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,
5401 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
5402 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
5403];
5404
5405#[inline]
5406fn scan_8x8_fwd(raster: &[i32; 64]) -> [i32; 64] {
5407 std::array::from_fn(|i| raster[ZIGZAG_8X8[i]])
5408}
5409
5410fn gather_i8_enc(
5413 fe: &FrameEncoder,
5414 px: usize,
5415 py: usize,
5416 avail_top: bool,
5417 avail_left: bool,
5418 bx: usize,
5419 by: usize,
5420) -> ([u8; 16], [u8; 8], u8, bool) {
5421 let (cw, w4) = (fe.cw, fe.mb_w * 4);
5422 let mut top = [0u8; 16];
5423 let mut left = [0u8; 8];
5424 let mut corner = 0;
5425 if avail_top {
5426 for i in 0..8 {
5427 top[i] = fe.rec_y[(py - 1) * cw + px + i];
5428 }
5429 let tr_avail = bx + 2 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 2)];
5430 for i in 0..8 {
5431 top[8 + i] = if tr_avail {
5432 fe.rec_y[(py - 1) * cw + px + 8 + i]
5433 } else {
5434 top[7]
5435 };
5436 }
5437 }
5438 if avail_left {
5439 for i in 0..8 {
5440 left[i] = fe.rec_y[(py + i) * cw + px - 1];
5441 }
5442 }
5443 let avail_corner = avail_top && avail_left;
5444 if avail_corner {
5445 corner = fe.rec_y[(py - 1) * cw + px - 1];
5446 }
5447 (top, left, corner, avail_corner)
5448}
5449
5450fn plan_i8x8(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I8Plan {
5453 let w4 = fe.mb_w * 4;
5454 let mut modes = [2u8; 4];
5455 let mut q = [[0i32; 64]; 4];
5456 let mut cbp_luma = 0u32;
5457 let mut nonzero = 0i64;
5458 let weight = [16i32; 64];
5459
5460 for b8 in 0..4usize {
5461 let (b8x, b8y) = (b8 % 2, b8 / 2);
5462 let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
5463 let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2); let avail_top = b8y > 0 || mb_y > 0;
5465 let avail_left = b8x > 0 || mb_x > 0;
5466 let (top, left, corner, avail_corner) =
5467 gather_i8_enc(fe, px, py, avail_top, avail_left, bx, by);
5468
5469 let predicted = predict_i4_mode(fe, bx, by);
5473 let mut best_m = 2u8;
5474 let mut best_cost = i64::MAX;
5475 for m in 0..9u8 {
5476 if !i4_mode_available(m, avail_top, avail_left) {
5477 continue;
5478 }
5479 let pred = intra8x8_pred(m, avail_top, avail_left, avail_corner, &top, &left, corner);
5480 let mut cost = satd_8x8(sy, fe.cw, px, py, &pred);
5481 if m != predicted {
5482 cost += 4 * fe.qp as i64; }
5484 if cost < best_cost {
5485 best_cost = cost;
5486 best_m = m;
5487 }
5488 }
5489 modes[b8] = best_m;
5490
5491 let pred = intra8x8_pred(best_m, avail_top, avail_left, avail_corner, &top, &left, corner);
5493 let mut res = [0i32; 64];
5494 for dy in 0..8 {
5495 for dx in 0..8 {
5496 res[dy * 8 + dx] =
5497 sy[(py + dy) * fe.cw + (px + dx)] as i32 - pred[dy * 8 + dx] as i32;
5498 }
5499 }
5500 let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, fe.idz);
5501 let nz = levels.iter().filter(|&&v| v != 0).count();
5502 if nz > 0 {
5503 cbp_luma |= 1 << b8;
5504 }
5505 nonzero += nz as i64;
5506 q[b8] = levels;
5507
5508 let res_r = inverse_quant_8x8(&levels, qp, &weight);
5509 let predb: [i32; 64] = std::array::from_fn(|i| pred[i] as i32);
5510 let recon = add_residual_8x8(&res_r, &predb);
5511 for dy in 0..8 {
5512 for dx in 0..8 {
5513 fe.rec_y[(py + dy) * fe.cw + (px + dx)] = recon[dy * 8 + dx];
5514 }
5515 }
5516 for sry in 0..2 {
5519 for srx in 0..2 {
5520 fe.modes_y[(by + sry) * w4 + (bx + srx)] = best_m;
5521 fe.coded_y[(by + sry) * w4 + (bx + srx)] = true;
5522 }
5523 }
5524 }
5525 I8Plan {
5526 modes,
5527 q,
5528 cbp_luma,
5529 nonzero,
5530 }
5531}
5532
5533#[allow(clippy::too_many_arguments)]
5541fn plan_inter8_luma(
5542 sy: &[u8],
5543 cw: usize,
5544 mb_x: usize,
5545 mb_y: usize,
5546 pred_y: &[u8; 256],
5547 qp: u8,
5548) -> ([[i32; 64]; 4], u32, f64, [u8; 256], i64) {
5549 let weight = [16i32; 64];
5550 let mut q8 = [[0i32; 64]; 4];
5551 let mut cbp = 0u32;
5552 let mut rate = 0f64;
5553 let mut rec = [0u8; 256];
5554 let mut ssd = 0i64;
5555 for b8 in 0..4usize {
5556 let (b8x, b8y) = (b8 % 2, b8 / 2);
5557 let mut res = [0i32; 64];
5558 for dy in 0..8 {
5559 for dx in 0..8 {
5560 let sx = mb_x * 16 + b8x * 8 + dx;
5561 let syy = mb_y * 16 + b8y * 8 + dy;
5562 let p = pred_y[(b8y * 8 + dy) * 16 + (b8x * 8 + dx)] as i32;
5563 res[dy * 8 + dx] = sy[syy * cw + sx] as i32 - p;
5564 }
5565 }
5566 let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, 6);
5567 let mut nz = false;
5568 for &l in &levels {
5569 if l != 0 {
5570 nz = true;
5571 rate += rdoq_rate((l as i64).abs());
5572 }
5573 }
5574 if nz {
5575 cbp |= 1 << b8;
5576 }
5577 q8[b8] = levels;
5578
5579 let res_r = inverse_quant_8x8(&levels, qp, &weight);
5580 let predb: [i32; 64] =
5581 std::array::from_fn(|i| pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32);
5582 let recon = add_residual_8x8(&res_r, &predb);
5583 for dy in 0..8 {
5584 for dx in 0..8 {
5585 let ri = (b8y * 8 + dy) * 16 + (b8x * 8 + dx);
5586 rec[ri] = recon[dy * 8 + dx];
5587 let sx = mb_x * 16 + b8x * 8 + dx;
5588 let syy = mb_y * 16 + b8y * 8 + dy;
5589 let d = recon[dy * 8 + dx] as i64 - sy[syy * cw + sx] as i64;
5590 ssd += d * d;
5591 }
5592 }
5593 }
5594 (q8, cbp, rate, rec, ssd)
5595}
5596
5597#[inline]
5602fn i16_pred(
5603 fe: &FrameEncoder,
5604 mode: I16Mode,
5605 avail_top: bool,
5606 avail_left: bool,
5607 top: &[u8; 16],
5608 left: &[u8; 16],
5609 corner: u8,
5610 lx: usize,
5611 ly: usize,
5612) -> [u8; 256] {
5613 #[cfg(accel)]
5614 if avail_top && avail_left {
5615 let mode_n = match mode {
5616 I16Mode::Vertical => 0,
5617 I16Mode::Horizontal => 1,
5618 I16Mode::Dc => 2,
5619 I16Mode::Plane => 3,
5620 };
5621 let mut p = AlignedMb([0; 256]);
5622 rusty_h264_accel::i16x16_luma_pred(mode_n, &mut p.0, &fe.rec_y[..], ly * fe.cw + lx, fe.cw);
5623 return p.0;
5624 }
5625 let _ = (fe, lx, ly);
5626 luma16x16_pred(mode, avail_top, avail_left, top, left, corner)
5627}
5628
5629#[inline]
5633#[allow(clippy::too_many_arguments)]
5634fn chroma_pred(
5635 fe: &FrameEncoder,
5636 mode: u8,
5637 avail_top: bool,
5638 avail_left: bool,
5639 c: usize,
5640 top: &[u8; 8],
5641 left: &[u8; 8],
5642 corner: u8,
5643 cx: usize,
5644 cy: usize,
5645) -> [u8; 64] {
5646 #[cfg(accel)]
5647 if avail_top && avail_left && (mode == 2 || mode == 3) {
5648 let plane = if c == 0 { &fe.rec_u } else { &fe.rec_v };
5649 let mut p = AlignedMb([0; 256]);
5650 rusty_h264_accel::chroma8x8_pred(mode, &mut p.0[..64], &plane[..], cy * fe.ccw + cx, fe.ccw);
5651 let mut out = [0u8; 64];
5652 out.copy_from_slice(&p.0[..64]);
5653 return out;
5654 }
5655 let _ = (fe, c, cx, cy);
5656 chroma8x8_pred(mode, avail_top, avail_left, top, left, corner)
5657}
5658
5659fn predict_i4_mode(fe: &FrameEncoder, bx: usize, by: usize) -> u8 {
5662 if bx == 0 || by == 0 {
5663 return 2;
5664 }
5665 let w4 = fe.mb_w * 4;
5666 fe.modes_y[by * w4 + (bx - 1)].min(fe.modes_y[(by - 1) * w4 + bx])
5667}
5668
5669#[allow(clippy::too_many_arguments)]
5670const RDOQ_ZZ: [usize; 16] = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
5672
5673#[inline]
5678fn rdoq_rate(level: i64) -> f64 {
5679 if level == 0 {
5680 1.0
5681 } else if level == 1 {
5682 3.0 } else {
5684 3.0 + (level - 1).min(13) as f64
5686 }
5687}
5688
5689fn rdoq(coeffs: &[i32; 16], qp: u8, dz_div: i64, strength: f64, first: usize) -> [i32; 16] {
5696 let mut q = quantize(coeffs, qp, dz_div);
5697 if strength <= 0.0 {
5698 return q;
5699 }
5700 let lambda = strength * 2f64.powf((qp as f64 - 12.0) / 3.0);
5701 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
5705 const POS: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7];
5706 let dist = |p: usize, level: i64| -> f64 {
5707 let e = coeffs[p].unsigned_abs() as f64 - level as f64 * (65536.0 / mf[POS[p]] as f64);
5708 e * e
5709 };
5710 for i in first..16 {
5712 let p = RDOQ_ZZ[i];
5713 let m = q[p].unsigned_abs() as i64;
5714 if m == 0 {
5715 continue;
5716 }
5717 let j_keep = dist(p, m) + lambda * rdoq_rate(m);
5718 let j_down = dist(p, m - 1) + lambda * rdoq_rate(m - 1);
5719 if j_down < j_keep {
5720 let nl = (m - 1) as i32;
5721 q[p] = if q[p] < 0 { -nl } else { nl };
5722 }
5723 }
5724 loop {
5729 let Some(li) = (first..16).rev().find(|&i| q[RDOQ_ZZ[i]] != 0) else {
5730 break;
5731 };
5732 let p = RDOQ_ZZ[li];
5733 let m = q[p].unsigned_abs() as i64;
5734 let prev = (first..li).rev().find(|&i| q[RDOQ_ZZ[i]] != 0);
5735 let base = prev.map_or(first, |j| j + 1);
5736 let bits = rdoq_rate(m) + 1.0 + (li - base) as f64; let d_add = dist(p, 0) - dist(p, m);
5738 if d_add < lambda * bits {
5739 q[p] = 0;
5740 } else {
5741 break;
5742 }
5743 }
5744 q
5745}
5746
5747fn plan_mb(
5752 fe: &mut FrameEncoder,
5753 mb_x: usize,
5754 mb_y: usize,
5755 sy: &[u8],
5756 su: &[u8],
5757 sv: &[u8],
5758) -> MbPlan {
5759 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCode);
5760 let qp = fe.qp;
5761 let qpc = fe.qpc;
5762 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
5764
5765 let (lx, ly) = (mb_x * 16, mb_y * 16);
5767 let avail_top = mb_y > 0;
5768 let avail_left = mb_x > 0;
5769 let mut top = [0u8; 16];
5770 let mut left = [0u8; 16];
5771 if avail_top {
5772 for i in 0..16 {
5773 top[i] = fe.rec_y[(ly - 1) * fe.cw + lx + i];
5774 }
5775 }
5776 if avail_left {
5777 for i in 0..16 {
5778 left[i] = fe.rec_y[(ly + i) * fe.cw + lx - 1];
5779 }
5780 }
5781 let corner = if avail_top && avail_left {
5782 fe.rec_y[(ly - 1) * fe.cw + lx - 1]
5783 } else {
5784 0
5785 };
5786
5787 let w4 = fe.mb_w * 4;
5788
5789 let mut i16_mode = I16Mode::Dc;
5791 let mut best_pred = i16_pred(fe, I16Mode::Dc, avail_top, avail_left, &top, &left, corner, lx, ly);
5792 let mut best_cost = satd_16x16(sy, fe.cw, lx, ly, &best_pred);
5793 for mode in [I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
5794 if !mode.available(avail_top, avail_left) {
5795 continue;
5796 }
5797 let pred = i16_pred(fe, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
5798 let cost = satd_16x16(sy, fe.cw, lx, ly, &pred);
5799 if cost < best_cost {
5800 best_cost = cost;
5801 i16_mode = mode;
5802 best_pred = pred;
5803 }
5804 }
5805 let mut dc4x4 = [0i32; 16];
5808 let mut i16_q = [[0i32; 16]; 16];
5809 #[cfg(accel)]
5814 let (i16_dc_levels, _i16_recon_dc, recon16) = {
5815 #[repr(align(16))]
5816 struct A([i16; 256]);
5817 let mut dct = A([0i16; 256]);
5818 let base = ly * fe.cw + lx;
5819 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
5820 rusty_h264_accel::dct_four_t4(
5821 &mut dct.0[qi * 64..qi * 64 + 64],
5822 &sy[base + qy * fe.cw + qx..],
5823 fe.cw,
5824 &best_pred[qy * 16 + qx..],
5825 16,
5826 );
5827 }
5828 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
5829 dc4x4[lby * 4 + lbx] = dct.0[blk * 16] as i32;
5830 }
5831 if fe.rdoq_strength > 0.0 {
5832 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
5836 let coeffs: [i32; 16] = std::array::from_fn(|i| dct.0[blk * 16 + i] as i32);
5837 let mut q = rdoq(&coeffs, qp, fe.idz, fe.rdoq_strength, 1);
5838 q[0] = 0;
5839 i16_q[lby * 4 + lbx] = q;
5840 }
5841 } else {
5842 let ff = rusty_h264_common::transform::quant_dz_ff(qp, fe.idz);
5843 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
5844 for qi in 0..4 {
5845 rusty_h264_accel::quant_four_4x4(&mut dct.0[qi * 64..qi * 64 + 64], &ff, mf);
5846 }
5847 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
5848 let q = &mut i16_q[lby * 4 + lbx];
5849 q[0] = 0;
5850 for i in 1..16 {
5851 q[i] = dct.0[blk * 16 + i] as i32;
5852 }
5853 }
5854 }
5855 let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
5856 let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
5857 let mut recon16 = [0u8; 256];
5860 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
5861 let mut deq = dequantize(&i16_q[lby * 4 + lbx], qp);
5862 deq[0] = i16_recon_dc[lby * 4 + lbx];
5863 for i in 0..16 {
5864 dct.0[blk * 16 + i] = deq[i] as i16;
5865 }
5866 }
5867 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
5868 rusty_h264_accel::idct_four_t4_rec(
5869 &mut recon16[qy * 16 + qx..],
5870 16,
5871 &best_pred[qy * 16 + qx..],
5872 16,
5873 &dct.0[qi * 64..qi * 64 + 64],
5874 );
5875 }
5876 (i16_dc_levels, i16_recon_dc, recon16)
5877 };
5878 #[cfg(not(accel))]
5879 let (i16_dc_levels, _i16_recon_dc, recon16) = {
5880 let mut res_blocks = [[0i32; 16]; 16];
5881 for by in 0..4 {
5882 for bx in 0..4 {
5883 let predb = pred_block(&best_pred, bx, by);
5884 res_blocks[by * 4 + bx] = residual(sy, fe.cw, lx + bx * 4, ly + by * 4, &predb);
5885 }
5886 }
5887 let mut coeffs = [[0i32; 16]; 16];
5888 forward_dct_blocks(&res_blocks, &mut coeffs);
5889 for i in 0..16 {
5890 dc4x4[i] = coeffs[i][0];
5891 let mut q = rdoq(&coeffs[i], qp, fe.idz, fe.rdoq_strength, 1);
5892 q[0] = 0;
5893 i16_q[i] = q;
5894 }
5895 let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
5896 let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
5897 let mut recon16 = [0u8; 256];
5898 let mut deq_blocks = [[0i32; 16]; 16];
5899 for i in 0..16 {
5900 deq_blocks[i] = dequantize(&i16_q[i], qp);
5901 deq_blocks[i][0] = i16_recon_dc[i];
5902 }
5903 let mut idct = [[0i32; 16]; 16];
5904 inverse_dct_blocks(&deq_blocks, &mut idct);
5905 for by in 0..4 {
5906 for bx in 0..4 {
5907 let s = add_residual_4x4(&idct[by * 4 + bx], &pred_block(&best_pred, bx, by));
5908 for dy in 0..4 {
5909 for dx in 0..4 {
5910 recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)] = s[dy * 4 + dx];
5911 }
5912 }
5913 }
5914 }
5915 (i16_dc_levels, i16_recon_dc, recon16)
5916 };
5917 let i16_cbp15 = i16_q.iter().any(|b| b[1..].iter().any(|&c| c != 0));
5918 let i16_dc_nz = i16_dc_levels.iter().filter(|&&v| v != 0).count() as i64;
5919 let i16_ac_nz: i64 = i16_q
5920 .iter()
5921 .map(|b| b[1..].iter().filter(|&&v| v != 0).count() as i64)
5922 .sum();
5923 let i16_rate = i16_dc_nz + i16_ac_nz + if i16_cbp15 { 16 } else { 0 };
5925 let mut ssd16 = 0i64;
5927 for dy in 0..16 {
5928 for dx in 0..16 {
5929 let d = recon16[dy * 16 + dx] as i64 - sy[(ly + dy) * fe.cw + (lx + dx)] as i64;
5930 ssd16 += d * d;
5931 }
5932 }
5933
5934 let (cx, cy) = (mb_x * 8, mb_y * 8);
5936 let mut ntop = [[0u8; 8]; 2];
5938 let mut nleft = [[0u8; 8]; 2];
5939 let mut ncorner = [0u8; 2];
5940 for c in 0..2 {
5941 let rec_c = if c == 0 { &fe.rec_u } else { &fe.rec_v };
5942 if avail_top {
5943 for i in 0..8 {
5944 ntop[c][i] = rec_c[(cy - 1) * fe.ccw + cx + i];
5945 }
5946 }
5947 if avail_left {
5948 for i in 0..8 {
5949 nleft[c][i] = rec_c[(cy + i) * fe.ccw + cx - 1];
5950 }
5951 }
5952 if avail_top && avail_left {
5953 ncorner[c] = rec_c[(cy - 1) * fe.ccw + cx - 1];
5954 }
5955 }
5956 let mut chroma_mode = 0u8;
5957 let mut best_c_cost = i64::MAX;
5958 for m in 0..4u8 {
5959 if !chroma_mode_available(m, avail_top, avail_left) {
5960 continue;
5961 }
5962 let mut cost = 0i64;
5963 for c in 0..2 {
5964 let src = if c == 0 { su } else { sv };
5965 let pred8 = chroma_pred(fe, m, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy);
5966 cost += satd_8x8(src, fe.ccw, cx, cy, &pred8);
5967 }
5968 if cost < best_c_cost {
5969 best_c_cost = cost;
5970 chroma_mode = m;
5971 }
5972 }
5973
5974 let mut c_dc_levels = [[0i32; 4]; 2];
5975 let mut c_q_blocks = [[[0i32; 16]; 4]; 2];
5976 let mut any_chroma_ac = false;
5977 let mut any_chroma_dc = false;
5978 for c in 0..2 {
5979 let src = if c == 0 { su } else { sv };
5980 let pred8 =
5981 chroma_pred(fe, chroma_mode, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy);
5982 let pblk = |bx: usize, by: usize| -> [i32; 16] {
5983 let mut predb = [0i32; 16];
5984 for dy in 0..4 {
5985 for dx in 0..4 {
5986 predb[dy * 4 + dx] = pred8[(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
5987 }
5988 }
5989 predb
5990 };
5991 let mut dc2x2 = [0i32; 4];
5995 let mut qbs = [[0i32; 16]; 4];
5996 #[cfg(accel)]
5997 let recon_dc = {
5998 #[repr(align(16))]
5999 struct A([i16; 64]);
6000 let mut d = A([0i16; 64]);
6001 rusty_h264_accel::dct_four_t4(&mut d.0, &src[cy * fe.ccw + cx..], fe.ccw, &pred8, 8);
6002 for i in 0..4 {
6003 dc2x2[i] = d.0[i * 16] as i32;
6004 }
6005 if fe.rdoq_strength > 0.0 {
6006 for i in 0..4 {
6008 let coeffs: [i32; 16] = std::array::from_fn(|j| d.0[i * 16 + j] as i32);
6009 let mut q = rdoq(&coeffs, qpc, fe.idz, fe.rdoq_strength, 1);
6010 q[0] = 0;
6011 if q[1..].iter().any(|&v| v != 0) {
6012 any_chroma_ac = true;
6013 }
6014 qbs[i] = q;
6015 }
6016 } else {
6017 let ff = rusty_h264_common::transform::quant_dz_ff(qpc, fe.idz);
6018 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
6019 rusty_h264_accel::quant_four_4x4(&mut d.0, &ff, mf);
6020 for i in 0..4 {
6021 let q = &mut qbs[i];
6022 q[0] = 0;
6023 for j in 1..16 {
6024 let v = d.0[i * 16 + j] as i32;
6025 q[j] = v;
6026 if v != 0 {
6027 any_chroma_ac = true;
6028 }
6029 }
6030 }
6031 }
6032 let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
6033 if dl.iter().any(|&v| v != 0) {
6034 any_chroma_dc = true;
6035 }
6036 let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
6037 for i in 0..4 {
6038 let deq = dequantize(&qbs[i], qpc);
6039 for j in 0..16 {
6040 d.0[i * 16 + j] = deq[j] as i16;
6041 }
6042 d.0[i * 16] = recon_dc[i] as i16;
6043 }
6044 let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
6045 rusty_h264_accel::idct_four_t4_rec(&mut plane[cy * fe.ccw + cx..], fe.ccw, &pred8, 8, &d.0);
6046 c_dc_levels[c] = dl;
6047 recon_dc
6048 };
6049 #[cfg(not(accel))]
6050 let recon_dc = {
6051 let mut res_blocks = [[0i32; 16]; 4];
6052 for by in 0..2 {
6053 for bx in 0..2 {
6054 res_blocks[by * 2 + bx] =
6055 residual(src, fe.ccw, cx + bx * 4, cy + by * 4, &pblk(bx, by));
6056 }
6057 }
6058 let mut coeffs = [[0i32; 16]; 4];
6059 forward_dct_blocks(&res_blocks, &mut coeffs);
6060 for i in 0..4 {
6061 dc2x2[i] = coeffs[i][0];
6062 let mut q = rdoq(&coeffs[i], qpc, fe.idz, fe.rdoq_strength, 1);
6063 q[0] = 0;
6064 qbs[i] = q;
6065 if q[1..].iter().any(|&v| v != 0) {
6066 any_chroma_ac = true;
6067 }
6068 }
6069 let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
6070 if dl.iter().any(|&v| v != 0) {
6071 any_chroma_dc = true;
6072 }
6073 let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
6074 let mut deq_blocks = [[0i32; 16]; 4];
6075 for i in 0..4 {
6076 deq_blocks[i] = dequantize(&qbs[i], qpc);
6077 deq_blocks[i][0] = recon_dc[i];
6078 }
6079 let mut idct = [[0i32; 16]; 4];
6080 inverse_dct_blocks(&deq_blocks, &mut idct);
6081 let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
6082 for by in 0..2 {
6083 for bx in 0..2 {
6084 let s = add_residual_4x4(&idct[by * 2 + bx], &pblk(bx, by));
6085 store(plane, fe.ccw, cx + bx * 4, cy + by * 4, &s);
6086 }
6087 }
6088 c_dc_levels[c] = dl;
6089 recon_dc
6090 };
6091 let _ = recon_dc;
6092 c_q_blocks[c] = qbs;
6093 }
6094 let cbp_chroma: u32 = if any_chroma_ac {
6095 2
6096 } else if any_chroma_dc {
6097 1
6098 } else {
6099 0
6100 };
6101
6102 let base = ly * fe.cw + lx;
6108 let i4 = if i16_rate > 2 {
6109 Some(plan_i4x4(fe, sy, mb_x, mb_y, qp))
6110 } else {
6111 None
6112 };
6113 let (j4, i4_recon) = match &i4 {
6114 Some(p) => {
6115 let mut ssd = 0i64;
6116 let mut rec = [0u8; 256];
6117 for i in 0..256 {
6118 let v = fe.rec_y[base + (i / 16) * fe.cw + i % 16];
6119 rec[i] = v;
6120 let d = v as i64 - sy[base + (i / 16) * fe.cw + i % 16] as i64;
6121 ssd += d * d;
6122 }
6123 (ssd as f64 + lambda * (p.nonzero + 16) as f64, Some(rec))
6124 }
6125 None => (f64::INFINITY, None),
6126 };
6127 let i8 = if fe.transform_8x8 {
6128 Some(plan_i8x8(fe, sy, mb_x, mb_y, qp))
6129 } else {
6130 None
6131 };
6132 let j8 = match &i8 {
6133 Some(p) => {
6134 let mut ssd = 0i64;
6135 for i in 0..256 {
6136 let d = fe.rec_y[base + (i / 16) * fe.cw + i % 16] as i64
6137 - sy[base + (i / 16) * fe.cw + i % 16] as i64;
6138 ssd += d * d;
6139 }
6140 ssd as f64 + lambda * (p.nonzero + 16) as f64
6141 }
6142 None => f64::INFINITY,
6143 };
6144 let j16 = ssd16 as f64 + lambda * i16_rate as f64;
6145
6146 let (use_i4, i4, i8) = if i8.is_some() && j8 <= j4 && j8 <= j16 {
6148 (true, None, i8)
6150 } else if i4.is_some() && j4 < j16 {
6151 let rec = i4_recon.unwrap();
6153 for i in 0..256 {
6154 fe.rec_y[base + (i / 16) * fe.cw + i % 16] = rec[i];
6155 }
6156 let modes = i4.as_ref().unwrap().modes;
6157 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6158 fe.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = modes[lby * 4 + lbx];
6159 }
6160 (true, i4, None)
6161 } else {
6162 for by in 0..4 {
6164 for bx in 0..4 {
6165 for dy in 0..4 {
6166 for dx in 0..4 {
6167 fe.rec_y[(ly + by * 4 + dy) * fe.cw + (lx + bx * 4 + dx)] =
6168 recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)];
6169 }
6170 }
6171 }
6172 }
6173 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6174 fe.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
6175 }
6176 (false, None, None)
6177 };
6178 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6180 fe.coded_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = true;
6181 }
6182
6183 MbPlan {
6184 use_i4,
6185 i16_mode,
6186 i16_cbp15,
6187 i16_dc_levels,
6188 i16_q,
6189 i4,
6190 i8,
6191 chroma_mode,
6192 cbp_chroma,
6193 c_dc_levels,
6194 c_q_blocks,
6195 }
6196}
6197
6198fn encode_mb(
6201 fe: &mut FrameEncoder,
6202 w: &mut BitWriter,
6203 mb_x: usize,
6204 mb_y: usize,
6205 sy: &[u8],
6206 su: &[u8],
6207 sv: &[u8],
6208 is_p: bool,
6209) {
6210 let plan = plan_mb(fe, mb_x, mb_y, sy, su, sv);
6211 let mb_type_offset = if is_p { 5 } else { 0 };
6213 let w4 = fe.mb_w * 4;
6214 let cbp_chroma = plan.cbp_chroma;
6215
6216 if let Some(i8) = plan.i8.as_ref().filter(|_| plan.use_i4) {
6218 let cbp = i8.cbp_luma | (cbp_chroma << 4);
6222 w.write_ue(mb_type_offset); w.write_bit(true); for b8 in 0..4usize {
6225 let (bx, by) = (mb_x * 4 + (b8 % 2) * 2, mb_y * 4 + (b8 / 2) * 2);
6226 let predicted = predict_i4_mode(fe, bx, by);
6227 let actual = i8.modes[b8];
6228 if actual == predicted {
6229 w.write_bit(true);
6230 } else {
6231 w.write_bit(false);
6232 let rem = if actual < predicted { actual } else { actual - 1 };
6233 w.write_bits(rem as u32, 3);
6234 }
6235 }
6236 w.write_ue(plan.chroma_mode as u32); write_cbp_intra(w, cbp);
6238 if cbp != 0 {
6239 w.write_se(fe.qp_delta());
6240 }
6241 fe.nnz_cache_load(mb_x, mb_y);
6242 for b8 in 0..4usize {
6243 let (b8x, b8y) = (b8 % 2, b8 / 2);
6244 let scan8 = scan_8x8_fwd(&i8.q[b8]);
6245 for sub in 0..4usize {
6246 let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
6247 let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
6248 let total = if i8.cbp_luma & (1 << b8) != 0 {
6249 let nc = fe.nc_pred(cx, cy);
6250 let blk: [i32; 16] = std::array::from_fn(|k| scan8[4 * k + sub]);
6251 encode_residual_block(w, &blk, 16, nc) as u8
6252 } else {
6253 0
6254 };
6255 fe.nnz_cache_set(cx, cy, total);
6256 fe.nnz_y[by * w4 + bx] = total;
6257 }
6258 }
6259 } else if plan.use_i4 {
6260 let i4 = plan.i4.as_ref().unwrap();
6261 let cbp = i4.cbp_luma | (cbp_chroma << 4);
6262 w.write_ue(mb_type_offset); if fe.transform_8x8 {
6264 w.write_bit(false); }
6266 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6267 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6268 let predicted = predict_i4_mode(fe, bx, by);
6269 let actual = i4.modes[lby * 4 + lbx];
6270 if actual == predicted {
6271 w.write_bit(true);
6272 } else {
6273 w.write_bit(false);
6274 let rem = if actual < predicted { actual } else { actual - 1 };
6275 w.write_bits(rem as u32, 3);
6276 }
6277 }
6278 w.write_ue(plan.chroma_mode as u32); write_cbp_intra(w, cbp);
6280 if cbp != 0 {
6281 w.write_se(fe.qp_delta()); }
6283 fe.nnz_cache_load(mb_x, mb_y);
6284 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6285 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6286 let total = if i4.cbp_luma & (1 << (blk / 4)) != 0 {
6287 let nc = fe.nc_pred(lbx, lby);
6288 let scan16 = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
6289 encode_residual_block(w, &scan16, 16, nc) as u8
6290 } else {
6291 0
6292 };
6293 fe.nnz_cache_set(lbx, lby, total);
6294 fe.nnz_y[by * w4 + bx] = total;
6295 }
6296 } else {
6297 let mb_type = 1 + plan.i16_mode as u32 + 4 * cbp_chroma + if plan.i16_cbp15 { 12 } else { 0 };
6298 w.write_ue(mb_type + mb_type_offset);
6299 w.write_ue(plan.chroma_mode as u32); w.write_se(fe.qp_delta()); fe.nnz_cache_load(mb_x, mb_y);
6302 let nc_dc = fe.nc_pred(0, 0);
6303 let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
6304 encode_residual_block(w, &dc_scan, 16, nc_dc);
6305 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6306 fe.nnz_cache_set(lbx, lby, 0);
6307 fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
6308 }
6309 if plan.i16_cbp15 {
6310 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6311 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6312 let nc = fe.nc_pred(lbx, lby);
6313 let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
6314 let total = encode_residual_block(w, &ac, 15, nc) as u8;
6315 fe.nnz_cache_set(lbx, lby, total);
6316 fe.nnz_y[by * w4 + bx] = total;
6317 }
6318 }
6319 }
6320
6321 if cbp_chroma != 0 {
6323 for c in 0..2 {
6324 encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
6325 }
6326 }
6327 if cbp_chroma == 2 {
6328 fe.chroma_cache_load(mb_x, mb_y);
6329 let w2 = fe.mb_w * 2;
6330 for c in 0..2 {
6331 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
6332 let nc = fe.chroma_nc_pred(c, bx, by);
6333 let ac = scan_4x4_ac(&plan.c_q_blocks[c][by * 2 + bx]);
6334 let total = encode_residual_block(w, &ac, 15, nc) as u8;
6335 fe.chroma_nnz_cache_set(c, bx, by, total);
6336 fe.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
6337 }
6338 }
6339 }
6340}
6341
6342const CB_NZC_CACHE: [usize; 24] = [
6353 9, 10, 17, 18, 11, 12, 19, 20, 25, 26, 33, 34, 27, 28, 35, 36, 14, 15, 22, 23, 38, 39, 46, 47, ];
6357const CB_RES_MAXPOS: [i32; 11] = [0, 15, 14, 15, 3, 14, 63, 3, 3, 14, 14];
6358const CB_RES_MAXC2: [i32; 11] = [0, 4, 4, 4, 3, 4, 4, 3, 3, 4, 4];
6359const CB_RES_CBF: [usize; 11] = [0, 0, 4, 8, 12, 16, 0, 12, 12, 16, 16];
6360const CB_RES_MAP: [usize; 11] = [0, 0, 15, 29, 44, 47, 0, 44, 44, 47, 47];
6361const CB_RES_ONE: [usize; 11] = [0, 0, 10, 20, 30, 39, 0, 30, 30, 39, 39];
6362const CB_RP_I16_DC: usize = 1;
6363const CB_RP_I16_AC: usize = 2;
6364const CB_RP_LUMA_4X4: usize = 3;
6365const CB_RP_CHROMA_DC: usize = 7;
6366const CB_RP_CHROMA_AC: usize = 9;
6367
6368fn cb_unary(cab: &mut CabacEncoder, ctx: usize, off: usize, value: u32) {
6371 if value == 0 {
6372 cab.encode_decision(ctx, 0);
6373 return;
6374 }
6375 cab.encode_decision(ctx, 1);
6376 for _ in 0..value - 1 {
6377 cab.encode_decision(ctx + off, 1);
6378 }
6379 cab.encode_decision(ctx + off, 0);
6380}
6381
6382fn cb_exp_bypass(cab: &mut CabacEncoder, mut k: i32, mut n: u32) {
6384 while n >= (1 << k) {
6385 cab.encode_bypass(1);
6386 n -= 1 << k;
6387 k += 1;
6388 }
6389 cab.encode_bypass(0);
6390 while k > 0 {
6391 k -= 1;
6392 cab.encode_bypass((n >> k) & 1);
6393 }
6394}
6395
6396fn cb_ueg_level(cab: &mut CabacEncoder, ctx: usize, value: u32) {
6399 if value == 0 {
6400 cab.encode_decision(ctx, 0);
6401 return;
6402 }
6403 let ones = value.min(13);
6404 for _ in 0..ones {
6405 cab.encode_decision(ctx, 1);
6406 }
6407 if value < 13 {
6408 cab.encode_decision(ctx, 0);
6409 } else {
6410 cb_exp_bypass(cab, 0, value - 13);
6411 }
6412}
6413
6414fn cb_mb_qp_delta(cab: &mut CabacEncoder, last_delta_qp: &mut i32, delta: i32) {
6416 const O: usize = 60;
6417 let ctx_inc = (*last_delta_qp != 0) as usize;
6418 if delta == 0 {
6419 cab.encode_decision(O + ctx_inc, 0);
6420 } else {
6421 cab.encode_decision(O + ctx_inc, 1);
6422 let code = 2 * delta.unsigned_abs() - (delta > 0) as u32;
6424 cb_unary(cab, O + 2, 1, code - 1);
6425 }
6426 *last_delta_qp = delta;
6427}
6428
6429fn cb_chroma_pred_mode(cab: &mut CabacEncoder, ctx_inc: usize, mode: u8) {
6431 const C: usize = 64;
6432 if mode == 0 {
6433 cab.encode_decision(C + ctx_inc, 0);
6434 return;
6435 }
6436 cab.encode_decision(C + ctx_inc, 1);
6437 if mode == 1 {
6438 cab.encode_decision(C + 3, 0);
6439 } else if mode == 2 {
6440 cab.encode_decision(C + 3, 1);
6441 cab.encode_decision(C + 3, 0);
6442 } else {
6443 cab.encode_decision(C + 3, 1);
6444 cab.encode_decision(C + 3, 1);
6445 }
6446}
6447
6448fn cb_mb_type_i(
6450 cab: &mut CabacEncoder,
6451 ctx_inc: usize,
6452 use_i4: bool,
6453 i16_mode: u32,
6454 cbp_chroma: u32,
6455 cbp_luma15: bool,
6456) {
6457 const O: usize = 3;
6458 if use_i4 {
6459 cab.encode_decision(O + ctx_inc, 0); return;
6461 }
6462 cab.encode_decision(O + ctx_inc, 1);
6463 cab.encode_terminate(false); cab.encode_decision(O + 3, cbp_luma15 as u32);
6465 if cbp_chroma != 0 {
6466 cab.encode_decision(O + 4, 1);
6467 cab.encode_decision(O + 5, (cbp_chroma == 2) as u32);
6468 } else {
6469 cab.encode_decision(O + 4, 0);
6470 }
6471 cab.encode_decision(O + 6, (i16_mode >> 1) & 1);
6472 cab.encode_decision(O + 7, i16_mode & 1);
6473}
6474
6475fn cb_intra4x4_pred_mode(cab: &mut CabacEncoder, predicted: u8, actual: u8) {
6477 const IPR: usize = 68;
6478 if actual == predicted {
6479 cab.encode_decision(IPR, 1);
6480 } else {
6481 cab.encode_decision(IPR, 0);
6482 let rem = if actual < predicted { actual } else { actual - 1 } as u32;
6483 cab.encode_decision(IPR + 1, rem & 1);
6484 cab.encode_decision(IPR + 1, (rem >> 1) & 1);
6485 cab.encode_decision(IPR + 1, (rem >> 2) & 1);
6486 }
6487}
6488
6489fn cb_cbp(cab: &mut CabacEncoder, top: Option<u8>, left: Option<u8>, cbp: u32) {
6491 const CBP: usize = 73;
6492 let t = |m: u32| top.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
6493 let l = |m: u32| left.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
6494 let nb = |x: u32| (x == 0) as u32;
6495 let b0 = cbp & 1;
6496 let b1 = (cbp >> 1) & 1;
6497 let b2 = (cbp >> 2) & 1;
6498 let b3 = (cbp >> 3) & 1;
6499 cab.encode_decision(CBP + (l(1 << 1) + (t(1 << 2) << 1)) as usize, b0);
6500 cab.encode_decision(CBP + (nb(b0) + (t(1 << 3) << 1)) as usize, b1);
6501 cab.encode_decision(CBP + (l(1 << 3) + (nb(b0) << 1)) as usize, b2);
6502 cab.encode_decision(CBP + (nb(b2) + (nb(b1) << 1)) as usize, b3);
6503 let cbp_chroma = cbp >> 4;
6504 let ct = top.map_or(0u32, |c| ((c >> 4) != 0) as u32);
6505 let cl = left.map_or(0u32, |c| ((c >> 4) != 0) as u32);
6506 cab.encode_decision(CBP + 4 + (cl + (ct << 1)) as usize, (cbp_chroma != 0) as u32);
6507 if cbp_chroma != 0 {
6508 let ct2 = top.map_or(0u32, |c| ((c >> 4) == 2) as u32);
6509 let cl2 = left.map_or(0u32, |c| ((c >> 4) == 2) as u32);
6510 cab.encode_decision(CBP + 8 + (cl2 + (ct2 << 1)) as usize, (cbp_chroma == 2) as u32);
6511 }
6512}
6513
6514#[allow(clippy::too_many_arguments)]
6517fn cb_residual(
6518 cab: &mut CabacEncoder,
6519 nzc: &mut [u8; 48],
6520 cbf_dc: &mut u16,
6521 iz: usize,
6522 rp: usize,
6523 is_intra: bool,
6524 ndc: (Option<u16>, Option<u16>),
6525 coeffs: &[i32],
6526) -> u32 {
6527 let is_dc = rp == CB_RP_I16_DC || rp == CB_RP_CHROMA_DC || rp == CB_RP_CHROMA_DC + 1;
6528 let (mut na, mut nb) = (is_intra as u8, is_intra as u8);
6529 let scan = CB_NZC_CACHE[iz.min(23)];
6530 if is_dc {
6531 if let Some(t) = ndc.0 {
6532 nb = ((t >> rp) & 1) as u8;
6533 }
6534 if let Some(l) = ndc.1 {
6535 na = ((l >> rp) & 1) as u8;
6536 }
6537 } else {
6538 if nzc[scan - 8] != 0xff {
6539 nb = (nzc[scan - 8] != 0) as u8;
6540 }
6541 if nzc[scan - 1] != 0xff {
6542 na = (nzc[scan - 1] != 0) as u8;
6543 }
6544 }
6545 let maxpos = CB_RES_MAXPOS[rp] as usize;
6546 let coeff_num = coeffs[..=maxpos].iter().filter(|&&c| c != 0).count() as u32;
6547 let cbf = coeff_num != 0;
6548 cab.encode_decision(85 + CB_RES_CBF[rp] + (na + (nb << 1)) as usize, cbf as u32);
6549 if !cbf {
6550 if !is_dc {
6551 nzc[scan] = 0;
6552 }
6553 return 0;
6554 }
6555 if is_dc {
6556 *cbf_dc |= 1 << rp;
6557 }
6558 let map = 105 + CB_RES_MAP[rp];
6560 let last = 166 + CB_RES_MAP[rp];
6561 let lastnz = (0..=maxpos).rev().find(|&i| coeffs[i] != 0).unwrap();
6562 for i in 0..maxpos {
6563 let s = coeffs[i] != 0;
6564 cab.encode_decision(map + i, s as u32);
6565 if s {
6566 let is_last = i == lastnz;
6567 cab.encode_decision(last + i, is_last as u32);
6568 if is_last {
6569 break;
6570 }
6571 }
6572 }
6573 let one = 227 + CB_RES_ONE[rp];
6575 let abs = 232 + CB_RES_ONE[rp];
6576 let maxc2 = CB_RES_MAXC2[rp];
6577 let (mut c1, mut c2) = (1i32, 0i32);
6578 for i in (0..=maxpos).rev() {
6579 if coeffs[i] != 0 {
6580 let av = coeffs[i].unsigned_abs();
6581 let gt1 = av > 1;
6582 cab.encode_decision(one + c1 as usize, gt1 as u32);
6583 if gt1 {
6584 cb_ueg_level(cab, abs + c2 as usize, av - 2);
6585 c2 = (c2 + 1).min(maxc2);
6586 c1 = 0;
6587 } else if c1 != 0 {
6588 c1 = (c1 + 1).min(4);
6589 }
6590 cab.encode_bypass((coeffs[i] < 0) as u32);
6591 }
6592 }
6593 if !is_dc {
6594 nzc[scan] = coeff_num as u8;
6595 }
6596 coeff_num
6597}
6598
6599fn cb_build_nzc(mb_nzc: &[[u8; 24]], top: Option<usize>, left: Option<usize>) -> [u8; 48] {
6602 let mut nzc = [0xffu8; 48];
6603 if let Some(t) = top {
6604 let tn = mb_nzc[t];
6605 nzc[1..5].copy_from_slice(&tn[12..16]);
6606 (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
6607 (nzc[6], nzc[7]) = (tn[20], tn[21]);
6608 (nzc[30], nzc[31]) = (tn[22], tn[23]);
6609 }
6610 if let Some(l) = left {
6611 let ln = mb_nzc[l];
6612 (nzc[8], nzc[16], nzc[24], nzc[32]) = (ln[3], ln[7], ln[11], ln[15]);
6613 (nzc[13], nzc[21], nzc[37], nzc[45]) = (ln[17], ln[21], ln[19], ln[23]);
6614 }
6615 nzc
6616}
6617
6618fn cb_export_nzc(nzc: &[u8; 48]) -> [u8; 24] {
6620 let mut mn = [0u8; 24];
6621 for k in 0..4 {
6622 mn[k] = nzc[9 + k];
6623 mn[4 + k] = nzc[17 + k];
6624 mn[8 + k] = nzc[25 + k];
6625 mn[12 + k] = nzc[33 + k];
6626 }
6627 (mn[16], mn[17], mn[20], mn[21]) = (nzc[14], nzc[15], nzc[22], nzc[23]);
6628 (mn[18], mn[19], mn[22], mn[23]) = (nzc[38], nzc[39], nzc[46], nzc[47]);
6629 for v in mn.iter_mut() {
6630 if *v == 0xff {
6631 *v = 0;
6632 }
6633 }
6634 mn
6635}
6636
6637struct CabacState {
6640 cat: Vec<u8>, cmode: Vec<i32>, mb_cbp: Vec<u8>, cbf_dc: Vec<u16>, mb_nzc: Vec<[u8; 24]>, mb_mvd: Vec<[[i16; 2]; 16]>, mb_ref: Vec<[i8; 16]>, mb_mvd1: Vec<[[i16; 2]; 16]>, mb_ref1: Vec<[i8; 16]>, mb_skip: Vec<bool>, mb_direct: Vec<bool>, last_delta_qp: i32,
6653}
6654
6655impl CabacState {
6656 fn new(n: usize) -> Self {
6657 CabacState {
6658 cat: vec![0; n],
6659 cmode: vec![0; n],
6660 mb_cbp: vec![0; n],
6661 cbf_dc: vec![0; n],
6662 mb_nzc: vec![[0u8; 24]; n],
6663 mb_mvd: vec![[[0i16; 2]; 16]; n],
6664 mb_ref: vec![[-1i8; 16]; n],
6665 mb_mvd1: vec![[[0i16; 2]; 16]; n],
6666 mb_ref1: vec![[-1i8; 16]; n],
6667 mb_skip: vec![false; n],
6668 mb_direct: vec![false; n],
6669 last_delta_qp: 0,
6670 }
6671 }
6672}
6673
6674fn emit_mb_cabac_i(
6678 fe: &mut FrameEncoder,
6679 cab: &mut CabacEncoder,
6680 cs: &mut CabacState,
6681 plan: &MbPlan,
6682 mb_x: usize,
6683 mb_y: usize,
6684) {
6685 let mb_w = fe.mb_w;
6686 let addr = mb_y * mb_w + mb_x;
6687 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
6688 let left = if mb_x > 0 { Some(addr - 1) } else { None };
6689
6690 let li = left.map_or(0, |a| (cs.cat[a] >= 2) as usize);
6692 let ti = top.map_or(0, |a| (cs.cat[a] >= 2) as usize);
6693 if plan.use_i4 {
6694 cb_mb_type_i(cab, li + ti, true, 0, 0, false);
6695 } else {
6696 cb_mb_type_i(cab, li + ti, false, plan.i16_mode as u32, plan.cbp_chroma, plan.i16_cbp15);
6697 }
6698 emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
6699}
6700
6701#[allow(clippy::too_many_arguments)]
6705fn emit_intra_body_cabac(
6706 fe: &mut FrameEncoder,
6707 cab: &mut CabacEncoder,
6708 cs: &mut CabacState,
6709 plan: &MbPlan,
6710 mb_x: usize,
6711 mb_y: usize,
6712 addr: usize,
6713 top: Option<usize>,
6714 left: Option<usize>,
6715) {
6716 let w4 = fe.mb_w * 4;
6717 let cbp_chroma = plan.cbp_chroma;
6718 let cci = left.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize)
6720 + top.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize);
6721
6722 let mut nzc;
6723 let mut cbfdc = 0u16;
6724 let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
6725
6726 if !plan.use_i4 {
6727 cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
6729 cs.cmode[addr] = plan.chroma_mode as i32;
6730 cs.cat[addr] = 2;
6731 cs.mb_cbp[addr] = ((cbp_chroma as u8) << 4) | if plan.i16_cbp15 { 15 } else { 0 };
6732 nzc = cb_build_nzc(&cs.mb_nzc, top, left);
6733
6734 let delta = fe.qp_delta();
6735 cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
6736
6737 let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
6739 cb_residual(cab, &mut nzc, &mut cbfdc, 0, CB_RP_I16_DC, true, ndc, &dc_scan);
6740 for (iz, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6742 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6743 let total = if plan.i16_cbp15 {
6744 let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
6745 cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_I16_AC, true, ndc, &ac)
6746 } else {
6747 nzc[CB_NZC_CACHE[iz]] = 0;
6748 0
6749 };
6750 fe.nnz_y[by * w4 + bx] = total as u8;
6751 }
6752 cb_emit_chroma_residual(cab, fe, &mut nzc, &mut cbfdc, ndc, true, plan.cbp_chroma, &plan.c_dc_levels, &plan.c_q_blocks, mb_x, mb_y);
6753 } else {
6754 let i4 = plan.i4.as_ref().unwrap();
6756 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6757 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6758 let predicted = predict_i4_mode(fe, bx, by);
6759 cb_intra4x4_pred_mode(cab, predicted, i4.modes[lby * 4 + lbx]);
6760 }
6761 cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
6762 cs.cmode[addr] = plan.chroma_mode as i32;
6763 cs.cat[addr] = 0;
6764 let cbp = i4.cbp_luma | (cbp_chroma << 4);
6765 cb_cbp(cab, top.map(|a| cs.mb_cbp[a]), left.map(|a| cs.mb_cbp[a]), cbp);
6766 cs.mb_cbp[addr] = cbp as u8;
6767 nzc = cb_build_nzc(&cs.mb_nzc, top, left);
6768
6769 if cbp == 0 {
6770 cs.last_delta_qp = 0;
6771 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6772 fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
6773 }
6774 } else {
6775 let delta = fe.qp_delta();
6776 cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
6777 for id8 in 0..4usize {
6778 for id4 in 0..4usize {
6779 let iz = id8 * 4 + id4;
6780 let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
6781 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6782 let total = if i4.cbp_luma & (1 << id8) != 0 {
6783 let sc = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
6784 cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_LUMA_4X4, true, ndc, &sc)
6785 } else {
6786 nzc[CB_NZC_CACHE[iz]] = 0;
6787 0
6788 };
6789 fe.nnz_y[by * w4 + bx] = total as u8;
6790 }
6791 }
6792 cb_emit_chroma_residual(cab, fe, &mut nzc, &mut cbfdc, ndc, true, plan.cbp_chroma, &plan.c_dc_levels, &plan.c_q_blocks, mb_x, mb_y);
6793 }
6794 }
6795
6796 cs.cbf_dc[addr] = cbfdc;
6797 cs.mb_nzc[addr] = cb_export_nzc(&nzc);
6798}
6799
6800#[allow(clippy::too_many_arguments)]
6804fn cb_emit_chroma_residual(
6805 cab: &mut CabacEncoder,
6806 fe: &mut FrameEncoder,
6807 nzc: &mut [u8; 48],
6808 cbfdc: &mut u16,
6809 ndc: (Option<u16>, Option<u16>),
6810 is_intra: bool,
6811 cbp_chroma: u32,
6812 c_dc_levels: &[[i32; 4]; 2],
6813 c_q: &[[[i32; 16]; 4]; 2],
6814 mb_x: usize,
6815 mb_y: usize,
6816) {
6817 let w2 = fe.mb_w * 2;
6818 if cbp_chroma >= 1 {
6819 for i in 0..2usize {
6820 cb_residual(cab, nzc, cbfdc, 16 + i * 4, CB_RP_CHROMA_DC + i, is_intra, ndc, &c_dc_levels[i]);
6821 }
6822 }
6823 if cbp_chroma == 2 {
6824 for i in 0..2usize {
6825 for (id4, &(bx, by)) in CHROMA_4X4_SCAN_XY.iter().enumerate() {
6826 let ac = scan_4x4_ac(&c_q[i][by * 2 + bx]);
6827 let total = cb_residual(
6828 cab, nzc, cbfdc, 16 + i * 4 + id4, CB_RP_CHROMA_AC + i, is_intra, ndc, &ac,
6829 );
6830 fe.nnz_c[i][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total as u8;
6831 }
6832 }
6833 }
6834}
6835
6836pub fn encode_slice_data_cabac_intra(
6841 w: &mut BitWriter,
6842 cfg: &EncoderConfig,
6843 frame: &YuvFrame,
6844 qp: u8,
6845 qpo: &[i32],
6846) -> crate::RefFrame {
6847 let mut fe = FrameEncoder::new(cfg);
6848 fe.qp = qp;
6849 fe.qpc = chroma_qp(qp);
6850 fe.cur_qp = qp;
6851 if cfg.cabac_dz_div > 0 {
6852 fe.idz = cfg.cabac_dz_div; }
6854 let (sy, su, sv) = coded_source(cfg, frame);
6855 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
6856 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
6858 let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
6859
6860 fe.rdoq_strength = if cfg.gop_size <= 1 { cfg.cabac_rdoq } else { 0.0 };
6866 let mut cab = CabacEncoder::new(qp as i32, 0, true);
6868 let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
6869 let total = fe.mb_w * fe.mb_h;
6870
6871 for mb_y in 0..fe.mb_h {
6872 for mb_x in 0..fe.mb_w {
6873 let mb_idx = mb_y * fe.mb_w + mb_x;
6874 fe.qp = aq_qp[mb_idx];
6875 fe.qpc = chroma_qp(aq_qp[mb_idx]);
6876 let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
6877 emit_mb_cabac_i(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
6878 mb_qpy[mb_idx] = fe.cur_qp;
6879 cab.encode_terminate(mb_idx + 1 == total);
6881 }
6882 }
6883
6884 while !w.is_byte_aligned() {
6886 w.write_bit(true);
6887 }
6888 for b in cab.into_bytes() {
6889 w.write_bits(b as u32, 8);
6890 }
6891
6892 let ref_id: Vec<i32> = fe.ref_idx_y.iter().map(|&r| if r >= 0 { r } else { i32::MIN }).collect();
6894 let info = rusty_h264_common::deblock::BlockInfo {
6895 inter: &fe.inter_y,
6896 nnz: &fe.nnz_y,
6897 mv: &fe.mv_y,
6898 ref_id: &ref_id,
6899 mv1: &[],
6900 ref_id1: &[],
6901 w4: fe.mb_w * 4,
6902 t8x8: &[],
6903 bs: &[],
6904 };
6905 rusty_h264_common::deblock::filter_frame(
6906 &mut fe.rec_y, &mut fe.rec_u, &mut fe.rec_v, fe.mb_w, fe.mb_h, &mb_qpy, 0, 0, 0, &info,
6907 );
6908 let w4 = fe.mb_w * 4;
6909 crate::RefFrame {
6910 y: fe.rec_y,
6911 u: fe.rec_u,
6912 v: fe.rec_v,
6913 poc: 0,
6914 frame_num: 0,
6915 mv: fe.mv_y,
6916 ref_idx: fe.ref_idx_y,
6917 w4,
6918 hpel: std::sync::OnceLock::new(),
6920 }
6921}
6922
6923const CB_CACHE30: [usize; 16] = [7, 8, 13, 14, 9, 10, 15, 16, 19, 20, 25, 26, 21, 22, 27, 28];
6933const CB_G_SCAN4: [usize; 16] = [0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15];
6935
6936fn cb_ueg_mv(cab: &mut CabacEncoder, base: usize, v: u32) {
6939 const P2C: [usize; 8] = [0, 1, 2, 3, 3, 3, 3, 3];
6940 if v == 0 {
6941 cab.encode_decision(base, 0);
6942 return;
6943 }
6944 cab.encode_decision(base, 1);
6945 if v <= 7 {
6946 let mut count = 1;
6948 for _ in 0..v - 1 {
6949 cab.encode_decision(base + P2C[count], 1);
6950 count += 1;
6951 }
6952 cab.encode_decision(base + P2C[count], 0);
6953 } else {
6954 let mut count = 1;
6956 for _ in 0..7 {
6957 cab.encode_decision(base + P2C[count], 1);
6958 count += 1;
6959 }
6960 cb_exp_bypass(cab, 3, v - 8);
6961 }
6962}
6963
6964fn cb_mvd(cab: &mut CabacEncoder, comp: usize, ctx_inc: usize, d: i32) {
6967 let base = 40 + comp * 7;
6968 if d == 0 {
6969 cab.encode_decision(base + ctx_inc, 0);
6970 return;
6971 }
6972 cab.encode_decision(base + ctx_inc, 1);
6973 cb_ueg_mv(cab, base + 3, d.unsigned_abs() - 1); cab.encode_bypass((d < 0) as u32);
6975}
6976
6977fn cb_mb_skip(cab: &mut CabacEncoder, ctx_inc: usize, skip: bool) {
6979 cab.encode_decision(ctx_inc, skip as u32);
6980}
6981
6982fn cb_ref_idx(cab: &mut CabacEncoder, ctx0: usize, r: u32) {
6986 const B: usize = 54;
6987 let mut v = r;
6988 let mut bin_idx = 0u32;
6989 loop {
6990 let bin = (v > 0) as u32;
6991 let ctx = match bin_idx {
6992 0 => ctx0,
6993 1 => 4,
6994 _ => 5,
6995 };
6996 cab.encode_decision(B + ctx, bin);
6997 if bin == 0 {
6998 break;
6999 }
7000 v -= 1;
7001 bin_idx += 1;
7002 }
7003}
7004
7005fn cb_mb_type_p_inter(cab: &mut CabacEncoder, mode: u8) {
7008 const S: usize = 11;
7009 cab.encode_decision(S + 3, 0); match mode {
7011 0 => {
7012 cab.encode_decision(S + 4, 0);
7013 cab.encode_decision(S + 5, 0);
7014 }
7015 3 => {
7016 cab.encode_decision(S + 4, 0);
7018 cab.encode_decision(S + 5, 1);
7019 }
7020 1 => {
7021 cab.encode_decision(S + 4, 1);
7022 cab.encode_decision(S + 6, 1);
7023 }
7024 _ => {
7025 cab.encode_decision(S + 4, 1);
7027 cab.encode_decision(S + 6, 0);
7028 }
7029 }
7030}
7031
7032fn cb_sub_mb_type_p(cab: &mut CabacEncoder, sub_type: u8) {
7035 const S: usize = 21;
7036 match sub_type {
7037 0 => cab.encode_decision(S, 1),
7038 _ => unreachable!("only 8x8 sub_mb_type (0) emitted"),
7039 }
7040}
7041
7042fn cb_mb_type_p_intra(cab: &mut CabacEncoder, plan: &MbPlan) {
7046 const S: usize = 11;
7047 cab.encode_decision(S + 3, 1); if plan.use_i4 {
7049 cab.encode_decision(S + 6, 0); return;
7051 }
7052 cab.encode_decision(S + 6, 1); cab.encode_terminate(false); cab.encode_decision(S + 7, plan.i16_cbp15 as u32);
7055 if plan.cbp_chroma != 0 {
7056 cab.encode_decision(S + 8, 1);
7057 cab.encode_decision(S + 8, (plan.cbp_chroma == 2) as u32);
7058 } else {
7059 cab.encode_decision(S + 8, 0);
7060 }
7061 cab.encode_decision(S + 9, (plan.i16_mode as u32 >> 1) & 1);
7062 cab.encode_decision(S + 9, plan.i16_mode as u32 & 1);
7063}
7064
7065fn p_partition_layout(mode: u8) -> &'static [(usize, &'static [usize])] {
7069 match mode {
7070 1 => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7]), (8, &[8, 9, 10, 11, 12, 13, 14, 15])],
7071 2 => &[(0, &[0, 1, 2, 3, 8, 9, 10, 11]), (4, &[4, 5, 6, 7, 12, 13, 14, 15])],
7072 3 => &[(0, &[0, 1, 2, 3]), (4, &[4, 5, 6, 7]), (8, &[8, 9, 10, 11]), (12, &[12, 13, 14, 15])],
7074 _ => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])],
7075 }
7076}
7077
7078#[allow(clippy::too_many_arguments)]
7081fn cb_emit_mvd_partition(
7082 cab: &mut CabacEncoder,
7083 part_idx: usize,
7084 zblocks: &[usize],
7085 mvdc: &mut [[i16; 2]; 30],
7086 refc: &mut [i8; 30],
7087 mmvd: &mut [[i16; 2]; 16],
7088 mref: &mut [i8; 16],
7089 mvd: (i32, i32),
7090 ref_idx: i8, ) {
7092 let s = CB_CACHE30[part_idx];
7093 let ctx = |comp: usize| -> usize {
7094 let mut a = 0i32;
7095 if refc[s - 6] >= 0 {
7096 a += mvdc[s - 6][comp].unsigned_abs() as i32;
7097 }
7098 if refc[s - 1] >= 0 {
7099 a += mvdc[s - 1][comp].unsigned_abs() as i32;
7100 }
7101 if a >= 3 {
7102 1 + (a > 32) as usize
7103 } else {
7104 0
7105 }
7106 };
7107 cb_mvd(cab, 0, ctx(0), mvd.0);
7108 cb_mvd(cab, 1, ctx(1), mvd.1);
7109 let (mx, my) = (mvd.0 as i16, mvd.1 as i16);
7110 for &zb in zblocks {
7111 mvdc[CB_CACHE30[zb]] = [mx, my];
7112 refc[CB_CACHE30[zb]] = ref_idx;
7113 mmvd[CB_G_SCAN4[zb]] = [mx, my];
7114 mref[CB_G_SCAN4[zb]] = ref_idx;
7115 }
7116}
7117
7118fn emit_mb_cabac_p_inter(
7121 fe: &mut FrameEncoder,
7122 cab: &mut CabacEncoder,
7123 cs: &mut CabacState,
7124 mode: u8,
7125 plan: &InterPlan,
7126 mb_x: usize,
7127 mb_y: usize,
7128 num_refs: usize,
7129) {
7130 let mb_w = fe.mb_w;
7131 let addr = mb_y * mb_w + mb_x;
7132 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7133 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7134
7135 cb_mb_type_p_inter(cab, mode);
7136 if mode == 3 {
7138 for _ in 0..4 {
7139 cb_sub_mb_type_p(cab, 0);
7140 }
7141 }
7142
7143 let mut mvdc = [[0i16; 2]; 30];
7145 let mut refc = [-1i8; 30];
7146 cb_fill_inter_cache(&cs.mb_ref, &cs.mb_mvd, &mut refc, &mut mvdc, top, left, addr, mb_w);
7147 let mut mmvd = [[0i16; 2]; 16];
7148 let mut mref = [0i8; 16];
7149 let layout = p_partition_layout(mode);
7150 if num_refs > 1 {
7153 for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
7154 let r = plan.plan_refs[part];
7155 let s = CB_CACHE30[part_idx];
7156 let ctx0 = (refc[s - 1] > 0) as usize + 2 * (refc[s - 6] > 0) as usize;
7157 cb_ref_idx(cab, ctx0, r as u32);
7158 for &zb in zblocks {
7159 refc[CB_CACHE30[zb]] = r as i8;
7160 }
7161 }
7162 }
7163 for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
7165 cb_emit_mvd_partition(
7166 cab, part_idx, zblocks, &mut mvdc, &mut refc, &mut mmvd, &mut mref, plan.mvds[part],
7167 plan.plan_refs[part] as i8,
7168 );
7169 }
7170 cs.mb_mvd[addr] = mmvd;
7171 cs.mb_ref[addr] = mref;
7172 cs.cat[addr] = 100;
7173 cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7174}
7175
7176#[allow(clippy::too_many_arguments)]
7179fn cb_emit_inter_residual(
7180 fe: &mut FrameEncoder,
7181 cab: &mut CabacEncoder,
7182 cs: &mut CabacState,
7183 plan: &InterPlan,
7184 mb_x: usize,
7185 mb_y: usize,
7186 addr: usize,
7187 top: Option<usize>,
7188 left: Option<usize>,
7189) {
7190 let w4 = fe.mb_w * 4;
7191 let cbp = plan.cbp;
7192 let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
7193 cb_cbp(cab, top.map(|a| cs.mb_cbp[a]), left.map(|a| cs.mb_cbp[a]), cbp);
7194 cs.mb_cbp[addr] = cbp as u8;
7195 let mut nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7196 let mut cbfdc = 0u16;
7197 let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
7198
7199 if cbp == 0 {
7200 cs.last_delta_qp = 0;
7201 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7202 fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
7203 }
7204 } else {
7205 let delta = fe.qp_delta();
7206 cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7207 for id8 in 0..4usize {
7208 for id4 in 0..4usize {
7209 let iz = id8 * 4 + id4;
7210 let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
7211 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7212 let total = if cbp_luma & (1 << id8) != 0 {
7213 let sc = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
7214 cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_LUMA_4X4, false, ndc, &sc)
7215 } else {
7216 nzc[CB_NZC_CACHE[iz]] = 0;
7217 0
7218 };
7219 fe.nnz_y[by * w4 + bx] = total as u8;
7220 }
7221 }
7222 cb_emit_chroma_residual(cab, fe, &mut nzc, &mut cbfdc, ndc, false, cbp_chroma, &plan.c_dc_levels, &plan.c_q, mb_x, mb_y);
7223 }
7224 cs.cbf_dc[addr] = cbfdc;
7225 cs.mb_nzc[addr] = cb_export_nzc(&nzc);
7226}
7227
7228fn emit_mb_cabac_p_intra(
7231 fe: &mut FrameEncoder,
7232 cab: &mut CabacEncoder,
7233 cs: &mut CabacState,
7234 plan: &MbPlan,
7235 mb_x: usize,
7236 mb_y: usize,
7237) {
7238 let mb_w = fe.mb_w;
7239 let addr = mb_y * mb_w + mb_x;
7240 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7241 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7242 cb_mb_type_p_intra(cab, plan);
7243 emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7244}
7245
7246fn emit_p_skip_cabac(cab: &mut CabacEncoder, cs: &mut CabacState, addr: usize, top: Option<usize>, left: Option<usize>) {
7251 let sctx = 11
7252 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7253 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7254 cb_mb_skip(cab, sctx, true);
7255 cs.mb_skip[addr] = true;
7256 cs.cat[addr] = 100;
7257 cs.last_delta_qp = 0;
7258}
7259
7260pub fn encode_slice_data_cabac_p(
7264 w: &mut BitWriter,
7265 cfg: &EncoderConfig,
7266 frame: &YuvFrame,
7267 qp: u8,
7268 refs: &[crate::RefFrame],
7269 qpo: &[i32],
7270) -> crate::RefFrame {
7271 let mut fe = FrameEncoder::new(cfg);
7272 fe.qp = qp;
7273 fe.qpc = chroma_qp(qp);
7274 fe.cur_qp = qp;
7275 if cfg.cabac_dz_div > 0 {
7276 fe.idz = cfg.cabac_dz_div; }
7278 let (sy, su, sv) = coded_source(cfg, frame);
7279 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
7280 let num_refs = refs.len();
7281 if fe.me_wide && !refs.is_empty() {
7283 let coh = global_mc_residual(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7284 if std::env::var("RFF_ME_COH_DBG").is_ok() {
7285 eprintln!("ME_COH qp{qp} residual={coh:.2}");
7286 }
7287 if coh < fe.me_wide_coh {
7288 fe.me_wide = false;
7289 }
7290 }
7291 if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
7298 let hr = me_wide_headroom(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7299 if me_wide_hr_dbg() {
7300 eprintln!("ME_HR qp{qp} headroom={hr:.2}");
7301 }
7302 if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
7303 fe.me_wide = false;
7304 }
7305 }
7306 if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
7309 let (mg, dc) = b2_mgain(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7310 if me_sadt_dbg() {
7311 eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
7312 }
7313 fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
7314 }
7315 if fe.satd_q > 0.0 {
7316 let mut vars: Vec<i64> = (0..fe.mb_h)
7317 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
7318 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
7319 .collect();
7320 vars.sort_unstable();
7321 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
7322 fe.satd_var_thresh = vars[idx];
7323 }
7324 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
7325 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
7327 let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
7328
7329 let mut greedy_free = 0usize;
7333 let mut greedy_seen = 0usize;
7334 let mut greedy_on = fe.greedy_min_free == 0;
7335 let greedy_learn = (fe.mb_w * fe.mb_h / 8).max(64);
7336 let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false); let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
7338 let total = fe.mb_w * fe.mb_h;
7339
7340 let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
7343 for mb_y in 0..fe.mb_h {
7344 for mb_x in 0..fe.mb_w {
7345 let mb_idx = mb_y * fe.mb_w + mb_x;
7346 let addr = mb_idx;
7347 let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
7348 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7349 fe.qp = aq_qp[mb_idx];
7350 fe.qpc = chroma_qp(aq_qp[mb_idx]);
7351
7352 let mut inter: Option<InterChoice> = None;
7354 let mut did_skip = false;
7355 if num_refs > 0 {
7356 let mv_skip = fe.skip_mv(mb_x, mb_y);
7357 let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
7358 let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
7359 let skip_c = if luma_free || !fe.fast {
7360 fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
7361 } else {
7362 [[0u8; 64]; 2]
7363 };
7364 let is_free = luma_free && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &skip_c);
7365 let skip_sad = if fe.fast {
7366 0
7367 } else {
7368 let (lx, ly) = (mb_x * 16, mb_y * 16);
7369 let mut s = 0u32;
7370 for dy in 0..16 {
7371 let src = &sy[(ly + dy) * fe.cw + lx..][..16];
7372 let p = &skip_y[dy * 16..][..16];
7373 s += src.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
7374 }
7375 s
7376 };
7377 greedy_seen += 1;
7378 if greedy_seen >= greedy_learn {
7379 greedy_on = fe.greedy_min_free == 0
7380 || greedy_free * 100 >= greedy_seen * fe.greedy_min_free as usize;
7381 }
7382 if is_free {
7383 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
7384 if !fe.fast {
7385 fe.mb_was_skip[mb_idx] = true;
7386 fe.mb_skip_sad[mb_idx] = skip_sad;
7387 }
7388 greedy_free += 1;
7389 did_skip = true;
7390 } else {
7391 let (lx, ly) = (mb_x * 16, mb_y * 16);
7392 let nb = {
7393 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMvPred);
7394 fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
7395 };
7396 let lme = lambda.sqrt() * cfg.cabac_lambda_scale;
7397 if fe.fast {
7398 fe.mb_use_satd = fe.satd_q > 0.0
7399 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
7400 let (r16, mv16, cost_inter) =
7401 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
7402 let cost_intra = if fe.mb_use_satd {
7403 fe.best_i16_satd(&sy, mb_x, mb_y)
7404 } else {
7405 fe.best_i16_sad(&sy, mb_x, mb_y)
7406 } + (lme * fe.tune_intra_penalty) as i64;
7407 inter = if cost_intra < cost_inter {
7408 None
7409 } else {
7410 Some((0, vec![(r16, mv16)]))
7411 };
7412 } else {
7413 if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
7415 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
7416 fe.mb_was_skip[mb_idx] = true;
7417 fe.mb_skip_sad[mb_idx] = skip_sad;
7418 did_skip = true;
7419 } else {
7420 let (r16, mv16, c16) =
7421 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
7422 let mut best_c = c16;
7423 let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
7424 const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
7425 let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
7426 let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
7427 let split_t = split_t();
7428 if c16 > split_gate && (split_t <= 0.0 || (c16 as f64) >= split_t * lme) {
7429 let (rt, mvt, ct) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 8, &[mv16], lme);
7430 let (rb, mvb, cb) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly + 8, 16, 8, &[mv16], lme);
7431 let (rl, mvl, cl) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 8, 16, &[mv16], lme);
7432 let (rr, mvr, cr) = fe.best_part(refs, &sy, &nb, num_refs, lx + 8, ly, 8, 16, &[mv16], lme);
7433 if ct + cb < best_c {
7434 best_c = ct + cb;
7435 pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
7436 }
7437 if cl + cr < best_c {
7438 best_c = cl + cr;
7439 pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
7440 }
7441 if fe.sub8x8 {
7443 let mut c8 = (lme * 4.0) as i64;
7444 let mut p8 = Vec::with_capacity(4);
7445 for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
7446 let (r, mv, c) = fe.best_part(
7447 refs, &sy, &nb, num_refs, lx + qx, ly + qy, 8, 8, &[mv16], lme,
7448 );
7449 c8 += c;
7450 p8.push((r, mv));
7451 }
7452 if c8 < best_c {
7453 best_c = c8;
7454 pick = Some((3u8, p8));
7455 }
7456 }
7457 }
7458 if fe.sp_defer.get() {
7463 if let Some((mode, parts)) = pick.as_mut() {
7464 let regions: &[(usize, usize, usize, usize)] = match mode {
7465 1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
7466 2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
7467 3 => &[(0, 0, 8, 8), (8, 0, 8, 8), (0, 8, 8, 8), (8, 8, 8, 8)],
7468 _ => &[(0, 0, 16, 16)],
7469 };
7470 let mut tot = if *mode == 3 { (lme * 4.0) as i64 } else { 0 };
7471 for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
7472 let (r, mv) = parts[i];
7473 let (m2, c2) = fe.refine_part(
7474 refs, &sy, &nb, num_refs, lx + qx, ly + qy, pw, ph, lme, r, mv,
7475 );
7476 parts[i] = (r, m2);
7477 tot += c2;
7478 }
7479 best_c = tot;
7480 }
7481 }
7482 let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
7483 + (lme * fe.tune_intra_penalty) as i64;
7484 inter = if c_intra < best_c { None } else { pick };
7485 fe.mb_was_skip[mb_idx] = false;
7486 fe.mb_skip_sad[mb_idx] = skip_sad;
7487 }
7488 }
7489 }
7490 }
7491
7492 if did_skip {
7494 emit_p_skip_cabac(&mut cab, &mut cs, addr, top, left);
7495 mb_qpy[mb_idx] = fe.cur_qp;
7496 cab.encode_terminate(mb_idx + 1 == total);
7497 continue;
7498 }
7499 let sctx = 11
7501 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7502 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7503 cb_mb_skip(&mut cab, sctx, false);
7504 cs.mb_skip[addr] = false;
7505 match inter {
7506 Some((mode, parts)) => {
7507 let plan = fe.plan_inter_mb(refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts, None);
7508 let _ge = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
7512 emit_mb_cabac_p_inter(&mut fe, &mut cab, &mut cs, mode, &plan, mb_x, mb_y, num_refs);
7513 }
7514 None => {
7515 let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
7516 let _ge = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
7517 emit_mb_cabac_p_intra(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
7518 }
7519 }
7520 mb_qpy[mb_idx] = fe.cur_qp;
7521 cab.encode_terminate(mb_idx + 1 == total);
7522 }
7523 }
7524
7525 while !w.is_byte_aligned() {
7526 w.write_bit(true);
7527 }
7528 for b in cab.into_bytes() {
7529 w.write_bits(b as u32, 8);
7530 }
7531
7532 let ref_id: Vec<i32> = fe.ref_idx_y.iter().map(|&r| if r >= 0 { r } else { i32::MIN }).collect();
7534 let info = rusty_h264_common::deblock::BlockInfo {
7535 inter: &fe.inter_y,
7536 nnz: &fe.nnz_y,
7537 mv: &fe.mv_y,
7538 ref_id: &ref_id,
7539 mv1: &[],
7540 ref_id1: &[],
7541 w4: fe.mb_w * 4,
7542 t8x8: &[],
7543 bs: &[],
7544 };
7545 rusty_h264_common::deblock::filter_frame(
7546 &mut fe.rec_y, &mut fe.rec_u, &mut fe.rec_v, fe.mb_w, fe.mb_h, &mb_qpy, 0, 0, 0, &info,
7547 );
7548 let w4 = fe.mb_w * 4;
7549 crate::RefFrame {
7550 y: fe.rec_y,
7551 u: fe.rec_u,
7552 v: fe.rec_v,
7553 poc: 0,
7554 frame_num: 0,
7555 mv: fe.mv_y,
7556 ref_idx: fe.ref_idx_y,
7557 w4,
7558 hpel: std::sync::OnceLock::new(),
7560 }
7561}
7562
7563fn cb_fill_inter_cache(
7573 mb_ref: &[[i8; 16]],
7574 mb_mvd: &[[[i16; 2]; 16]],
7575 refc: &mut [i8; 30],
7576 mvdc: &mut [[i16; 2]; 30],
7577 top: Option<usize>,
7578 left: Option<usize>,
7579 addr: usize,
7580 mb_w: usize,
7581) {
7582 if let Some(l) = left {
7583 for (ci, bi) in [(6usize, 3usize), (12, 7), (18, 11), (24, 15)] {
7584 refc[ci] = mb_ref[l][bi];
7585 mvdc[ci] = mb_mvd[l][bi];
7586 }
7587 }
7588 if let Some(t) = top {
7589 for (ci, bi) in [(1usize, 12usize), (2, 13), (3, 14), (4, 15)] {
7590 refc[ci] = mb_ref[t][bi];
7591 mvdc[ci] = mb_mvd[t][bi];
7592 }
7593 }
7594 let mb_x = addr % mb_w;
7595 let mb_y = addr / mb_w;
7596 if mb_x > 0 && mb_y > 0 {
7597 let a = addr - mb_w - 1;
7598 (refc[0], mvdc[0]) = (mb_ref[a][15], mb_mvd[a][15]);
7599 }
7600 if mb_y > 0 && mb_x + 1 < mb_w {
7601 let a = addr - mb_w + 1;
7602 (refc[5], mvdc[5]) = (mb_ref[a][12], mb_mvd[a][12]);
7603 }
7604}
7605
7606fn cb_mb_type_b(cab: &mut CabacEncoder, ctx_inc: usize, dir: u8) {
7609 const B: usize = 27;
7610 match dir {
7611 0 => cab.encode_decision(B + ctx_inc, 0), 1 => {
7613 cab.encode_decision(B + ctx_inc, 1);
7614 cab.encode_decision(B + 3, 0);
7615 cab.encode_decision(B + 5, 0); }
7617 2 => {
7618 cab.encode_decision(B + ctx_inc, 1);
7619 cab.encode_decision(B + 3, 0);
7620 cab.encode_decision(B + 5, 1); }
7622 _ => {
7623 cab.encode_decision(B + ctx_inc, 1);
7625 cab.encode_decision(B + 3, 1);
7626 cab.encode_decision(B + 4, 0);
7627 cab.encode_decision(B + 5, 0);
7628 cab.encode_decision(B + 5, 0);
7629 cab.encode_decision(B + 5, 0);
7630 }
7631 }
7632}
7633
7634const CB_ALL16: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
7635
7636fn emit_mb_cabac_b(
7639 fe: &mut FrameEncoder,
7640 cab: &mut CabacEncoder,
7641 cs: &mut CabacState,
7642 dir: u8,
7643 plan: &InterPlan,
7644 mb_x: usize,
7645 mb_y: usize,
7646) {
7647 let mb_w = fe.mb_w;
7648 let addr = mb_y * mb_w + mb_x;
7649 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7650 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7651
7652 let bci = left.map_or(0, |a| (!cs.mb_direct[a]) as usize)
7653 + top.map_or(0, |a| (!cs.mb_direct[a]) as usize);
7654 cb_mb_type_b(cab, bci, dir);
7655
7656 let mut mvdc0 = [[0i16; 2]; 30];
7658 let mut refc0 = [-1i8; 30];
7659 let mut mvdc1 = [[0i16; 2]; 30];
7660 let mut refc1 = [-1i8; 30];
7661 cb_fill_inter_cache(&cs.mb_ref, &cs.mb_mvd, &mut refc0, &mut mvdc0, top, left, addr, mb_w);
7662 cb_fill_inter_cache(&cs.mb_ref1, &cs.mb_mvd1, &mut refc1, &mut mvdc1, top, left, addr, mb_w);
7663 let mut mmvd0 = [[0i16; 2]; 16];
7664 let mut mref0 = [-1i8; 16];
7665 let mut mmvd1 = [[0i16; 2]; 16];
7666 let mut mref1 = [-1i8; 16];
7667 let (use0, use1) = (dir == 1 || dir == 3, dir == 2 || dir == 3);
7668 if dir == 0 {
7669 mref0 = [0i8; 16];
7672 mref1 = [0i8; 16];
7673 } else {
7674 let mut k = 0;
7676 if use0 {
7677 cb_emit_mvd_partition(cab, 0, &CB_ALL16, &mut mvdc0, &mut refc0, &mut mmvd0, &mut mref0, plan.mvds[k], 0);
7678 k += 1;
7679 }
7680 if use1 {
7681 cb_emit_mvd_partition(cab, 0, &CB_ALL16, &mut mvdc1, &mut refc1, &mut mmvd1, &mut mref1, plan.mvds[k], 0);
7682 }
7683 }
7684 cs.mb_mvd[addr] = mmvd0;
7685 cs.mb_ref[addr] = mref0;
7686 cs.mb_mvd1[addr] = mmvd1;
7687 cs.mb_ref1[addr] = mref1;
7688 cs.mb_direct[addr] = dir == 0;
7689 cs.cat[addr] = 100;
7690 cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7691}
7692
7693fn emit_b_skip_cabac(cab: &mut CabacEncoder, cs: &mut CabacState, addr: usize, top: Option<usize>, left: Option<usize>) {
7697 let sctx = 24
7698 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7699 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7700 cb_mb_skip(cab, sctx, true);
7701 cs.mb_skip[addr] = true;
7702 cs.cat[addr] = 100;
7703 cs.mb_direct[addr] = true;
7704 cs.mb_ref[addr] = [0i8; 16];
7705 cs.mb_ref1[addr] = [0i8; 16];
7706 cs.last_delta_qp = 0;
7707}
7708
7709#[allow(clippy::too_many_arguments)]
7713pub fn encode_slice_data_cabac_b(
7714 w: &mut BitWriter,
7715 cfg: &EncoderConfig,
7716 frame: &YuvFrame,
7717 qp: u8,
7718 poc: i32,
7719 l0: &crate::RefFrame,
7720 l1: &crate::RefFrame,
7721 qpo: &[i32],
7722) {
7723 let mut fe = FrameEncoder::new(cfg);
7724 fe.qp = qp;
7725 fe.qpc = chroma_qp(qp);
7726 fe.cur_qp = qp;
7727 if cfg.cabac_dz_div > 0 {
7728 fe.idz = cfg.cabac_dz_div; }
7730 fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
7731 let (sy, su, sv) = coded_source(cfg, frame);
7732 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
7733 let lme = lambda.sqrt() * cfg.cabac_lambda_scale;
7734 let refs = std::slice::from_ref(l0);
7735 if fe.satd_q > 0.0 {
7736 let mut vars: Vec<i64> = (0..fe.mb_h)
7737 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
7738 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
7739 .collect();
7740 vars.sort_unstable();
7741 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
7742 fe.satd_var_thresh = vars[idx];
7743 }
7744 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
7745 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
7747
7748 let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false);
7749 let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
7750 let total = fe.mb_w * fe.mb_h;
7751
7752 for mb_y in 0..fe.mb_h {
7753 for mb_x in 0..fe.mb_w {
7754 let mb_idx = mb_y * fe.mb_w + mb_x;
7755 let addr = mb_idx;
7756 let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
7757 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7758 fe.qp = aq_qp[mb_idx];
7759 fe.qpc = chroma_qp(aq_qp[mb_idx]);
7760 let (lx, ly) = (mb_x * 16, mb_y * 16);
7761 let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
7762 fe.mb_use_satd =
7763 fe.satd_q > 0.0 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
7764 let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
7765 let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
7766 let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
7767 let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
7768 let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
7769 if fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
7771 && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc)
7772 {
7773 fe.commit_direct_motion(mb_x, mb_y, &dmotion);
7774 emit_b_skip_cabac(&mut cab, &mut cs, addr, top, left);
7775 cab.encode_terminate(mb_idx + 1 == total);
7776 continue;
7777 }
7778 let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
7779 let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
7780 let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
7781 let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
7782 let r_bi = mvd_bits(mv0.0 - pmv0.0) + mvd_bits(mv0.1 - pmv0.1)
7783 + mvd_bits(mv1.0 - pmv1.0) + mvd_bits(mv1.1 - pmv1.1);
7784 let j_bi = d_bi + (lme * r_bi as f64) as i64;
7785 let (mut dir, mut best) = (0u8, d_direct);
7786 if j0 < best { dir = 1; best = j0; }
7787 if j1 < best { dir = 2; best = j1; }
7788 if j_bi < best { dir = 3; best = j_bi; }
7789 let _ = best;
7790 let sctx = 24
7792 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7793 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7794 cb_mb_skip(&mut cab, sctx, false);
7795 cs.mb_skip[addr] = false;
7796 let bspec = BInter { dir, l1, mv0, mv1 };
7797 let plan = fe.plan_inter_mb(refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
7798 emit_mb_cabac_b(&mut fe, &mut cab, &mut cs, dir, &plan, mb_x, mb_y);
7799 cab.encode_terminate(mb_idx + 1 == total);
7800 }
7801 }
7802
7803 while !w.is_byte_aligned() {
7804 w.write_bit(true);
7805 }
7806 for b in cab.into_bytes() {
7807 w.write_bits(b as u32, 8);
7808 }
7809 }
7811
7812pub fn encode_all_skip_b_cabac(w: &mut BitWriter, cfg: &EncoderConfig, qp: u8, n: usize) {
7816 let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false);
7817 for i in 0..n {
7818 cab.encode_decision(24, 1);
7821 cab.encode_terminate(i + 1 == n);
7822 }
7823 while !w.is_byte_aligned() {
7824 w.write_bit(true);
7825 }
7826 for b in cab.into_bytes() {
7827 w.write_bits(b as u32, 8);
7828 }
7829}