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
111static MV_COST_TAB: std::sync::OnceLock<Vec<u16>> = std::sync::OnceLock::new();
114fn build_mv_cost() -> Vec<u16> {
115 (0..4096u32)
116 .map(|a| {
117 let c = 2.0 * ((a + 1) as f64).log2() + 0.718 + if a != 0 { 1.0 } else { 0.0 };
118 (c * 4.0).round() as u16
121 })
122 .collect()
123}
124static MV_SMOOTH: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
130pub fn set_mv_smooth(on: bool) {
131 MV_SMOOTH.store(if on { 2 } else { 0 }, core::sync::atomic::Ordering::Relaxed)
132}
133pub fn set_mv_smooth_mode(m: u32) {
134 MV_SMOOTH.store(m.min(3), core::sync::atomic::Ordering::Relaxed)
135}
136static MV_TRUE_BIASED: std::sync::OnceLock<Vec<u16>> = std::sync::OnceLock::new();
145fn build_true_biased() -> Vec<u16> {
146 let bias_q4 = (std::env::var("RFF_MVCOST_BIAS")
147 .ok()
148 .and_then(|v| v.parse::<f64>().ok())
149 .unwrap_or(1.0)
150 * 4.0)
151 .round() as u16;
152 crate::mvd_cost_tab::MVD_TRUE_COST4
153 .iter()
154 .enumerate()
155 .map(|(d, &c)| if d == 0 { c } else { c.saturating_add(bias_q4) })
156 .collect()
157}
158
159fn mv_smooth_t() -> f64 {
160 static T: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
161 *T.get_or_init(|| std::env::var("RFF_MVCOST_T").ok().and_then(|v| v.parse().ok()).unwrap_or(0.10))
162}
163#[inline]
169fn mv_cost_kind(frame_smooth: bool) -> u32 {
170 match mv_smooth_mode() {
171 0 => 0,
172 1 => frame_smooth as u32,
177 2 => 1, _ => 2, }
180}
181#[inline]
182fn mv_smooth_mode() -> u32 {
183 match MV_SMOOTH.load(core::sync::atomic::Ordering::Relaxed) {
184 m @ 0..=3 => m,
185 _ => {
186 static E: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
187 *E.get_or_init(|| {
191 std::env::var("RFF_MVCOST").ok().and_then(|v| v.parse().ok()).unwrap_or(1)
192 })
193 }
194 }
195}
196
197fn mectx_enabled() -> bool {
203 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
204 *E.get_or_init(|| std::env::var("RFF_MECTX").map(|v| v != "0").unwrap_or(true))
205}
206
207fn satd_avg_enabled() -> bool {
208 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
209 *E.get_or_init(|| std::env::var("RFF_SATD_AVG").map(|v| v != "0").unwrap_or(true))
210}
211
212static ME_SADFP: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
222pub fn set_me_sadfp(on: bool) {
223 ME_SADFP.store(if on { 2 } else { 0 }, core::sync::atomic::Ordering::Relaxed)
225}
226pub fn set_me_sadfp_mode(m: u32) {
227 ME_SADFP.store(m.min(2), core::sync::atomic::Ordering::Relaxed)
228}
229fn me_sadfp_mode() -> u32 {
230 match ME_SADFP.load(core::sync::atomic::Ordering::Relaxed) {
231 u32::MAX => {
232 static INIT: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
233 *INIT.get_or_init(|| {
240 std::env::var("RFF_ME_SADFP").ok().and_then(|v| v.parse().ok()).unwrap_or(1)
241 })
242 }
243 m => m,
244 }
245}
246
247fn me_sadt() -> f64 {
251 static T: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
252 *T.get_or_init(|| std::env::var("RFF_ME_SADT").ok().and_then(|s| s.parse().ok()).unwrap_or(0.13))
253}
254fn me_sadt_dbg() -> bool {
255 static D: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
256 *D.get_or_init(|| std::env::var_os("RFF_ME_SADT_DBG").is_some())
257}
258
259static SP_FC: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
269pub fn set_sp_fc(on: bool) {
270 SP_FC.store(on as u32, core::sync::atomic::Ordering::Relaxed)
271}
272fn sp_fc_enabled() -> bool {
273 match SP_FC.load(core::sync::atomic::Ordering::Relaxed) {
274 0 => false,
275 1 => true,
276 _ => {
277 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
278 *E.get_or_init(|| std::env::var("RFF_SP_FC").map(|v| v != "0").unwrap_or(false))
279 }
280 }
281}
282
283static ME_FC: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
284pub fn set_me_fc(on: bool) {
285 ME_FC.store(on as u32, core::sync::atomic::Ordering::Relaxed)
286}
287fn me_fc_enabled() -> bool {
288 match ME_FC.load(core::sync::atomic::Ordering::Relaxed) {
289 0 => false,
290 1 => true,
291 _ => {
292 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
293 *E.get_or_init(|| std::env::var("RFF_ME_FC").map(|v| v != "0").unwrap_or(true))
294 }
295 }
296}
297
298static SPLIT_MG: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
308pub fn set_split_mg(milli: u32) {
309 SPLIT_MG.store(milli, core::sync::atomic::Ordering::Relaxed)
310}
311fn split_mg() -> f64 {
312 match SPLIT_MG.load(core::sync::atomic::Ordering::Relaxed) {
313 u32::MAX => {
314 static E: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
315 *E.get_or_init(|| {
316 std::env::var("RFF_SPLIT_MG").ok().and_then(|v| v.parse().ok()).unwrap_or(0.0)
317 })
318 }
319 m => m as f64 / 1000.0,
320 }
321}
322
323fn me_sad_dcmax() -> f64 {
329 static T: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
330 *T.get_or_init(|| std::env::var("RFF_ME_SADDC").ok().and_then(|s| s.parse().ok()).unwrap_or(0.6))
331}
332
333fn b2_mgain(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> (f64, f64) {
347 const WIDE: isize = 8;
348 const STEP: isize = 4;
349 const TARGET: usize = 24;
350 let sad16 = |bx: usize, by: usize, rx: isize, ry: isize| -> Option<u32> {
351 if rx < 0 || ry < 0 || rx as usize + 16 > cw || ry as usize + 16 > ch {
352 return None;
353 }
354 let (rx, ry) = (rx as usize, ry as usize);
355 let mut s = 0u32;
356 for dy in 0..16 {
357 let a = &sy[(by + dy) * cw + bx..][..16];
358 let b = &ref_y[(ry + dy) * cw + rx..][..16];
359 s += a.iter().zip(b).map(|(&p, &q)| p.abs_diff(q) as u32).sum::<u32>();
360 }
361 Some(s)
362 };
363 let (mbw, mbh) = (cw / 16, ch / 16);
364 if mbw < 6 || mbh < 6 {
365 return (0.0, 0.0);
366 }
367 let inner = (mbw - 4) * (mbh - 4);
368 let stride = (inner / TARGET).max(1);
369 let (mut acc, mut dc, mut n) = (0.0f64, 0.0f64, 0u32);
370 let mut i = 0usize;
371 while i < inner {
372 let (mx, my) = (2 + i % (mbw - 4), 2 + i / (mbw - 4));
373 let (bx, by) = (mx * 16, my * 16);
374 if let Some(s0) = sad16(bx, by, bx as isize, by as isize) {
375 let (mut ms, mut mr) = (0u32, 0u32);
376 for dy in 0..16 {
377 ms += sy[(by + dy) * cw + bx..][..16].iter().map(|&v| v as u32).sum::<u32>();
378 mr += ref_y[(by + dy) * cw + bx..][..16].iter().map(|&v| v as u32).sum::<u32>();
379 }
380 dc += ms.abs_diff(mr) as f64 / (s0 + 1) as f64;
381 let mut best = s0;
382 let mut dy = -WIDE;
383 while dy <= WIDE {
384 let mut dx = -WIDE;
385 while dx <= WIDE {
386 if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
387 best = best.min(s);
388 }
389 dx += STEP;
390 }
391 dy += STEP;
392 }
393 acc += (s0 - best) as f64 / (s0 + 1) as f64;
394 n += 1;
395 }
396 i += stride;
397 }
398 if n == 0 { (0.0, 0.0) } else { (acc / n as f64, dc / n as f64) }
399}
400
401static SP_MAXIT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
406pub fn set_sp_maxit(n: u32) {
407 SP_MAXIT.store(n, core::sync::atomic::Ordering::Relaxed)
408}
409fn sp_maxit() -> u32 {
410 match SP_MAXIT.load(core::sync::atomic::Ordering::Relaxed) {
411 u32::MAX => {
412 static INIT: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
413 *INIT.get_or_init(|| {
414 std::env::var("RFF_SP_MAXIT").ok().and_then(|v| v.parse().ok()).unwrap_or(0)
415 })
416 }
417 n => n,
418 }
419}
420
421fn me_sadfp_lambda() -> f64 {
426 static E: std::sync::OnceLock<f64> = std::sync::OnceLock::new();
427 *E.get_or_init(|| {
430 std::env::var("RFF_ME_SADL").ok().and_then(|v| v.parse().ok()).unwrap_or(0.5)
431 })
432}
433
434#[cfg(feature = "profile")]
438pub mod spstats {
439 use core::sync::atomic::{AtomicU64, Ordering};
440 pub static POS: [AtomicU64; 2 * 8 * 2] = [const { AtomicU64::new(0) }; 32];
442 pub static IT: [AtomicU64; 2 * 6 * 2] = [const { AtomicU64::new(0) }; 24];
444 #[inline]
445 pub fn ev(st: usize, pos: usize, it: u32) {
446 POS[(st * 8 + pos.min(7)) * 2].fetch_add(1, Ordering::Relaxed);
447 IT[(st * 6 + (it.max(1) as usize - 1).min(5)) * 2].fetch_add(1, Ordering::Relaxed);
448 }
449 #[inline]
450 pub fn imp(st: usize, pos: usize, it: u32) {
451 POS[(st * 8 + pos.min(7)) * 2 + 1].fetch_add(1, Ordering::Relaxed);
452 IT[(st * 6 + (it.max(1) as usize - 1).min(5)) * 2 + 1].fetch_add(1, Ordering::Relaxed);
453 }
454 pub static REDUNDANT: AtomicU64 = AtomicU64::new(0);
456 #[inline]
457 pub fn redundant() { REDUNDANT.fetch_add(1, Ordering::Relaxed); }
458 pub fn reset() {
459 for c in POS.iter() { c.store(0, Ordering::Relaxed); }
460 for c in IT.iter() { c.store(0, Ordering::Relaxed); }
461 REDUNDANT.store(0, Ordering::Relaxed);
462 }
463 pub fn snapshot() -> (Vec<u64>, Vec<u64>) {
464 (POS.iter().map(|c| c.load(Ordering::Relaxed)).collect(),
465 IT.iter().map(|c| c.load(Ordering::Relaxed)).collect())
466 }
467 pub fn redundant_count() -> u64 { REDUNDANT.load(Ordering::Relaxed) }
468}
469
470#[cfg(feature = "profile")]
472pub mod satdpath {
473 use core::sync::atomic::{AtomicU64, Ordering};
474 pub static C: [AtomicU64; 3] = [const { AtomicU64::new(0) }; 3];
475 #[inline]
476 pub fn bump(i: usize) { C[i].fetch_add(1, Ordering::Relaxed); }
477 pub fn reset() { for c in C.iter() { c.store(0, Ordering::Relaxed); } }
478 pub fn snapshot() -> Vec<u64> { C.iter().map(|c| c.load(Ordering::Relaxed)).collect() }
479}
480
481pub const DIA_RUNGS: [i32; 5] = [64, 32, 16, 8, 4];
498pub const DIA_DEFAULT: u32 = 0b11100;
500pub static DIA_MASK: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(u32::MAX);
501pub fn set_dia_mask(m: u32) { DIA_MASK.store(m, core::sync::atomic::Ordering::Relaxed) }
502fn dia_mask() -> u32 {
503 let m = DIA_MASK.load(core::sync::atomic::Ordering::Relaxed);
504 if m != u32::MAX { return m; }
505 static INIT: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
506 *INIT.get_or_init(|| match std::env::var("RFF_DIA_LADDER") {
507 Ok(v) => {
508 let want: Vec<i32> = v.split(',').filter_map(|t| t.trim().parse().ok()).collect();
509 let mut m = 0u32;
510 for (i, r) in DIA_RUNGS.iter().enumerate() {
511 if want.contains(r) { m |= 1 << i; }
512 }
513 if m == 0 { DIA_DEFAULT } else { m }
514 }
515 Err(_) => DIA_DEFAULT,
516 })
517}
518
519#[cfg(feature = "profile")]
524pub mod diastats {
525 use core::sync::atomic::{AtomicU64, Ordering};
526 pub static C: [AtomicU64; 12] = [const { AtomicU64::new(0) }; 12];
528 #[inline]
529 pub fn ev(i: usize) { C[i * 2].fetch_add(1, Ordering::Relaxed); }
530 #[inline]
531 pub fn imp(i: usize) { C[i * 2 + 1].fetch_add(1, Ordering::Relaxed); }
532 pub fn reset() { for c in C.iter() { c.store(0, Ordering::Relaxed); } }
533 pub fn snapshot() -> Vec<(u64, u64)> {
534 (0..6).map(|i| (C[i * 2].load(Ordering::Relaxed), C[i * 2 + 1].load(Ordering::Relaxed))).collect()
535 }
536}
537
538pub(crate) static SUBPEL_PAT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
550
551pub(crate) static SP_DISPATCH: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(u32::MAX);
554
555fn sp_dispatch_cfg() -> (u32, i64) {
556 use std::sync::OnceLock;
557 let forced = SP_DISPATCH.load(std::sync::atomic::Ordering::Relaxed);
558 if forced == 0 {
559 return (0, 0);
560 }
561 static C: OnceLock<(u32, i64)> = OnceLock::new();
562 *C.get_or_init(|| {
563 let on = std::env::var("RFF_SUBPEL_DISPATCH").map(|s| s != "0").unwrap_or(false);
571 if !on {
572 return (0, 0);
573 }
574 let k = std::env::var("RFF_SUBPEL_LEARN").ok().and_then(|s| s.parse().ok()).unwrap_or(200);
575 let t = std::env::var("RFF_SUBPEL_T").ok().and_then(|s| s.parse().ok()).unwrap_or(67);
576 (k, t)
577 })
578}
579
580fn subpel_pattern_override() -> Option<u32> {
582 let v = SUBPEL_PAT.load(std::sync::atomic::Ordering::Relaxed);
583 if v != u32::MAX {
584 return Some(v);
585 }
586 if let Some(e) = std::env::var("RFF_SUBPEL_PAT").ok().and_then(|s| s.parse::<u32>().ok()) {
587 SUBPEL_PAT.store(e, std::sync::atomic::Ordering::Relaxed);
588 return Some(e);
589 }
590 None
591}
592
593fn subpel_pattern() -> u32 {
594 let v = SUBPEL_PAT.load(std::sync::atomic::Ordering::Relaxed);
595 if v != u32::MAX {
596 return v;
597 }
598 let d = std::env::var("RFF_SUBPEL_PAT").ok().and_then(|s| s.parse().ok()).unwrap_or(0);
601 SUBPEL_PAT.store(d, std::sync::atomic::Ordering::Relaxed);
602 d
603}
604
605mod subpel_harvest {
614 use std::fs::File;
615 use std::io::Write;
616 use std::sync::{Mutex, OnceLock};
617
618 fn sink() -> &'static Option<Mutex<File>> {
619 static S: OnceLock<Option<Mutex<File>>> = OnceLock::new();
620 S.get_or_init(|| {
621 std::env::var("RFF_SUBPEL_HARVEST").ok().and_then(|p| {
622 let mut f = File::create(p).ok()?;
623 let _ = writeln!(f, "pre,post,lambda,w,h,evals,to_best,ring1");
624 Some(Mutex::new(f))
625 })
626 })
627 }
628
629 #[inline]
630 pub fn enabled() -> bool {
631 sink().is_some()
632 }
633
634 #[allow(clippy::too_many_arguments)]
635 pub fn record(pre: i64, post: i64, lambda: f64, w: usize, h: usize, evals: u32, to_best: u32, ring1: i64) {
636 if let Some(m) = sink() {
637 if let Ok(mut f) = m.lock() {
638 let _ = writeln!(f, "{pre},{post},{lambda:.4},{w},{h},{evals},{to_best},{ring1}");
639 }
640 }
641 }
642}
643
644fn bdirect_planes_enabled() -> bool {
648 use std::sync::OnceLock;
649 static ON: OnceLock<bool> = OnceLock::new();
650 *ON.get_or_init(|| std::env::var("RFF_BDIRECT_PLANES").map(|s| s != "0").unwrap_or(true))
651}
652
653fn me_batch_enabled() -> bool {
654 use std::sync::OnceLock;
655 static ON: OnceLock<bool> = OnceLock::new();
656 *ON.get_or_init(|| std::env::var("RFF_ME_BATCH").map(|s| s != "0").unwrap_or(true))
657}
658
659#[cfg(accel)]
663#[repr(align(16))]
664struct AlignedMb([u8; 256]);
665
666#[derive(Clone, Copy)]
671struct BInter<'a> {
672 dir: u8,
673 l1: &'a crate::RefFrame,
674 mv0: (i32, i32),
675 mv1: (i32, i32),
676}
677
678#[cfg(accel)]
681#[repr(align(16))]
682struct AlignedDct([i16; 256]);
683
684fn mb_variance(sy: &[u8], cw: usize, mb_x: usize, mb_y: usize) -> i64 {
689 let base = mb_y * 16 * cw + mb_x * 16;
690 let (mut s, mut ss) = (0u32, 0u32);
695 for r in 0..16 {
696 let row = &sy[base + r * cw..base + r * cw + 16];
697 for &p in row {
698 let v = p as u32;
699 s += v;
700 ss += v * v;
701 }
702 }
703 ss as i64 - (s as i64) * (s as i64) / 256 }
706
707fn aq_qp_map(sy: &[u8], cw: usize, mb_w: usize, mb_h: usize, base_qp: u8, strength: f64) -> Vec<u8> {
714 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncAq);
715 const AQ_DQP_MAX: i32 = 4;
716 let n = mb_w * mb_h;
717 if strength == 0.0 || n == 0 {
718 return vec![base_qp; n];
719 }
720 let mut var = Vec::with_capacity(n);
723 let mut lv = Vec::with_capacity(n);
724 for my in 0..mb_h {
725 for mx in 0..mb_w {
726 let v = (mb_variance(sy, cw, mx, my) + 1) as f64;
727 var.push(v);
728 lv.push(v.log2());
729 }
730 }
731 let mean_lv = lv.iter().sum::<f64>() / n as f64;
732 const AQ_SPREAD_LO: f64 = 1.5;
739 const AQ_SPREAD_HI: f64 = 5.0;
740 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();
742 let factor = (1.0 - (std_lv - AQ_SPREAD_LO) / (AQ_SPREAD_HI - AQ_SPREAD_LO)).clamp(AQ_SPREAD_MIN, 1.0);
743 let eff_strength = strength * factor;
744 let dqp: Vec<i32> = lv
746 .iter()
747 .map(|&l| (eff_strength * (l - mean_lv)).round() as i32)
748 .map(|d| d.clamp(-AQ_DQP_MAX, AQ_DQP_MAX))
749 .collect();
750 let sum_v: f64 = var.iter().sum();
755 let qstep: [f64; (2 * AQ_DQP_MAX + 1) as usize] =
760 std::array::from_fn(|i| 2f64.powf(-((i as i32 - AQ_DQP_MAX) as f64) / 6.0));
761 let sum_vs: f64 = var
762 .iter()
763 .zip(&dqp)
764 .map(|(&v, &d)| v * qstep[(d + AQ_DQP_MAX) as usize])
765 .sum();
766 let c = (6.0 * (sum_vs / sum_v).log2()).round() as i32;
767 dqp.iter()
768 .map(|&d| (base_qp as i32 + c + d).clamp(0, 51) as u8)
769 .collect()
770}
771
772fn me_wide_hr_thresh() -> f64 {
817 use std::sync::OnceLock;
818 static T: OnceLock<f64> = OnceLock::new();
819 *T.get_or_init(|| std::env::var("RFF_ME_HR").ok().and_then(|s| s.parse().ok()).unwrap_or(16.0))
820}
821
822fn me_wide_hr_dbg() -> bool {
824 use std::sync::OnceLock;
825 static D: OnceLock<bool> = OnceLock::new();
826 *D.get_or_init(|| std::env::var_os("RFF_ME_HR_DBG").is_some())
827}
828
829fn me_wide_headroom(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
830 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> {
835 if rx < 0 || ry < 0 || rx as usize + 16 > cw || ry as usize + 16 > ch {
836 return None;
837 }
838 let (rx, ry) = (rx as usize, ry as usize);
839 let mut s = 0u32;
840 for dy in 0..16 {
841 let a = &sy[(by + dy) * cw + bx..][..16];
842 let b = &ref_y[(ry + dy) * cw + rx..][..16];
843 s += a.iter().zip(b).map(|(&p, &q)| p.abs_diff(q) as u32).sum::<u32>();
844 }
845 Some(s)
846 };
847 let (mbw, mbh) = (cw / 16, ch / 16);
850 if mbw < 6 || mbh < 6 {
851 return 0.0;
852 }
853 let inner = (mbw - 4) * (mbh - 4);
854 let stride = (inner / TARGET).max(1);
855 let (mut acc, mut n) = (0.0f64, 0u32);
856 let mut i = 0usize;
857 while i < inner {
858 let (mx, my) = (2 + i % (mbw - 4), 2 + i / (mbw - 4));
859 let (bx, by) = (mx * 16, my * 16);
860 let mut best_local = u32::MAX;
861 for dy in -LOCAL..=LOCAL {
862 for dx in -LOCAL..=LOCAL {
863 if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
864 best_local = best_local.min(s);
865 }
866 }
867 }
868 let mut best_wide = best_local;
869 let mut dy = -WIDE;
870 while dy <= WIDE {
871 let mut dx = -WIDE;
872 while dx <= WIDE {
873 if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
874 best_wide = best_wide.min(s);
875 }
876 dx += STEP;
877 }
878 dy += STEP;
879 }
880 if best_local > 0 {
881 acc += (best_local - best_wide) as f64 / best_local as f64;
882 n += 1;
883 }
884 i += stride;
885 }
886 if n == 0 {
887 0.0
888 } else {
889 100.0 * acc / n as f64
890 }
891}
892
893fn global_mc_residual(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
894 if cw < 48 || ch < 48 {
895 return f64::INFINITY;
896 }
897 let sad = |dx: isize, dy: isize| -> u64 {
898 let mut s = 0u64;
899 let mut y = 16;
900 while y < ch - 16 {
901 let cbase = (y * cw) as isize;
902 let rbase = (y as isize + dy) * cw as isize + dx;
903 let mut x = 16isize;
904 while x < (cw - 16) as isize {
905 let c = sy[(cbase + x) as usize] as i32;
906 let r = ref_y[(rbase + x) as usize] as i32;
907 s += (c - r).unsigned_abs() as u64;
908 x += 8;
909 }
910 y += 8;
911 }
912 s
913 };
914 let (mut best, mut bc) = ((0isize, 0isize), u64::MAX);
915 let mut dy = -12;
916 while dy <= 12 {
917 let mut dx = -12;
918 while dx <= 12 {
919 let c = sad(dx, dy);
920 if c < bc {
921 bc = c;
922 best = (dx, dy);
923 }
924 dx += 4;
925 }
926 dy += 4;
927 }
928 for dy in best.1 - 3..=best.1 + 3 {
929 for dx in best.0 - 3..=best.0 + 3 {
930 let c = sad(dx, dy);
931 if c < bc {
932 bc = c;
933 }
934 }
935 }
936 let nx = (16..cw - 16).step_by(8).count();
937 let ny = (16..ch - 16).step_by(8).count();
938 bc as f64 / (nx * ny).max(1) as f64
939}
940
941fn apply_mbtree_qpo(aq_qp: &mut [u8], qpo: &[i32]) {
945 if qpo.len() == aq_qp.len() {
946 for (q, &o) in aq_qp.iter_mut().zip(qpo) {
947 *q = (*q as i32 + o).clamp(0, 51) as u8;
948 }
949 }
950}
951
952fn implicit_bi_weights(cur_poc: i32, l0_poc: i32, l1_poc: i32) -> (i32, i32) {
958 let td = (l1_poc - l0_poc).clamp(-128, 127);
959 let tb = (cur_poc - l0_poc).clamp(-128, 127);
960 if td == 0 {
961 return (32, 32);
962 }
963 let tx = (16384 + td.abs() / 2) / td;
964 let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
965 let w1 = dsf >> 2;
966 if !(-64..=128).contains(&w1) {
967 return (32, 32);
968 }
969 (64 - w1, w1)
970}
971
972#[inline(always)]
976fn bi_blend(p: i32, q: i32, w: (i32, i32)) -> u8 {
977 ((p * w.0 + q * w.1 + 32) >> 6).clamp(0, 255) as u8
978}
979
980#[cfg(accel)]
985#[inline]
986fn scan_4x4_dcac_i16(d: &[i16]) -> [i32; 16] {
987 [
988 d[0] as i32, d[1] as i32, d[4] as i32, d[8] as i32, d[5] as i32, d[2] as i32,
989 d[3] as i32, d[6] as i32, d[9] as i32, d[12] as i32, d[13] as i32, d[10] as i32,
990 d[7] as i32, d[11] as i32, d[14] as i32, d[15] as i32,
991 ]
992}
993
994
995pub struct FrameEncoder {
998 mb_w: usize,
999 mb_h: usize,
1000 qp: u8, qpc: u8, cur_qp: u8,
1005 bi_w: (i32, i32),
1009 cw: usize, ccw: usize, rec_y: AlignedBytes,
1013 rec_u: AlignedBytes,
1014 rec_v: AlignedBytes,
1015 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)>,
1026 ref_idx1_y: Vec<i32>,
1027 idz: i64, rdoq_strength: f64, transform_8x8: bool, sub8x8: bool, me_wide: bool, sadfp: bool,
1035 mv_smooth: bool,
1039 do_splits: bool,
1041 me_wide_var: u64, me_rescue: i64, me_wide_coh: f64, me_range: i32, me_fast: bool, me_learn: u32,
1056 me_payoff_pct: u32,
1057 sp_single_pass: bool,
1068 sp_defer: std::cell::Cell<bool>,
1074 sp_learn_n: std::cell::Cell<u32>,
1075 sp_ring1: std::cell::Cell<i64>,
1076 sp_total: std::cell::Cell<i64>,
1077 sp_1pass: std::cell::Cell<bool>,
1078 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,
1088 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],
1104 nnz_c_cache: [[u8; 9]; 2],
1106 mb_skip_sad: Vec<u32>,
1112 mb_was_skip: Vec<bool>,
1113}
1114
1115type InterChoice = (u8, Vec<(i32, (i32, i32))>);
1118
1119const SKIP_RATE_BITS: f64 = 1.0;
1122
1123
1124
1125pub static EXT_MV: std::sync::Mutex<Vec<Vec<(i32, i32)>>> = std::sync::Mutex::new(Vec::new());
1130pub static MVCMP: [std::sync::atomic::AtomicU64; 7] = {
1132 const Z: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1133 [Z; 7]
1134};
1135pub static MVCMP_FRAME: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1136fn mv_force_on() -> bool {
1142 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1143 *ON.get_or_init(|| std::env::var("RFF_MV_FORCE").map_or(false, |v| v != "0"))
1144}
1145fn mv_cmp_on() -> bool {
1146 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1147 *ON.get_or_init(|| std::env::var("RFF_MV_CMP").map_or(false, |v| v != "0"))
1148}
1149
1150pub static MC_COUNT: [std::sync::atomic::AtomicU64; 2] = [
1154 std::sync::atomic::AtomicU64::new(0),
1155 std::sync::atomic::AtomicU64::new(0),
1156];
1157fn mc_count_on() -> bool {
1158 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1159 *ON.get_or_init(|| std::env::var("RFF_MC_COUNT").map_or(false, |v| v != "0"))
1160}
1161
1162pub static ME_PROBE: [std::sync::atomic::AtomicU64; 7] = {
1164 const Z: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1165 [Z; 7]
1166};
1167
1168fn me_oracle_on() -> bool {
1170 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1171 *ON.get_or_init(|| std::env::var("RFF_ME_ORACLE").map_or(false, |v| v != "0"))
1172}
1173
1174const SPLIT_GATE_BITS: f64 = 60.0;
1180
1181const FAST_INTRA_PENALTY_BITS: f64 = 24.0;
1185
1186#[derive(Default)]
1194struct MbState {
1195 rec_y: Vec<u8>,
1196 rec_u: Vec<u8>,
1197 rec_v: Vec<u8>,
1198 nnz_y: Vec<u8>,
1199 nnz_c: [Vec<u8>; 2],
1200 mv_y: Vec<(i32, i32)>,
1201 inter_y: Vec<bool>,
1202 ref_idx_y: Vec<i32>,
1203 coded_y: Vec<bool>,
1204 modes_y: Vec<u8>,
1205 cur_qp: u8,
1211}
1212
1213fn fast_intra_enabled() -> bool {
1219 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1220 *ON.get_or_init(|| std::env::var("RUSTY_FAST_INTRA").map_or(true, |v| v != "0"))
1221}
1222
1223fn coded_source(cfg: &EncoderConfig, frame: &YuvFrame) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
1224 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncSource);
1225 let cw = cfg.mb_width() * 16;
1226 let ch = cfg.mb_height() * 16;
1227 if frame.width == cw && frame.height == ch {
1230 return (frame.y.clone(), frame.u.clone(), frame.v.clone());
1231 }
1232 let y = clamp_plane(&frame.y, frame.width, frame.height, cw, ch);
1233 let u = clamp_plane(&frame.u, frame.chroma_width(), frame.chroma_height(), cw / 2, ch / 2);
1234 let v = clamp_plane(&frame.v, frame.chroma_width(), frame.chroma_height(), cw / 2, ch / 2);
1235 (y, u, v)
1236}
1237
1238fn clamp_plane(plane: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
1252 let mut out = vec![0u8; ow * oh];
1253 for y in 0..oh {
1254 let sy = y.min(h - 1);
1255 let src = &plane[sy * w..sy * w + w];
1256 let dst = &mut out[y * ow..y * ow + ow];
1257 if ow <= w {
1258 dst.copy_from_slice(&src[..ow]);
1259 } else {
1260 dst[..w].copy_from_slice(src);
1261 dst[w..].fill(src[w - 1]);
1262 }
1263 }
1264 out
1265}
1266
1267#[cfg(test)]
1270fn clamp_plane_per_pixel(plane: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
1271 let mut out = vec![0u8; ow * oh];
1272 for y in 0..oh {
1273 for x in 0..ow {
1274 out[y * ow + x] = plane[y.min(h - 1) * w + x.min(w - 1)];
1275 }
1276 }
1277 out
1278}
1279
1280#[cfg(test)]
1281mod source_tests {
1282 use super::*;
1283
1284 #[test]
1285 fn clamp_plane_matches_per_pixel_oracle() {
1286 let mut s: u32 = 0xDEAD_BEEF;
1287 let mut rnd = || {
1288 s = s.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
1289 (s >> 24) as u8
1290 };
1291 let cases = [
1294 (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), ];
1302 for (w, h, ow, oh) in cases {
1303 let plane: Vec<u8> = (0..w * h).map(|_| rnd()).collect();
1304 assert_eq!(
1305 clamp_plane(&plane, w, h, ow, oh),
1306 clamp_plane_per_pixel(&plane, w, h, ow, oh),
1307 "clamp mismatch for {w}x{h} -> {ow}x{oh}"
1308 );
1309 }
1310 }
1311}
1312
1313impl FrameEncoder {
1314 fn new(cfg: &EncoderConfig) -> Self {
1315 let (mb_w, mb_h) = (cfg.mb_width(), cfg.mb_height());
1316 let (cw, ch) = (mb_w * 16, mb_h * 16);
1317 let (ccw, cch) = (cw / 2, ch / 2);
1318 Self {
1319 mb_w,
1320 mb_h,
1321 qp: cfg.qp,
1322 qpc: chroma_qp(cfg.qp),
1323 cur_qp: cfg.qp,
1324 bi_w: (32, 32),
1325 cw,
1326 ccw,
1327 rec_y: AlignedBytes::zeroed(cw * ch),
1328 rec_u: AlignedBytes::zeroed(ccw * cch),
1329 rec_v: AlignedBytes::zeroed(ccw * cch),
1330 nnz_y: vec![0; (mb_w * 4) * (mb_h * 4)],
1331 nnz_c: [vec![0; (mb_w * 2) * (mb_h * 2)], vec![0; (mb_w * 2) * (mb_h * 2)]],
1332 modes_y: vec![2; (mb_w * 4) * (mb_h * 4)],
1333 coded_y: vec![false; (mb_w * 4) * (mb_h * 4)],
1334 mv_y: vec![(0, 0); (mb_w * 4) * (mb_h * 4)],
1335 inter_y: vec![false; (mb_w * 4) * (mb_h * 4)],
1336 ref_idx_y: vec![-1; (mb_w * 4) * (mb_h * 4)],
1337 mv1_y: vec![(0, 0); (mb_w * 4) * (mb_h * 4)],
1338 ref_idx1_y: vec![-1; (mb_w * 4) * (mb_h * 4)],
1339 idz: if cfg.gop_size <= 1 { 2 } else { 3 },
1342 rdoq_strength: 0.0, transform_8x8: cfg.transform_8x8,
1344 sub8x8: std::env::var("RFF_SUB8X8").ok().map(|s| s == "1")
1353 .or(cfg.sub_8x8)
1354 .unwrap_or(cfg.preset == crate::config::Preset::Quality),
1355 sadfp: me_sadfp_mode() == 2,
1375 mv_smooth: false,
1376 do_splits: true,
1377 me_wide: std::env::var("RFF_ME_WIDE").ok().map(|s| s == "1")
1378 .or(cfg.me_wide)
1379 .unwrap_or(cfg.preset == crate::config::Preset::Quality),
1380 me_wide_var: std::env::var("RFF_ME_WIDE_VAR").ok().and_then(|s| s.parse().ok()).unwrap_or(800),
1381 me_rescue: std::env::var("RFF_ME_RESCUE").ok().and_then(|s| s.parse().ok()).unwrap_or(3),
1382 me_wide_coh: std::env::var("RFF_ME_COH").ok().and_then(|s| s.parse().ok()).unwrap_or(4.0),
1383 me_range: std::env::var("RFF_ME_RANGE").ok().and_then(|s| s.parse().ok()).unwrap_or(24),
1384 me_fast: std::env::var("RFF_ME_FASTMO").map(|s| s != "0").unwrap_or(true),
1385 me_learn: std::env::var("RFF_ME_LEARN").ok().and_then(|s| s.parse().ok()).unwrap_or(40),
1386 me_payoff_pct: std::env::var("RFF_ME_PAYOFF").ok().and_then(|s| s.parse().ok()).unwrap_or(15),
1387 sp_single_pass: cfg.preset == crate::config::Preset::Balanced,
1393 sp_defer: std::cell::Cell::new({
1394 let a = DEFER_SUBPEL.load(std::sync::atomic::Ordering::Relaxed) != 0
1395 || std::env::var("RFF_DEFER_SUBPEL").map(|v| v != "0").unwrap_or(false);
1396 a && cfg.preset == crate::config::Preset::Quality
1401 }),
1402 sp_learn_n: std::cell::Cell::new(0),
1403 sp_ring1: std::cell::Cell::new(0),
1404 sp_total: std::cell::Cell::new(0),
1405 sp_1pass: std::cell::Cell::new(false),
1406 resc_n: std::cell::Cell::new(0),
1407 resc_big: std::cell::Cell::new(0),
1408 resc_off: std::cell::Cell::new(false),
1409 inter8x8: std::env::var("RFF_INTER8")
1410 .ok()
1411 .and_then(|s| s.parse().ok())
1412 .unwrap_or(1),
1413 inter8_pen: std::env::var("RFF_INTER8_PEN")
1418 .ok()
1419 .and_then(|s| s.parse().ok())
1420 .unwrap_or(8),
1421 fast: cfg.preset != crate::config::Preset::Quality,
1423 skip_accel_check: cfg.tune_skip_accel_check,
1424 coded_path_v2: cfg.coded_path_v2,
1425 aq_strength: cfg.aq_strength,
1426 tune_lambda_scale: cfg.tune_lambda_scale,
1427 tune_intra_penalty: cfg.tune_intra_penalty,
1428 satd_q: cfg.tune_satd_q,
1429 subpel_force: cfg.tune_subpel || cfg.preset == crate::config::Preset::Balanced,
1430 me_snap: cfg.tune_me_snap,
1431 me_subpel_iter: cfg.tune_me_subpel_iter,
1432 greedy_skip: cfg.tune_greedy_skip,
1433 greedy_min_free: cfg.tune_greedy_skip_min_free.unwrap_or(85),
1434 rd_skip: cfg.tune_rd_skip,
1435 rd_skip_fast_t: cfg.tune_rd_skip_fast_t.unwrap_or(0.0),
1436 rd_skip_min_free: cfg.tune_rd_skip_min_free.unwrap_or(
1437 if cfg.preset == crate::config::Preset::Fast { 60 } else { 90 },
1438 ),
1439 satd_var_thresh: i64::MAX,
1440 mb_use_satd: false,
1441 nnz_l_cache: [0x80; 25],
1442 nnz_c_cache: [[0x80; 9]; 2],
1443 mb_skip_sad: vec![0; mb_w * mb_h],
1444 mb_was_skip: vec![false; mb_w * mb_h],
1445 }
1446 }
1447
1448 fn pred_skip_sad(&self, mb_x: usize, mb_y: usize) -> u32 {
1454 let mbw = self.mb_w;
1455 let at = |x: isize, y: isize| -> Option<(bool, u32)> {
1456 if x < 0 || y < 0 || x >= mbw as isize {
1457 return None;
1458 }
1459 let i = y as usize * mbw + x as usize;
1460 Some((self.mb_was_skip[i], self.mb_skip_sad[i]))
1461 };
1462 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);
1467 let (sa, sb, sc) = (sad(a), sad(b), sad(c));
1468 if b.is_none() && c.is_none() && a.is_some() {
1470 return sa;
1471 }
1472 match (
1473 a.is_some_and(|(s, _)| s),
1474 b.is_some_and(|(s, _)| s),
1475 c.is_some_and(|(s, _)| s),
1476 ) {
1477 (true, false, false) => sa,
1478 (false, true, false) => sb,
1479 (false, false, true) => sc,
1480 _ => sb.max(sa.min(sc)).min(sa.max(sc)), }
1482 }
1483
1484 fn qp_delta(&mut self) -> i32 {
1489 let d = self.qp as i32 - self.cur_qp as i32;
1490 self.cur_qp = self.qp;
1491 d
1492 }
1493
1494 fn mv_neighbors(&self, mb_x: usize, mb_y: usize) -> [MvNeighbor; 3] {
1497 let w4 = self.mb_w * 4;
1498 let get = |avail: bool, bx: isize, by: isize| {
1499 if avail {
1500 let idx = by as usize * w4 + bx as usize;
1501 MvNeighbor {
1502 available: true,
1503 mv: self.mv_y[idx],
1504 ref_idx: self.ref_idx_y[idx],
1505 }
1506 } else {
1507 MvNeighbor::NONE
1508 }
1509 };
1510 let (bx, by) = (mb_x as isize * 4, mb_y as isize * 4);
1511 let a = get(mb_x > 0, bx - 1, by);
1512 let b = get(mb_y > 0, bx, by - 1);
1513 let c = if mb_y > 0 && mb_x + 1 < self.mb_w {
1515 get(true, bx + 4, by - 1)
1516 } else {
1517 get(mb_x > 0 && mb_y > 0, bx - 1, by - 1)
1518 };
1519 [a, b, c]
1520 }
1521
1522 fn skip_mv(&self, mb_x: usize, mb_y: usize) -> (i32, i32) {
1525 let [a, b, c] = self.mv_neighbors(mb_x, mb_y);
1526 if !a.available
1527 || !b.available
1528 || (a.ref_idx == 0 && a.mv == (0, 0))
1529 || (b.ref_idx == 0 && b.mv == (0, 0))
1530 {
1531 (0, 0)
1532 } else {
1533 predict_mv(a, b, c, 0)
1534 }
1535 }
1536
1537 fn set_mb_mv(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32), inter: bool, refi: i32) {
1540 let w4 = self.mb_w * 4;
1541 for dy in 0..4 {
1542 for dx in 0..4 {
1543 let idx = (mb_y * 4 + dy) * w4 + (mb_x * 4 + dx);
1544 self.mv_y[idx] = mv;
1545 self.inter_y[idx] = inter;
1546 self.ref_idx_y[idx] = if inter { refi } else { -1 };
1547 }
1548 }
1549 }
1550
1551 fn mv_neighbors_block(&self, pbx: isize, pby: isize, pwb: isize) -> [MvNeighbor; 3] {
1555 let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
1556 let get = |bx: isize, by: isize| -> MvNeighbor {
1557 if bx < 0 || by < 0 || bx >= w4 || by >= h4 || !self.coded_y[(by * w4 + bx) as usize] {
1558 MvNeighbor::NONE
1559 } else {
1560 let idx = (by * w4 + bx) as usize;
1561 MvNeighbor { available: true, mv: self.mv_y[idx], ref_idx: self.ref_idx_y[idx] }
1562 }
1563 };
1564 let a = get(pbx - 1, pby);
1565 let b = get(pbx, pby - 1);
1566 let mut c = get(pbx + pwb, pby - 1);
1567 if !c.available {
1568 c = get(pbx - 1, pby - 1); }
1570 [a, b, c]
1571 }
1572
1573 fn mv_neighbors_block_list(&self, pbx: isize, pby: isize, pwb: isize, list: usize) -> [MvNeighbor; 3] {
1579 let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
1580 let (mvg, refg): (&[(i32, i32)], &[i32]) = if list == 0 {
1581 (&self.mv_y, &self.ref_idx_y)
1582 } else {
1583 (&self.mv1_y, &self.ref_idx1_y)
1584 };
1585 let get = |bx: isize, by: isize| -> MvNeighbor {
1586 if bx < 0 || by < 0 || bx >= w4 || by >= h4 || !self.coded_y[(by * w4 + bx) as usize] {
1587 MvNeighbor::NONE
1588 } else {
1589 let idx = (by * w4 + bx) as usize;
1590 MvNeighbor { available: true, mv: mvg[idx], ref_idx: refg[idx] }
1591 }
1592 };
1593 let a = get(pbx - 1, pby);
1594 let b = get(pbx, pby - 1);
1595 let mut c = get(pbx + pwb, pby - 1);
1596 if !c.available {
1597 c = get(pbx - 1, pby - 1); }
1599 [a, b, c]
1600 }
1601
1602 #[allow(clippy::too_many_arguments)]
1612 #[inline]
1613 fn mc_satd_hp(
1614 &self,
1615 reference: &crate::RefFrame,
1616 hp: Option<&rusty_h264_common::inter::HpelPlanes>,
1617 hr_on: bool,
1618 sa_on: bool,
1622 src_row: &[u8],
1623 lx: usize,
1624 ly: usize,
1625 rw: usize,
1626 rh: usize,
1627 mv: (i32, i32),
1628 ) -> i64 {
1629 #[cfg(not(accel))]
1630 let _ = sa_on;
1631 #[cfg(feature = "profile")]
1632 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1633 let ch = self.mb_h * 16;
1634 let cw = self.cw;
1635 let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1636 let interior_fullpel = mv.0 & 3 == 0
1637 && mv.1 & 3 == 0
1638 && ix0 >= 0
1639 && iy0 >= 0
1640 && ix0 + rw as isize <= cw as isize
1641 && iy0 + rh as isize <= ch as isize;
1642 #[cfg(feature = "profile")]
1643 {
1644 let fullpel = mv.0 & 3 == 0 && mv.1 & 3 == 0;
1645 satdpath::bump(if interior_fullpel { 0 } else if fullpel { 1 } else { 2 });
1646 }
1647 if interior_fullpel {
1648 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1649 let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1650 return satd_px(src_row, cw, &reference.y[ry0 * cw + rx0..], cw, rw, rh);
1651 }
1652 if let Some(hp) = hp {
1653 if hr_on {
1654 if let Some((plane, base, stride)) =
1655 rusty_h264_common::inter::hpel_ref(hp, lx, ly, rw, rh, mv.0, mv.1)
1656 {
1657 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1658 return satd_px(src_row, cw, &plane[base..], stride, rw, rh);
1659 }
1660 }
1661 #[cfg(accel)]
1668 if sa_on {
1669 if let Some((pa, ba, pb, bb, stride)) =
1670 rusty_h264_common::inter::hpel_qpel_refs(hp, lx, ly, rw, rh, mv.0, mv.1)
1671 {
1672 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1673 if let Some(v) = rusty_h264_accel::satd_avg(
1674 src_row, cw, &pa[ba..], &pb[bb..], stride, rw, rh,
1675 ) {
1676 return v as i64;
1677 }
1678 }
1679 }
1680 let mut pred = [0u8; 256];
1681 if rusty_h264_common::inter::hpel_block(hp, lx, ly, rw, rh, mv.0, mv.1, &mut pred) {
1682 return satd_px(src_row, cw, &pred, rw, rw, rh);
1683 }
1684 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1685 return satd_px(src_row, cw, &pred, rw, rw, rh);
1686 }
1687 let mut pred = [0u8; 256];
1688 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1689 satd_px(src_row, cw, &pred, rw, rw, rh)
1690 }
1691
1692 #[allow(clippy::too_many_arguments)]
1700 #[inline]
1701 fn mc_sad_hp(
1702 &self,
1703 reference: &crate::RefFrame,
1704 hp: Option<&rusty_h264_common::inter::HpelPlanes>,
1705 hr_on: bool,
1706 src_row: &[u8],
1707 lx: usize,
1708 ly: usize,
1709 rw: usize,
1710 rh: usize,
1711 mv: (i32, i32),
1712 _asrc: Option<&[u8; 256]>,
1713 ) -> i64 {
1714 #[cfg(feature = "profile")]
1715 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1716 let ch = self.mb_h * 16;
1717 let cw = self.cw;
1718 let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1719 let interior_fullpel = mv.0 & 3 == 0
1720 && mv.1 & 3 == 0
1721 && ix0 >= 0
1722 && iy0 >= 0
1723 && ix0 + rw as isize <= cw as isize
1724 && iy0 + rh as isize <= ch as isize;
1725 if interior_fullpel {
1726 let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1727 #[cfg(accel)]
1728 if rw == 16 && rh == 16 {
1729 if let Some(src) = _asrc {
1730 return rusty_h264_accel::sad_16x16(src, 16, &reference.y[ry0 * cw + rx0..], cw)
1731 as i64;
1732 }
1733 }
1734 return sad_strided(src_row, cw, &reference.y[ry0 * cw + rx0..], cw, rw, rh);
1735 }
1736 if let Some(hp) = hp {
1737 if hr_on {
1738 if let Some((plane, base, stride)) =
1741 rusty_h264_common::inter::hpel_ref(hp, lx, ly, rw, rh, mv.0, mv.1)
1742 {
1743 return sad_strided(src_row, cw, &plane[base..], stride, rw, rh);
1744 }
1745 if let Some((pa, ba, pb, bb, stride)) =
1747 rusty_h264_common::inter::hpel_qpel_refs(hp, lx, ly, rw, rh, mv.0, mv.1)
1748 {
1749 return sad_avg_strided(src_row, cw, &pa[ba..], &pb[bb..], stride, rw, rh);
1750 }
1751 }
1752 let mut pred = [0u8; 256];
1753 if rusty_h264_common::inter::hpel_block(hp, lx, ly, rw, rh, mv.0, mv.1, &mut pred) {
1754 return sad_strided(src_row, cw, &pred, rw, rw, rh);
1755 }
1756 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1757 return sad_strided(src_row, cw, &pred, rw, rw, rh);
1758 }
1759 let mut pred = [0u8; 256];
1760 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1761 sad_strided(src_row, cw, &pred, rw, rw, rh)
1762 }
1763
1764 #[allow(clippy::too_many_arguments)]
1773 fn mc_sad(
1774 &self,
1775 reference: &crate::RefFrame,
1776 sy: &[u8],
1777 lx: usize,
1778 ly: usize,
1779 rw: usize,
1780 rh: usize,
1781 mv: (i32, i32),
1782 _asrc: Option<&[u8; 256]>,
1785 ) -> i64 {
1786 #[cfg(feature = "profile")]
1790 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1791 let ch = self.mb_h * 16;
1792 let cw = self.cw;
1793 let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1794 let interior_fullpel = mv.0 & 3 == 0
1795 && mv.1 & 3 == 0
1796 && ix0 >= 0
1797 && iy0 >= 0
1798 && ix0 + rw as isize <= cw as isize
1799 && iy0 + rh as isize <= ch as isize;
1800 #[cfg(accel)]
1804 if interior_fullpel && rw == 16 && rh == 16 {
1805 if let Some(src) = _asrc {
1806 let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1807 return rusty_h264_accel::sad_16x16(src, 16, &reference.y[ry0 * cw + rx0..], cw)
1808 as i64;
1809 }
1810 }
1811 let mut sad = 0u32;
1812 if interior_fullpel {
1813 let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1815 let refy = &reference.y;
1816 for dy in 0..rh {
1817 let s = &sy[(ly + dy) * cw + lx..][..rw];
1818 let r = &refy[(ry0 + dy) * cw + rx0..][..rw];
1819 sad += s.iter().zip(r).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1820 }
1821 } else {
1822 let mut pred = [0u8; 256];
1823 let from_planes = !self.fast
1825 && rusty_h264_common::inter::hpel_block(
1826 reference.hpel(cw, ch),
1827 lx,
1828 ly,
1829 rw,
1830 rh,
1831 mv.0,
1832 mv.1,
1833 &mut pred,
1834 );
1835 if !from_planes {
1836 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1837 }
1838 for dy in 0..rh {
1839 let s = &sy[(ly + dy) * cw + lx..][..rw];
1840 let p = &pred[dy * rw..][..rw];
1841 sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1842 }
1843 }
1844 sad as i64
1845 }
1846
1847 fn bi_dist(
1853 &self,
1854 l0: &crate::RefFrame,
1855 l1: &crate::RefFrame,
1856 sy: &[u8],
1857 lx: usize,
1858 ly: usize,
1859 mv0: (i32, i32),
1860 mv1: (i32, i32),
1861 ) -> i64 {
1862 #[cfg(feature = "profile")]
1864 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(4);
1865 let ch = self.mb_h * 16;
1866 let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
1867 mc_luma(&l0.y, self.cw, ch, lx, ly, 16, 16, mv0.0, mv0.1, &mut a);
1868 mc_luma(&l1.y, self.cw, ch, lx, ly, 16, 16, mv1.0, mv1.1, &mut b);
1869 let mut avg = [0u8; 256];
1870 for i in 0..256 {
1871 avg[i] = bi_blend(a[i] as i32, b[i] as i32, self.bi_w);
1872 }
1873 if self.fast && !self.mb_use_satd {
1874 let mut sad = 0u32;
1875 for dy in 0..16 {
1876 let s = &sy[(ly + dy) * self.cw + lx..][..16];
1877 let p = &avg[dy * 16..][..16];
1878 sad += s.iter().zip(p).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
1879 }
1880 sad as i64
1881 } else {
1882 satd_px(&sy[ly * self.cw + lx..], self.cw, &avg, 16, 16, 16)
1883 }
1884 }
1885
1886 fn pred_dist(&self, sy: &[u8], lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
1890 if self.fast && !self.mb_use_satd {
1891 let mut sad = 0u32;
1892 for dy in 0..16 {
1893 let s = &sy[(ly + dy) * self.cw + lx..][..16];
1894 let p = &pred[dy * 16..][..16];
1895 sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1896 }
1897 sad as i64
1898 } else {
1899 satd_px(&sy[ly * self.cw + lx..], self.cw, pred, 16, 16, 16)
1900 }
1901 }
1902
1903 fn col_zero(&self, l1: &crate::RefFrame, bx: usize, by: usize) -> bool {
1908 if l1.w4 == 0 {
1909 return false;
1910 }
1911 let idx = by * l1.w4 + bx;
1912 if idx >= l1.ref_idx.len() {
1913 return false;
1914 }
1915 l1.ref_idx[idx] == 0 && l1.mv[idx].0.abs() <= 1 && l1.mv[idx].1.abs() <= 1
1916 }
1917
1918 #[allow(clippy::too_many_arguments)]
1922 fn b_mc_block(
1923 &self,
1924 l0: &crate::RefFrame,
1925 l1: &crate::RefFrame,
1926 mb_x: usize,
1927 mb_y: usize,
1928 dx: usize,
1929 dy: usize,
1930 refi0: i32,
1931 m0: (i32, i32),
1932 refi1: i32,
1933 m1: (i32, i32),
1934 pred_y: &mut [u8; 256],
1935 c_pred: &mut [[u8; 64]; 2],
1936 ) {
1937 #[cfg(feature = "profile")]
1939 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(4);
1940 let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
1941 let (px, py) = (mb_x * 16 + dx, mb_y * 16 + dy);
1942 let (mut a, mut b) = ([0u8; 16], [0u8; 16]);
1943 let mc4 = |r: &crate::RefFrame, mv: (i32, i32), out: &mut [u8; 16]| {
1951 if !self.fast
1952 && bdirect_planes_enabled()
1953 && rusty_h264_common::inter::hpel_block(
1954 r.hpel(self.cw, ch), px, py, 4, 4, mv.0, mv.1, out,
1955 )
1956 {
1957 return;
1958 }
1959 mc_luma(&r.y, self.cw, ch, px, py, 4, 4, mv.0, mv.1, out);
1960 };
1961 if refi0 >= 0 {
1962 mc4(l0, m0, &mut a);
1963 }
1964 if refi1 >= 0 {
1965 mc4(l1, m1, &mut b);
1966 }
1967 for yy in 0..4 {
1968 for xx in 0..4 {
1969 let i = yy * 4 + xx;
1970 let v = match (refi0 >= 0, refi1 >= 0) {
1971 (true, true) => bi_blend(a[i] as i32, b[i] as i32, self.bi_w),
1972 (true, false) => a[i],
1973 _ => b[i],
1974 };
1975 pred_y[(dy + yy) * 16 + (dx + xx)] = v;
1976 }
1977 }
1978 let (cpx, cpy) = (mb_x * 8 + dx / 2, mb_y * 8 + dy / 2);
1980 for c in 0..2 {
1981 let (r0, r1) = if c == 0 { (&l0.u, &l1.u) } else { (&l0.v, &l1.v) };
1982 let (mut ca, mut cb) = ([0u8; 4], [0u8; 4]);
1983 if refi0 >= 0 {
1984 mc_chroma(r0, self.ccw, cch, cpx, cpy, 2, 2, m0.0, m0.1, &mut ca);
1985 }
1986 if refi1 >= 0 {
1987 mc_chroma(r1, self.ccw, cch, cpx, cpy, 2, 2, m1.0, m1.1, &mut cb);
1988 }
1989 for yy in 0..2 {
1990 for xx in 0..2 {
1991 let i = yy * 2 + xx;
1992 let v = match (refi0 >= 0, refi1 >= 0) {
1993 (true, true) => bi_blend(ca[i] as i32, cb[i] as i32, self.bi_w),
1994 (true, false) => ca[i],
1995 _ => cb[i],
1996 };
1997 c_pred[c][(dy / 2 + yy) * 8 + (dx / 2 + xx)] = v;
1998 }
1999 }
2000 }
2001 }
2002
2003 fn b_direct(
2008 &self,
2009 l0: &crate::RefFrame,
2010 l1: &crate::RefFrame,
2011 mb_x: usize,
2012 mb_y: usize,
2013 ) -> ([u8; 256], [[u8; 64]; 2], [(i32, (i32, i32), i32, (i32, i32)); 16]) {
2014 let (nbx, nby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
2015 let n0 = self.mv_neighbors_block_list(nbx, nby, 4, 0);
2016 let n1 = self.mv_neighbors_block_list(nbx, nby, 4, 1);
2017 let min_pos = |a: i32, b: i32| if a < 0 { b } else if b < 0 { a } else { a.min(b) };
2018 let rid = |n: &[MvNeighbor; 3]| min_pos(min_pos(n[0].ref_idx, n[1].ref_idx), n[2].ref_idx);
2019 let (mut refi0, mut refi1) = (rid(&n0), rid(&n1));
2020 let direct_zero = refi0 < 0 && refi1 < 0;
2021 if direct_zero {
2022 refi0 = 0;
2023 refi1 = 0;
2024 }
2025 let mv0 = if refi0 >= 0 && !direct_zero { predict_mv(n0[0], n0[1], n0[2], refi0) } else { (0, 0) };
2026 let mv1 = if refi1 >= 0 && !direct_zero { predict_mv(n1[0], n1[1], n1[2], refi1) } else { (0, 0) };
2027 let mut pred_y = [0u8; 256];
2028 let mut c_pred = [[0u8; 64]; 2];
2029 let mut motion = [(0i32, (0i32, 0i32), 0i32, (0i32, 0i32)); 16];
2030 for sby in 0..4 {
2031 for sbx in 0..4 {
2032 let cz = !direct_zero && self.col_zero(l1, mb_x * 4 + sbx, mb_y * 4 + sby);
2033 let m0 = if refi0 == 0 && cz { (0, 0) } else { mv0 };
2034 let m1 = if refi1 == 0 && cz { (0, 0) } else { mv1 };
2035 motion[sby * 4 + sbx] = (refi0, m0, refi1, m1);
2036 self.b_mc_block(l0, l1, mb_x, mb_y, sbx * 4, sby * 4, refi0, m0, refi1, m1, &mut pred_y, &mut c_pred);
2037 }
2038 }
2039 (pred_y, c_pred, motion)
2040 }
2041
2042 fn commit_direct_motion(&mut self, mb_x: usize, mb_y: usize, motion: &[(i32, (i32, i32), i32, (i32, i32)); 16]) {
2045 let w4 = self.mb_w * 4;
2046 for sby in 0..4 {
2047 for sbx in 0..4 {
2048 let (refi0, m0, refi1, m1) = motion[sby * 4 + sbx];
2049 let idx = (mb_y * 4 + sby) * w4 + (mb_x * 4 + sbx);
2050 self.inter_y[idx] = true;
2051 self.coded_y[idx] = true;
2052 self.mv_y[idx] = m0;
2053 self.ref_idx_y[idx] = refi0;
2054 self.mv1_y[idx] = m1;
2055 self.ref_idx1_y[idx] = refi1;
2056 }
2057 }
2058 }
2059
2060 #[allow(clippy::too_many_arguments)]
2069 fn motion_search(
2075 &self,
2076 reference: &crate::RefFrame,
2077 sy: &[u8],
2078 lx: usize,
2079 ly: usize,
2080 rw: usize,
2081 rh: usize,
2082 predictors: &[(i32, i32)],
2083 lambda_me: f64,
2084 start: Option<(i32, i32)>,
2088 ) -> ((i32, i32), i64) {
2089 let mvk = mv_cost_kind(self.mv_smooth);
2095 let mvbits = |d: i32| -> u32 {
2096 match mvk {
2106 1 => {
2107 let a = d.unsigned_abs().min(4095) as usize;
2108 MV_COST_TAB.get_or_init(build_mv_cost)[a] as u32
2109 }
2110 2 => {
2111 let a = d.unsigned_abs().min(4095) as usize;
2112 MV_TRUE_BIASED.get_or_init(build_true_biased)[a] as u32
2113 }
2114 _ => {
2115 let codenum = if d > 0 { (2 * d - 1) as u32 } else { (-2 * d) as u32 };
2116 1 + 2 * (31 - (codenum + 1).leading_zeros())
2117 }
2118 }
2119 };
2120 let center = predictors[0];
2121 let probe = me_oracle_on();
2122 let sadfp = !self.fast && start.is_none() && self.sadfp;
2128 #[cfg(accel)]
2133 let asrc_buf = if (self.fast || sadfp) && rw == 16 && rh == 16 {
2134 let mut a = AlignedMb([0u8; 256]);
2135 for dy in 0..16 {
2136 a.0[dy * 16..dy * 16 + 16].copy_from_slice(&sy[(ly + dy) * self.cw + lx..][..16]);
2137 }
2138 Some(a)
2139 } else {
2140 None
2141 };
2142 #[cfg(accel)]
2143 let asrc: Option<&[u8; 256]> = asrc_buf.as_ref().map(|a| &a.0);
2144 #[cfg(not(accel))]
2145 let asrc: Option<&[u8; 256]> = None;
2146 let use_sad = self.fast && !self.mb_use_satd;
2154 let cw = self.cw;
2155 let hp: Option<&rusty_h264_common::inter::HpelPlanes> =
2159 if !self.fast { Some(reference.hpel(cw, self.mb_h * 16)) } else { None };
2160 let hr_on = hpel_ref_enabled();
2161 let sa_on = cfg!(accel) && hr_on && satd_avg_enabled();
2164 let src_row = &sy[ly * cw + lx..];
2165 #[cfg(accel)]
2171 let mectx = if !use_sad && mectx_enabled() {
2172 hp.and_then(|p| {
2173 rusty_h264_accel::MeCtx::new(
2174 src_row, cw, &p.f, &p.h, &p.v, &p.c, p.stride, p.pad, p.pw, p.ph,
2175 lx, ly, rw, rh,
2176 )
2177 })
2178 } else {
2179 None
2180 };
2181 let cost = |mv: (i32, i32)| -> i64 {
2182 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2183 let lam_r = if mvk != 0 { lambda_me * 0.25 } else { lambda_me };
2186 let dist = if use_sad {
2189 self.mc_sad(reference, sy, lx, ly, rw, rh, mv, asrc)
2190 } else {
2191 #[cfg(accel)]
2192 {
2193 match mectx.as_ref().and_then(|c| c.eval(mv.0, mv.1)) {
2194 Some(d) => d as i64,
2195 None => {
2196 self.mc_satd_hp(reference, hp, hr_on, sa_on, src_row, lx, ly, rw, rh, mv)
2197 }
2198 }
2199 }
2200 #[cfg(not(accel))]
2201 {
2202 self.mc_satd_hp(reference, hp, hr_on, sa_on, src_row, lx, ly, rw, rh, mv)
2203 }
2204 };
2205 dist + (lam_r * rate as f64) as i64
2206 };
2207 let lam_fp = lambda_me * if sadfp { me_sadfp_lambda() } else { 1.0 };
2211 let cost_fp = |mv: (i32, i32)| -> i64 {
2212 if !sadfp {
2213 return cost(mv);
2214 }
2215 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2216 self.mc_sad_hp(reference, hp, hr_on, src_row, lx, ly, rw, rh, mv, asrc)
2217 + (lam_fp * rate as f64) as i64
2218 };
2219 let refine_only = start.is_some();
2221 let (mut best, mut best_c) = match start {
2222 Some(mv) => (mv, cost(mv)),
2223 None => {
2224 let mut b = (0, 0);
2225 let mut bc = cost_fp(b);
2226 for &p in predictors {
2227 let pc = cost_fp(p);
2228 if pc < bc {
2229 bc = pc;
2230 b = p;
2231 }
2232 }
2233 (b, bc)
2234 }
2235 };
2236 let (seed_mv, mut seed_c) = (best, best_c);
2244 if !refine_only && self.me_snap && (best.0 & 3 != 0 || best.1 & 3 != 0) {
2245 let snapped = ((best.0 + 2).div_euclid(4) * 4, (best.1 + 2).div_euclid(4) * 4);
2246 best_c = cost_fp(snapped);
2247 best = snapped;
2248 }
2249 let mut ladder = [0i32; 5];
2263 let mut nladder = 0usize;
2264 let steps: &[i32] = if self.fast {
2265 &[16, 4]
2266 } else {
2267 let m = dia_mask();
2268 for (i, r) in DIA_RUNGS.iter().enumerate() {
2269 if m & (1 << i) != 0 {
2270 ladder[nladder] = *r;
2271 nladder += 1;
2272 }
2273 }
2274 &ladder[..nladder]
2275 };
2276 let _gd = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeDiamond);
2277 let fc = !self.fast && cfg!(accel) && me_fc_enabled()
2285 && matches!((rw, rh), (16, 16) | (16, 8) | (8, 16) | (8, 8));
2286 let ch_px = self.mb_h as isize * 16;
2287 for (_si, &step) in steps.iter().enumerate() {
2288 if refine_only {
2289 break;
2290 }
2291 loop {
2292 #[cfg(accel)]
2293 if fc && best.0 & 3 == 0 && best.1 & 3 == 0 {
2294 let s = (step >> 2) as isize;
2296 let (bx, by) = (lx as isize + (best.0 >> 2) as isize, ly as isize + (best.1 >> 2) as isize);
2297 if bx - s >= 0 && by - s >= 0 && bx + s + rw as isize <= cw as isize && by + s + rh as isize <= ch_px {
2298 let offs = [
2299 (by * cw as isize + bx + s) as usize,
2300 (by * cw as isize + bx - s) as usize,
2301 ((by + s) * cw as isize + bx) as usize,
2302 ((by - s) * cw as isize + bx) as usize,
2303 ];
2304 let batch = if rw != 16 {
2310 None
2311 } else if sadfp {
2312 rusty_h264_accel::sad_x4(src_row, cw, &reference.y, offs, cw, rw, rh)
2313 } else {
2314 rusty_h264_accel::satd_x4(src_row, cw, &reference.y, offs, cw, rw, rh)
2315 };
2316 {
2317 let ring = [(step, 0), (-step, 0), (0, step), (0, -step)];
2318 let (mut bi, mut bc) = (usize::MAX, best_c);
2319 for (i, &(dx, dy)) in ring.iter().enumerate() {
2320 let mv = (best.0 + dx, best.1 + dy);
2321 let cc = match batch {
2322 Some(sads) => {
2323 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2324 sads[i] as i64 + (lam_fp * rate as f64) as i64
2325 }
2326 None => cost_fp(mv),
2327 };
2328 #[cfg(feature = "profile")]
2329 diastats::ev(_si);
2330 if cc < bc {
2331 bc = cc;
2332 bi = i;
2333 }
2334 }
2335 if bi == usize::MAX {
2336 break;
2337 }
2338 best_c = bc;
2339 best = (best.0 + ring[bi].0, best.1 + ring[bi].1);
2340 #[cfg(feature = "profile")]
2341 diastats::imp(_si);
2342 continue;
2343 }
2344 }
2345 }
2346 let mut improved = false;
2347 for &(dx, dy) in &[(step, 0), (-step, 0), (0, step), (0, -step)] {
2348 let c = (best.0 + dx, best.1 + dy);
2349 let cc = cost_fp(c);
2350 #[cfg(feature = "profile")]
2351 diastats::ev(_si);
2352 if cc < best_c {
2353 best_c = cc;
2354 best = c;
2355 improved = true;
2356 #[cfg(feature = "profile")]
2357 diastats::imp(_si);
2358 }
2359 }
2360 if !improved {
2361 break;
2362 }
2363 }
2364 }
2365 drop(_gd);
2378 if sadfp {
2383 best_c = cost(best);
2384 seed_c = cost(seed_mv);
2385 }
2386 let _gr = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeRescue);
2387 let flat = |sself: &Self| {
2393 !refine_only && {
2394 let (mut s, mut ss) = (0u64, 0u64);
2395 for dy in 0..rh {
2396 for dx in 0..rw {
2397 let v = sy[(ly + dy) * sself.cw + lx + dx] as u64;
2398 s += v;
2399 ss += v * v;
2400 }
2401 }
2402 let n = (rw * rh) as u64;
2403 (ss - s * s / n) / n < sself.me_wide_var
2404 }
2405 };
2406 if self.me_wide && !self.fast && (self.me_fast || flat(self)) && !self.resc_off.get() {
2417 let rate_b = mvbits(best.0 - center.0) + mvbits(best.1 - center.1);
2424 let dist = best_c - (lambda_me * rate_b as f64) as i64;
2425 if dist / (rw * rh).max(1) as i64 > self.me_rescue {
2426 let pre_c = best_c;
2436 let (cx, cy) = ((best.0 + 2).div_euclid(4) * 4, (best.1 + 2).div_euclid(4) * 4);
2437 let mut gb = best;
2438 let cw = self.cw;
2447 let r = self.me_range;
2448 let batched = rw == 16 && rh == 16 && cfg!(accel) && {
2449 let (icdx, icdy) = (cx >> 2, cy >> 2);
2450 lx as i32 + icdx >= r
2451 && lx as i32 + icdx + r + 16 <= cw as i32
2452 && ly as i32 + icdy >= r
2453 && ly as i32 + icdy + r + 16 <= (self.mb_h * 16) as i32
2454 && me_batch_enabled()
2455 };
2456 #[cfg(accel)]
2457 if batched {
2458 let (icdx, icdy) = ((cx >> 2), (cy >> 2));
2459 let src = &sy[ly * cw + lx..];
2460 let mut dy = -r;
2461 while dy <= r {
2462 let rby = (ly as i32 + icdy + dy) as usize;
2463 let mut dx = -r;
2464 while dx <= r {
2465 let rbx = (lx as i32 + icdx + dx) as usize;
2466 let satd =
2467 2 * rusty_h264_accel::satd_16x16(src, cw, &reference.y[rby * cw + rbx..], cw) as i64;
2468 let mv = (cx + dx * 4, cy + dy * 4);
2469 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2470 let cc = satd + (lambda_me * rate as f64) as i64;
2471 if cc < best_c {
2472 best_c = cc;
2473 gb = mv;
2474 }
2475 dx += 2;
2476 }
2477 dy += 2;
2478 }
2479 }
2480 if !batched {
2481 let mut dy = -r;
2482 while dy <= r {
2483 let mut dx = -r;
2484 while dx <= r {
2485 let cc = cost((cx + dx * 4, cy + dy * 4));
2486 if cc < best_c {
2487 best_c = cc;
2488 gb = (cx + dx * 4, cy + dy * 4);
2489 }
2490 dx += 2;
2491 }
2492 dy += 2;
2493 }
2494 }
2495 best = gb;
2496 for dy in -1..=1 {
2497 for dx in -1..=1 {
2498 let c = (best.0 + dx * 4, best.1 + dy * 4);
2499 let cc = cost(c);
2500 if cc < best_c {
2501 best_c = cc;
2502 best = c;
2503 }
2504 }
2505 }
2506 let n = self.resc_n.get();
2515 if n < self.me_learn {
2516 self.resc_n.set(n + 1);
2517 if best_c * 16 <= pre_c * 15 {
2518 self.resc_big.set(self.resc_big.get() + 1);
2519 }
2520 if n + 1 == self.me_learn
2521 && self.resc_big.get() * 100 < self.me_learn * self.me_payoff_pct
2522 {
2523 self.resc_off.set(true);
2524 }
2525 }
2526 }
2527 }
2528 drop(_gr);
2529 let _gs = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeSubpel);
2530 if probe {
2536 let mut ob = center;
2539 let mut oc = i64::MAX;
2540 for gy in -24i32..=24 {
2541 for gx in -24i32..=24 {
2542 let c = (center.0 + gx * 4, center.1 + gy * 4);
2543 let cc = cost(c);
2544 if cc < oc {
2545 oc = cc;
2546 ob = c;
2547 }
2548 }
2549 }
2550 let fullpel_best = ob;
2551 for &st in &[2i32, 1] {
2552 for &(dx, dy) in &[(st, 0), (-st, 0), (0, st), (0, -st)] {
2553 let c = (ob.0 + dx, ob.1 + dy);
2554 let cc = cost(c);
2555 if cc < oc {
2556 oc = cc;
2557 ob = c;
2558 }
2559 }
2560 }
2561 let mut oc_sp = oc;
2565 for dy in -3i32..=3 {
2566 for dx in -3i32..=3 {
2567 let c = (fullpel_best.0 + dx, fullpel_best.1 + dy);
2568 let cc = cost(c);
2569 if cc < oc_sp {
2570 oc_sp = cc;
2571 }
2572 }
2573 }
2574 let (mut mb_, mut mc_) = (best, best_c);
2576 for &st in &[2i32, 1] {
2577 for &(dx, dy) in &[(st, 0), (-st, 0), (0, st), (0, -st)] {
2578 let c = (mb_.0 + dx, mb_.1 + dy);
2579 let cc = cost(c);
2580 if cc < mc_ {
2581 mc_ = cc;
2582 mb_ = c;
2583 }
2584 }
2585 }
2586 use std::sync::atomic::Ordering::Relaxed;
2587 ME_PROBE[0].fetch_add(1, Relaxed);
2588 ME_PROBE[1].fetch_add(mc_.max(0) as u64, Relaxed);
2589 ME_PROBE[2].fetch_add(oc.max(0) as u64, Relaxed);
2590 ME_PROBE[3].fetch_add((mc_ > oc) as u64, Relaxed);
2591 ME_PROBE[5].fetch_add(oc_sp.max(0) as u64, Relaxed);
2592 ME_PROBE[6].fetch_add((mc_ > oc_sp) as u64, Relaxed);
2593 }
2594 let subpel: &[i32] = if (self.fast && !self.subpel_force) || (self.sp_defer.get() && !refine_only) {
2595 &[]
2596 } else {
2597 &[2, 1]
2598 };
2599 let (hv_pre, mut hv_evals) = (best_c, 0u32);
2601 let (mut hv_to_best, mut hv_ring1) = (0u32, i64::MIN);
2605 let mut pat = subpel_pattern_override()
2606 .unwrap_or(if self.sp_single_pass { 2 } else { 0 });
2607 let (sp_learn, sp_t) = sp_dispatch_cfg();
2608 let sp_dispatching = sp_learn > 0 && pat == 0 && !subpel.is_empty();
2610 if sp_dispatching && self.sp_learn_n.get() >= sp_learn && self.sp_1pass.get() {
2611 pat = 2;
2612 }
2613 const SP_MEMO_N: usize = 64;
2626 #[inline(always)]
2627 fn sp_slot(mv: (i32, i32)) -> usize {
2628 ((mv.0 & 7) as usize) | (((mv.1 & 7) as usize) << 3)
2629 }
2630 let mut memo_mv = [(i32::MIN, i32::MIN); SP_MEMO_N];
2631 let mut memo_c = [0i64; SP_MEMO_N];
2632 if !subpel.is_empty() {
2633 let s0 = sp_slot(best);
2634 memo_mv[s0] = best;
2635 memo_c[s0] = best_c;
2636 }
2637 #[cfg(feature = "profile")]
2642 let mut seen: Vec<(i32, i32)> = Vec::with_capacity(64);
2643 #[cfg(feature = "profile")]
2644 {
2645 seen.push(best);
2646 }
2647 let sp_cap = sp_maxit();
2655 let sp_fc = sp_fc_enabled() && !self.fast && cfg!(accel)
2657 && matches!((rw, rh), (16, 16) | (16, 8) | (8, 16) | (8, 8));
2658 for &step in subpel {
2659 let ring8 = [
2664 (step, 0), (-step, 0), (0, step), (0, -step),
2665 (step, step), (-step, -step), (step, -step), (-step, step),
2666 ];
2667 let ring4 = [(step, 0), (-step, 0), (0, step), (0, -step)];
2668 let ring: &[(i32, i32)] = if pat & 1 != 0 { &ring4 } else { &ring8 };
2669 let mut _iter = 0u32;
2670 loop {
2671 #[cfg(accel)]
2680 if sp_fc && step == 1 && pat & 1 == 0 {
2681 _iter += 1;
2682 let hp8 = hp.expect("sp_fc implies non-fast, which resolves hp");
2683 let ring8 = [
2684 (1, 0), (-1, 0), (0, 1), (0, -1),
2685 (1, 1), (-1, -1), (1, -1), (-1, 1),
2686 ];
2687 let mut prs: [Option<(&[u8], usize, &[u8], usize, usize)>; 8] = [None; 8];
2688 let mut all = true;
2689 for (i, &(dx, dy)) in ring8.iter().enumerate() {
2690 prs[i] = rusty_h264_common::inter::hpel_qpel_refs(
2691 hp8, lx, ly, rw, rh, best.0 + dx, best.1 + dy,
2692 );
2693 all &= prs[i].is_some();
2694 }
2695 if all {
2696 let stride = prs[0].unwrap().4;
2697 let pack = |a: usize, b: usize, c2: usize, d: usize| {
2701 if rw != 16 {
2702 return None;
2703 }
2704 let g = |i: usize| {
2705 let (pa, oa, pb, ob, _) = prs[i].unwrap();
2706 (pa, oa, pb, ob)
2707 };
2708 rusty_h264_accel::satd_avg_x4(
2709 src_row, cw, [g(a), g(b), g(c2), g(d)], stride, rw, rh,
2710 )
2711 };
2712 {
2713 let (ax, di) = (pack(0, 1, 2, 3), pack(4, 5, 6, 7));
2714 let (mut bi, mut bc) = (usize::MAX, best_c);
2715 for i in 0..8 {
2716 let (dx, dy) = ring8[i];
2717 let mv = (best.0 + dx, best.1 + dy);
2718 let cc = match (i < 4, &ax, &di) {
2719 (true, Some(ax), _) => {
2720 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2721 ax[i] as i64 + (lambda_me * rate as f64) as i64
2722 }
2723 (false, _, Some(di)) => {
2724 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2725 di[i - 4] as i64 + (lambda_me * rate as f64) as i64
2726 }
2727 _ => cost(mv),
2728 };
2729 hv_evals += 1;
2730 if cc < bc {
2731 bc = cc;
2732 bi = i;
2733 }
2734 }
2735 if hv_ring1 == i64::MIN {
2736 hv_ring1 = if bi == usize::MAX { best_c } else { bc };
2737 }
2738 if bi == usize::MAX
2739 || !self.me_subpel_iter
2740 || pat & 2 != 0
2741 || (sp_cap != 0 && _iter >= sp_cap)
2742 {
2743 if bi != usize::MAX {
2744 best_c = bc;
2745 best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2746 hv_to_best = hv_evals;
2747 }
2748 break;
2749 }
2750 best_c = bc;
2751 best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2752 hv_to_best = hv_evals;
2753 continue;
2754 }
2755 }
2756 _iter -= 1;
2757 }
2758 #[cfg(accel)]
2759 if sp_fc && step == 2 && best.0 & 3 == 0 && best.1 & 3 == 0 && pat & 1 == 0 {
2760 _iter += 1;
2761 let hp8 = hp.expect("sp_fc implies non-fast, which resolves hp");
2762 let ring8 = [
2763 (step, 0), (-step, 0), (0, step), (0, -step),
2764 (step, step), (-step, -step), (step, -step), (-step, step),
2765 ];
2766 let mut refs8: [Option<(&[u8], usize, usize)>; 8] = [None; 8];
2767 let mut all = true;
2768 for (i, &(dx, dy)) in ring8.iter().enumerate() {
2769 refs8[i] = rusty_h264_common::inter::hpel_ref(
2770 hp8, lx, ly, rw, rh, best.0 + dx, best.1 + dy,
2771 );
2772 all &= refs8[i].is_some();
2773 }
2774 if all {
2775 let stride = refs8[0].unwrap().2;
2776 let pack = |a: usize, b: usize, c2: usize, d: usize| {
2779 if rw != 16 {
2780 return None;
2781 }
2782 let g = |i: usize| {
2783 let (p, o, _) = refs8[i].unwrap();
2784 (p, o)
2785 };
2786 rusty_h264_accel::satd_x4p(
2787 src_row, cw, [g(a), g(b), g(c2), g(d)], stride, rw, rh,
2788 )
2789 };
2790 {
2791 let (ax, di) = (pack(0, 1, 2, 3), pack(4, 5, 6, 7));
2792 let (mut bi, mut bc) = (usize::MAX, best_c);
2793 for i in 0..8 {
2794 let (dx, dy) = ring8[i];
2795 let mv = (best.0 + dx, best.1 + dy);
2796 let cc = match (i < 4, &ax, &di) {
2797 (true, Some(ax), _) => {
2798 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2799 ax[i] as i64 + (lambda_me * rate as f64) as i64
2800 }
2801 (false, _, Some(di)) => {
2802 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2803 di[i - 4] as i64 + (lambda_me * rate as f64) as i64
2804 }
2805 _ => cost(mv),
2806 };
2807 hv_evals += 1;
2808 if cc < bc {
2809 bc = cc;
2810 bi = i;
2811 }
2812 }
2813 if hv_ring1 == i64::MIN {
2814 hv_ring1 = if bi == usize::MAX { best_c } else { bc };
2815 }
2816 if bi == usize::MAX
2817 || !self.me_subpel_iter
2818 || pat & 2 != 0
2819 || (sp_cap != 0 && _iter >= sp_cap)
2820 {
2821 if bi != usize::MAX {
2822 best_c = bc;
2823 best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2824 hv_to_best = hv_evals;
2825 }
2826 break;
2827 }
2828 best_c = bc;
2829 best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2830 hv_to_best = hv_evals;
2831 continue;
2832 }
2833 }
2834 _iter -= 1; }
2836 let mut improved = false;
2837 _iter += 1;
2838 for (_pi, &(dx, dy)) in ring.iter().enumerate() {
2839 let c = (best.0 + dx, best.1 + dy);
2840 let slot = sp_slot(c);
2841 let cc = if memo_mv[slot] == c {
2842 memo_c[slot]
2843 } else {
2844 let v = cost(c);
2845 memo_mv[slot] = c;
2846 memo_c[slot] = v;
2847 v
2848 };
2849 hv_evals += 1;
2850 #[cfg(feature = "profile")]
2854 {
2855 spstats::ev(if step == 2 { 0 } else { 1 }, _pi, _iter);
2856 if seen.contains(&c) {
2857 spstats::redundant();
2858 } else {
2859 seen.push(c);
2860 }
2861 }
2862 if cc < best_c {
2863 best_c = cc;
2864 best = c;
2865 improved = true;
2866 hv_to_best = hv_evals;
2867 #[cfg(feature = "profile")]
2868 spstats::imp(if step == 2 { 0 } else { 1 }, _pi, _iter);
2869 }
2870 }
2871 if hv_ring1 == i64::MIN {
2872 hv_ring1 = best_c;
2873 }
2874 if !improved
2875 || !self.me_subpel_iter
2876 || pat & 2 != 0
2877 || (sp_cap != 0 && _iter >= sp_cap)
2878 {
2879 break;
2880 }
2881 }
2882 }
2883 if sp_dispatching {
2884 let n = self.sp_learn_n.get();
2885 if n < sp_learn {
2886 self.sp_learn_n.set(n + 1);
2887 if hv_ring1 != i64::MIN {
2888 self.sp_ring1.set(self.sp_ring1.get() + (hv_pre - hv_ring1).max(0));
2889 self.sp_total.set(self.sp_total.get() + (hv_pre - best_c).max(0));
2890 }
2891 if n + 1 == sp_learn {
2892 let tot = self.sp_total.get();
2893 self.sp_1pass.set(tot > 0 && self.sp_ring1.get() * 100 >= tot * sp_t);
2895 }
2896 }
2897 }
2898 if !subpel.is_empty() && subpel_harvest::enabled() {
2899 subpel_harvest::record(hv_pre, best_c, lambda_me, rw, rh, hv_evals, hv_to_best, hv_ring1);
2900 }
2901 if self.me_snap && seed_c < best_c {
2904 best = seed_mv;
2905 best_c = seed_c;
2906 }
2907 (best, best_c)
2908 }
2909
2910 #[allow(clippy::too_many_arguments)]
2915 #[allow(clippy::too_many_arguments)]
2920 fn encode_inter_mb(
2921 &mut self,
2922 w: &mut BitWriter,
2923 refs: &[crate::RefFrame],
2924 sy: &[u8],
2925 su: &[u8],
2926 sv: &[u8],
2927 mb_x: usize,
2928 mb_y: usize,
2929 mode: u8,
2930 parts: &[(i32, (i32, i32))],
2931 ) {
2932 if self.coded_path_v2 {
2933 self.encode_inter_mb_v2(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2934 } else {
2935 self.encode_inter_mb_v1(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2936 }
2937 }
2938
2939 #[allow(clippy::too_many_arguments)]
2947 fn encode_inter_mb_v2(
2948 &mut self,
2949 w: &mut BitWriter,
2950 refs: &[crate::RefFrame],
2951 sy: &[u8],
2952 su: &[u8],
2953 sv: &[u8],
2954 mb_x: usize,
2955 mb_y: usize,
2956 mode: u8,
2957 parts: &[(i32, (i32, i32))],
2958 ) {
2959 #[cfg(feature = "profile")]
2961 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(1);
2962 #[cfg(not(accel))]
2963 {
2964 self.encode_inter_mb_v1(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2965 }
2966 #[cfg(accel)]
2967 {
2968 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncInterCode);
2969 let (qp, qpc) = (self.qp, self.qpc);
2970 let w4 = self.mb_w * 4;
2971 let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
2972
2973 let mut pred_y = [0u8; 256];
2975 let mut c_pred = [[0u8; 64]; 2];
2976 let mut mvds = [(0i32, 0i32); 4];
2977 let mut n_mvd = 0;
2978 let _g_mc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
2979 for (part, &(rx, ry, rw, rh)) in inter_partitions(mode).iter().enumerate() {
2980 let (refi, mv) = parts[part];
2981 let reference = &refs[refi as usize];
2982 let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
2983 let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
2984 let pmv = predict_partition_mv(mode, part, a, b, c, refi);
2985 mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
2986 n_mvd += 1;
2987 for by in ry / 4..ry / 4 + rh / 4 {
2988 for bx in rx / 4..rx / 4 + rw / 4 {
2989 let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
2990 self.mv_y[idx] = mv;
2991 self.inter_y[idx] = true;
2992 self.ref_idx_y[idx] = refi;
2993 self.coded_y[idx] = true;
2994 }
2995 }
2996 if rw == 16 && rh == 16 {
2997 self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
2998 } else {
2999 let mut tmp = [0u8; 256];
3000 self.mc_luma_cached(reference, mb_x * 16 + rx, mb_y * 16 + ry, rw, rh, mv.0, mv.1, &mut tmp);
3001 if rw == 8 {
3005 for dy in 0..rh {
3006 pred_y[(ry + dy) * 16 + rx..][..8].copy_from_slice(&tmp[dy * 8..][..8]);
3007 }
3008 } else {
3009 for dy in 0..rh {
3010 pred_y[(ry + dy) * 16 + rx..][..16].copy_from_slice(&tmp[dy * 16..][..16]);
3011 }
3012 }
3013 }
3014 let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
3015 for cc in 0..2 {
3016 let rc = if cc == 0 { &reference.u } else { &reference.v };
3017 if crw == 8 && crh == 8 {
3018 mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut c_pred[cc]);
3019 } else {
3020 let mut tc = [0u8; 64];
3021 mc_chroma(rc, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv.0, mv.1, &mut tc);
3022 if crw == 4 {
3024 for dy in 0..crh {
3025 c_pred[cc][(cry + dy) * 8 + crx..][..4].copy_from_slice(&tc[dy * 4..][..4]);
3026 }
3027 } else {
3028 for dy in 0..crh {
3029 c_pred[cc][(cry + dy) * 8 + crx..][..8].copy_from_slice(&tc[dy * 8..][..8]);
3030 }
3031 }
3032 }
3033 }
3034 }
3035
3036 let mut dctw = AlignedDct([0i16; 256]);
3038 let dct = &mut dctw.0;
3039 let mut cbp_luma = 0u32;
3040 drop(_g_mc);
3041 let _g_tq = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncTq);
3042 let base = mb_y * 16 * self.cw + mb_x * 16;
3043 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3044 rusty_h264_accel::dct_four_t4(
3045 &mut dct[qi * 64..qi * 64 + 64],
3046 &sy[base + qy * self.cw + qx..],
3047 self.cw,
3048 &pred_y[qy * 16 + qx..],
3049 16,
3050 );
3051 }
3052 let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
3053 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
3054 for qi in 0..4 {
3055 rusty_h264_accel::quant_four_4x4(&mut dct[qi * 64..qi * 64 + 64], &ff, mf);
3056 }
3057 for blk in 0..16 {
3059 if dct[blk * 16..blk * 16 + 16].iter().any(|&v| v != 0) {
3060 cbp_luma |= 1 << (blk / 4);
3061 }
3062 }
3063
3064 let mut c_dc_levels = [[0i32; 4]; 2];
3066 let mut c_recon_dc = [[0i32; 4]; 2];
3067 let mut c_q = [[[0i32; 16]; 4]; 2];
3068 let (mut any_ac, mut any_dc) = (false, false);
3069 for c in 0..2 {
3070 let src = if c == 0 { su } else { sv };
3071 let dc2x2 = {
3072 #[repr(align(16))]
3073 struct A([i16; 64]);
3074 let mut cdct = A([0i16; 64]);
3075 rusty_h264_accel::dct_four_t4(
3076 &mut cdct.0,
3077 &src[(mb_y * 8) * self.ccw + mb_x * 8..],
3078 self.ccw,
3079 &c_pred[c],
3080 8,
3081 );
3082 let dc = [cdct.0[0] as i32, cdct.0[16] as i32, cdct.0[32] as i32, cdct.0[48] as i32];
3083 let ffc = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
3084 let mfc = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
3085 rusty_h264_accel::quant_four_4x4(&mut cdct.0, &ffc, mfc);
3086 for i in 0..4 {
3087 let q = &mut c_q[c][i];
3088 q[0] = 0;
3089 for j in 1..16 {
3090 let v = cdct.0[i * 16 + j] as i32;
3091 q[j] = v;
3092 if v != 0 {
3093 any_ac = true;
3094 }
3095 }
3096 }
3097 dc
3098 };
3099 let dl = forward_quant_chroma_dc(&dc2x2, qpc, false);
3100 if dl.iter().any(|&v| v != 0) {
3101 any_dc = true;
3102 }
3103 c_recon_dc[c] = inverse_quant_chroma_dc(&dl, qpc);
3104 c_dc_levels[c] = dl;
3105 }
3106 let cbp_chroma: u32 = if any_ac { 2 } else if any_dc { 1 } else { 0 };
3107 let cbp = cbp_luma | (cbp_chroma << 4);
3108
3109 drop(_g_tq);
3111 let _g_syn = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
3112 w.write_ue(mode as u32);
3113 let num_refs = refs.len();
3114 if num_refs > 1 {
3115 for &(refi, _) in parts {
3116 write_ref_idx(w, refi, num_refs);
3117 }
3118 }
3119 for &(mvdx, mvdy) in &mvds[..n_mvd] {
3120 w.write_se(mvdx);
3121 w.write_se(mvdy);
3122 }
3123 write_cbp_inter(w, cbp);
3124 if cbp != 0 {
3125 w.write_se(self.qp_delta()); }
3127 self.nnz_cache_load(mb_x, mb_y);
3128 drop(_g_syn);
3129
3130 let _g_scan = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Scatter);
3132 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3133 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
3134 let total = if cbp_luma & (1 << (blk / 4)) != 0 {
3135 let nc = self.nc_pred(lbx, lby);
3136 let scan16 = scan_4x4_dcac_i16(&dct[blk * 16..blk * 16 + 16]);
3137 encode_residual_block(w, &scan16, 16, nc) as u8
3138 } else {
3139 0
3140 };
3141 self.nnz_cache_set(lbx, lby, total);
3142 self.nnz_y[by * w4 + bx] = total;
3143 }
3144 if cbp_chroma != 0 {
3145 for c in 0..2 {
3146 encode_residual_block(w, &c_dc_levels[c], 4, -1);
3147 }
3148 }
3149 if cbp_chroma == 2 {
3150 self.chroma_cache_load(mb_x, mb_y);
3151 let w2 = self.mb_w * 2;
3152 for c in 0..2 {
3153 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
3154 let nc = self.chroma_nc_pred(c, bx, by);
3155 let ac = scan_4x4_ac(&c_q[c][by * 2 + bx]);
3156 let total = encode_residual_block(w, &ac, 15, nc) as u8;
3157 self.chroma_nnz_cache_set(c, bx, by, total);
3158 self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
3159 }
3160 }
3161 }
3162 drop(_g_scan);
3163
3164 let _g_rec = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
3166 #[repr(align(16))]
3167 struct Align16([i16; 64]);
3168 let mut dct_in = Align16([0i16; 64]);
3169 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3170 let rec_off = base + qy * self.cw + qx;
3171 if cbp_luma & (1 << qi) == 0 {
3172 for r in 0..8 {
3173 let (dsti, srci) = (rec_off + r * self.cw, (qy + r) * 16 + qx);
3174 self.rec_y[dsti..dsti + 8].copy_from_slice(&pred_y[srci..srci + 8]);
3175 }
3176 continue;
3177 }
3178 for k in 0..4 {
3179 let blk = qi * 4 + k;
3180 let mut lvl = [0i32; 16];
3181 for i in 0..16 {
3182 lvl[i] = dct[blk * 16 + i] as i32;
3183 }
3184 let deq = dequantize(&lvl, qp);
3185 for i in 0..16 {
3186 dct_in.0[k * 16 + i] = deq[i] as i16;
3187 }
3188 }
3189 rusty_h264_accel::idct_four_t4_rec(
3190 &mut self.rec_y[rec_off..],
3191 self.cw,
3192 &pred_y[qy * 16 + qx..],
3193 16,
3194 &dct_in.0,
3195 );
3196 }
3197 for c in 0..2 {
3199 let base_c = (mb_y * 8) * self.ccw + mb_x * 8;
3200 let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3201 if cbp_chroma == 0 {
3202 for r in 0..8 {
3203 let dsti = base_c + r * self.ccw;
3204 plane[dsti..dsti + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
3205 }
3206 } else {
3207 #[repr(align(16))]
3208 struct A([i16; 64]);
3209 let mut d = A([0i16; 64]);
3210 for i in 0..4 {
3211 let deq = dequantize(&c_q[c][i], qpc);
3212 for j in 0..16 {
3213 d.0[i * 16 + j] = deq[j] as i16;
3214 }
3215 d.0[i * 16] = c_recon_dc[c][i] as i16;
3216 }
3217 rusty_h264_accel::idct_four_t4_rec(&mut plane[base_c..], self.ccw, &c_pred[c], 8, &d.0);
3218 }
3219 }
3220 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3221 self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
3222 }
3223 }
3224 }
3225
3226 #[allow(clippy::too_many_arguments)]
3227 fn encode_inter_mb_v1(
3228 &mut self,
3229 w: &mut BitWriter,
3230 refs: &[crate::RefFrame],
3231 sy: &[u8],
3232 su: &[u8],
3233 sv: &[u8],
3234 mb_x: usize,
3235 mb_y: usize,
3236 mode: u8,
3237 parts: &[(i32, (i32, i32))],
3238 ) {
3239 self.encode_inter_mb_v1_b(w, refs, sy, su, sv, mb_x, mb_y, mode, parts, None);
3240 }
3241
3242 #[allow(clippy::too_many_arguments)]
3253 fn plan_inter_mb(
3254 &mut self,
3255 refs: &[crate::RefFrame],
3256 sy: &[u8],
3257 su: &[u8],
3258 sv: &[u8],
3259 mb_x: usize,
3260 mb_y: usize,
3261 mode: u8,
3262 parts: &[(i32, (i32, i32))],
3263 bspec: Option<BInter>,
3264 ) -> InterPlan {
3265 #[cfg(feature = "profile")]
3267 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(1);
3268 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncInterCode);
3269 let (qp, qpc) = (self.qp, self.qpc);
3270 let w4 = self.mb_w * 4;
3271 let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
3272
3273 let mut pred_y = [0u8; 256];
3275 let mut c_pred = [[0u8; 64]; 2];
3276 let mut mvds = [(0i32, 0i32); 4]; let mut plan_refs = [0i32; 4]; let mut n_mvd = 0;
3279 let _g_mc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
3280 if let Some(b) = bspec.filter(|b| b.dir == 0) {
3281 let (dp, dc, motion) = self.b_direct(&refs[0], b.l1, mb_x, mb_y);
3283 pred_y = dp;
3284 c_pred = dc;
3285 self.commit_direct_motion(mb_x, mb_y, &motion);
3286 } else if let Some(b) = bspec {
3287 let use0 = b.dir == 1 || b.dir == 3;
3289 let use1 = b.dir == 2 || b.dir == 3;
3290 let (lx, ly) = (mb_x * 16, mb_y * 16);
3291 let (cx, cy) = (mb_x * 8, mb_y * 8);
3292 let (pbx, pby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
3293 if use0 {
3295 let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, 4, 0);
3296 let p = predict_partition_mv(0, 0, a, c0, c1, 0);
3297 mvds[n_mvd] = (b.mv0.0 - p.0, b.mv0.1 - p.1);
3298 n_mvd += 1;
3299 }
3300 if use1 {
3301 let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, 4, 1);
3302 let p = predict_partition_mv(0, 0, a, c0, c1, 0);
3303 mvds[n_mvd] = (b.mv1.0 - p.0, b.mv1.1 - p.1);
3304 n_mvd += 1;
3305 }
3306 let mut a_y = [0u8; 256];
3309 let mut b_y = [0u8; 256];
3310 let mut a_c = [[0u8; 64]; 2];
3311 let mut b_c = [[0u8; 64]; 2];
3312 if use0 {
3313 mc_luma(&refs[0].y, self.cw, ch, lx, ly, 16, 16, b.mv0.0, b.mv0.1, &mut a_y);
3314 mc_chroma(&refs[0].u, self.ccw, cch, cx, cy, 8, 8, b.mv0.0, b.mv0.1, &mut a_c[0]);
3315 mc_chroma(&refs[0].v, self.ccw, cch, cx, cy, 8, 8, b.mv0.0, b.mv0.1, &mut a_c[1]);
3316 }
3317 if use1 {
3318 mc_luma(&b.l1.y, self.cw, ch, lx, ly, 16, 16, b.mv1.0, b.mv1.1, &mut b_y);
3319 mc_chroma(&b.l1.u, self.ccw, cch, cx, cy, 8, 8, b.mv1.0, b.mv1.1, &mut b_c[0]);
3320 mc_chroma(&b.l1.v, self.ccw, cch, cx, cy, 8, 8, b.mv1.0, b.mv1.1, &mut b_c[1]);
3321 }
3322 match (use0, use1) {
3323 (true, true) => {
3324 for i in 0..256 {
3325 pred_y[i] = bi_blend(a_y[i] as i32, b_y[i] as i32, self.bi_w);
3326 }
3327 for c in 0..2 {
3328 for i in 0..64 {
3329 c_pred[c][i] = bi_blend(a_c[c][i] as i32, b_c[c][i] as i32, self.bi_w);
3330 }
3331 }
3332 }
3333 (true, false) => {
3334 pred_y = a_y;
3335 c_pred = a_c;
3336 }
3337 _ => {
3338 pred_y = b_y;
3339 c_pred = b_c;
3340 }
3341 }
3342 for by in 0..4 {
3344 for bx in 0..4 {
3345 let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
3346 self.inter_y[idx] = true;
3347 self.coded_y[idx] = true;
3348 self.mv_y[idx] = if use0 { b.mv0 } else { (0, 0) };
3349 self.ref_idx_y[idx] = if use0 { 0 } else { -1 };
3350 self.mv1_y[idx] = if use1 { b.mv1 } else { (0, 0) };
3351 self.ref_idx1_y[idx] = if use1 { 0 } else { -1 };
3352 }
3353 }
3354 } else {
3355 for (part, &(rx, ry, rw, rh)) in inter_partitions(mode).iter().enumerate() {
3356 let (refi, mv) = parts[part];
3357 plan_refs[part] = refi; let reference = &refs[refi as usize];
3359 let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
3360 let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
3361 let pmv = predict_partition_mv(mode, part, a, b, c, refi);
3362 mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
3363 n_mvd += 1;
3364 for by in ry / 4..ry / 4 + rh / 4 {
3366 for bx in rx / 4..rx / 4 + rw / 4 {
3367 let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
3368 self.mv_y[idx] = mv;
3369 self.inter_y[idx] = true;
3370 self.ref_idx_y[idx] = refi;
3371 self.coded_y[idx] = true;
3372 }
3373 }
3374 if rw == 16 && rh == 16 {
3377 self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
3378 } else {
3379 let mut tmp = [0u8; 256];
3380 self.mc_luma_cached(reference, mb_x * 16 + rx, mb_y * 16 + ry, rw, rh, mv.0, mv.1, &mut tmp);
3381 if rw == 8 {
3383 for dy in 0..rh {
3384 pred_y[(ry + dy) * 16 + rx..][..8].copy_from_slice(&tmp[dy * 8..][..8]);
3385 }
3386 } else {
3387 for dy in 0..rh {
3388 pred_y[(ry + dy) * 16 + rx..][..16].copy_from_slice(&tmp[dy * 16..][..16]);
3389 }
3390 }
3391 }
3392 let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
3394 for cc in 0..2 {
3395 let rc = if cc == 0 { &reference.u } else { &reference.v };
3396 if crw == 8 && crh == 8 {
3397 mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut c_pred[cc]);
3398 } else {
3399 let mut tc = [0u8; 64];
3400 mc_chroma(rc, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv.0, mv.1, &mut tc);
3401 if crw == 4 {
3403 for dy in 0..crh {
3404 c_pred[cc][(cry + dy) * 8 + crx..][..4].copy_from_slice(&tc[dy * 4..][..4]);
3405 }
3406 } else {
3407 for dy in 0..crh {
3408 c_pred[cc][(cry + dy) * 8 + crx..][..8].copy_from_slice(&tc[dy * 8..][..8]);
3409 }
3410 }
3411 }
3412 }
3413 }
3414 } let mut q_blocks = [[0i32; 16]; 16]; let mut cbp_luma = 0u32;
3419 #[allow(unused_mut)]
3422 let mut t8x8 = false;
3423 #[allow(unused_mut)]
3424 let mut q8 = [[0i32; 64]; 4];
3425 drop(_g_mc);
3426 let _g_tq = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncTq);
3427 #[cfg(accel)]
3428 {
3429 let mut dctw = AlignedDct([0i16; 256]);
3434 let dct = &mut dctw.0;
3435 let base = mb_y * 16 * self.cw + mb_x * 16;
3436 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3437 rusty_h264_accel::dct_four_t4(
3438 &mut dct[qi * 64..qi * 64 + 64],
3439 &sy[base + qy * self.cw + qx..],
3440 self.cw,
3441 &pred_y[qy * 16 + qx..],
3442 16,
3443 );
3444 }
3445 let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
3446 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
3447 for qi in 0..4 {
3448 rusty_h264_accel::quant_four_4x4(&mut dct[qi * 64..qi * 64 + 64], &ff, mf);
3449 }
3450 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3451 let mut nz = false;
3452 for i in 0..16 {
3453 let v = dct[blk * 16 + i] as i32;
3454 q_blocks[lby * 4 + lbx][i] = v;
3455 nz |= v != 0;
3456 }
3457 if nz {
3458 cbp_luma |= 1 << (blk / 4);
3459 }
3460 }
3461 }
3462 #[cfg(not(accel))]
3463 {
3464 let mut res_blocks = [[0i32; 16]; 16]; for lby in 0..4 {
3467 for lbx in 0..4 {
3468 let b = &mut res_blocks[lby * 4 + lbx];
3469 for dy in 0..4 {
3470 for dx in 0..4 {
3471 let sx = mb_x * 16 + lbx * 4 + dx;
3472 let syy = mb_y * 16 + lby * 4 + dy;
3473 b[dy * 4 + dx] = sy[syy * self.cw + sx] as i32
3474 - pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3475 }
3476 }
3477 }
3478 }
3479 let mut coeffs = [[0i32; 16]; 16];
3480 forward_dct_blocks(&res_blocks, &mut coeffs);
3481 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3482 let q = rdoq(&coeffs[lby * 4 + lbx], qp, 6, self.rdoq_strength, 0);
3483 if q.iter().any(|&v| v != 0) {
3484 cbp_luma |= 1 << (blk / 4);
3485 }
3486 q_blocks[lby * 4 + lbx] = q;
3487 }
3488 }
3489
3490 {
3496 if self.transform_8x8 && self.inter8x8 != 0 {
3497 let lambda =
3498 0.85 * self.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
3499 let mut ssd4 = 0i64;
3500 let mut rate4 = 0f64;
3501 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3502 let mut predb = [0i32; 16];
3503 for dy in 0..4 {
3504 for dx in 0..4 {
3505 predb[dy * 4 + dx] =
3506 pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3507 }
3508 }
3509 let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3510 let s = reconstruct_4x4(&deq, &predb);
3511 for dy in 0..4 {
3512 for dx in 0..4 {
3513 let sx = mb_x * 16 + lbx * 4 + dx;
3514 let syy = mb_y * 16 + lby * 4 + dy;
3515 let d = s[dy * 4 + dx] as i64 - sy[syy * self.cw + sx] as i64;
3516 ssd4 += d * d;
3517 }
3518 }
3519 for &l in &q_blocks[lby * 4 + lbx] {
3520 if l != 0 {
3521 rate4 += rdoq_rate((l as i64).abs());
3522 }
3523 }
3524 }
3525 let (q8c, cbp8, rate8, _rec8, ssd8) =
3526 plan_inter8_luma(sy, self.cw, mb_x, mb_y, &pred_y, qp);
3527 let j4 = ssd4 as f64 + lambda * (rate4 + 16.0);
3530 let j8 = ssd8 as f64 + lambda * (rate8 + 16.0 + self.inter8_pen as f64);
3531 if cbp8 > 0 && j8 < j4 {
3532 t8x8 = true;
3533 cbp_luma = cbp8;
3534 q8 = q8c;
3535 }
3536 }
3537 }
3538
3539 let mut c_dc_levels = [[0i32; 4]; 2];
3541 let mut c_recon_dc = [[0i32; 4]; 2];
3542 let mut c_q = [[[0i32; 16]; 4]; 2];
3543 let (mut any_ac, mut any_dc) = (false, false);
3544 for c in 0..2 {
3545 let src = if c == 0 { su } else { sv };
3546 #[cfg(accel)]
3552 let (mut dc2x2, applied) = {
3553 #[repr(align(16))]
3554 struct A([i16; 64]);
3555 let mut dct = A([0i16; 64]);
3556 rusty_h264_accel::dct_four_t4(
3557 &mut dct.0,
3558 &src[(mb_y * 8) * self.ccw + mb_x * 8..],
3559 self.ccw,
3560 &c_pred[c],
3561 8,
3562 );
3563 let dc = [
3564 dct.0[0] as i32,
3565 dct.0[16] as i32,
3566 dct.0[32] as i32,
3567 dct.0[48] as i32,
3568 ];
3569 let ffc = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
3570 let mfc = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
3571 rusty_h264_accel::quant_four_4x4(&mut dct.0, &ffc, mfc);
3572 for i in 0..4 {
3573 let q = &mut c_q[c][i];
3574 q[0] = 0;
3575 for j in 1..16 {
3576 let v = dct.0[i * 16 + j] as i32;
3577 q[j] = v;
3578 if v != 0 {
3579 any_ac = true;
3580 }
3581 }
3582 }
3583 (dc, true)
3584 };
3585 #[cfg(not(accel))]
3586 let (mut dc2x2, applied) = ([0i32; 4], false);
3587 if !applied {
3588 let mut res_blocks = [[0i32; 16]; 4];
3590 for by in 0..2 {
3591 for bx in 0..2 {
3592 let b = &mut res_blocks[by * 2 + bx];
3593 for dy in 0..4 {
3594 for dx in 0..4 {
3595 let sx = mb_x * 8 + bx * 4 + dx;
3596 let syy = mb_y * 8 + by * 4 + dy;
3597 b[dy * 4 + dx] = src[syy * self.ccw + sx] as i32
3598 - c_pred[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
3599 }
3600 }
3601 }
3602 }
3603 let mut coeffs = [[0i32; 16]; 4];
3604 forward_dct_blocks(&res_blocks, &mut coeffs);
3605 for i in 0..4 {
3606 dc2x2[i] = coeffs[i][0];
3607 let mut q = rdoq(&coeffs[i], qpc, 6, self.rdoq_strength, 1);
3608 q[0] = 0;
3609 if q[1..].iter().any(|&v| v != 0) {
3610 any_ac = true;
3611 }
3612 c_q[c][i] = q;
3613 }
3614 }
3615 let dl = forward_quant_chroma_dc(&dc2x2, qpc, false);
3616 if dl.iter().any(|&v| v != 0) {
3617 any_dc = true;
3618 }
3619 c_recon_dc[c] = inverse_quant_chroma_dc(&dl, qpc);
3620 c_dc_levels[c] = dl;
3621 }
3622 let cbp_chroma: u32 = if any_ac { 2 } else if any_dc { 1 } else { 0 };
3623 let cbp = cbp_luma | (cbp_chroma << 4);
3624
3625 drop(_g_tq);
3626 let _g_rec = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
3627 #[cfg(accel)]
3629 if t8x8 {
3630 let weight = [16i32; 64];
3633 for b8 in 0..4usize {
3634 let (b8x, b8y) = (b8 % 2, b8 / 2);
3635 let res_r = inverse_quant_8x8(&q8[b8], qp, &weight);
3636 let predb: [i32; 64] = std::array::from_fn(|i| {
3637 pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
3638 });
3639 let recon = add_residual_8x8(&res_r, &predb);
3640 for dy in 0..8 {
3641 for dx in 0..8 {
3642 let px = mb_x * 16 + b8x * 8 + dx;
3643 let py = mb_y * 16 + b8y * 8 + dy;
3644 self.rec_y[py * self.cw + px] = recon[dy * 8 + dx];
3645 }
3646 }
3647 }
3648 } else {
3649 #[repr(align(16))]
3658 struct Align16([i16; 64]);
3659 let mut dct_in = Align16([0i16; 64]);
3660 let base = mb_y * 16 * self.cw + mb_x * 16;
3661 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3662 let rec_off = base + qy * self.cw + qx;
3663 if cbp_luma & (1 << qi) == 0 {
3664 for r in 0..8 {
3665 let (dsti, srci) = (rec_off + r * self.cw, (qy + r) * 16 + qx);
3666 self.rec_y[dsti..dsti + 8].copy_from_slice(&pred_y[srci..srci + 8]);
3667 }
3668 continue;
3669 }
3670 for k in 0..4 {
3671 let blk = qi * 4 + k;
3672 let (lbx, lby) = LUMA_4X4_SCAN_XY[blk];
3673 let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3674 for i in 0..16 {
3675 dct_in.0[k * 16 + i] = deq[i] as i16;
3676 }
3677 }
3678 rusty_h264_accel::idct_four_t4_rec(
3679 &mut self.rec_y[rec_off..],
3680 self.cw,
3681 &pred_y[qy * 16 + qx..],
3682 16,
3683 &dct_in.0,
3684 );
3685 }
3686 }
3687 #[cfg(not(accel))]
3688 if t8x8 {
3689 let weight = [16i32; 64];
3691 for b8 in 0..4usize {
3692 let (b8x, b8y) = (b8 % 2, b8 / 2);
3693 let res_r = inverse_quant_8x8(&q8[b8], qp, &weight);
3694 let predb: [i32; 64] = std::array::from_fn(|i| {
3695 pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
3696 });
3697 let recon = add_residual_8x8(&res_r, &predb);
3698 for dy in 0..8 {
3699 for dx in 0..8 {
3700 let px = mb_x * 16 + b8x * 8 + dx;
3701 let py = mb_y * 16 + b8y * 8 + dy;
3702 self.rec_y[py * self.cw + px] = recon[dy * 8 + dx];
3703 }
3704 }
3705 }
3706 } else {
3707 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3708 let mut predb = [0i32; 16];
3709 for dy in 0..4 {
3710 for dx in 0..4 {
3711 predb[dy * 4 + dx] = pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3712 }
3713 }
3714 let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3715 let s = reconstruct_4x4(&deq, &predb);
3716 store(&mut self.rec_y, self.cw, mb_x * 16 + lbx * 4, mb_y * 16 + lby * 4, &s);
3717 }
3718 }
3719 for c in 0..2 {
3720 #[cfg(accel)]
3725 {
3726 let base = (mb_y * 8) * self.ccw + mb_x * 8;
3727 let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3728 if cbp_chroma == 0 {
3729 for r in 0..8 {
3731 let dsti = base + r * self.ccw;
3732 plane[dsti..dsti + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
3733 }
3734 } else {
3735 #[repr(align(16))]
3736 struct A([i16; 64]);
3737 let mut d = A([0i16; 64]);
3738 for i in 0..4 {
3739 let deq = dequantize(&c_q[c][i], qpc);
3740 for j in 0..16 {
3741 d.0[i * 16 + j] = deq[j] as i16;
3742 }
3743 d.0[i * 16] = c_recon_dc[c][i] as i16;
3744 }
3745 rusty_h264_accel::idct_four_t4_rec(&mut plane[base..], self.ccw, &c_pred[c], 8, &d.0);
3746 }
3747 }
3748 #[cfg(not(accel))]
3749 {
3750 let mut deq_blocks = [[0i32; 16]; 4];
3753 for i in 0..4 {
3754 deq_blocks[i] = dequantize(&c_q[c][i], qpc);
3755 deq_blocks[i][0] = c_recon_dc[c][i];
3756 }
3757 let mut res = [[0i32; 16]; 4];
3758 inverse_dct_blocks(&deq_blocks, &mut res);
3759 let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3760 for by in 0..2 {
3761 for bx in 0..2 {
3762 let mut predb = [0i32; 16];
3763 for dy in 0..4 {
3764 for dx in 0..4 {
3765 predb[dy * 4 + dx] = c_pred[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
3766 }
3767 }
3768 let s = add_residual_4x4(&res[by * 2 + bx], &predb);
3769 store(plane, self.ccw, mb_x * 8 + bx * 4, mb_y * 8 + by * 4, &s);
3770 }
3771 }
3772 }
3773 }
3774 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3776 self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
3777 }
3778 InterPlan { mvds, plan_refs, n_mvd, cbp, q_blocks, c_dc_levels, c_q, t8x8, q8 }
3779 }
3780
3781 #[allow(clippy::too_many_arguments)]
3784 fn encode_inter_mb_v1_b(
3785 &mut self,
3786 w: &mut BitWriter,
3787 refs: &[crate::RefFrame],
3788 sy: &[u8],
3789 su: &[u8],
3790 sv: &[u8],
3791 mb_x: usize,
3792 mb_y: usize,
3793 mode: u8,
3794 parts: &[(i32, (i32, i32))],
3795 bspec: Option<BInter>,
3796 ) {
3797 let plan = self.plan_inter_mb(refs, sy, su, sv, mb_x, mb_y, mode, parts, bspec);
3798 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
3799 self.emit_inter_cavlc(w, refs.len(), mb_x, mb_y, mode, parts, bspec, &plan);
3800 }
3801
3802 #[allow(clippy::too_many_arguments)]
3804 fn emit_inter_cavlc(
3805 &mut self,
3806 w: &mut BitWriter,
3807 num_refs: usize,
3808 mb_x: usize,
3809 mb_y: usize,
3810 mode: u8,
3811 parts: &[(i32, (i32, i32))],
3812 bspec: Option<BInter>,
3813 plan: &InterPlan,
3814 ) {
3815 let w4 = self.mb_w * 4;
3816 let (cbp, cbp_luma, cbp_chroma) = (plan.cbp, plan.cbp & 15, plan.cbp >> 4);
3817 w.write_ue(bspec.map_or(mode as u32, |b| b.dir as u32)); if mode == 3 {
3824 for _ in 0..4 {
3825 w.write_ue(0);
3826 }
3827 }
3828 if num_refs > 1 {
3829 for &(refi, _) in parts {
3830 write_ref_idx(w, refi, num_refs);
3831 }
3832 }
3833 for &(mvdx, mvdy) in &plan.mvds[..plan.n_mvd] {
3834 w.write_se(mvdx);
3835 w.write_se(mvdy);
3836 }
3837 write_cbp_inter(w, cbp);
3838 if cbp_luma > 0 && self.transform_8x8 {
3842 w.write_bit(plan.t8x8);
3843 }
3844 if cbp != 0 {
3845 w.write_se(self.qp_delta()); }
3847 self.nnz_cache_load(mb_x, mb_y);
3848 if plan.t8x8 {
3849 for b8 in 0..4usize {
3853 let (b8x, b8y) = (b8 % 2, b8 / 2);
3854 let scan8 = scan_8x8_fwd(&plan.q8[b8]);
3855 for sub in 0..4usize {
3856 let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
3857 let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
3858 let total = if cbp_luma & (1 << b8) != 0 {
3859 let nc = self.nc_pred(cx, cy);
3860 let blk: [i32; 16] = std::array::from_fn(|k| scan8[4 * k + sub]);
3861 encode_residual_block(w, &blk, 16, nc) as u8
3862 } else {
3863 0
3864 };
3865 self.nnz_cache_set(cx, cy, total);
3866 self.nnz_y[by * w4 + bx] = total;
3867 }
3868 }
3869 } else {
3870 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3871 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
3872 let total = if cbp_luma & (1 << (blk / 4)) != 0 {
3873 let nc = self.nc_pred(lbx, lby);
3874 let scan16 = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
3875 encode_residual_block(w, &scan16, 16, nc) as u8
3876 } else {
3877 0
3878 };
3879 self.nnz_cache_set(lbx, lby, total);
3880 self.nnz_y[by * w4 + bx] = total;
3881 }
3882 }
3883 if cbp_chroma != 0 {
3884 for c in 0..2 {
3885 encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
3886 }
3887 }
3888 if cbp_chroma == 2 {
3889 self.chroma_cache_load(mb_x, mb_y);
3890 let w2 = self.mb_w * 2;
3891 for c in 0..2 {
3892 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
3893 let nc = self.chroma_nc_pred(c, bx, by);
3894 let ac = scan_4x4_ac(&plan.c_q[c][by * 2 + bx]);
3895 let total = encode_residual_block(w, &ac, 15, nc) as u8;
3896 self.chroma_nnz_cache_set(c, bx, by, total);
3897 self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
3898 }
3899 }
3900 }
3901 }
3902
3903 #[inline]
3913 fn mc_luma_cached(
3914 &self,
3915 reference: &crate::RefFrame,
3916 x0: usize,
3917 y0: usize,
3918 bw: usize,
3919 bh: usize,
3920 mvx: i32,
3921 mvy: i32,
3922 out: &mut [u8],
3923 ) {
3924 let ch = self.mb_h * 16;
3925 let cw = self.cw;
3926 if !self.fast {
3927 let p = reference.hpel(cw, ch);
3928 if rusty_h264_common::inter::hpel_block(p, x0, y0, bw, bh, mvx, mvy, out) {
3929 return;
3930 }
3931 if let Some((plane, base, stride)) =
3932 rusty_h264_common::inter::hpel_ref(p, x0, y0, bw, bh, mvx, mvy)
3933 {
3934 for r in 0..bh {
3935 out[r * bw..r * bw + bw].copy_from_slice(&plane[base + r * stride..][..bw]);
3936 }
3937 return;
3938 }
3939 }
3940 mc_luma(&reference.y, cw, ch, x0, y0, bw, bh, mvx, mvy, out);
3941 }
3942
3943 fn skip_predict_luma(
3949 &self,
3950 refs: &[crate::RefFrame],
3951 mb_x: usize,
3952 mb_y: usize,
3953 mv: (i32, i32),
3954 ) -> [u8; 256] {
3955 #[cfg(feature = "profile")]
3957 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(3);
3958 let reference = &refs[0]; let ch = self.mb_h * 16;
3960 let mut pred_y = [0u8; 256];
3961 self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
3962 pred_y
3963 }
3964
3965 fn skip_predict_chroma(
3967 &self,
3968 refs: &[crate::RefFrame],
3969 mb_x: usize,
3970 mb_y: usize,
3971 mv: (i32, i32),
3972 ) -> [[u8; 64]; 2] {
3973 let reference = &refs[0];
3974 let cch = self.mb_h * 8;
3975 let mut pred_c = [[0u8; 64]; 2];
3976 for c in 0..2 {
3977 let rc = if c == 0 { &reference.u } else { &reference.v };
3978 mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut pred_c[c]);
3979 }
3980 pred_c
3981 }
3982
3983 fn skip_luma_is_free(&self, sy: &[u8], mb_x: usize, mb_y: usize, pred_y: &[u8; 256]) -> bool {
3988 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFree);
3989 let qp = self.qp;
3990 #[cfg(accel)]
3997 if self.skip_accel_check {
3998 #[repr(align(16))]
3999 struct Align16([i16; 64]);
4000 let mut dct = Align16([0i16; 64]);
4001 let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
4002 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
4003 for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
4004 rusty_h264_accel::dct_four_t4(
4005 &mut dct.0,
4006 &sy[(mb_y * 16 + qy) * self.cw + mb_x * 16 + qx..],
4007 self.cw,
4008 &pred_y[qy * 16 + qx..],
4009 16,
4010 );
4011 rusty_h264_accel::quant_four_4x4(&mut dct.0, &ff, mf);
4012 if dct.0.iter().any(|&v| v != 0) {
4013 return false;
4014 }
4015 }
4016 return true;
4017 }
4018 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
4024 let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
4025 let mut t_min = i32::MAX;
4026 for p in 0..8 {
4027 let t = (65536 + mf[p] as i32 - 1) / mf[p] as i32 - ff[p] as i32;
4028 t_min = t_min.min(t);
4029 }
4030 let t_dc = (65536 + mf[0] as i32 - 1) / mf[0] as i32 - ff[0] as i32;
4031 for by in 0..4 {
4036 for bx in 0..4 {
4037 let mut res = [0i32; 16];
4038 let (mut sad, mut dc) = (0i32, 0i32);
4039 for dy in 0..4 {
4040 for dx in 0..4 {
4041 let sx = mb_x * 16 + bx * 4 + dx;
4042 let syy = mb_y * 16 + by * 4 + dy;
4043 let d = sy[syy * self.cw + sx] as i32
4044 - pred_y[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
4045 res[dy * 4 + dx] = d;
4046 sad += d.abs();
4047 dc += d;
4048 }
4049 }
4050 if 4 * sad < t_min {
4051 continue; }
4053 if dc.abs() >= t_dc {
4054 return false; }
4056 if quantize(&forward_core(&res), qp, 6).iter().any(|&v| v != 0) {
4057 return false;
4058 }
4059 }
4060 }
4061 true
4062 }
4063
4064 fn skip_chroma_is_free(
4066 &self,
4067 su: &[u8],
4068 sv: &[u8],
4069 mb_x: usize,
4070 mb_y: usize,
4071 pred_c: &[[u8; 64]; 2],
4072 ) -> bool {
4073 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFree);
4074 let qpc = self.qpc;
4075 #[cfg(accel)]
4081 if self.skip_accel_check {
4082 #[repr(align(16))]
4083 struct Align16C([i16; 64]);
4084 let mut dct = Align16C([0i16; 64]);
4085 let ff = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
4086 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
4087 for c in 0..2 {
4088 let src = if c == 0 { su } else { sv };
4089 rusty_h264_accel::dct_four_t4(
4090 &mut dct.0,
4091 &src[(mb_y * 8) * self.ccw + mb_x * 8..],
4092 self.ccw,
4093 &pred_c[c],
4094 8,
4095 );
4096 let dc2x2 = [
4097 dct.0[0] as i32,
4098 dct.0[16] as i32,
4099 dct.0[32] as i32,
4100 dct.0[48] as i32,
4101 ];
4102 rusty_h264_accel::quant_four_4x4(&mut dct.0, &ff, mf);
4103 for b in 0..4 {
4104 if dct.0[b * 16 + 1..b * 16 + 16].iter().any(|&v| v != 0) {
4105 return false;
4106 }
4107 }
4108 if forward_quant_chroma_dc(&dc2x2, qpc, false).iter().any(|&v| v != 0) {
4109 return false;
4110 }
4111 }
4112 return true;
4113 }
4114 for c in 0..2 {
4115 let src = if c == 0 { su } else { sv };
4116 let mut dc2x2 = [0i32; 4];
4117 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
4118 let mut res = [0i32; 16];
4119 for dy in 0..4 {
4120 for dx in 0..4 {
4121 let sx = mb_x * 8 + bx * 4 + dx;
4122 let syy = mb_y * 8 + by * 4 + dy;
4123 res[dy * 4 + dx] = src[syy * self.ccw + sx] as i32
4124 - pred_c[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
4125 }
4126 }
4127 let coeffs = forward_core(&res);
4128 dc2x2[by * 2 + bx] = coeffs[0];
4129 if quantize(&coeffs, qpc, 6)[1..].iter().any(|&v| v != 0) {
4130 return false;
4131 }
4132 }
4133 if forward_quant_chroma_dc(&dc2x2, qpc, false).iter().any(|&v| v != 0) {
4134 return false;
4135 }
4136 }
4137 true
4138 }
4139
4140 #[allow(clippy::too_many_arguments)]
4142 fn pred_ssd(
4143 &self,
4144 sy: &[u8],
4145 su: &[u8],
4146 sv: &[u8],
4147 mb_x: usize,
4148 mb_y: usize,
4149 pred_y: &[u8; 256],
4150 pred_c: &[[u8; 64]; 2],
4151 ) -> i64 {
4152 let mut ssd = 0i64;
4153 for dy in 0..16 {
4154 for dx in 0..16 {
4155 let d = sy[(mb_y * 16 + dy) * self.cw + mb_x * 16 + dx] as i64
4156 - pred_y[dy * 16 + dx] as i64;
4157 ssd += d * d;
4158 }
4159 }
4160 for c in 0..2 {
4161 let src = if c == 0 { su } else { sv };
4162 for dy in 0..8 {
4163 for dx in 0..8 {
4164 let d = src[(mb_y * 8 + dy) * self.ccw + mb_x * 8 + dx] as i64
4165 - pred_c[c][dy * 8 + dx] as i64;
4166 ssd += d * d;
4167 }
4168 }
4169 }
4170 ssd
4171 }
4172
4173 fn mb_ssd(&self, sy: &[u8], su: &[u8], sv: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4175 let mut ssd = 0i64;
4176 for dy in 0..16 {
4177 for dx in 0..16 {
4178 let i = (mb_y * 16 + dy) * self.cw + mb_x * 16 + dx;
4179 let d = sy[i] as i64 - self.rec_y[i] as i64;
4180 ssd += d * d;
4181 }
4182 }
4183 for c in 0..2 {
4184 let (src, rec) = if c == 0 { (su, &self.rec_u) } else { (sv, &self.rec_v) };
4185 for dy in 0..8 {
4186 for dx in 0..8 {
4187 let i = (mb_y * 8 + dy) * self.ccw + mb_x * 8 + dx;
4188 let d = src[i] as i64 - rec[i] as i64;
4189 ssd += d * d;
4190 }
4191 }
4192 }
4193 ssd
4194 }
4195
4196 #[allow(clippy::too_many_arguments)]
4199 fn commit_skip_probe_marker(&self) {}
4200 fn commit_skip(
4201 &mut self,
4202 mb_x: usize,
4203 mb_y: usize,
4204 mv: (i32, i32),
4205 pred_y: &[u8; 256],
4206 pred_c: &[[u8; 64]; 2],
4207 ) {
4208 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MvGrid);
4209 let base = mb_y * 16 * self.cw + mb_x * 16;
4212 for r in 0..16 {
4213 let d = base + r * self.cw;
4214 self.rec_y[d..d + 16].copy_from_slice(&pred_y[r * 16..r * 16 + 16]);
4215 }
4216 let cbase = mb_y * 8 * self.ccw + mb_x * 8;
4217 for c in 0..2 {
4218 let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
4219 for r in 0..8 {
4220 let d = cbase + r * self.ccw;
4221 plane[d..d + 8].copy_from_slice(&pred_c[c][r * 8..r * 8 + 8]);
4222 }
4223 }
4224 self.set_mb_mv(mb_x, mb_y, mv, true, 0);
4225 let w4 = self.mb_w * 4;
4226 for row in 0..4 {
4227 let st = (mb_y * 4 + row) * w4 + mb_x * 4;
4228 self.modes_y[st..st + 4].fill(2);
4229 self.coded_y[st..st + 4].fill(true);
4230 }
4231 }
4232
4233 #[allow(clippy::too_many_arguments)]
4239 fn trial_inter(
4240 &mut self,
4241 refs: &[crate::RefFrame],
4242 sy: &[u8],
4243 su: &[u8],
4244 sv: &[u8],
4245 mb_x: usize,
4246 mb_y: usize,
4247 mode: u8,
4248 parts: &[(i32, (i32, i32))],
4249 ) -> (i64, usize) {
4250 let snap = self.save_mb(mb_x, mb_y);
4251 let mut scratch = BitWriter::new();
4252 self.encode_inter_mb(&mut scratch, refs, sy, su, sv, mb_x, mb_y, mode, parts);
4253 let bits = scratch.bit_len();
4254 let ssd = self.mb_ssd(sy, su, sv, mb_x, mb_y);
4255 self.load_mb(mb_x, mb_y, &snap);
4256 (ssd, bits)
4257 }
4258
4259 fn trial_intra(
4263 &mut self,
4264 sy: &[u8],
4265 su: &[u8],
4266 sv: &[u8],
4267 mb_x: usize,
4268 mb_y: usize,
4269 is_p: bool,
4270 ) -> (i64, usize) {
4271 let snap = self.save_mb(mb_x, mb_y);
4272 let mut scratch = BitWriter::new();
4273 encode_mb(self, &mut scratch, mb_x, mb_y, sy, su, sv, is_p);
4274 let bits = scratch.bit_len();
4275 let ssd = self.mb_ssd(sy, su, sv, mb_x, mb_y);
4276 self.load_mb(mb_x, mb_y, &snap);
4277 (ssd, bits)
4278 }
4279
4280 #[allow(clippy::too_many_arguments)]
4285 fn best_part(
4286 &self,
4287 refs: &[crate::RefFrame],
4288 sy: &[u8],
4289 nb: &[MvNeighbor; 3],
4290 num_refs: usize,
4291 rx: usize,
4292 ry: usize,
4293 rw: usize,
4294 rh: usize,
4295 extra: &[(i32, i32)],
4296 lme: f64,
4297 ) -> (i32, (i32, i32), i64) {
4298 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMe);
4299 let [a, b, c] = *nb;
4300 let (mut br, mut bmv, mut bc) = (0i32, (0, 0), i64::MAX);
4301 for r in 0..num_refs {
4302 let mut seeds = vec![predict_mv(a, b, c, r as i32)];
4303 seeds.extend_from_slice(extra);
4304 let (mv, cost) = self.motion_search(&refs[r], sy, rx, ry, rw, rh, &seeds, lme, None);
4305 let cost = cost + (lme * ref_bits(r, num_refs) as f64) as i64;
4306 if cost < bc {
4307 bc = cost;
4308 br = r as i32;
4309 bmv = mv;
4310 }
4311 }
4312 (br, bmv, bc)
4313 }
4314
4315 #[allow(clippy::too_many_arguments)]
4319 fn refine_part(
4320 &self,
4321 refs: &[crate::RefFrame],
4322 sy: &[u8],
4323 nb: &[MvNeighbor; 3],
4324 num_refs: usize,
4325 rx: usize,
4326 ry: usize,
4327 rw: usize,
4328 rh: usize,
4329 lme: f64,
4330 r: i32,
4331 mv: (i32, i32),
4332 ) -> ((i32, i32), i64) {
4333 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMe);
4334 let [a, b, c] = *nb;
4335 let rb = (lme * ref_bits(r as usize, num_refs) as f64) as i64;
4336 let seeds = [predict_mv(a, b, c, r)];
4337 let (m, cc) = self.motion_search(&refs[r as usize], sy, rx, ry, rw, rh, &seeds, lme, Some(mv));
4338 (m, cc + rb)
4339 }
4340
4341 fn best_i16_sad(&self, sy: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4345 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCost);
4346 let (lx, ly) = (mb_x * 16, mb_y * 16);
4347 let (avail_top, avail_left) = (mb_y > 0, mb_x > 0);
4348 let mut top = [0u8; 16];
4349 let mut left = [0u8; 16];
4350 if avail_top {
4351 for i in 0..16 {
4352 top[i] = self.rec_y[(ly - 1) * self.cw + lx + i];
4353 }
4354 }
4355 if avail_left {
4356 for i in 0..16 {
4357 left[i] = self.rec_y[(ly + i) * self.cw + lx - 1];
4358 }
4359 }
4360 let corner = if avail_top && avail_left {
4361 self.rec_y[(ly - 1) * self.cw + lx - 1]
4362 } else {
4363 0
4364 };
4365 let mut best = i64::MAX;
4366 for mode in [I16Mode::Dc, I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
4367 if !mode.available(avail_top, avail_left) {
4368 continue;
4369 }
4370 let pred = i16_pred(self, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
4371 best = best.min(sad_16x16(sy, self.cw, lx, ly, &pred));
4372 }
4373 best
4374 }
4375
4376 fn best_i16_satd(&self, sy: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4379 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCost);
4380 let (lx, ly) = (mb_x * 16, mb_y * 16);
4381 let (avail_top, avail_left) = (mb_y > 0, mb_x > 0);
4382 let mut top = [0u8; 16];
4383 let mut left = [0u8; 16];
4384 if avail_top {
4385 for i in 0..16 {
4386 top[i] = self.rec_y[(ly - 1) * self.cw + lx + i];
4387 }
4388 }
4389 if avail_left {
4390 for i in 0..16 {
4391 left[i] = self.rec_y[(ly + i) * self.cw + lx - 1];
4392 }
4393 }
4394 let corner = if avail_top && avail_left {
4395 self.rec_y[(ly - 1) * self.cw + lx - 1]
4396 } else {
4397 0
4398 };
4399 let mut best = i64::MAX;
4400 for mode in [I16Mode::Dc, I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
4401 if !mode.available(avail_top, avail_left) {
4402 continue;
4403 }
4404 let pred = i16_pred(self, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
4405 best = best.min(satd_16x16(sy, self.cw, lx, ly, &pred));
4406 }
4407 best
4408 }
4409
4410 fn save_mb(&self, mb_x: usize, mb_y: usize) -> MbState {
4413 let mut d = MbState::default();
4414 self.save_mb_into(mb_x, mb_y, &mut d);
4415 d
4416 }
4417
4418 fn save_mb_into(&self, mb_x: usize, mb_y: usize, d: &mut MbState) {
4422 let w4 = self.mb_w * 4;
4423 let w2 = self.mb_w * 2;
4424 macro_rules! reg4 {
4425 ($v:expr, $o:expr) => {{
4426 $o.clear();
4427 for dy in 0..4 {
4428 for dx in 0..4 {
4429 $o.push($v[(mb_y * 4 + dy) * w4 + mb_x * 4 + dx]);
4430 }
4431 }
4432 }};
4433 }
4434 macro_rules! regn {
4435 ($v:expr, $o:expr, $n:expr, $ox:expr, $oy:expr, $stride:expr) => {{
4436 $o.clear();
4437 for dy in 0..$n {
4438 for dx in 0..$n {
4439 $o.push($v[($oy + dy) * $stride + $ox + dx]);
4440 }
4441 }
4442 }};
4443 }
4444 regn!(self.rec_y, d.rec_y, 16, mb_x * 16, mb_y * 16, self.cw);
4445 regn!(self.rec_u, d.rec_u, 8, mb_x * 8, mb_y * 8, self.ccw);
4446 regn!(self.rec_v, d.rec_v, 8, mb_x * 8, mb_y * 8, self.ccw);
4447 reg4!(self.nnz_y, d.nnz_y);
4448 regn!(self.nnz_c[0], d.nnz_c[0], 2, mb_x * 2, mb_y * 2, w2);
4449 regn!(self.nnz_c[1], d.nnz_c[1], 2, mb_x * 2, mb_y * 2, w2);
4450 reg4!(self.mv_y, d.mv_y);
4451 reg4!(self.inter_y, d.inter_y);
4452 reg4!(self.ref_idx_y, d.ref_idx_y);
4453 reg4!(self.coded_y, d.coded_y);
4454 reg4!(self.modes_y, d.modes_y);
4455 d.cur_qp = self.cur_qp;
4456 }
4457
4458 fn load_mb(&mut self, mb_x: usize, mb_y: usize, s: &MbState) {
4460 let w4 = self.mb_w * 4;
4461 let w2 = self.mb_w * 2;
4462 macro_rules! put4 {
4463 ($v:expr, $src:expr) => {
4464 for dy in 0..4 {
4465 for dx in 0..4 {
4466 $v[(mb_y * 4 + dy) * w4 + mb_x * 4 + dx] = $src[dy * 4 + dx];
4467 }
4468 }
4469 };
4470 }
4471 macro_rules! putn {
4472 ($v:expr, $src:expr, $n:expr, $ox:expr, $oy:expr, $stride:expr) => {
4473 for dy in 0..$n {
4474 for dx in 0..$n {
4475 $v[($oy + dy) * $stride + $ox + dx] = $src[dy * $n + dx];
4476 }
4477 }
4478 };
4479 }
4480 putn!(self.rec_y, s.rec_y, 16, mb_x * 16, mb_y * 16, self.cw);
4481 putn!(self.rec_u, s.rec_u, 8, mb_x * 8, mb_y * 8, self.ccw);
4482 putn!(self.rec_v, s.rec_v, 8, mb_x * 8, mb_y * 8, self.ccw);
4483 put4!(self.nnz_y, s.nnz_y);
4484 putn!(self.nnz_c[0], s.nnz_c[0], 2, mb_x * 2, mb_y * 2, w2);
4485 putn!(self.nnz_c[1], s.nnz_c[1], 2, mb_x * 2, mb_y * 2, w2);
4486 put4!(self.mv_y, s.mv_y);
4487 put4!(self.inter_y, s.inter_y);
4488 put4!(self.ref_idx_y, s.ref_idx_y);
4489 put4!(self.coded_y, s.coded_y);
4490 put4!(self.modes_y, s.modes_y);
4491 self.cur_qp = s.cur_qp;
4492 }
4493
4494 fn nnz_cache_load(&mut self, mb_x: usize, mb_y: usize) {
4499 let w4 = self.mb_w * 4;
4500 for lbx in 0..4 {
4501 self.nnz_l_cache[1 + lbx] = if mb_y == 0 {
4502 0x80
4503 } else {
4504 self.nnz_y[(mb_y * 4 - 1) * w4 + (mb_x * 4 + lbx)]
4505 };
4506 }
4507 for lby in 0..4 {
4508 self.nnz_l_cache[(lby + 1) * 5] = if mb_x == 0 {
4509 0x80
4510 } else {
4511 self.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 - 1)]
4512 };
4513 }
4514 }
4515
4516 #[inline]
4520 fn nc_pred(&self, lbx: usize, lby: usize) -> i32 {
4521 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;
4524 if r < 0x80 {
4525 (r + 1) >> 1
4526 } else {
4527 r & 0x7f
4528 }
4529 }
4530
4531 #[inline]
4533 fn nnz_cache_set(&mut self, lbx: usize, lby: usize, total: u8) {
4534 self.nnz_l_cache[(lby + 1) * 5 + (lbx + 1)] = total;
4535 }
4536
4537 fn chroma_cache_load(&mut self, mb_x: usize, mb_y: usize) {
4541 let w2 = self.mb_w * 2;
4542 for c in 0..2 {
4543 for bx in 0..2 {
4544 self.nnz_c_cache[c][1 + bx] = if mb_y == 0 {
4545 0x80
4546 } else {
4547 self.nnz_c[c][(mb_y * 2 - 1) * w2 + (mb_x * 2 + bx)]
4548 };
4549 }
4550 for by in 0..2 {
4551 self.nnz_c_cache[c][(by + 1) * 3] = if mb_x == 0 {
4552 0x80
4553 } else {
4554 self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 - 1)]
4555 };
4556 }
4557 }
4558 }
4559
4560 #[inline]
4562 fn chroma_nc_pred(&self, c: usize, bx: usize, by: usize) -> i32 {
4563 let left = self.nnz_c_cache[c][(by + 1) * 3 + bx] as i32;
4564 let top = self.nnz_c_cache[c][by * 3 + (bx + 1)] as i32;
4565 let r = left + top;
4566 if r < 0x80 {
4567 (r + 1) >> 1
4568 } else {
4569 r & 0x7f
4570 }
4571 }
4572
4573 #[inline]
4575 fn chroma_nnz_cache_set(&mut self, c: usize, bx: usize, by: usize, total: u8) {
4576 self.nnz_c_cache[c][(by + 1) * 3 + (bx + 1)] = total;
4577 }
4578}
4579
4580#[inline(never)]
4597fn derive_mb_bs_from(
4598 fe: &FrameEncoder,
4599 mb_x: usize,
4600 mb_y: usize,
4601 kind: rusty_h264_common::deblock::MbKind,
4602) -> rusty_h264_common::deblock::MbBs {
4603 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncBs);
4604 let view = rusty_h264_common::deblock::BlockInfo {
4605 inter: &fe.inter_y,
4606 nnz: &fe.nnz_y,
4607 mv: &fe.mv_y,
4608 ref_id: &fe.ref_idx_y,
4609 mv1: &[],
4610 ref_id1: &[],
4611 w4: fe.mb_w * 4,
4612 t8x8: &[],
4613 bs: &[],
4614 };
4615 rusty_h264_common::deblock::derive_mb_kind(&view, mb_x, mb_y, kind)
4616}
4617
4618pub fn encode_slice_data(
4619 w: &mut BitWriter,
4620 cfg: &EncoderConfig,
4621 frame: &YuvFrame,
4622 qp: u8,
4623 is_p: bool,
4624 refs: &[crate::RefFrame],
4625 qpo: &[i32],
4626) -> crate::RefFrame {
4627 let _g_prep = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncPrep);
4628 let mut fe = FrameEncoder::new(cfg);
4629 let precomp = rusty_h264_common::deblock::precomputed_bs_enabled();
4630 let mut bs_grid =
4631 vec![rusty_h264_common::deblock::MbBs::UNSET; if precomp { fe.mb_w * fe.mb_h } else { 0 }];
4632 fe.qp = qp;
4633 fe.qpc = chroma_qp(qp);
4634 fe.cur_qp = qp;
4635 if cfg.cabac_dz_div > 0 {
4636 fe.idz = cfg.cabac_dz_div; } let (sy, su, sv) = coded_source(cfg, frame);
4639 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
4640 let num_refs = refs.len();
4641 if is_p && fe.me_wide && !refs.is_empty()
4646 && global_mc_residual(&sy, fe.cw, fe.mb_h * 16, &refs[0].y) < fe.me_wide_coh
4647 {
4648 fe.me_wide = false;
4649 }
4650 if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
4657 let hr = me_wide_headroom(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
4658 if me_wide_hr_dbg() {
4659 eprintln!("ME_HR qp{qp} headroom={hr:.2}");
4660 }
4661 if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
4662 fe.me_wide = false;
4663 }
4664 }
4665 if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
4670 let (mg, dc) = b2_mgain(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
4671 if me_sadt_dbg() {
4672 eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
4673 }
4674 fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
4675 if mv_smooth_mode() == 1 {
4678 fe.mv_smooth = mg >= mv_smooth_t() && dc <= me_sad_dcmax();
4681 }
4682 let smg = split_mg();
4684 if smg > 0.0 {
4685 fe.do_splits = mg >= smg;
4686 }
4687 }
4688 if is_p && fe.satd_q > 0.0 {
4695 let mut vars: Vec<i64> = (0..fe.mb_h)
4696 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
4697 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
4698 .collect();
4699 vars.sort_unstable();
4700 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
4701 fe.satd_var_thresh = vars[idx];
4702 }
4703 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
4708 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
4710 let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
4711 let mut skip_run = 0u32;
4712 if is_p && mv_cmp_on() {
4721 MVCMP_FRAME.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
4722 }
4723 let mut rdskip_snap = MbState::default();
4725 let mut rdskip_free = 0usize;
4726 let mut rdskip_seen = 0usize;
4727 let mut rdskip_on = false;
4728 let mut greedy_on = fe.greedy_min_free == 0; let rdskip_learn = (fe.mb_w * fe.mb_h / 8).max(64);
4730 let rdskip_min_free = fe.rd_skip_min_free as usize;
4731
4732 drop(_g_prep);
4733 let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
4734 for mb_y in 0..fe.mb_h {
4735 for mb_x in 0..fe.mb_w {
4736 let mb_idx = mb_y * fe.mb_w + mb_x;
4737 fe.qp = aq_qp[mb_idx];
4738 fe.qpc = chroma_qp(aq_qp[mb_idx]);
4739 let mut inter: Option<InterChoice> = None;
4742 let mut coded: Option<BitWriter> = None;
4746 if is_p {
4747 if num_refs > 0 {
4748 let _g_skip = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncSkip);
4752 let _g_smc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Neighbors);
4753 rdskip_seen += 1;
4754 if rdskip_seen >= rdskip_learn {
4755 rdskip_on = rdskip_free * 100 >= rdskip_seen * rdskip_min_free;
4756 greedy_on = fe.greedy_min_free == 0
4757 || rdskip_free * 100 >= rdskip_seen * fe.greedy_min_free as usize;
4758 }
4759 let mv_skip = fe.skip_mv(mb_x, mb_y);
4760 let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
4761 drop(_g_smc);
4762 let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
4763 let skip_c = if luma_free || !fe.fast {
4766 fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
4767 } else {
4768 [[0u8; 64]; 2]
4769 };
4770 let is_free =
4771 luma_free && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &skip_c);
4772 let skip_sad = if fe.fast {
4774 0
4775 } else {
4776 let (lx, ly) = (mb_x * 16, mb_y * 16);
4777 let mut s = 0u32;
4778 for dy in 0..16 {
4779 let src = &sy[(ly + dy) * fe.cw + lx..][..16];
4780 let p = &skip_y[dy * 16..][..16];
4781 s += src.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
4782 }
4783 s
4784 };
4785 if is_free {
4786 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
4787 if !fe.fast {
4788 fe.mb_was_skip[mb_idx] = true;
4789 fe.mb_skip_sad[mb_idx] = skip_sad;
4790 }
4791 mb_qpy[mb_idx] = fe.cur_qp; rdskip_free += 1;
4793 if precomp {
4794 bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, rusty_h264_common::deblock::MbKind::Skip);
4795 }
4796 skip_run += 1;
4797 continue;
4798 }
4799 drop(_g_skip);
4800 let (lx, ly) = (mb_x * 16, mb_y * 16);
4801 let nb = {
4802 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMvPred);
4803 fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
4804 };
4805 let lme = lambda.sqrt();
4806
4807 if fe.fast {
4808 fe.mb_use_satd = fe.satd_q > 0.0
4820 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
4821 let (r16, mv16, cost_inter) =
4822 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
4823 let cost_intra = if fe.mb_use_satd {
4824 fe.best_i16_satd(&sy, mb_x, mb_y)
4825 } else {
4826 fe.best_i16_sad(&sy, mb_x, mb_y)
4827 } + (lme * fe.tune_intra_penalty) as i64;
4828 inter = if cost_intra < cost_inter {
4829 None } else {
4831 Some((0, vec![(r16, mv16)]))
4832 };
4833 } else {
4834 if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
4844 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
4845 fe.mb_was_skip[mb_idx] = true;
4846 fe.mb_skip_sad[mb_idx] = skip_sad;
4847 mb_qpy[mb_idx] = fe.cur_qp; if precomp {
4849 bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, rusty_h264_common::deblock::MbKind::Skip);
4850 }
4851 skip_run += 1;
4852 continue;
4853 }
4854
4855 let (r16, mv16, c16) =
4857 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
4858 let mut best_c = c16;
4859 let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
4860
4861 const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
4864 let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
4865 let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
4866 let split_t = split_t();
4867 if fe.do_splits && c16 > split_gate && (split_t <= 0.0 || (c16 as f64) >= split_t * lme) {
4868 let (rt, mvt, ct) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 8, &[mv16], lme);
4869 let (rb, mvb, cb) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly + 8, 16, 8, &[mv16], lme);
4870 let (rl, mvl, cl) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 8, 16, &[mv16], lme);
4871 let (rr, mvr, cr) = fe.best_part(refs, &sy, &nb, num_refs, lx + 8, ly, 8, 16, &[mv16], lme);
4872 if ct + cb < best_c {
4873 best_c = ct + cb;
4874 pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
4875 }
4876 if cl + cr < best_c {
4877 best_c = cl + cr;
4878 pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
4879 }
4880
4881 if fe.sub8x8 {
4886 let mut c8 = (lme * 4.0) as i64; let mut p8 = Vec::with_capacity(4);
4888 for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
4889 let (r, mv, c) = fe.best_part(
4890 refs, &sy, &nb, num_refs, lx + qx, ly + qy, 8, 8, &[mv16], lme,
4891 );
4892 c8 += c;
4893 p8.push((r, mv));
4894 }
4895 if c8 < best_c {
4896 best_c = c8;
4897 pick = Some((3u8, p8));
4898 }
4899 }
4900 }
4901
4902 if fe.sp_defer.get() {
4907 if let Some((mode, parts)) = pick.as_mut() {
4908 let regions: &[(usize, usize, usize, usize)] = match mode {
4909 1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
4910 2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
4911 3 => &[(0, 0, 8, 8), (8, 0, 8, 8), (0, 8, 8, 8), (8, 8, 8, 8)],
4912 _ => &[(0, 0, 16, 16)],
4913 };
4914 let mut tot = if *mode == 3 { (lme * 4.0) as i64 } else { 0 };
4915 for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
4916 let (r, mv) = parts[i];
4917 let (m2, c2) = fe.refine_part(
4918 refs, &sy, &nb, num_refs, lx + qx, ly + qy, pw, ph, lme, r, mv,
4919 );
4920 parts[i] = (r, m2);
4921 tot += c2;
4922 }
4923 best_c = tot;
4924 }
4925 }
4926 if split_harvest::enabled() {
4927 let won = match pick.as_ref().map(|p| p.0) {
4928 Some(0) | None => 0u8,
4929 Some(m) => m,
4930 };
4931 split_harvest::record(c16, best_c, lme, split_gate, won);
4932 }
4933 let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
4936 + (lme * fe.tune_intra_penalty) as i64;
4937 inter = if c_intra < best_c { None } else { pick };
4938 fe.mb_was_skip[mb_idx] = false;
4939 fe.mb_skip_sad[mb_idx] = skip_sad;
4940 }
4941
4942 if fe.rd_skip && rdskip_on && inter.is_some() {
4954 let skip_cp = fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip);
4955 let ssd_s = fe.pred_ssd(&sy, &su, &sv, mb_x, mb_y, &skip_y, &skip_cp);
4961 debug_assert_eq!(ssd_s, {
4962 let snap = fe.save_mb(mb_x, mb_y);
4963 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
4964 let v = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y);
4965 fe.load_mb(mb_x, mb_y, &snap);
4966 v
4967 }, "skip prediction SSD must equal the committed-skip reconstruction SSD");
4968 let j_skip = ssd_s as f64 + lambda;
4970 let take_skip = if fe.rd_skip_fast_t > 0.0
4976 && (ssd_s as f64) <= lambda * fe.rd_skip_fast_t
4977 {
4978 true
4979 } else {
4980 fe.save_mb_into(mb_x, mb_y, &mut rdskip_snap);
4986 let mut scratch = BitWriter::new();
4987 {
4988 let (m, p) = inter.as_ref().unwrap();
4989 fe.encode_inter_mb(
4990 &mut scratch, refs, &sy, &su, &sv, mb_x, mb_y, *m, p,
4991 );
4992 }
4993 let bits_c = scratch.bit_len();
4994 let ssd_c = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y);
4995 let won = j_skip <= ssd_c as f64 + lambda * bits_c as f64;
4996 if won {
4997 fe.load_mb(mb_x, mb_y, &rdskip_snap); true
4999 } else {
5000 coded = Some(scratch); false
5002 }
5003 };
5004 if take_skip {
5005 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
5006 if !fe.fast {
5007 fe.mb_was_skip[mb_idx] = true;
5008 fe.mb_skip_sad[mb_idx] = skip_sad;
5009 }
5010 mb_qpy[mb_idx] = fe.cur_qp;
5011 if precomp {
5012 bs_grid[mb_idx] = derive_mb_bs_from(
5013 &fe, mb_x, mb_y,
5014 rusty_h264_common::deblock::MbKind::Skip,
5015 );
5016 }
5017 skip_run += 1;
5018 continue;
5019 }
5020 }
5021 }
5022 w.write_ue(skip_run); skip_run = 0;
5024 }
5025 if mv_force_on() && is_p && inter.is_some() {
5026 let fi = MVCMP_FRAME.load(std::sync::atomic::Ordering::Relaxed);
5027 let ext = EXT_MV.lock().unwrap();
5028 if let Some(field) = ext.get(fi) {
5029 let w4 = fe.mb_w * 4;
5030 let b0 = (mb_y * 4) * w4 + mb_x * 4;
5031 let uniform = (0..4).all(|r| {
5034 (0..4).all(|c| field.get(b0 + r * w4 + c) == field.get(b0))
5035 });
5036 if uniform {
5037 if let Some(&emv) = field.get(b0) {
5038 inter = Some((0, vec![(0, emv)]));
5039 MVCMP[6].fetch_add(1, std::sync::atomic::Ordering::Relaxed);
5040 }
5041 }
5042 }
5043 }
5044 if mv_cmp_on() && is_p {
5045 if let Some((mode, parts)) = inter.as_ref() {
5046 let fi = MVCMP_FRAME.load(std::sync::atomic::Ordering::Relaxed);
5047 let ext = EXT_MV.lock().unwrap();
5048 if let Some(field) = ext.get(fi) {
5049 let bidx = (mb_y * 4) * (fe.mb_w * 4) + mb_x * 4;
5050 if let Some(&emv) = field.get(bidx) {
5051 let (mode, parts) = (*mode, parts.clone());
5052 drop(ext);
5053 let (so, bo) =
5056 fe.trial_inter(refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts);
5057 let (se, be) = fe.trial_inter(
5058 refs, &sy, &su, &sv, mb_x, mb_y, 0, &[(0, emv)],
5059 );
5060 let jo = so as f64 + lambda * bo as f64;
5061 let je = se as f64 + lambda * be as f64;
5062 use std::sync::atomic::Ordering::Relaxed;
5063 MVCMP[0].fetch_add(1, Relaxed);
5064 MVCMP[1].fetch_add(bo as u64, Relaxed);
5065 MVCMP[2].fetch_add(be as u64, Relaxed);
5066 MVCMP[3].fetch_add(so.max(0) as u64, Relaxed);
5067 MVCMP[4].fetch_add(se.max(0) as u64, Relaxed);
5068 MVCMP[5].fetch_add((je < jo) as u64, Relaxed);
5069 MVCMP[6].fetch_add((parts[0].1 != emv) as u64, Relaxed);
5070 }
5071 }
5072 }
5073 }
5074 let mb_kind = match &inter {
5077 Some((_, parts)) if parts.len() == 1 => {
5080 rusty_h264_common::deblock::MbKind::InterUniform
5081 }
5082 Some(_) => rusty_h264_common::deblock::MbKind::Inter,
5083 None => rusty_h264_common::deblock::MbKind::Intra,
5084 };
5085 match inter {
5086 Some((mode, parts)) => match coded {
5087 Some(sc) => w.append(&sc),
5090 None => {
5091 fe.encode_inter_mb(w, refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts)
5092 }
5093 },
5094 None => encode_mb(&mut fe, w, mb_x, mb_y, &sy, &su, &sv, is_p),
5095 }
5096 mb_qpy[mb_idx] = fe.cur_qp; if precomp {
5098 bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, mb_kind);
5099 }
5100 }
5101 }
5102 debug_assert!(
5103 !precomp || bs_grid.iter().all(|b| *b != rusty_h264_common::deblock::MbBs::UNSET),
5104 "a macroblock loop exit failed to store its boundary strengths"
5105 );
5106 if is_p && skip_run > 0 {
5107 w.write_ue(skip_run); }
5109 w.rbsp_trailing_bits();
5110
5111 drop(_g_loop);
5115 let _g_fin = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFinal);
5116 let info = rusty_h264_common::deblock::BlockInfo {
5121 inter: &fe.inter_y,
5122 nnz: &fe.nnz_y,
5123 mv: &fe.mv_y,
5124 ref_id: &fe.ref_idx_y,
5125 mv1: &[],
5126 ref_id1: &[],
5127 w4: fe.mb_w * 4,
5128 t8x8: &[],
5129 bs: &bs_grid,
5130 };
5131 drop(_g_fin);
5134 rusty_h264_common::deblock::filter_frame(
5135 &mut fe.rec_y,
5136 &mut fe.rec_u,
5137 &mut fe.rec_v,
5138 fe.mb_w,
5139 fe.mb_h,
5140 &mb_qpy,
5141 0, 0, 0, &info,
5145 );
5146 let w4 = fe.mb_w * 4;
5147 crate::RefFrame {
5148 y: fe.rec_y,
5149 u: fe.rec_u,
5150 v: fe.rec_v,
5151 poc: 0, frame_num: 0, mv: fe.mv_y,
5155 ref_idx: fe.ref_idx_y,
5156 w4,
5157 hpel: std::sync::OnceLock::new(),
5159 }
5160}
5161
5162#[allow(clippy::too_many_arguments)]
5173#[allow(clippy::too_many_arguments)]
5174pub fn encode_slice_data_b(
5175 w: &mut BitWriter,
5176 cfg: &EncoderConfig,
5177 frame: &YuvFrame,
5178 qp: u8,
5179 poc: i32,
5180 l0: &crate::RefFrame,
5181 l1: &crate::RefFrame,
5182 qpo: &[i32],
5183) {
5184 let mut fe = FrameEncoder::new(cfg);
5185 fe.qp = qp;
5186 fe.qpc = chroma_qp(qp);
5187 fe.cur_qp = qp;
5188 if cfg.cabac_dz_div > 0 {
5189 fe.idz = cfg.cabac_dz_div; } fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
5194 let (sy, su, sv) = coded_source(cfg, frame);
5195 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
5196 let lme = lambda.sqrt();
5197 let refs = std::slice::from_ref(l0); if fe.satd_q > 0.0 {
5201 let mut vars: Vec<i64> = (0..fe.mb_h)
5202 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
5203 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
5204 .collect();
5205 vars.sort_unstable();
5206 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
5207 fe.satd_var_thresh = vars[idx];
5208 }
5209 let mut skip_run = 0u32; for mb_y in 0..fe.mb_h {
5211 for mb_x in 0..fe.mb_w {
5212 let (lx, ly) = (mb_x * 16, mb_y * 16);
5213 let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
5214 fe.mb_use_satd =
5215 fe.satd_q > 0.0 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
5216 let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
5219 let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
5220 let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
5221 let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
5222 let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
5226 if fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
5234 && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc)
5235 {
5236 fe.commit_direct_motion(mb_x, mb_y, &dmotion);
5237 skip_run += 1;
5238 continue;
5239 }
5240 let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
5241 let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
5242 let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
5243 let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
5245 let r_bi = mvd_bits(mv0.0 - pmv0.0) + mvd_bits(mv0.1 - pmv0.1)
5246 + mvd_bits(mv1.0 - pmv1.0) + mvd_bits(mv1.1 - pmv1.1);
5247 let j_bi = d_bi + (lme * r_bi as f64) as i64;
5248 let (mut dir, mut best) = (0u8, d_direct);
5253 if j0 < best { dir = 1; best = j0; }
5254 if j1 < best { dir = 2; best = j1; }
5255 if j_bi < best { dir = 3; best = j_bi; }
5256 let _ = best;
5257 w.write_ue(skip_run); skip_run = 0;
5259 let bspec = BInter { dir, l1, mv0, mv1 };
5260 fe.encode_inter_mb_v1_b(w, refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
5261 }
5262 }
5263 if skip_run > 0 {
5264 w.write_ue(skip_run); }
5266 w.rbsp_trailing_bits();
5267}
5268
5269#[inline(always)]
5273fn mvd_bits(d: i32) -> u32 {
5274 let codenum = if d > 0 { (2 * d - 1) as u32 } else { (-2 * d) as u32 };
5275 1 + 2 * (31 - (codenum + 1).leading_zeros())
5276}
5277
5278fn write_ref_idx(w: &mut BitWriter, refi: i32, num_refs: usize) {
5282 if num_refs == 2 {
5283 w.write_bit(refi == 0); } else {
5285 w.write_ue(refi as u32);
5286 }
5287}
5288
5289fn ref_bits(r: usize, num_refs: usize) -> u32 {
5292 if num_refs <= 1 {
5293 0
5294 } else if num_refs == 2 {
5295 1
5296 } else {
5297 let mut n = r as u32 + 1;
5298 let mut len = 1;
5299 while n > 1 {
5300 n >>= 1;
5301 len += 2;
5302 }
5303 len
5304 }
5305}
5306
5307fn residual(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[i32; 16]) -> [i32; 16] {
5308 let mut r = [0i32; 16];
5309 for dy in 0..4 {
5310 for dx in 0..4 {
5311 r[dy * 4 + dx] = src[(y0 + dy) * stride + (x0 + dx)] as i32 - pred[dy * 4 + dx];
5312 }
5313 }
5314 r
5315}
5316
5317fn store(plane: &mut [u8], stride: usize, x0: usize, y0: usize, s: &[u8; 16]) {
5319 for dy in 0..4 {
5320 for dx in 0..4 {
5321 plane[(y0 + dy) * stride + (x0 + dx)] = s[dy * 4 + dx];
5322 }
5323 }
5324}
5325
5326fn pred_block(pred: &[u8; 256], bx: usize, by: usize) -> [i32; 16] {
5329 let mut p = [0i32; 16];
5330 for dy in 0..4 {
5331 for dx in 0..4 {
5332 p[dy * 4 + dx] = pred[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
5333 }
5334 }
5335 p
5336}
5337
5338#[inline]
5349pub(crate) fn satd_px(src: &[u8], ss: usize, pred: &[u8], ps: usize, w: usize, h: usize) -> i64 {
5350 #[cfg(accel)]
5351 {
5352 let asm = match (w, h) {
5353 (16, 16) => Some(rusty_h264_accel::satd_16x16(src, ss, pred, ps)),
5354 (16, 8) => Some(rusty_h264_accel::satd_16x8(src, ss, pred, ps)),
5355 (8, 16) => Some(rusty_h264_accel::satd_8x16(src, ss, pred, ps)),
5356 (8, 8) => Some(rusty_h264_accel::satd_8x8(src, ss, pred, ps)),
5357 (4, 4) => Some(rusty_h264_accel::satd_4x4(src, ss, pred, ps)),
5358 _ => None,
5359 };
5360 if let Some(v) = asm {
5361 return 2 * v as i64;
5362 }
5363 }
5364 let (nbx, nby) = (w / 4, h / 4);
5366 let mut blocks = [[0i32; 16]; 16];
5367 let mut bi = 0;
5368 for by in 0..nby {
5369 for bx in 0..nbx {
5370 let blk = &mut blocks[bi];
5371 for dy in 0..4 {
5372 for dx in 0..4 {
5373 blk[dy * 4 + dx] =
5374 src[(by * 4 + dy) * ss + bx * 4 + dx] as i32 - pred[(by * 4 + dy) * ps + bx * 4 + dx] as i32;
5375 }
5376 }
5377 bi += 1;
5378 }
5379 }
5380 satd_4x4_sum(&blocks[..nbx * nby])
5381}
5382
5383#[inline]
5388fn sad_strided(src: &[u8], ss: usize, r: &[u8], rs: usize, w: usize, h: usize) -> i64 {
5389 #[cfg(accel)]
5390 {
5391 match (w, h) {
5392 (16, 16) => return rusty_h264_accel::sad_16x16(src, ss, r, rs) as i64,
5393 (16, 8) => return rusty_h264_accel::sad_16x8(src, ss, r, rs) as i64,
5394 (8, 16) => return rusty_h264_accel::sad_8x16(src, ss, r, rs) as i64,
5395 _ => {}
5396 }
5397 }
5398 let mut sad = 0u32;
5399 for dy in 0..h {
5400 let a = &src[dy * ss..][..w];
5401 let b = &r[dy * rs..][..w];
5402 sad += a.iter().zip(b).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
5403 }
5404 sad as i64
5405}
5406
5407#[inline]
5411fn sad_avg_strided(src: &[u8], ss: usize, a: &[u8], b: &[u8], rs: usize, w: usize, h: usize) -> i64 {
5412 let mut sad = 0u32;
5413 for dy in 0..h {
5414 let s = &src[dy * ss..][..w];
5415 let pa = &a[dy * rs..][..w];
5416 let pb = &b[dy * rs..][..w];
5417 for i in 0..w {
5418 let p = ((pa[i] as u16 + pb[i] as u16 + 1) >> 1) as u8;
5419 sad += s[i].abs_diff(p) as u32;
5420 }
5421 }
5422 sad as i64
5423}
5424
5425fn satd_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
5426 satd_px(&src[ly * stride + lx..], stride, pred, 16, 16, 16)
5427}
5428
5429fn sad_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
5433 let mut sad = 0u32;
5434 for dy in 0..16 {
5435 let s = &src[(ly + dy) * stride + lx..][..16];
5436 let p = &pred[dy * 16..][..16];
5437 sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
5438 }
5439 sad as i64
5440}
5441
5442fn satd_8x8(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[u8; 64]) -> i64 {
5444 satd_px(&src[y0 * stride + x0..], stride, pred, 8, 8, 8)
5445}
5446
5447fn satd_4x4(src: &[u8], stride: usize, px: usize, py: usize, pred: &[u8; 16]) -> i64 {
5449 satd_px(&src[py * stride + px..], stride, pred, 4, 4, 4)
5450}
5451
5452fn i4_mode_available(mode: u8, top: bool, left: bool) -> bool {
5454 match mode {
5455 0 | 3 | 7 => top, 1 | 8 => left, 2 => true, _ => top && left, }
5460}
5461
5462struct I4Plan {
5465 modes: [u8; 16], q: [[i32; 16]; 16], cbp_luma: u32, nonzero: i64, }
5470
5471struct MbPlan {
5477 use_i4: bool,
5478 i16_mode: I16Mode,
5481 i16_cbp15: bool,
5482 i16_dc_levels: [i32; 16],
5483 i16_q: [[i32; 16]; 16],
5484 i4: Option<I4Plan>,
5486 i8: Option<I8Plan>,
5489 chroma_mode: u8,
5491 cbp_chroma: u32,
5492 c_dc_levels: [[i32; 4]; 2],
5493 c_q_blocks: [[[i32; 16]; 4]; 2],
5494}
5495
5496struct InterPlan {
5503 mvds: [(i32, i32); 4], plan_refs: [i32; 4], n_mvd: usize,
5506 cbp: u32,
5507 q_blocks: [[i32; 16]; 16], c_dc_levels: [[i32; 4]; 2],
5509 c_q: [[[i32; 16]; 4]; 2],
5510 t8x8: bool, q8: [[i32; 64]; 4], }
5513
5514fn gather_i4(
5516 fe: &FrameEncoder,
5517 px: usize,
5518 py: usize,
5519 avail_top: bool,
5520 avail_left: bool,
5521 bx: usize,
5522 by: usize,
5523) -> ([u8; 8], [u8; 4], u8) {
5524 let (cw, w4) = (fe.cw, fe.mb_w * 4);
5525 let mut top = [0u8; 8];
5526 let mut left = [0u8; 4];
5527 let mut corner = 0;
5528 if avail_top {
5529 for i in 0..4 {
5530 top[i] = fe.rec_y[(py - 1) * cw + px + i];
5531 }
5532 let tr_avail = bx + 1 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 1)];
5533 for i in 0..4 {
5534 top[4 + i] = if tr_avail {
5535 fe.rec_y[(py - 1) * cw + px + 4 + i]
5536 } else {
5537 top[3]
5538 };
5539 }
5540 }
5541 if avail_left {
5542 for i in 0..4 {
5543 left[i] = fe.rec_y[(py + i) * cw + px - 1];
5544 }
5545 }
5546 if avail_top && avail_left {
5547 corner = fe.rec_y[(py - 1) * cw + px - 1];
5548 }
5549 (top, left, corner)
5550}
5551
5552#[inline]
5559fn modes_at(fe: &FrameEncoder, modes: &[u8; 16], lbx: usize, lby: usize, dx: isize, dy: isize, bx: usize, by: usize) -> u8 {
5560 let (nx, ny) = (lbx as isize + dx, lby as isize + dy);
5561 if (0..4).contains(&nx) && (0..4).contains(&ny) {
5562 modes[ny as usize * 4 + nx as usize]
5563 } else {
5564 let w4 = fe.mb_w * 4;
5565 let gx = (bx as isize + dx) as usize;
5566 let gy = (by as isize + dy) as usize;
5567 fe.modes_y[gy * w4 + gx]
5568 }
5569}
5570
5571fn plan_i4x4(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I4Plan {
5572 let w4 = fe.mb_w * 4;
5573 let mut modes = [2u8; 16];
5574 let mut q = [[0i32; 16]; 16];
5575 let mut cbp_luma = 0u32;
5576 let mut nonzero = 0i64;
5577
5578 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
5579 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
5580 let (px, py) = (bx * 4, by * 4);
5581 let avail_top = by > 0;
5582 let avail_left = bx > 0;
5583 let (top, left, corner) = gather_i4(fe, px, py, avail_top, avail_left, bx, by);
5584
5585 let mut best_m = 2u8;
5590 let mut best_cost = i64::MAX;
5591 if fe.fast && fast_intra_enabled() {
5592 let lm = if bx > 0 { modes_at(fe, &modes, lbx, lby, -1, 0, bx, by) } else { 2 };
5593 let tm = if by > 0 { modes_at(fe, &modes, lbx, lby, 0, -1, bx, by) } else { 2 };
5594 let mpm = lm.min(tm);
5595 let mut cands = [mpm, 2u8, 0, 1];
5596 for i in 1..4 {
5597 for j in 0..i {
5598 if cands[i] == cands[j] {
5599 cands[i] = 255;
5600 }
5601 }
5602 }
5603 for &m in cands.iter() {
5604 if m == 255 || !i4_mode_available(m, avail_top, avail_left) {
5605 continue;
5606 }
5607 let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
5608 let cost = satd_4x4(sy, fe.cw, px, py, &pred);
5609 if cost < best_cost {
5610 best_cost = cost;
5611 best_m = m;
5612 }
5613 }
5614 } else {
5615 for m in 0..9u8 {
5616 if !i4_mode_available(m, avail_top, avail_left) {
5617 continue;
5618 }
5619 let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
5620 let cost = satd_4x4(sy, fe.cw, px, py, &pred);
5621 if cost < best_cost {
5622 best_cost = cost;
5623 best_m = m;
5624 }
5625 }
5626 }
5627
5628 let pred = intra4x4_pred(best_m, avail_top, avail_left, &top, &left, corner);
5630 let mut predb = [0i32; 16];
5631 for i in 0..16 {
5632 predb[i] = pred[i] as i32;
5633 }
5634 let res = residual(sy, fe.cw, px, py, &predb);
5635 let qb = rdoq(&forward_core(&res), qp, fe.idz, fe.rdoq_strength, 0); let s = reconstruct_4x4(&dequantize(&qb, qp), &predb);
5637 store(&mut fe.rec_y, fe.cw, px, py, &s);
5638 fe.coded_y[by * w4 + bx] = true;
5639
5640 let nz = qb.iter().filter(|&&v| v != 0).count();
5641 if nz > 0 {
5642 cbp_luma |= 1 << ((lby / 2) * 2 + (lbx / 2));
5643 }
5644 nonzero += nz as i64;
5645 modes[lby * 4 + lbx] = best_m;
5646 q[lby * 4 + lbx] = qb;
5647 }
5648 I4Plan {
5649 modes,
5650 q,
5651 cbp_luma,
5652 nonzero,
5653 }
5654}
5655
5656struct I8Plan {
5660 modes: [u8; 4], q: [[i32; 64]; 4], cbp_luma: u32, nonzero: i64, }
5665
5666const ZIGZAG_8X8: [usize; 64] = [
5669 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,
5670 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
5671 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
5672];
5673
5674#[inline]
5675fn scan_8x8_fwd(raster: &[i32; 64]) -> [i32; 64] {
5676 std::array::from_fn(|i| raster[ZIGZAG_8X8[i]])
5677}
5678
5679fn gather_i8_enc(
5682 fe: &FrameEncoder,
5683 px: usize,
5684 py: usize,
5685 avail_top: bool,
5686 avail_left: bool,
5687 bx: usize,
5688 by: usize,
5689) -> ([u8; 16], [u8; 8], u8, bool) {
5690 let (cw, w4) = (fe.cw, fe.mb_w * 4);
5691 let mut top = [0u8; 16];
5692 let mut left = [0u8; 8];
5693 let mut corner = 0;
5694 if avail_top {
5695 for i in 0..8 {
5696 top[i] = fe.rec_y[(py - 1) * cw + px + i];
5697 }
5698 let tr_avail = bx + 2 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 2)];
5699 for i in 0..8 {
5700 top[8 + i] = if tr_avail {
5701 fe.rec_y[(py - 1) * cw + px + 8 + i]
5702 } else {
5703 top[7]
5704 };
5705 }
5706 }
5707 if avail_left {
5708 for i in 0..8 {
5709 left[i] = fe.rec_y[(py + i) * cw + px - 1];
5710 }
5711 }
5712 let avail_corner = avail_top && avail_left;
5713 if avail_corner {
5714 corner = fe.rec_y[(py - 1) * cw + px - 1];
5715 }
5716 (top, left, corner, avail_corner)
5717}
5718
5719fn plan_i8x8(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I8Plan {
5722 let w4 = fe.mb_w * 4;
5723 let mut modes = [2u8; 4];
5724 let mut q = [[0i32; 64]; 4];
5725 let mut cbp_luma = 0u32;
5726 let mut nonzero = 0i64;
5727 let weight = [16i32; 64];
5728
5729 for b8 in 0..4usize {
5730 let (b8x, b8y) = (b8 % 2, b8 / 2);
5731 let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
5732 let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2); let avail_top = b8y > 0 || mb_y > 0;
5734 let avail_left = b8x > 0 || mb_x > 0;
5735 let (top, left, corner, avail_corner) =
5736 gather_i8_enc(fe, px, py, avail_top, avail_left, bx, by);
5737
5738 let predicted = predict_i4_mode(fe, bx, by);
5742 let mut best_m = 2u8;
5743 let mut best_cost = i64::MAX;
5744 for m in 0..9u8 {
5745 if !i4_mode_available(m, avail_top, avail_left) {
5746 continue;
5747 }
5748 let pred = intra8x8_pred(m, avail_top, avail_left, avail_corner, &top, &left, corner);
5749 let mut cost = satd_8x8(sy, fe.cw, px, py, &pred);
5750 if m != predicted {
5751 cost += 4 * fe.qp as i64; }
5753 if cost < best_cost {
5754 best_cost = cost;
5755 best_m = m;
5756 }
5757 }
5758 modes[b8] = best_m;
5759
5760 let pred = intra8x8_pred(best_m, avail_top, avail_left, avail_corner, &top, &left, corner);
5762 let mut res = [0i32; 64];
5763 for dy in 0..8 {
5764 for dx in 0..8 {
5765 res[dy * 8 + dx] =
5766 sy[(py + dy) * fe.cw + (px + dx)] as i32 - pred[dy * 8 + dx] as i32;
5767 }
5768 }
5769 let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, fe.idz);
5770 let nz = levels.iter().filter(|&&v| v != 0).count();
5771 if nz > 0 {
5772 cbp_luma |= 1 << b8;
5773 }
5774 nonzero += nz as i64;
5775 q[b8] = levels;
5776
5777 let res_r = inverse_quant_8x8(&levels, qp, &weight);
5778 let predb: [i32; 64] = std::array::from_fn(|i| pred[i] as i32);
5779 let recon = add_residual_8x8(&res_r, &predb);
5780 for dy in 0..8 {
5781 for dx in 0..8 {
5782 fe.rec_y[(py + dy) * fe.cw + (px + dx)] = recon[dy * 8 + dx];
5783 }
5784 }
5785 for sry in 0..2 {
5788 for srx in 0..2 {
5789 fe.modes_y[(by + sry) * w4 + (bx + srx)] = best_m;
5790 fe.coded_y[(by + sry) * w4 + (bx + srx)] = true;
5791 }
5792 }
5793 }
5794 I8Plan {
5795 modes,
5796 q,
5797 cbp_luma,
5798 nonzero,
5799 }
5800}
5801
5802#[allow(clippy::too_many_arguments)]
5810fn plan_inter8_luma(
5811 sy: &[u8],
5812 cw: usize,
5813 mb_x: usize,
5814 mb_y: usize,
5815 pred_y: &[u8; 256],
5816 qp: u8,
5817) -> ([[i32; 64]; 4], u32, f64, [u8; 256], i64) {
5818 let weight = [16i32; 64];
5819 let mut q8 = [[0i32; 64]; 4];
5820 let mut cbp = 0u32;
5821 let mut rate = 0f64;
5822 let mut rec = [0u8; 256];
5823 let mut ssd = 0i64;
5824 for b8 in 0..4usize {
5825 let (b8x, b8y) = (b8 % 2, b8 / 2);
5826 let mut res = [0i32; 64];
5827 for dy in 0..8 {
5828 for dx in 0..8 {
5829 let sx = mb_x * 16 + b8x * 8 + dx;
5830 let syy = mb_y * 16 + b8y * 8 + dy;
5831 let p = pred_y[(b8y * 8 + dy) * 16 + (b8x * 8 + dx)] as i32;
5832 res[dy * 8 + dx] = sy[syy * cw + sx] as i32 - p;
5833 }
5834 }
5835 let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, 6);
5836 let mut nz = false;
5837 for &l in &levels {
5838 if l != 0 {
5839 nz = true;
5840 rate += rdoq_rate((l as i64).abs());
5841 }
5842 }
5843 if nz {
5844 cbp |= 1 << b8;
5845 }
5846 q8[b8] = levels;
5847
5848 let res_r = inverse_quant_8x8(&levels, qp, &weight);
5849 let predb: [i32; 64] =
5850 std::array::from_fn(|i| pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32);
5851 let recon = add_residual_8x8(&res_r, &predb);
5852 for dy in 0..8 {
5853 for dx in 0..8 {
5854 let ri = (b8y * 8 + dy) * 16 + (b8x * 8 + dx);
5855 rec[ri] = recon[dy * 8 + dx];
5856 let sx = mb_x * 16 + b8x * 8 + dx;
5857 let syy = mb_y * 16 + b8y * 8 + dy;
5858 let d = recon[dy * 8 + dx] as i64 - sy[syy * cw + sx] as i64;
5859 ssd += d * d;
5860 }
5861 }
5862 }
5863 (q8, cbp, rate, rec, ssd)
5864}
5865
5866#[inline]
5871fn i16_pred(
5872 fe: &FrameEncoder,
5873 mode: I16Mode,
5874 avail_top: bool,
5875 avail_left: bool,
5876 top: &[u8; 16],
5877 left: &[u8; 16],
5878 corner: u8,
5879 lx: usize,
5880 ly: usize,
5881) -> [u8; 256] {
5882 #[cfg(accel)]
5883 if avail_top && avail_left {
5884 let mode_n = match mode {
5885 I16Mode::Vertical => 0,
5886 I16Mode::Horizontal => 1,
5887 I16Mode::Dc => 2,
5888 I16Mode::Plane => 3,
5889 };
5890 let mut p = AlignedMb([0; 256]);
5891 rusty_h264_accel::i16x16_luma_pred(mode_n, &mut p.0, &fe.rec_y[..], ly * fe.cw + lx, fe.cw);
5892 return p.0;
5893 }
5894 let _ = (fe, lx, ly);
5895 luma16x16_pred(mode, avail_top, avail_left, top, left, corner)
5896}
5897
5898#[inline]
5902#[allow(clippy::too_many_arguments)]
5903fn chroma_pred(
5904 fe: &FrameEncoder,
5905 mode: u8,
5906 avail_top: bool,
5907 avail_left: bool,
5908 c: usize,
5909 top: &[u8; 8],
5910 left: &[u8; 8],
5911 corner: u8,
5912 cx: usize,
5913 cy: usize,
5914) -> [u8; 64] {
5915 #[cfg(accel)]
5916 if avail_top && avail_left && (mode == 2 || mode == 3) {
5917 let plane = if c == 0 { &fe.rec_u } else { &fe.rec_v };
5918 let mut p = AlignedMb([0; 256]);
5919 rusty_h264_accel::chroma8x8_pred(mode, &mut p.0[..64], &plane[..], cy * fe.ccw + cx, fe.ccw);
5920 let mut out = [0u8; 64];
5921 out.copy_from_slice(&p.0[..64]);
5922 return out;
5923 }
5924 let _ = (fe, c, cx, cy);
5925 chroma8x8_pred(mode, avail_top, avail_left, top, left, corner)
5926}
5927
5928fn predict_i4_mode(fe: &FrameEncoder, bx: usize, by: usize) -> u8 {
5931 if bx == 0 || by == 0 {
5932 return 2;
5933 }
5934 let w4 = fe.mb_w * 4;
5935 fe.modes_y[by * w4 + (bx - 1)].min(fe.modes_y[(by - 1) * w4 + bx])
5936}
5937
5938#[allow(clippy::too_many_arguments)]
5939const RDOQ_ZZ: [usize; 16] = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
5941
5942#[inline]
5947fn rdoq_rate(level: i64) -> f64 {
5948 if level == 0 {
5949 1.0
5950 } else if level == 1 {
5951 3.0 } else {
5953 3.0 + (level - 1).min(13) as f64
5955 }
5956}
5957
5958fn rdoq(coeffs: &[i32; 16], qp: u8, dz_div: i64, strength: f64, first: usize) -> [i32; 16] {
5965 let mut q = quantize(coeffs, qp, dz_div);
5966 if strength <= 0.0 {
5967 return q;
5968 }
5969 let lambda = strength * 2f64.powf((qp as f64 - 12.0) / 3.0);
5970 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
5974 const POS: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7];
5975 let dist = |p: usize, level: i64| -> f64 {
5976 let e = coeffs[p].unsigned_abs() as f64 - level as f64 * (65536.0 / mf[POS[p]] as f64);
5977 e * e
5978 };
5979 for i in first..16 {
5981 let p = RDOQ_ZZ[i];
5982 let m = q[p].unsigned_abs() as i64;
5983 if m == 0 {
5984 continue;
5985 }
5986 let j_keep = dist(p, m) + lambda * rdoq_rate(m);
5987 let j_down = dist(p, m - 1) + lambda * rdoq_rate(m - 1);
5988 if j_down < j_keep {
5989 let nl = (m - 1) as i32;
5990 q[p] = if q[p] < 0 { -nl } else { nl };
5991 }
5992 }
5993 loop {
5998 let Some(li) = (first..16).rev().find(|&i| q[RDOQ_ZZ[i]] != 0) else {
5999 break;
6000 };
6001 let p = RDOQ_ZZ[li];
6002 let m = q[p].unsigned_abs() as i64;
6003 let prev = (first..li).rev().find(|&i| q[RDOQ_ZZ[i]] != 0);
6004 let base = prev.map_or(first, |j| j + 1);
6005 let bits = rdoq_rate(m) + 1.0 + (li - base) as f64; let d_add = dist(p, 0) - dist(p, m);
6007 if d_add < lambda * bits {
6008 q[p] = 0;
6009 } else {
6010 break;
6011 }
6012 }
6013 q
6014}
6015
6016fn plan_mb(
6021 fe: &mut FrameEncoder,
6022 mb_x: usize,
6023 mb_y: usize,
6024 sy: &[u8],
6025 su: &[u8],
6026 sv: &[u8],
6027) -> MbPlan {
6028 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCode);
6029 let qp = fe.qp;
6030 let qpc = fe.qpc;
6031 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
6033
6034 let (lx, ly) = (mb_x * 16, mb_y * 16);
6036 let avail_top = mb_y > 0;
6037 let avail_left = mb_x > 0;
6038 let mut top = [0u8; 16];
6039 let mut left = [0u8; 16];
6040 if avail_top {
6041 for i in 0..16 {
6042 top[i] = fe.rec_y[(ly - 1) * fe.cw + lx + i];
6043 }
6044 }
6045 if avail_left {
6046 for i in 0..16 {
6047 left[i] = fe.rec_y[(ly + i) * fe.cw + lx - 1];
6048 }
6049 }
6050 let corner = if avail_top && avail_left {
6051 fe.rec_y[(ly - 1) * fe.cw + lx - 1]
6052 } else {
6053 0
6054 };
6055
6056 let w4 = fe.mb_w * 4;
6057
6058 let mut i16_mode = I16Mode::Dc;
6060 let mut best_pred = i16_pred(fe, I16Mode::Dc, avail_top, avail_left, &top, &left, corner, lx, ly);
6061 let mut best_cost = satd_16x16(sy, fe.cw, lx, ly, &best_pred);
6062 for mode in [I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
6063 if !mode.available(avail_top, avail_left) {
6064 continue;
6065 }
6066 let pred = i16_pred(fe, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
6067 let cost = satd_16x16(sy, fe.cw, lx, ly, &pred);
6068 if cost < best_cost {
6069 best_cost = cost;
6070 i16_mode = mode;
6071 best_pred = pred;
6072 }
6073 }
6074 let mut dc4x4 = [0i32; 16];
6077 let mut i16_q = [[0i32; 16]; 16];
6078 #[cfg(accel)]
6083 let (i16_dc_levels, _i16_recon_dc, recon16) = {
6084 #[repr(align(16))]
6085 struct A([i16; 256]);
6086 let mut dct = A([0i16; 256]);
6087 let base = ly * fe.cw + lx;
6088 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
6089 rusty_h264_accel::dct_four_t4(
6090 &mut dct.0[qi * 64..qi * 64 + 64],
6091 &sy[base + qy * fe.cw + qx..],
6092 fe.cw,
6093 &best_pred[qy * 16 + qx..],
6094 16,
6095 );
6096 }
6097 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6098 dc4x4[lby * 4 + lbx] = dct.0[blk * 16] as i32;
6099 }
6100 if fe.rdoq_strength > 0.0 {
6101 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6105 let coeffs: [i32; 16] = std::array::from_fn(|i| dct.0[blk * 16 + i] as i32);
6106 let mut q = rdoq(&coeffs, qp, fe.idz, fe.rdoq_strength, 1);
6107 q[0] = 0;
6108 i16_q[lby * 4 + lbx] = q;
6109 }
6110 } else {
6111 let ff = rusty_h264_common::transform::quant_dz_ff(qp, fe.idz);
6112 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
6113 for qi in 0..4 {
6114 rusty_h264_accel::quant_four_4x4(&mut dct.0[qi * 64..qi * 64 + 64], &ff, mf);
6115 }
6116 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6117 let q = &mut i16_q[lby * 4 + lbx];
6118 q[0] = 0;
6119 for i in 1..16 {
6120 q[i] = dct.0[blk * 16 + i] as i32;
6121 }
6122 }
6123 }
6124 let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
6125 let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
6126 let mut recon16 = [0u8; 256];
6129 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6130 let mut deq = dequantize(&i16_q[lby * 4 + lbx], qp);
6131 deq[0] = i16_recon_dc[lby * 4 + lbx];
6132 for i in 0..16 {
6133 dct.0[blk * 16 + i] = deq[i] as i16;
6134 }
6135 }
6136 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
6137 rusty_h264_accel::idct_four_t4_rec(
6138 &mut recon16[qy * 16 + qx..],
6139 16,
6140 &best_pred[qy * 16 + qx..],
6141 16,
6142 &dct.0[qi * 64..qi * 64 + 64],
6143 );
6144 }
6145 (i16_dc_levels, i16_recon_dc, recon16)
6146 };
6147 #[cfg(not(accel))]
6148 let (i16_dc_levels, _i16_recon_dc, recon16) = {
6149 let mut res_blocks = [[0i32; 16]; 16];
6150 for by in 0..4 {
6151 for bx in 0..4 {
6152 let predb = pred_block(&best_pred, bx, by);
6153 res_blocks[by * 4 + bx] = residual(sy, fe.cw, lx + bx * 4, ly + by * 4, &predb);
6154 }
6155 }
6156 let mut coeffs = [[0i32; 16]; 16];
6157 forward_dct_blocks(&res_blocks, &mut coeffs);
6158 for i in 0..16 {
6159 dc4x4[i] = coeffs[i][0];
6160 let mut q = rdoq(&coeffs[i], qp, fe.idz, fe.rdoq_strength, 1);
6161 q[0] = 0;
6162 i16_q[i] = q;
6163 }
6164 let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
6165 let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
6166 let mut recon16 = [0u8; 256];
6167 let mut deq_blocks = [[0i32; 16]; 16];
6168 for i in 0..16 {
6169 deq_blocks[i] = dequantize(&i16_q[i], qp);
6170 deq_blocks[i][0] = i16_recon_dc[i];
6171 }
6172 let mut idct = [[0i32; 16]; 16];
6173 inverse_dct_blocks(&deq_blocks, &mut idct);
6174 for by in 0..4 {
6175 for bx in 0..4 {
6176 let s = add_residual_4x4(&idct[by * 4 + bx], &pred_block(&best_pred, bx, by));
6177 for dy in 0..4 {
6178 for dx in 0..4 {
6179 recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)] = s[dy * 4 + dx];
6180 }
6181 }
6182 }
6183 }
6184 (i16_dc_levels, i16_recon_dc, recon16)
6185 };
6186 let i16_cbp15 = i16_q.iter().any(|b| b[1..].iter().any(|&c| c != 0));
6187 let i16_dc_nz = i16_dc_levels.iter().filter(|&&v| v != 0).count() as i64;
6188 let i16_ac_nz: i64 = i16_q
6189 .iter()
6190 .map(|b| b[1..].iter().filter(|&&v| v != 0).count() as i64)
6191 .sum();
6192 let i16_rate = i16_dc_nz + i16_ac_nz + if i16_cbp15 { 16 } else { 0 };
6194 let mut ssd16 = 0i64;
6196 for dy in 0..16 {
6197 for dx in 0..16 {
6198 let d = recon16[dy * 16 + dx] as i64 - sy[(ly + dy) * fe.cw + (lx + dx)] as i64;
6199 ssd16 += d * d;
6200 }
6201 }
6202
6203 let (cx, cy) = (mb_x * 8, mb_y * 8);
6205 let mut ntop = [[0u8; 8]; 2];
6207 let mut nleft = [[0u8; 8]; 2];
6208 let mut ncorner = [0u8; 2];
6209 for c in 0..2 {
6210 let rec_c = if c == 0 { &fe.rec_u } else { &fe.rec_v };
6211 if avail_top {
6212 for i in 0..8 {
6213 ntop[c][i] = rec_c[(cy - 1) * fe.ccw + cx + i];
6214 }
6215 }
6216 if avail_left {
6217 for i in 0..8 {
6218 nleft[c][i] = rec_c[(cy + i) * fe.ccw + cx - 1];
6219 }
6220 }
6221 if avail_top && avail_left {
6222 ncorner[c] = rec_c[(cy - 1) * fe.ccw + cx - 1];
6223 }
6224 }
6225 let mut chroma_mode = 0u8;
6226 let mut best_c_cost = i64::MAX;
6227 for m in 0..4u8 {
6228 if !chroma_mode_available(m, avail_top, avail_left) {
6229 continue;
6230 }
6231 let mut cost = 0i64;
6232 for c in 0..2 {
6233 let src = if c == 0 { su } else { sv };
6234 let pred8 = chroma_pred(fe, m, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy);
6235 cost += satd_8x8(src, fe.ccw, cx, cy, &pred8);
6236 }
6237 if cost < best_c_cost {
6238 best_c_cost = cost;
6239 chroma_mode = m;
6240 }
6241 }
6242
6243 let mut c_dc_levels = [[0i32; 4]; 2];
6244 let mut c_q_blocks = [[[0i32; 16]; 4]; 2];
6245 let mut any_chroma_ac = false;
6246 let mut any_chroma_dc = false;
6247 for c in 0..2 {
6248 let src = if c == 0 { su } else { sv };
6249 let pred8 =
6250 chroma_pred(fe, chroma_mode, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy);
6251 let pblk = |bx: usize, by: usize| -> [i32; 16] {
6252 let mut predb = [0i32; 16];
6253 for dy in 0..4 {
6254 for dx in 0..4 {
6255 predb[dy * 4 + dx] = pred8[(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
6256 }
6257 }
6258 predb
6259 };
6260 let mut dc2x2 = [0i32; 4];
6264 let mut qbs = [[0i32; 16]; 4];
6265 #[cfg(accel)]
6266 let recon_dc = {
6267 #[repr(align(16))]
6268 struct A([i16; 64]);
6269 let mut d = A([0i16; 64]);
6270 rusty_h264_accel::dct_four_t4(&mut d.0, &src[cy * fe.ccw + cx..], fe.ccw, &pred8, 8);
6271 for i in 0..4 {
6272 dc2x2[i] = d.0[i * 16] as i32;
6273 }
6274 if fe.rdoq_strength > 0.0 {
6275 for i in 0..4 {
6277 let coeffs: [i32; 16] = std::array::from_fn(|j| d.0[i * 16 + j] as i32);
6278 let mut q = rdoq(&coeffs, qpc, fe.idz, fe.rdoq_strength, 1);
6279 q[0] = 0;
6280 if q[1..].iter().any(|&v| v != 0) {
6281 any_chroma_ac = true;
6282 }
6283 qbs[i] = q;
6284 }
6285 } else {
6286 let ff = rusty_h264_common::transform::quant_dz_ff(qpc, fe.idz);
6287 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
6288 rusty_h264_accel::quant_four_4x4(&mut d.0, &ff, mf);
6289 for i in 0..4 {
6290 let q = &mut qbs[i];
6291 q[0] = 0;
6292 for j in 1..16 {
6293 let v = d.0[i * 16 + j] as i32;
6294 q[j] = v;
6295 if v != 0 {
6296 any_chroma_ac = true;
6297 }
6298 }
6299 }
6300 }
6301 let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
6302 if dl.iter().any(|&v| v != 0) {
6303 any_chroma_dc = true;
6304 }
6305 let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
6306 for i in 0..4 {
6307 let deq = dequantize(&qbs[i], qpc);
6308 for j in 0..16 {
6309 d.0[i * 16 + j] = deq[j] as i16;
6310 }
6311 d.0[i * 16] = recon_dc[i] as i16;
6312 }
6313 let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
6314 rusty_h264_accel::idct_four_t4_rec(&mut plane[cy * fe.ccw + cx..], fe.ccw, &pred8, 8, &d.0);
6315 c_dc_levels[c] = dl;
6316 recon_dc
6317 };
6318 #[cfg(not(accel))]
6319 let recon_dc = {
6320 let mut res_blocks = [[0i32; 16]; 4];
6321 for by in 0..2 {
6322 for bx in 0..2 {
6323 res_blocks[by * 2 + bx] =
6324 residual(src, fe.ccw, cx + bx * 4, cy + by * 4, &pblk(bx, by));
6325 }
6326 }
6327 let mut coeffs = [[0i32; 16]; 4];
6328 forward_dct_blocks(&res_blocks, &mut coeffs);
6329 for i in 0..4 {
6330 dc2x2[i] = coeffs[i][0];
6331 let mut q = rdoq(&coeffs[i], qpc, fe.idz, fe.rdoq_strength, 1);
6332 q[0] = 0;
6333 qbs[i] = q;
6334 if q[1..].iter().any(|&v| v != 0) {
6335 any_chroma_ac = true;
6336 }
6337 }
6338 let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
6339 if dl.iter().any(|&v| v != 0) {
6340 any_chroma_dc = true;
6341 }
6342 let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
6343 let mut deq_blocks = [[0i32; 16]; 4];
6344 for i in 0..4 {
6345 deq_blocks[i] = dequantize(&qbs[i], qpc);
6346 deq_blocks[i][0] = recon_dc[i];
6347 }
6348 let mut idct = [[0i32; 16]; 4];
6349 inverse_dct_blocks(&deq_blocks, &mut idct);
6350 let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
6351 for by in 0..2 {
6352 for bx in 0..2 {
6353 let s = add_residual_4x4(&idct[by * 2 + bx], &pblk(bx, by));
6354 store(plane, fe.ccw, cx + bx * 4, cy + by * 4, &s);
6355 }
6356 }
6357 c_dc_levels[c] = dl;
6358 recon_dc
6359 };
6360 let _ = recon_dc;
6361 c_q_blocks[c] = qbs;
6362 }
6363 let cbp_chroma: u32 = if any_chroma_ac {
6364 2
6365 } else if any_chroma_dc {
6366 1
6367 } else {
6368 0
6369 };
6370
6371 let base = ly * fe.cw + lx;
6377 let i4 = if i16_rate > 2 {
6378 Some(plan_i4x4(fe, sy, mb_x, mb_y, qp))
6379 } else {
6380 None
6381 };
6382 let (j4, i4_recon) = match &i4 {
6383 Some(p) => {
6384 let mut ssd = 0i64;
6385 let mut rec = [0u8; 256];
6386 for i in 0..256 {
6387 let v = fe.rec_y[base + (i / 16) * fe.cw + i % 16];
6388 rec[i] = v;
6389 let d = v as i64 - sy[base + (i / 16) * fe.cw + i % 16] as i64;
6390 ssd += d * d;
6391 }
6392 (ssd as f64 + lambda * (p.nonzero + 16) as f64, Some(rec))
6393 }
6394 None => (f64::INFINITY, None),
6395 };
6396 let i8 = if fe.transform_8x8 {
6397 Some(plan_i8x8(fe, sy, mb_x, mb_y, qp))
6398 } else {
6399 None
6400 };
6401 let j8 = match &i8 {
6402 Some(p) => {
6403 let mut ssd = 0i64;
6404 for i in 0..256 {
6405 let d = fe.rec_y[base + (i / 16) * fe.cw + i % 16] as i64
6406 - sy[base + (i / 16) * fe.cw + i % 16] as i64;
6407 ssd += d * d;
6408 }
6409 ssd as f64 + lambda * (p.nonzero + 16) as f64
6410 }
6411 None => f64::INFINITY,
6412 };
6413 let j16 = ssd16 as f64 + lambda * i16_rate as f64;
6414
6415 let (use_i4, i4, i8) = if i8.is_some() && j8 <= j4 && j8 <= j16 {
6417 (true, None, i8)
6419 } else if i4.is_some() && j4 < j16 {
6420 let rec = i4_recon.unwrap();
6422 for i in 0..256 {
6423 fe.rec_y[base + (i / 16) * fe.cw + i % 16] = rec[i];
6424 }
6425 let modes = i4.as_ref().unwrap().modes;
6426 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6427 fe.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = modes[lby * 4 + lbx];
6428 }
6429 (true, i4, None)
6430 } else {
6431 for by in 0..4 {
6433 for bx in 0..4 {
6434 for dy in 0..4 {
6435 for dx in 0..4 {
6436 fe.rec_y[(ly + by * 4 + dy) * fe.cw + (lx + bx * 4 + dx)] =
6437 recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)];
6438 }
6439 }
6440 }
6441 }
6442 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6443 fe.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
6444 }
6445 (false, None, None)
6446 };
6447 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6449 fe.coded_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = true;
6450 }
6451
6452 MbPlan {
6453 use_i4,
6454 i16_mode,
6455 i16_cbp15,
6456 i16_dc_levels,
6457 i16_q,
6458 i4,
6459 i8,
6460 chroma_mode,
6461 cbp_chroma,
6462 c_dc_levels,
6463 c_q_blocks,
6464 }
6465}
6466
6467fn encode_mb(
6470 fe: &mut FrameEncoder,
6471 w: &mut BitWriter,
6472 mb_x: usize,
6473 mb_y: usize,
6474 sy: &[u8],
6475 su: &[u8],
6476 sv: &[u8],
6477 is_p: bool,
6478) {
6479 let plan = plan_mb(fe, mb_x, mb_y, sy, su, sv);
6480 let mb_type_offset = if is_p { 5 } else { 0 };
6482 let w4 = fe.mb_w * 4;
6483 let cbp_chroma = plan.cbp_chroma;
6484
6485 if let Some(i8) = plan.i8.as_ref().filter(|_| plan.use_i4) {
6487 let cbp = i8.cbp_luma | (cbp_chroma << 4);
6491 w.write_ue(mb_type_offset); w.write_bit(true); for b8 in 0..4usize {
6494 let (bx, by) = (mb_x * 4 + (b8 % 2) * 2, mb_y * 4 + (b8 / 2) * 2);
6495 let predicted = predict_i4_mode(fe, bx, by);
6496 let actual = i8.modes[b8];
6497 if actual == predicted {
6498 w.write_bit(true);
6499 } else {
6500 w.write_bit(false);
6501 let rem = if actual < predicted { actual } else { actual - 1 };
6502 w.write_bits(rem as u32, 3);
6503 }
6504 }
6505 w.write_ue(plan.chroma_mode as u32); write_cbp_intra(w, cbp);
6507 if cbp != 0 {
6508 w.write_se(fe.qp_delta());
6509 }
6510 fe.nnz_cache_load(mb_x, mb_y);
6511 for b8 in 0..4usize {
6512 let (b8x, b8y) = (b8 % 2, b8 / 2);
6513 let scan8 = scan_8x8_fwd(&i8.q[b8]);
6514 for sub in 0..4usize {
6515 let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
6516 let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
6517 let total = if i8.cbp_luma & (1 << b8) != 0 {
6518 let nc = fe.nc_pred(cx, cy);
6519 let blk: [i32; 16] = std::array::from_fn(|k| scan8[4 * k + sub]);
6520 encode_residual_block(w, &blk, 16, nc) as u8
6521 } else {
6522 0
6523 };
6524 fe.nnz_cache_set(cx, cy, total);
6525 fe.nnz_y[by * w4 + bx] = total;
6526 }
6527 }
6528 } else if plan.use_i4 {
6529 let i4 = plan.i4.as_ref().unwrap();
6530 let cbp = i4.cbp_luma | (cbp_chroma << 4);
6531 w.write_ue(mb_type_offset); if fe.transform_8x8 {
6533 w.write_bit(false); }
6535 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6536 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6537 let predicted = predict_i4_mode(fe, bx, by);
6538 let actual = i4.modes[lby * 4 + lbx];
6539 if actual == predicted {
6540 w.write_bit(true);
6541 } else {
6542 w.write_bit(false);
6543 let rem = if actual < predicted { actual } else { actual - 1 };
6544 w.write_bits(rem as u32, 3);
6545 }
6546 }
6547 w.write_ue(plan.chroma_mode as u32); write_cbp_intra(w, cbp);
6549 if cbp != 0 {
6550 w.write_se(fe.qp_delta()); }
6552 fe.nnz_cache_load(mb_x, mb_y);
6553 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6554 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6555 let total = if i4.cbp_luma & (1 << (blk / 4)) != 0 {
6556 let nc = fe.nc_pred(lbx, lby);
6557 let scan16 = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
6558 encode_residual_block(w, &scan16, 16, nc) as u8
6559 } else {
6560 0
6561 };
6562 fe.nnz_cache_set(lbx, lby, total);
6563 fe.nnz_y[by * w4 + bx] = total;
6564 }
6565 } else {
6566 let mb_type = 1 + plan.i16_mode as u32 + 4 * cbp_chroma + if plan.i16_cbp15 { 12 } else { 0 };
6567 w.write_ue(mb_type + mb_type_offset);
6568 w.write_ue(plan.chroma_mode as u32); w.write_se(fe.qp_delta()); fe.nnz_cache_load(mb_x, mb_y);
6571 let nc_dc = fe.nc_pred(0, 0);
6572 let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
6573 encode_residual_block(w, &dc_scan, 16, nc_dc);
6574 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6575 fe.nnz_cache_set(lbx, lby, 0);
6576 fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
6577 }
6578 if plan.i16_cbp15 {
6579 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6580 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6581 let nc = fe.nc_pred(lbx, lby);
6582 let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
6583 let total = encode_residual_block(w, &ac, 15, nc) as u8;
6584 fe.nnz_cache_set(lbx, lby, total);
6585 fe.nnz_y[by * w4 + bx] = total;
6586 }
6587 }
6588 }
6589
6590 if cbp_chroma != 0 {
6592 for c in 0..2 {
6593 encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
6594 }
6595 }
6596 if cbp_chroma == 2 {
6597 fe.chroma_cache_load(mb_x, mb_y);
6598 let w2 = fe.mb_w * 2;
6599 for c in 0..2 {
6600 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
6601 let nc = fe.chroma_nc_pred(c, bx, by);
6602 let ac = scan_4x4_ac(&plan.c_q_blocks[c][by * 2 + bx]);
6603 let total = encode_residual_block(w, &ac, 15, nc) as u8;
6604 fe.chroma_nnz_cache_set(c, bx, by, total);
6605 fe.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
6606 }
6607 }
6608 }
6609}
6610
6611const CB_NZC_CACHE: [usize; 24] = [
6622 9, 10, 17, 18, 11, 12, 19, 20, 25, 26, 33, 34, 27, 28, 35, 36, 14, 15, 22, 23, 38, 39, 46, 47, ];
6626const CB_RES_MAXPOS: [i32; 11] = [0, 15, 14, 15, 3, 14, 63, 3, 3, 14, 14];
6627const CB_RES_MAXC2: [i32; 11] = [0, 4, 4, 4, 3, 4, 4, 3, 3, 4, 4];
6628const CB_RES_CBF: [usize; 11] = [0, 0, 4, 8, 12, 16, 0, 12, 12, 16, 16];
6629const CB_RES_MAP: [usize; 11] = [0, 0, 15, 29, 44, 47, 0, 44, 44, 47, 47];
6630const CB_RES_ONE: [usize; 11] = [0, 0, 10, 20, 30, 39, 0, 30, 30, 39, 39];
6631const CB_RP_I16_DC: usize = 1;
6632const CB_RP_I16_AC: usize = 2;
6633const CB_RP_LUMA_4X4: usize = 3;
6634const CB_RP_CHROMA_DC: usize = 7;
6635const CB_RP_CHROMA_AC: usize = 9;
6636
6637fn cb_unary(cab: &mut CabacEncoder, ctx: usize, off: usize, value: u32) {
6640 if value == 0 {
6641 cab.encode_decision(ctx, 0);
6642 return;
6643 }
6644 cab.encode_decision(ctx, 1);
6645 for _ in 0..value - 1 {
6646 cab.encode_decision(ctx + off, 1);
6647 }
6648 cab.encode_decision(ctx + off, 0);
6649}
6650
6651fn cb_exp_bypass(cab: &mut CabacEncoder, mut k: i32, mut n: u32) {
6653 while n >= (1 << k) {
6654 cab.encode_bypass(1);
6655 n -= 1 << k;
6656 k += 1;
6657 }
6658 cab.encode_bypass(0);
6659 while k > 0 {
6660 k -= 1;
6661 cab.encode_bypass((n >> k) & 1);
6662 }
6663}
6664
6665fn cb_ueg_level(cab: &mut CabacEncoder, ctx: usize, value: u32) {
6668 if value == 0 {
6669 cab.encode_decision(ctx, 0);
6670 return;
6671 }
6672 let ones = value.min(13);
6673 for _ in 0..ones {
6674 cab.encode_decision(ctx, 1);
6675 }
6676 if value < 13 {
6677 cab.encode_decision(ctx, 0);
6678 } else {
6679 cb_exp_bypass(cab, 0, value - 13);
6680 }
6681}
6682
6683fn cb_mb_qp_delta(cab: &mut CabacEncoder, last_delta_qp: &mut i32, delta: i32) {
6685 const O: usize = 60;
6686 let ctx_inc = (*last_delta_qp != 0) as usize;
6687 if delta == 0 {
6688 cab.encode_decision(O + ctx_inc, 0);
6689 } else {
6690 cab.encode_decision(O + ctx_inc, 1);
6691 let code = 2 * delta.unsigned_abs() - (delta > 0) as u32;
6693 cb_unary(cab, O + 2, 1, code - 1);
6694 }
6695 *last_delta_qp = delta;
6696}
6697
6698fn cb_chroma_pred_mode(cab: &mut CabacEncoder, ctx_inc: usize, mode: u8) {
6700 const C: usize = 64;
6701 if mode == 0 {
6702 cab.encode_decision(C + ctx_inc, 0);
6703 return;
6704 }
6705 cab.encode_decision(C + ctx_inc, 1);
6706 if mode == 1 {
6707 cab.encode_decision(C + 3, 0);
6708 } else if mode == 2 {
6709 cab.encode_decision(C + 3, 1);
6710 cab.encode_decision(C + 3, 0);
6711 } else {
6712 cab.encode_decision(C + 3, 1);
6713 cab.encode_decision(C + 3, 1);
6714 }
6715}
6716
6717fn cb_mb_type_i(
6719 cab: &mut CabacEncoder,
6720 ctx_inc: usize,
6721 use_i4: bool,
6722 i16_mode: u32,
6723 cbp_chroma: u32,
6724 cbp_luma15: bool,
6725) {
6726 const O: usize = 3;
6727 if use_i4 {
6728 cab.encode_decision(O + ctx_inc, 0); return;
6730 }
6731 cab.encode_decision(O + ctx_inc, 1);
6732 cab.encode_terminate(false); cab.encode_decision(O + 3, cbp_luma15 as u32);
6734 if cbp_chroma != 0 {
6735 cab.encode_decision(O + 4, 1);
6736 cab.encode_decision(O + 5, (cbp_chroma == 2) as u32);
6737 } else {
6738 cab.encode_decision(O + 4, 0);
6739 }
6740 cab.encode_decision(O + 6, (i16_mode >> 1) & 1);
6741 cab.encode_decision(O + 7, i16_mode & 1);
6742}
6743
6744fn cb_intra4x4_pred_mode(cab: &mut CabacEncoder, predicted: u8, actual: u8) {
6746 const IPR: usize = 68;
6747 if actual == predicted {
6748 cab.encode_decision(IPR, 1);
6749 } else {
6750 cab.encode_decision(IPR, 0);
6751 let rem = if actual < predicted { actual } else { actual - 1 } as u32;
6752 cab.encode_decision(IPR + 1, rem & 1);
6753 cab.encode_decision(IPR + 1, (rem >> 1) & 1);
6754 cab.encode_decision(IPR + 1, (rem >> 2) & 1);
6755 }
6756}
6757
6758fn cb_cbp(cab: &mut CabacEncoder, top: Option<u8>, left: Option<u8>, cbp: u32) {
6760 const CBP: usize = 73;
6761 let t = |m: u32| top.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
6762 let l = |m: u32| left.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
6763 let nb = |x: u32| (x == 0) as u32;
6764 let b0 = cbp & 1;
6765 let b1 = (cbp >> 1) & 1;
6766 let b2 = (cbp >> 2) & 1;
6767 let b3 = (cbp >> 3) & 1;
6768 cab.encode_decision(CBP + (l(1 << 1) + (t(1 << 2) << 1)) as usize, b0);
6769 cab.encode_decision(CBP + (nb(b0) + (t(1 << 3) << 1)) as usize, b1);
6770 cab.encode_decision(CBP + (l(1 << 3) + (nb(b0) << 1)) as usize, b2);
6771 cab.encode_decision(CBP + (nb(b2) + (nb(b1) << 1)) as usize, b3);
6772 let cbp_chroma = cbp >> 4;
6773 let ct = top.map_or(0u32, |c| ((c >> 4) != 0) as u32);
6774 let cl = left.map_or(0u32, |c| ((c >> 4) != 0) as u32);
6775 cab.encode_decision(CBP + 4 + (cl + (ct << 1)) as usize, (cbp_chroma != 0) as u32);
6776 if cbp_chroma != 0 {
6777 let ct2 = top.map_or(0u32, |c| ((c >> 4) == 2) as u32);
6778 let cl2 = left.map_or(0u32, |c| ((c >> 4) == 2) as u32);
6779 cab.encode_decision(CBP + 8 + (cl2 + (ct2 << 1)) as usize, (cbp_chroma == 2) as u32);
6780 }
6781}
6782
6783#[allow(clippy::too_many_arguments)]
6786fn cb_residual(
6787 cab: &mut CabacEncoder,
6788 nzc: &mut [u8; 48],
6789 cbf_dc: &mut u16,
6790 iz: usize,
6791 rp: usize,
6792 is_intra: bool,
6793 ndc: (Option<u16>, Option<u16>),
6794 coeffs: &[i32],
6795) -> u32 {
6796 let is_dc = rp == CB_RP_I16_DC || rp == CB_RP_CHROMA_DC || rp == CB_RP_CHROMA_DC + 1;
6797 let (mut na, mut nb) = (is_intra as u8, is_intra as u8);
6798 let scan = CB_NZC_CACHE[iz.min(23)];
6799 if is_dc {
6800 if let Some(t) = ndc.0 {
6801 nb = ((t >> rp) & 1) as u8;
6802 }
6803 if let Some(l) = ndc.1 {
6804 na = ((l >> rp) & 1) as u8;
6805 }
6806 } else {
6807 if nzc[scan - 8] != 0xff {
6808 nb = (nzc[scan - 8] != 0) as u8;
6809 }
6810 if nzc[scan - 1] != 0xff {
6811 na = (nzc[scan - 1] != 0) as u8;
6812 }
6813 }
6814 let maxpos = CB_RES_MAXPOS[rp] as usize;
6815 let coeff_num = coeffs[..=maxpos].iter().filter(|&&c| c != 0).count() as u32;
6816 let cbf = coeff_num != 0;
6817 cab.encode_decision(85 + CB_RES_CBF[rp] + (na + (nb << 1)) as usize, cbf as u32);
6818 if !cbf {
6819 if !is_dc {
6820 nzc[scan] = 0;
6821 }
6822 return 0;
6823 }
6824 if is_dc {
6825 *cbf_dc |= 1 << rp;
6826 }
6827 let map = 105 + CB_RES_MAP[rp];
6829 let last = 166 + CB_RES_MAP[rp];
6830 let lastnz = (0..=maxpos).rev().find(|&i| coeffs[i] != 0).unwrap();
6831 for i in 0..maxpos {
6832 let s = coeffs[i] != 0;
6833 cab.encode_decision(map + i, s as u32);
6834 if s {
6835 let is_last = i == lastnz;
6836 cab.encode_decision(last + i, is_last as u32);
6837 if is_last {
6838 break;
6839 }
6840 }
6841 }
6842 let one = 227 + CB_RES_ONE[rp];
6844 let abs = 232 + CB_RES_ONE[rp];
6845 let maxc2 = CB_RES_MAXC2[rp];
6846 let (mut c1, mut c2) = (1i32, 0i32);
6847 for i in (0..=maxpos).rev() {
6848 if coeffs[i] != 0 {
6849 let av = coeffs[i].unsigned_abs();
6850 let gt1 = av > 1;
6851 cab.encode_decision(one + c1 as usize, gt1 as u32);
6852 if gt1 {
6853 cb_ueg_level(cab, abs + c2 as usize, av - 2);
6854 c2 = (c2 + 1).min(maxc2);
6855 c1 = 0;
6856 } else if c1 != 0 {
6857 c1 = (c1 + 1).min(4);
6858 }
6859 cab.encode_bypass((coeffs[i] < 0) as u32);
6860 }
6861 }
6862 if !is_dc {
6863 nzc[scan] = coeff_num as u8;
6864 }
6865 coeff_num
6866}
6867
6868fn cb_build_nzc(mb_nzc: &[[u8; 24]], top: Option<usize>, left: Option<usize>) -> [u8; 48] {
6871 let mut nzc = [0xffu8; 48];
6872 if let Some(t) = top {
6873 let tn = mb_nzc[t];
6874 nzc[1..5].copy_from_slice(&tn[12..16]);
6875 (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
6876 (nzc[6], nzc[7]) = (tn[20], tn[21]);
6877 (nzc[30], nzc[31]) = (tn[22], tn[23]);
6878 }
6879 if let Some(l) = left {
6880 let ln = mb_nzc[l];
6881 (nzc[8], nzc[16], nzc[24], nzc[32]) = (ln[3], ln[7], ln[11], ln[15]);
6882 (nzc[13], nzc[21], nzc[37], nzc[45]) = (ln[17], ln[21], ln[19], ln[23]);
6883 }
6884 nzc
6885}
6886
6887fn cb_export_nzc(nzc: &[u8; 48]) -> [u8; 24] {
6889 let mut mn = [0u8; 24];
6890 for k in 0..4 {
6891 mn[k] = nzc[9 + k];
6892 mn[4 + k] = nzc[17 + k];
6893 mn[8 + k] = nzc[25 + k];
6894 mn[12 + k] = nzc[33 + k];
6895 }
6896 (mn[16], mn[17], mn[20], mn[21]) = (nzc[14], nzc[15], nzc[22], nzc[23]);
6897 (mn[18], mn[19], mn[22], mn[23]) = (nzc[38], nzc[39], nzc[46], nzc[47]);
6898 for v in mn.iter_mut() {
6899 if *v == 0xff {
6900 *v = 0;
6901 }
6902 }
6903 mn
6904}
6905
6906struct CabacState {
6909 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,
6922}
6923
6924impl CabacState {
6925 fn new(n: usize) -> Self {
6926 CabacState {
6927 cat: vec![0; n],
6928 cmode: vec![0; n],
6929 mb_cbp: vec![0; n],
6930 cbf_dc: vec![0; n],
6931 mb_nzc: vec![[0u8; 24]; n],
6932 mb_mvd: vec![[[0i16; 2]; 16]; n],
6933 mb_ref: vec![[-1i8; 16]; n],
6934 mb_mvd1: vec![[[0i16; 2]; 16]; n],
6935 mb_ref1: vec![[-1i8; 16]; n],
6936 mb_skip: vec![false; n],
6937 mb_direct: vec![false; n],
6938 last_delta_qp: 0,
6939 }
6940 }
6941}
6942
6943fn emit_mb_cabac_i(
6947 fe: &mut FrameEncoder,
6948 cab: &mut CabacEncoder,
6949 cs: &mut CabacState,
6950 plan: &MbPlan,
6951 mb_x: usize,
6952 mb_y: usize,
6953) {
6954 let mb_w = fe.mb_w;
6955 let addr = mb_y * mb_w + mb_x;
6956 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
6957 let left = if mb_x > 0 { Some(addr - 1) } else { None };
6958
6959 let li = left.map_or(0, |a| (cs.cat[a] >= 2) as usize);
6961 let ti = top.map_or(0, |a| (cs.cat[a] >= 2) as usize);
6962 let acct = crate::bitacct::enabled();
6963 let t0 = if acct { cab.pos() } else { 0 };
6964 if plan.use_i4 {
6965 cb_mb_type_i(cab, li + ti, true, 0, 0, false);
6966 } else {
6967 cb_mb_type_i(cab, li + ti, false, plan.i16_mode as u32, plan.cbp_chroma, plan.i16_cbp15);
6968 }
6969 if acct {
6970 crate::bitacct::add(crate::bitacct::B::MbType, cab.pos() - t0);
6971 }
6972 let t1 = if acct { cab.pos() } else { 0 };
6973 emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
6974 if acct {
6975 crate::bitacct::add(crate::bitacct::B::IntraBody, cab.pos() - t1);
6976 }
6977}
6978
6979#[allow(clippy::too_many_arguments)]
6983fn emit_intra_body_cabac(
6984 fe: &mut FrameEncoder,
6985 cab: &mut CabacEncoder,
6986 cs: &mut CabacState,
6987 plan: &MbPlan,
6988 mb_x: usize,
6989 mb_y: usize,
6990 addr: usize,
6991 top: Option<usize>,
6992 left: Option<usize>,
6993) {
6994 let w4 = fe.mb_w * 4;
6995 let cbp_chroma = plan.cbp_chroma;
6996 let cci = left.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize)
6998 + top.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize);
6999
7000 let mut nzc;
7001 let mut cbfdc = 0u16;
7002 let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
7003
7004 if !plan.use_i4 {
7005 cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
7007 cs.cmode[addr] = plan.chroma_mode as i32;
7008 cs.cat[addr] = 2;
7009 cs.mb_cbp[addr] = ((cbp_chroma as u8) << 4) | if plan.i16_cbp15 { 15 } else { 0 };
7010 nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7011
7012 let delta = fe.qp_delta();
7013 cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7014
7015 let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
7017 cb_residual(cab, &mut nzc, &mut cbfdc, 0, CB_RP_I16_DC, true, ndc, &dc_scan);
7018 for (iz, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
7020 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7021 let total = if plan.i16_cbp15 {
7022 let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
7023 cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_I16_AC, true, ndc, &ac)
7024 } else {
7025 nzc[CB_NZC_CACHE[iz]] = 0;
7026 0
7027 };
7028 fe.nnz_y[by * w4 + bx] = total as u8;
7029 }
7030 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);
7031 } else {
7032 let i4 = plan.i4.as_ref().unwrap();
7034 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7035 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7036 let predicted = predict_i4_mode(fe, bx, by);
7037 cb_intra4x4_pred_mode(cab, predicted, i4.modes[lby * 4 + lbx]);
7038 }
7039 cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
7040 cs.cmode[addr] = plan.chroma_mode as i32;
7041 cs.cat[addr] = 0;
7042 let cbp = i4.cbp_luma | (cbp_chroma << 4);
7043 cb_cbp(cab, top.map(|a| cs.mb_cbp[a]), left.map(|a| cs.mb_cbp[a]), cbp);
7044 cs.mb_cbp[addr] = cbp as u8;
7045 nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7046
7047 if cbp == 0 {
7048 cs.last_delta_qp = 0;
7049 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7050 fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
7051 }
7052 } else {
7053 let delta = fe.qp_delta();
7054 cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7055 for id8 in 0..4usize {
7056 for id4 in 0..4usize {
7057 let iz = id8 * 4 + id4;
7058 let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
7059 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7060 let total = if i4.cbp_luma & (1 << id8) != 0 {
7061 let sc = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
7062 cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_LUMA_4X4, true, ndc, &sc)
7063 } else {
7064 nzc[CB_NZC_CACHE[iz]] = 0;
7065 0
7066 };
7067 fe.nnz_y[by * w4 + bx] = total as u8;
7068 }
7069 }
7070 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);
7071 }
7072 }
7073
7074 cs.cbf_dc[addr] = cbfdc;
7075 cs.mb_nzc[addr] = cb_export_nzc(&nzc);
7076}
7077
7078#[allow(clippy::too_many_arguments)]
7082fn cb_emit_chroma_residual(
7083 cab: &mut CabacEncoder,
7084 fe: &mut FrameEncoder,
7085 nzc: &mut [u8; 48],
7086 cbfdc: &mut u16,
7087 ndc: (Option<u16>, Option<u16>),
7088 is_intra: bool,
7089 cbp_chroma: u32,
7090 c_dc_levels: &[[i32; 4]; 2],
7091 c_q: &[[[i32; 16]; 4]; 2],
7092 mb_x: usize,
7093 mb_y: usize,
7094) {
7095 let w2 = fe.mb_w * 2;
7096 if cbp_chroma >= 1 {
7097 for i in 0..2usize {
7098 cb_residual(cab, nzc, cbfdc, 16 + i * 4, CB_RP_CHROMA_DC + i, is_intra, ndc, &c_dc_levels[i]);
7099 }
7100 }
7101 if cbp_chroma == 2 {
7102 for i in 0..2usize {
7103 for (id4, &(bx, by)) in CHROMA_4X4_SCAN_XY.iter().enumerate() {
7104 let ac = scan_4x4_ac(&c_q[i][by * 2 + bx]);
7105 let total = cb_residual(
7106 cab, nzc, cbfdc, 16 + i * 4 + id4, CB_RP_CHROMA_AC + i, is_intra, ndc, &ac,
7107 );
7108 fe.nnz_c[i][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total as u8;
7109 }
7110 }
7111 }
7112}
7113
7114pub fn encode_slice_data_cabac_intra(
7119 w: &mut BitWriter,
7120 cfg: &EncoderConfig,
7121 frame: &YuvFrame,
7122 qp: u8,
7123 qpo: &[i32],
7124) -> crate::RefFrame {
7125 let mut fe = FrameEncoder::new(cfg);
7126 fe.qp = qp;
7127 fe.qpc = chroma_qp(qp);
7128 fe.cur_qp = qp;
7129 if cfg.cabac_dz_div > 0 {
7130 fe.idz = cfg.cabac_dz_div; }
7132 let (sy, su, sv) = coded_source(cfg, frame);
7133 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
7134 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
7136 let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
7137
7138 fe.rdoq_strength = if cfg.gop_size <= 1 { cfg.cabac_rdoq } else { 0.0 };
7144 let mut cab = CabacEncoder::new(qp as i32, 0, true);
7146 let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
7147 let total = fe.mb_w * fe.mb_h;
7148
7149 for mb_y in 0..fe.mb_h {
7150 for mb_x in 0..fe.mb_w {
7151 let mb_idx = mb_y * fe.mb_w + mb_x;
7152 fe.qp = aq_qp[mb_idx];
7153 fe.qpc = chroma_qp(aq_qp[mb_idx]);
7154 let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
7155 emit_mb_cabac_i(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
7156 mb_qpy[mb_idx] = fe.cur_qp;
7157 {
7159 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7160 cab.encode_terminate(mb_idx + 1 == total);
7161 if crate::bitacct::enabled() {
7162 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
7163 }
7164 }
7165 }
7166 }
7167
7168 while !w.is_byte_aligned() {
7170 w.write_bit(true);
7171 }
7172 for b in cab.into_bytes() {
7173 w.write_bits(b as u32, 8);
7174 }
7175
7176 let ref_id: Vec<i32> = fe.ref_idx_y.iter().map(|&r| if r >= 0 { r } else { i32::MIN }).collect();
7178 let info = rusty_h264_common::deblock::BlockInfo {
7179 inter: &fe.inter_y,
7180 nnz: &fe.nnz_y,
7181 mv: &fe.mv_y,
7182 ref_id: &ref_id,
7183 mv1: &[],
7184 ref_id1: &[],
7185 w4: fe.mb_w * 4,
7186 t8x8: &[],
7187 bs: &[],
7188 };
7189 rusty_h264_common::deblock::filter_frame(
7190 &mut fe.rec_y, &mut fe.rec_u, &mut fe.rec_v, fe.mb_w, fe.mb_h, &mb_qpy, 0, 0, 0, &info,
7191 );
7192 let w4 = fe.mb_w * 4;
7193 crate::RefFrame {
7194 y: fe.rec_y,
7195 u: fe.rec_u,
7196 v: fe.rec_v,
7197 poc: 0,
7198 frame_num: 0,
7199 mv: fe.mv_y,
7200 ref_idx: fe.ref_idx_y,
7201 w4,
7202 hpel: std::sync::OnceLock::new(),
7204 }
7205}
7206
7207const CB_CACHE30: [usize; 16] = [7, 8, 13, 14, 9, 10, 15, 16, 19, 20, 25, 26, 21, 22, 27, 28];
7217const CB_G_SCAN4: [usize; 16] = [0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15];
7219
7220fn cb_ueg_mv(cab: &mut CabacEncoder, base: usize, v: u32) {
7223 const P2C: [usize; 8] = [0, 1, 2, 3, 3, 3, 3, 3];
7224 if v == 0 {
7225 cab.encode_decision(base, 0);
7226 return;
7227 }
7228 cab.encode_decision(base, 1);
7229 if v <= 7 {
7230 let mut count = 1;
7232 for _ in 0..v - 1 {
7233 cab.encode_decision(base + P2C[count], 1);
7234 count += 1;
7235 }
7236 cab.encode_decision(base + P2C[count], 0);
7237 } else {
7238 let mut count = 1;
7240 for _ in 0..7 {
7241 cab.encode_decision(base + P2C[count], 1);
7242 count += 1;
7243 }
7244 let tb = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7245 cb_exp_bypass(cab, 3, v - 8);
7246 if crate::bitacct::enabled() {
7247 crate::bitacct::add(crate::bitacct::B::MvdBypass, cab.pos() - tb);
7248 }
7249 }
7250}
7251
7252fn cb_mvd(cab: &mut CabacEncoder, comp: usize, ctx_inc: usize, d: i32) {
7255 let th = if crate::bitacct::enabled() { cab.pos() } else { u64::MAX };
7256 let base = 40 + comp * 7;
7257 if d == 0 {
7258 cab.encode_decision(base + ctx_inc, 0);
7259 if th != u64::MAX {
7260 crate::bitacct::add_mvd_sample(0, cab.pos() - th);
7261 }
7262 return;
7263 }
7264 cab.encode_decision(base + ctx_inc, 1);
7265 cb_ueg_mv(cab, base + 3, d.unsigned_abs() - 1); let ts = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7267 cab.encode_bypass((d < 0) as u32);
7268 if crate::bitacct::enabled() {
7269 crate::bitacct::add(crate::bitacct::B::MvdSign, cab.pos() - ts);
7270 }
7271 if th != u64::MAX {
7272 crate::bitacct::add_mvd_sample(d.unsigned_abs(), cab.pos() - th);
7273 }
7274}
7275
7276fn cb_mb_skip(cab: &mut CabacEncoder, ctx_inc: usize, skip: bool) {
7278 cab.encode_decision(ctx_inc, skip as u32);
7279}
7280
7281fn cb_ref_idx(cab: &mut CabacEncoder, ctx0: usize, r: u32) {
7285 const B: usize = 54;
7286 let mut v = r;
7287 let mut bin_idx = 0u32;
7288 loop {
7289 let bin = (v > 0) as u32;
7290 let ctx = match bin_idx {
7291 0 => ctx0,
7292 1 => 4,
7293 _ => 5,
7294 };
7295 cab.encode_decision(B + ctx, bin);
7296 if bin == 0 {
7297 break;
7298 }
7299 v -= 1;
7300 bin_idx += 1;
7301 }
7302}
7303
7304fn cb_mb_type_p_inter(cab: &mut CabacEncoder, mode: u8) {
7307 const S: usize = 11;
7308 cab.encode_decision(S + 3, 0); match mode {
7310 0 => {
7311 cab.encode_decision(S + 4, 0);
7312 cab.encode_decision(S + 5, 0);
7313 }
7314 3 => {
7315 cab.encode_decision(S + 4, 0);
7317 cab.encode_decision(S + 5, 1);
7318 }
7319 1 => {
7320 cab.encode_decision(S + 4, 1);
7321 cab.encode_decision(S + 6, 1);
7322 }
7323 _ => {
7324 cab.encode_decision(S + 4, 1);
7326 cab.encode_decision(S + 6, 0);
7327 }
7328 }
7329}
7330
7331fn cb_sub_mb_type_p(cab: &mut CabacEncoder, sub_type: u8) {
7334 const S: usize = 21;
7335 match sub_type {
7336 0 => cab.encode_decision(S, 1),
7337 _ => unreachable!("only 8x8 sub_mb_type (0) emitted"),
7338 }
7339}
7340
7341fn cb_mb_type_p_intra(cab: &mut CabacEncoder, plan: &MbPlan) {
7345 const S: usize = 11;
7346 cab.encode_decision(S + 3, 1); if plan.use_i4 {
7348 cab.encode_decision(S + 6, 0); return;
7350 }
7351 cab.encode_decision(S + 6, 1); cab.encode_terminate(false); cab.encode_decision(S + 7, plan.i16_cbp15 as u32);
7354 if plan.cbp_chroma != 0 {
7355 cab.encode_decision(S + 8, 1);
7356 cab.encode_decision(S + 8, (plan.cbp_chroma == 2) as u32);
7357 } else {
7358 cab.encode_decision(S + 8, 0);
7359 }
7360 cab.encode_decision(S + 9, (plan.i16_mode as u32 >> 1) & 1);
7361 cab.encode_decision(S + 9, plan.i16_mode as u32 & 1);
7362}
7363
7364fn p_partition_layout(mode: u8) -> &'static [(usize, &'static [usize])] {
7368 match mode {
7369 1 => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7]), (8, &[8, 9, 10, 11, 12, 13, 14, 15])],
7370 2 => &[(0, &[0, 1, 2, 3, 8, 9, 10, 11]), (4, &[4, 5, 6, 7, 12, 13, 14, 15])],
7371 3 => &[(0, &[0, 1, 2, 3]), (4, &[4, 5, 6, 7]), (8, &[8, 9, 10, 11]), (12, &[12, 13, 14, 15])],
7373 _ => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])],
7374 }
7375}
7376
7377#[allow(clippy::too_many_arguments)]
7380fn cb_emit_mvd_partition(
7381 cab: &mut CabacEncoder,
7382 part_idx: usize,
7383 zblocks: &[usize],
7384 mvdc: &mut [[i16; 2]; 30],
7385 refc: &mut [i8; 30],
7386 mmvd: &mut [[i16; 2]; 16],
7387 mref: &mut [i8; 16],
7388 mvd: (i32, i32),
7389 ref_idx: i8, ) {
7391 let s = CB_CACHE30[part_idx];
7392 let ctx = |comp: usize| -> usize {
7393 let mut a = 0i32;
7394 if refc[s - 6] >= 0 {
7395 a += mvdc[s - 6][comp].unsigned_abs() as i32;
7396 }
7397 if refc[s - 1] >= 0 {
7398 a += mvdc[s - 1][comp].unsigned_abs() as i32;
7399 }
7400 if a >= 3 {
7401 1 + (a > 32) as usize
7402 } else {
7403 0
7404 }
7405 };
7406 cb_mvd(cab, 0, ctx(0), mvd.0);
7407 cb_mvd(cab, 1, ctx(1), mvd.1);
7408 let (mx, my) = (mvd.0 as i16, mvd.1 as i16);
7409 for &zb in zblocks {
7410 mvdc[CB_CACHE30[zb]] = [mx, my];
7411 refc[CB_CACHE30[zb]] = ref_idx;
7412 mmvd[CB_G_SCAN4[zb]] = [mx, my];
7413 mref[CB_G_SCAN4[zb]] = ref_idx;
7414 }
7415}
7416
7417fn emit_mb_cabac_p_inter(
7420 fe: &mut FrameEncoder,
7421 cab: &mut CabacEncoder,
7422 cs: &mut CabacState,
7423 mode: u8,
7424 plan: &InterPlan,
7425 mb_x: usize,
7426 mb_y: usize,
7427 num_refs: usize,
7428) {
7429 let mb_w = fe.mb_w;
7430 let addr = mb_y * mb_w + mb_x;
7431 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7432 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7433
7434 let acct = crate::bitacct::enabled();
7437 let mut t0 = if acct { cab.pos() } else { 0 };
7438 cb_mb_type_p_inter(cab, mode);
7439 if mode == 3 {
7441 for _ in 0..4 {
7442 cb_sub_mb_type_p(cab, 0);
7443 }
7444 }
7445 if acct {
7446 crate::bitacct::add(crate::bitacct::B::MbType, cab.pos() - t0);
7447 t0 = cab.pos();
7448 }
7449
7450 let mut mvdc = [[0i16; 2]; 30];
7452 let mut refc = [-1i8; 30];
7453 cb_fill_inter_cache(&cs.mb_ref, &cs.mb_mvd, &mut refc, &mut mvdc, top, left, addr, mb_w);
7454 let mut mmvd = [[0i16; 2]; 16];
7455 let mut mref = [0i8; 16];
7456 let layout = p_partition_layout(mode);
7457 if num_refs > 1 {
7460 for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
7461 let r = plan.plan_refs[part];
7462 let s = CB_CACHE30[part_idx];
7463 let ctx0 = (refc[s - 1] > 0) as usize + 2 * (refc[s - 6] > 0) as usize;
7464 cb_ref_idx(cab, ctx0, r as u32);
7465 for &zb in zblocks {
7466 refc[CB_CACHE30[zb]] = r as i8;
7467 }
7468 }
7469 }
7470 if acct {
7471 crate::bitacct::add(crate::bitacct::B::RefIdx, cab.pos() - t0);
7472 t0 = cab.pos();
7473 }
7474 for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
7476 cb_emit_mvd_partition(
7477 cab, part_idx, zblocks, &mut mvdc, &mut refc, &mut mmvd, &mut mref, plan.mvds[part],
7478 plan.plan_refs[part] as i8,
7479 );
7480 }
7481 if acct {
7482 crate::bitacct::add(crate::bitacct::B::Mvd, cab.pos() - t0);
7483 }
7484 cs.mb_mvd[addr] = mmvd;
7485 cs.mb_ref[addr] = mref;
7486 cs.cat[addr] = 100;
7487 cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7488}
7489
7490#[allow(clippy::too_many_arguments)]
7493fn cb_emit_inter_residual(
7494 fe: &mut FrameEncoder,
7495 cab: &mut CabacEncoder,
7496 cs: &mut CabacState,
7497 plan: &InterPlan,
7498 mb_x: usize,
7499 mb_y: usize,
7500 addr: usize,
7501 top: Option<usize>,
7502 left: Option<usize>,
7503) {
7504 let w4 = fe.mb_w * 4;
7505 let cbp = plan.cbp;
7506 let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
7507 let acct = crate::bitacct::enabled();
7508 let mut t0 = if acct { cab.pos() } else { 0 };
7509 cb_cbp(cab, top.map(|a| cs.mb_cbp[a]), left.map(|a| cs.mb_cbp[a]), cbp);
7510 if acct {
7511 crate::bitacct::add(crate::bitacct::B::Cbp, cab.pos() - t0);
7512 }
7513 cs.mb_cbp[addr] = cbp as u8;
7514 let mut nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7515 let mut cbfdc = 0u16;
7516 let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
7517
7518 if cbp == 0 {
7519 cs.last_delta_qp = 0;
7520 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7521 fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
7522 }
7523 } else {
7524 let delta = fe.qp_delta();
7525 if acct { t0 = cab.pos(); }
7526 cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7527 if acct {
7528 crate::bitacct::add(crate::bitacct::B::QpDelta, cab.pos() - t0);
7529 t0 = cab.pos();
7530 }
7531 for id8 in 0..4usize {
7532 for id4 in 0..4usize {
7533 let iz = id8 * 4 + id4;
7534 let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
7535 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7536 let total = if cbp_luma & (1 << id8) != 0 {
7537 let sc = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
7538 cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_LUMA_4X4, false, ndc, &sc)
7539 } else {
7540 nzc[CB_NZC_CACHE[iz]] = 0;
7541 0
7542 };
7543 fe.nnz_y[by * w4 + bx] = total as u8;
7544 }
7545 }
7546 if acct {
7547 crate::bitacct::add(crate::bitacct::B::ResidLuma, cab.pos() - t0);
7548 t0 = cab.pos();
7549 }
7550 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);
7551 if acct {
7552 crate::bitacct::add(crate::bitacct::B::ResidChroma, cab.pos() - t0);
7553 }
7554 }
7555 cs.cbf_dc[addr] = cbfdc;
7556 cs.mb_nzc[addr] = cb_export_nzc(&nzc);
7557}
7558
7559fn emit_mb_cabac_p_intra(
7562 fe: &mut FrameEncoder,
7563 cab: &mut CabacEncoder,
7564 cs: &mut CabacState,
7565 plan: &MbPlan,
7566 mb_x: usize,
7567 mb_y: usize,
7568) {
7569 let mb_w = fe.mb_w;
7570 let addr = mb_y * mb_w + mb_x;
7571 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7572 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7573 let acct = crate::bitacct::enabled();
7574 let t0 = if acct { cab.pos() } else { 0 };
7575 cb_mb_type_p_intra(cab, plan);
7576 emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7577 if acct {
7578 crate::bitacct::add(crate::bitacct::B::IntraBody, cab.pos() - t0);
7581 }
7582}
7583
7584fn emit_p_skip_cabac(cab: &mut CabacEncoder, cs: &mut CabacState, addr: usize, top: Option<usize>, left: Option<usize>) {
7589 let sctx = 11
7590 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7591 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7592 let t0 = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7593 cb_mb_skip(cab, sctx, true);
7594 if crate::bitacct::enabled() {
7595 crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - t0);
7596 }
7597 cs.mb_skip[addr] = true;
7598 cs.cat[addr] = 100;
7599 cs.last_delta_qp = 0;
7600}
7601
7602pub fn encode_slice_data_cabac_p(
7606 w: &mut BitWriter,
7607 cfg: &EncoderConfig,
7608 frame: &YuvFrame,
7609 qp: u8,
7610 refs: &[crate::RefFrame],
7611 qpo: &[i32],
7612) -> crate::RefFrame {
7613 let mut fe = FrameEncoder::new(cfg);
7614 fe.qp = qp;
7615 fe.qpc = chroma_qp(qp);
7616 fe.cur_qp = qp;
7617 if cfg.cabac_dz_div > 0 {
7618 fe.idz = cfg.cabac_dz_div; }
7620 let (sy, su, sv) = coded_source(cfg, frame);
7621 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
7622 let num_refs = refs.len();
7623 if fe.me_wide && !refs.is_empty() {
7625 let coh = global_mc_residual(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7626 if std::env::var("RFF_ME_COH_DBG").is_ok() {
7627 eprintln!("ME_COH qp{qp} residual={coh:.2}");
7628 }
7629 if coh < fe.me_wide_coh {
7630 fe.me_wide = false;
7631 }
7632 }
7633 if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
7640 let hr = me_wide_headroom(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7641 if me_wide_hr_dbg() {
7642 eprintln!("ME_HR qp{qp} headroom={hr:.2}");
7643 }
7644 if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
7645 fe.me_wide = false;
7646 }
7647 }
7648 if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
7651 let (mg, dc) = b2_mgain(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7652 if me_sadt_dbg() {
7653 eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
7654 }
7655 fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
7656 if mv_smooth_mode() == 1 {
7659 fe.mv_smooth = mg >= mv_smooth_t() && dc <= me_sad_dcmax();
7662 }
7663 let smg = split_mg();
7665 if smg > 0.0 {
7666 fe.do_splits = mg >= smg;
7667 }
7668 }
7669 if fe.satd_q > 0.0 {
7670 let mut vars: Vec<i64> = (0..fe.mb_h)
7671 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
7672 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
7673 .collect();
7674 vars.sort_unstable();
7675 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
7676 fe.satd_var_thresh = vars[idx];
7677 }
7678 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
7679 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
7681 let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
7682
7683 let mut greedy_free = 0usize;
7687 let mut greedy_seen = 0usize;
7688 let mut greedy_on = fe.greedy_min_free == 0;
7689 let greedy_learn = (fe.mb_w * fe.mb_h / 8).max(64);
7690 let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false); let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
7692 let total = fe.mb_w * fe.mb_h;
7693
7694 let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
7697 for mb_y in 0..fe.mb_h {
7698 for mb_x in 0..fe.mb_w {
7699 let mb_idx = mb_y * fe.mb_w + mb_x;
7700 let addr = mb_idx;
7701 let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
7702 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7703 fe.qp = aq_qp[mb_idx];
7704 fe.qpc = chroma_qp(aq_qp[mb_idx]);
7705
7706 let mut inter: Option<InterChoice> = None;
7708 let mut did_skip = false;
7709 if num_refs > 0 {
7710 let mv_skip = fe.skip_mv(mb_x, mb_y);
7711 let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
7712 let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
7713 let skip_c = if luma_free || !fe.fast {
7714 fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
7715 } else {
7716 [[0u8; 64]; 2]
7717 };
7718 let is_free = luma_free && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &skip_c);
7719 let skip_sad = if fe.fast {
7720 0
7721 } else {
7722 let (lx, ly) = (mb_x * 16, mb_y * 16);
7723 let mut s = 0u32;
7724 for dy in 0..16 {
7725 let src = &sy[(ly + dy) * fe.cw + lx..][..16];
7726 let p = &skip_y[dy * 16..][..16];
7727 s += src.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
7728 }
7729 s
7730 };
7731 greedy_seen += 1;
7732 if greedy_seen >= greedy_learn {
7733 greedy_on = fe.greedy_min_free == 0
7734 || greedy_free * 100 >= greedy_seen * fe.greedy_min_free as usize;
7735 }
7736 if is_free {
7737 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
7738 if !fe.fast {
7739 fe.mb_was_skip[mb_idx] = true;
7740 fe.mb_skip_sad[mb_idx] = skip_sad;
7741 }
7742 greedy_free += 1;
7743 did_skip = true;
7744 } else {
7745 let (lx, ly) = (mb_x * 16, mb_y * 16);
7746 let nb = {
7747 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMvPred);
7748 fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
7749 };
7750 let lme = lambda.sqrt() * cfg.cabac_lambda_scale;
7751 if fe.fast {
7752 fe.mb_use_satd = fe.satd_q > 0.0
7753 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
7754 let (r16, mv16, cost_inter) =
7755 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
7756 let cost_intra = if fe.mb_use_satd {
7757 fe.best_i16_satd(&sy, mb_x, mb_y)
7758 } else {
7759 fe.best_i16_sad(&sy, mb_x, mb_y)
7760 } + (lme * fe.tune_intra_penalty) as i64;
7761 inter = if cost_intra < cost_inter {
7762 None
7763 } else {
7764 Some((0, vec![(r16, mv16)]))
7765 };
7766 } else {
7767 if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
7769 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
7770 fe.mb_was_skip[mb_idx] = true;
7771 fe.mb_skip_sad[mb_idx] = skip_sad;
7772 did_skip = true;
7773 } else {
7774 let (r16, mv16, c16) =
7775 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
7776 let mut best_c = c16;
7777 let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
7778 const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
7779 let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
7780 let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
7781 let split_t = split_t();
7782 if fe.do_splits && c16 > split_gate && (split_t <= 0.0 || (c16 as f64) >= split_t * lme) {
7783 let (rt, mvt, ct) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 8, &[mv16], lme);
7784 let (rb, mvb, cb) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly + 8, 16, 8, &[mv16], lme);
7785 let (rl, mvl, cl) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 8, 16, &[mv16], lme);
7786 let (rr, mvr, cr) = fe.best_part(refs, &sy, &nb, num_refs, lx + 8, ly, 8, 16, &[mv16], lme);
7787 if ct + cb < best_c {
7788 best_c = ct + cb;
7789 pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
7790 }
7791 if cl + cr < best_c {
7792 best_c = cl + cr;
7793 pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
7794 }
7795 if fe.sub8x8 {
7797 let mut c8 = (lme * 4.0) as i64;
7798 let mut p8 = Vec::with_capacity(4);
7799 for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
7800 let (r, mv, c) = fe.best_part(
7801 refs, &sy, &nb, num_refs, lx + qx, ly + qy, 8, 8, &[mv16], lme,
7802 );
7803 c8 += c;
7804 p8.push((r, mv));
7805 }
7806 if c8 < best_c {
7807 best_c = c8;
7808 pick = Some((3u8, p8));
7809 }
7810 }
7811 }
7812 if fe.sp_defer.get() {
7817 if let Some((mode, parts)) = pick.as_mut() {
7818 let regions: &[(usize, usize, usize, usize)] = match mode {
7819 1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
7820 2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
7821 3 => &[(0, 0, 8, 8), (8, 0, 8, 8), (0, 8, 8, 8), (8, 8, 8, 8)],
7822 _ => &[(0, 0, 16, 16)],
7823 };
7824 let mut tot = if *mode == 3 { (lme * 4.0) as i64 } else { 0 };
7825 for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
7826 let (r, mv) = parts[i];
7827 let (m2, c2) = fe.refine_part(
7828 refs, &sy, &nb, num_refs, lx + qx, ly + qy, pw, ph, lme, r, mv,
7829 );
7830 parts[i] = (r, m2);
7831 tot += c2;
7832 }
7833 best_c = tot;
7834 }
7835 }
7836 let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
7837 + (lme * fe.tune_intra_penalty) as i64;
7838 inter = if c_intra < best_c { None } else { pick };
7839 fe.mb_was_skip[mb_idx] = false;
7840 fe.mb_skip_sad[mb_idx] = skip_sad;
7841 }
7842 }
7843 }
7844 }
7845
7846 if did_skip {
7848 emit_p_skip_cabac(&mut cab, &mut cs, addr, top, left);
7849 mb_qpy[mb_idx] = fe.cur_qp;
7850 {
7851 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7852 {
7853 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7854 cab.encode_terminate(mb_idx + 1 == total);
7855 if crate::bitacct::enabled() {
7856 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
7857 }
7858 }
7859 if crate::bitacct::enabled() {
7860 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
7861 }
7862 }
7863 continue;
7864 }
7865 let sctx = 11
7867 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7868 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7869 let tskip = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7870 cb_mb_skip(&mut cab, sctx, false);
7871 if crate::bitacct::enabled() {
7872 crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - tskip);
7873 }
7874 cs.mb_skip[addr] = false;
7875 match inter {
7876 Some((mode, parts)) => {
7877 let plan = fe.plan_inter_mb(refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts, None);
7878 let _ge = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
7882 emit_mb_cabac_p_inter(&mut fe, &mut cab, &mut cs, mode, &plan, mb_x, mb_y, num_refs);
7883 }
7884 None => {
7885 let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
7886 let _ge = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
7887 emit_mb_cabac_p_intra(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
7888 }
7889 }
7890 mb_qpy[mb_idx] = fe.cur_qp;
7891 {
7892 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7893 cab.encode_terminate(mb_idx + 1 == total);
7894 if crate::bitacct::enabled() {
7895 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
7896 }
7897 }
7898 }
7899 }
7900
7901 while !w.is_byte_aligned() {
7902 w.write_bit(true);
7903 }
7904 for b in cab.into_bytes() {
7905 w.write_bits(b as u32, 8);
7906 }
7907
7908 let ref_id: Vec<i32> = fe.ref_idx_y.iter().map(|&r| if r >= 0 { r } else { i32::MIN }).collect();
7910 let info = rusty_h264_common::deblock::BlockInfo {
7911 inter: &fe.inter_y,
7912 nnz: &fe.nnz_y,
7913 mv: &fe.mv_y,
7914 ref_id: &ref_id,
7915 mv1: &[],
7916 ref_id1: &[],
7917 w4: fe.mb_w * 4,
7918 t8x8: &[],
7919 bs: &[],
7920 };
7921 rusty_h264_common::deblock::filter_frame(
7922 &mut fe.rec_y, &mut fe.rec_u, &mut fe.rec_v, fe.mb_w, fe.mb_h, &mb_qpy, 0, 0, 0, &info,
7923 );
7924 let w4 = fe.mb_w * 4;
7925 crate::RefFrame {
7926 y: fe.rec_y,
7927 u: fe.rec_u,
7928 v: fe.rec_v,
7929 poc: 0,
7930 frame_num: 0,
7931 mv: fe.mv_y,
7932 ref_idx: fe.ref_idx_y,
7933 w4,
7934 hpel: std::sync::OnceLock::new(),
7936 }
7937}
7938
7939fn cb_fill_inter_cache(
7949 mb_ref: &[[i8; 16]],
7950 mb_mvd: &[[[i16; 2]; 16]],
7951 refc: &mut [i8; 30],
7952 mvdc: &mut [[i16; 2]; 30],
7953 top: Option<usize>,
7954 left: Option<usize>,
7955 addr: usize,
7956 mb_w: usize,
7957) {
7958 if let Some(l) = left {
7959 for (ci, bi) in [(6usize, 3usize), (12, 7), (18, 11), (24, 15)] {
7960 refc[ci] = mb_ref[l][bi];
7961 mvdc[ci] = mb_mvd[l][bi];
7962 }
7963 }
7964 if let Some(t) = top {
7965 for (ci, bi) in [(1usize, 12usize), (2, 13), (3, 14), (4, 15)] {
7966 refc[ci] = mb_ref[t][bi];
7967 mvdc[ci] = mb_mvd[t][bi];
7968 }
7969 }
7970 let mb_x = addr % mb_w;
7971 let mb_y = addr / mb_w;
7972 if mb_x > 0 && mb_y > 0 {
7973 let a = addr - mb_w - 1;
7974 (refc[0], mvdc[0]) = (mb_ref[a][15], mb_mvd[a][15]);
7975 }
7976 if mb_y > 0 && mb_x + 1 < mb_w {
7977 let a = addr - mb_w + 1;
7978 (refc[5], mvdc[5]) = (mb_ref[a][12], mb_mvd[a][12]);
7979 }
7980}
7981
7982fn cb_mb_type_b(cab: &mut CabacEncoder, ctx_inc: usize, dir: u8) {
7985 const B: usize = 27;
7986 match dir {
7987 0 => cab.encode_decision(B + ctx_inc, 0), 1 => {
7989 cab.encode_decision(B + ctx_inc, 1);
7990 cab.encode_decision(B + 3, 0);
7991 cab.encode_decision(B + 5, 0); }
7993 2 => {
7994 cab.encode_decision(B + ctx_inc, 1);
7995 cab.encode_decision(B + 3, 0);
7996 cab.encode_decision(B + 5, 1); }
7998 _ => {
7999 cab.encode_decision(B + ctx_inc, 1);
8001 cab.encode_decision(B + 3, 1);
8002 cab.encode_decision(B + 4, 0);
8003 cab.encode_decision(B + 5, 0);
8004 cab.encode_decision(B + 5, 0);
8005 cab.encode_decision(B + 5, 0);
8006 }
8007 }
8008}
8009
8010const CB_ALL16: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
8011
8012fn emit_mb_cabac_b(
8015 fe: &mut FrameEncoder,
8016 cab: &mut CabacEncoder,
8017 cs: &mut CabacState,
8018 dir: u8,
8019 plan: &InterPlan,
8020 mb_x: usize,
8021 mb_y: usize,
8022) {
8023 let mb_w = fe.mb_w;
8024 let addr = mb_y * mb_w + mb_x;
8025 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
8026 let left = if mb_x > 0 { Some(addr - 1) } else { None };
8027
8028 let bci = left.map_or(0, |a| (!cs.mb_direct[a]) as usize)
8029 + top.map_or(0, |a| (!cs.mb_direct[a]) as usize);
8030 cb_mb_type_b(cab, bci, dir);
8031
8032 let mut mvdc0 = [[0i16; 2]; 30];
8034 let mut refc0 = [-1i8; 30];
8035 let mut mvdc1 = [[0i16; 2]; 30];
8036 let mut refc1 = [-1i8; 30];
8037 cb_fill_inter_cache(&cs.mb_ref, &cs.mb_mvd, &mut refc0, &mut mvdc0, top, left, addr, mb_w);
8038 cb_fill_inter_cache(&cs.mb_ref1, &cs.mb_mvd1, &mut refc1, &mut mvdc1, top, left, addr, mb_w);
8039 let mut mmvd0 = [[0i16; 2]; 16];
8040 let mut mref0 = [-1i8; 16];
8041 let mut mmvd1 = [[0i16; 2]; 16];
8042 let mut mref1 = [-1i8; 16];
8043 let (use0, use1) = (dir == 1 || dir == 3, dir == 2 || dir == 3);
8044 if dir == 0 {
8045 mref0 = [0i8; 16];
8048 mref1 = [0i8; 16];
8049 } else {
8050 let mut k = 0;
8052 if use0 {
8053 cb_emit_mvd_partition(cab, 0, &CB_ALL16, &mut mvdc0, &mut refc0, &mut mmvd0, &mut mref0, plan.mvds[k], 0);
8054 k += 1;
8055 }
8056 if use1 {
8057 cb_emit_mvd_partition(cab, 0, &CB_ALL16, &mut mvdc1, &mut refc1, &mut mmvd1, &mut mref1, plan.mvds[k], 0);
8058 }
8059 }
8060 cs.mb_mvd[addr] = mmvd0;
8061 cs.mb_ref[addr] = mref0;
8062 cs.mb_mvd1[addr] = mmvd1;
8063 cs.mb_ref1[addr] = mref1;
8064 cs.mb_direct[addr] = dir == 0;
8065 cs.cat[addr] = 100;
8066 cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
8067}
8068
8069fn emit_b_skip_cabac(cab: &mut CabacEncoder, cs: &mut CabacState, addr: usize, top: Option<usize>, left: Option<usize>) {
8073 let sctx = 24
8074 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
8075 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
8076 let t0 = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8077 cb_mb_skip(cab, sctx, true);
8078 if crate::bitacct::enabled() {
8079 crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - t0);
8080 }
8081 cs.mb_skip[addr] = true;
8082 cs.cat[addr] = 100;
8083 cs.mb_direct[addr] = true;
8084 cs.mb_ref[addr] = [0i8; 16];
8085 cs.mb_ref1[addr] = [0i8; 16];
8086 cs.last_delta_qp = 0;
8087}
8088
8089#[allow(clippy::too_many_arguments)]
8093pub fn encode_slice_data_cabac_b(
8094 w: &mut BitWriter,
8095 cfg: &EncoderConfig,
8096 frame: &YuvFrame,
8097 qp: u8,
8098 poc: i32,
8099 l0: &crate::RefFrame,
8100 l1: &crate::RefFrame,
8101 qpo: &[i32],
8102) {
8103 let mut fe = FrameEncoder::new(cfg);
8104 fe.qp = qp;
8105 fe.qpc = chroma_qp(qp);
8106 fe.cur_qp = qp;
8107 if cfg.cabac_dz_div > 0 {
8108 fe.idz = cfg.cabac_dz_div; }
8110 fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
8111 let (sy, su, sv) = coded_source(cfg, frame);
8112 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
8113 let lme = lambda.sqrt() * cfg.cabac_lambda_scale;
8114 let refs = std::slice::from_ref(l0);
8115 if fe.satd_q > 0.0 {
8116 let mut vars: Vec<i64> = (0..fe.mb_h)
8117 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
8118 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
8119 .collect();
8120 vars.sort_unstable();
8121 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
8122 fe.satd_var_thresh = vars[idx];
8123 }
8124 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
8125 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
8127
8128 let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false);
8129 let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
8130 let total = fe.mb_w * fe.mb_h;
8131
8132 for mb_y in 0..fe.mb_h {
8133 for mb_x in 0..fe.mb_w {
8134 let mb_idx = mb_y * fe.mb_w + mb_x;
8135 let addr = mb_idx;
8136 let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
8137 let left = if mb_x > 0 { Some(addr - 1) } else { None };
8138 fe.qp = aq_qp[mb_idx];
8139 fe.qpc = chroma_qp(aq_qp[mb_idx]);
8140 let (lx, ly) = (mb_x * 16, mb_y * 16);
8141 let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
8142 fe.mb_use_satd =
8143 fe.satd_q > 0.0 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
8144 let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
8145 let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
8146 let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
8147 let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
8148 let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
8149 if fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
8151 && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc)
8152 {
8153 fe.commit_direct_motion(mb_x, mb_y, &dmotion);
8154 emit_b_skip_cabac(&mut cab, &mut cs, addr, top, left);
8155 {
8156 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8157 {
8158 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8159 cab.encode_terminate(mb_idx + 1 == total);
8160 if crate::bitacct::enabled() {
8161 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8162 }
8163 }
8164 if crate::bitacct::enabled() {
8165 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8166 }
8167 }
8168 continue;
8169 }
8170 let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
8171 let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
8172 let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
8173 let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
8174 let r_bi = mvd_bits(mv0.0 - pmv0.0) + mvd_bits(mv0.1 - pmv0.1)
8175 + mvd_bits(mv1.0 - pmv1.0) + mvd_bits(mv1.1 - pmv1.1);
8176 let j_bi = d_bi + (lme * r_bi as f64) as i64;
8177 let (mut dir, mut best) = (0u8, d_direct);
8178 if j0 < best { dir = 1; best = j0; }
8179 if j1 < best { dir = 2; best = j1; }
8180 if j_bi < best { dir = 3; best = j_bi; }
8181 let _ = best;
8182 let sctx = 24
8184 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
8185 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
8186 let tskip = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8187 cb_mb_skip(&mut cab, sctx, false);
8188 if crate::bitacct::enabled() {
8189 crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - tskip);
8190 }
8191 cs.mb_skip[addr] = false;
8192 let bspec = BInter { dir, l1, mv0, mv1 };
8193 let plan = fe.plan_inter_mb(refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
8194 emit_mb_cabac_b(&mut fe, &mut cab, &mut cs, dir, &plan, mb_x, mb_y);
8195 {
8196 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8197 cab.encode_terminate(mb_idx + 1 == total);
8198 if crate::bitacct::enabled() {
8199 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8200 }
8201 }
8202 }
8203 }
8204
8205 while !w.is_byte_aligned() {
8206 w.write_bit(true);
8207 }
8208 for b in cab.into_bytes() {
8209 w.write_bits(b as u32, 8);
8210 }
8211 }
8213
8214pub fn encode_all_skip_b_cabac(w: &mut BitWriter, cfg: &EncoderConfig, qp: u8, n: usize) {
8218 let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false);
8219 for i in 0..n {
8220 cab.encode_decision(24, 1);
8223 cab.encode_terminate(i + 1 == n);
8224 }
8225 while !w.is_byte_aligned() {
8226 w.write_bit(true);
8227 }
8228 for b in cab.into_bytes() {
8229 w.write_bits(b as u32, 8);
8230 }
8231}