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 mvmode: u8,
679 parts2: [(u8, (i32, i32), (i32, i32)); 2],
680}
681
682#[cfg(accel)]
685#[repr(align(16))]
686struct AlignedDct([i16; 256]);
687
688fn mb_variance(sy: &[u8], cw: usize, mb_x: usize, mb_y: usize) -> i64 {
693 let base = mb_y * 16 * cw + mb_x * 16;
694 let (mut s, mut ss) = (0u32, 0u32);
699 for r in 0..16 {
700 let row = &sy[base + r * cw..base + r * cw + 16];
701 for &p in row {
702 let v = p as u32;
703 s += v;
704 ss += v * v;
705 }
706 }
707 ss as i64 - (s as i64) * (s as i64) / 256 }
710
711fn aq_qp_map(sy: &[u8], cw: usize, mb_w: usize, mb_h: usize, base_qp: u8, strength: f64) -> Vec<u8> {
718 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncAq);
719 const AQ_DQP_MAX: i32 = 4;
720 let n = mb_w * mb_h;
721 if strength == 0.0 || n == 0 {
722 return vec![base_qp; n];
723 }
724 let mut var = Vec::with_capacity(n);
727 let mut lv = Vec::with_capacity(n);
728 for my in 0..mb_h {
729 for mx in 0..mb_w {
730 let v = (mb_variance(sy, cw, mx, my) + 1) as f64;
731 var.push(v);
732 lv.push(v.log2());
733 }
734 }
735 let mean_lv = lv.iter().sum::<f64>() / n as f64;
736 const AQ_SPREAD_LO: f64 = 1.5;
743 const AQ_SPREAD_HI: f64 = 5.0;
744 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();
746 let factor = (1.0 - (std_lv - AQ_SPREAD_LO) / (AQ_SPREAD_HI - AQ_SPREAD_LO)).clamp(AQ_SPREAD_MIN, 1.0);
747 let eff_strength = strength * factor;
748 let dqp: Vec<i32> = lv
750 .iter()
751 .map(|&l| (eff_strength * (l - mean_lv)).round() as i32)
752 .map(|d| d.clamp(-AQ_DQP_MAX, AQ_DQP_MAX))
753 .collect();
754 let sum_v: f64 = var.iter().sum();
759 let qstep: [f64; (2 * AQ_DQP_MAX + 1) as usize] =
764 std::array::from_fn(|i| 2f64.powf(-((i as i32 - AQ_DQP_MAX) as f64) / 6.0));
765 let sum_vs: f64 = var
766 .iter()
767 .zip(&dqp)
768 .map(|(&v, &d)| v * qstep[(d + AQ_DQP_MAX) as usize])
769 .sum();
770 let c = (6.0 * (sum_vs / sum_v).log2()).round() as i32;
771 dqp.iter()
772 .map(|&d| (base_qp as i32 + c + d).clamp(0, 51) as u8)
773 .collect()
774}
775
776fn me_wide_hr_thresh() -> f64 {
821 use std::sync::OnceLock;
822 static T: OnceLock<f64> = OnceLock::new();
823 *T.get_or_init(|| std::env::var("RFF_ME_HR").ok().and_then(|s| s.parse().ok()).unwrap_or(16.0))
824}
825
826fn me_wide_hr_dbg() -> bool {
828 use std::sync::OnceLock;
829 static D: OnceLock<bool> = OnceLock::new();
830 *D.get_or_init(|| std::env::var_os("RFF_ME_HR_DBG").is_some())
831}
832
833fn me_wide_headroom(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
834 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> {
839 if rx < 0 || ry < 0 || rx as usize + 16 > cw || ry as usize + 16 > ch {
840 return None;
841 }
842 let (rx, ry) = (rx as usize, ry as usize);
843 let mut s = 0u32;
844 for dy in 0..16 {
845 let a = &sy[(by + dy) * cw + bx..][..16];
846 let b = &ref_y[(ry + dy) * cw + rx..][..16];
847 s += a.iter().zip(b).map(|(&p, &q)| p.abs_diff(q) as u32).sum::<u32>();
848 }
849 Some(s)
850 };
851 let (mbw, mbh) = (cw / 16, ch / 16);
854 if mbw < 6 || mbh < 6 {
855 return 0.0;
856 }
857 let inner = (mbw - 4) * (mbh - 4);
858 let stride = (inner / TARGET).max(1);
859 let (mut acc, mut n) = (0.0f64, 0u32);
860 let mut i = 0usize;
861 while i < inner {
862 let (mx, my) = (2 + i % (mbw - 4), 2 + i / (mbw - 4));
863 let (bx, by) = (mx * 16, my * 16);
864 let mut best_local = u32::MAX;
865 for dy in -LOCAL..=LOCAL {
866 for dx in -LOCAL..=LOCAL {
867 if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
868 best_local = best_local.min(s);
869 }
870 }
871 }
872 let mut best_wide = best_local;
873 let mut dy = -WIDE;
874 while dy <= WIDE {
875 let mut dx = -WIDE;
876 while dx <= WIDE {
877 if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
878 best_wide = best_wide.min(s);
879 }
880 dx += STEP;
881 }
882 dy += STEP;
883 }
884 if best_local > 0 {
885 acc += (best_local - best_wide) as f64 / best_local as f64;
886 n += 1;
887 }
888 i += stride;
889 }
890 if n == 0 {
891 0.0
892 } else {
893 100.0 * acc / n as f64
894 }
895}
896
897fn global_mc_residual(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
898 if cw < 48 || ch < 48 {
899 return f64::INFINITY;
900 }
901 let sad = |dx: isize, dy: isize| -> u64 {
902 let mut s = 0u64;
903 let mut y = 16;
904 while y < ch - 16 {
905 let cbase = (y * cw) as isize;
906 let rbase = (y as isize + dy) * cw as isize + dx;
907 let mut x = 16isize;
908 while x < (cw - 16) as isize {
909 let c = sy[(cbase + x) as usize] as i32;
910 let r = ref_y[(rbase + x) as usize] as i32;
911 s += (c - r).unsigned_abs() as u64;
912 x += 8;
913 }
914 y += 8;
915 }
916 s
917 };
918 let (mut best, mut bc) = ((0isize, 0isize), u64::MAX);
919 let mut dy = -12;
920 while dy <= 12 {
921 let mut dx = -12;
922 while dx <= 12 {
923 let c = sad(dx, dy);
924 if c < bc {
925 bc = c;
926 best = (dx, dy);
927 }
928 dx += 4;
929 }
930 dy += 4;
931 }
932 for dy in best.1 - 3..=best.1 + 3 {
933 for dx in best.0 - 3..=best.0 + 3 {
934 let c = sad(dx, dy);
935 if c < bc {
936 bc = c;
937 }
938 }
939 }
940 let nx = (16..cw - 16).step_by(8).count();
941 let ny = (16..ch - 16).step_by(8).count();
942 bc as f64 / (nx * ny).max(1) as f64
943}
944
945fn apply_mbtree_qpo(aq_qp: &mut [u8], qpo: &[i32]) {
949 if qpo.len() == aq_qp.len() {
950 for (q, &o) in aq_qp.iter_mut().zip(qpo) {
951 *q = (*q as i32 + o).clamp(0, 51) as u8;
952 }
953 }
954}
955
956fn implicit_bi_weights(cur_poc: i32, l0_poc: i32, l1_poc: i32) -> (i32, i32) {
962 let td = (l1_poc - l0_poc).clamp(-128, 127);
963 let tb = (cur_poc - l0_poc).clamp(-128, 127);
964 if td == 0 {
965 return (32, 32);
966 }
967 let tx = (16384 + td.abs() / 2) / td;
968 let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
969 let w1 = dsf >> 2;
970 if !(-64..=128).contains(&w1) {
971 return (32, 32);
972 }
973 (64 - w1, w1)
974}
975
976#[inline(always)]
980fn bi_blend(p: i32, q: i32, w: (i32, i32)) -> u8 {
981 ((p * w.0 + q * w.1 + 32) >> 6).clamp(0, 255) as u8
982}
983
984#[cfg(accel)]
989#[inline]
990fn scan_4x4_dcac_i16(d: &[i16]) -> [i32; 16] {
991 [
992 d[0] as i32, d[1] as i32, d[4] as i32, d[8] as i32, d[5] as i32, d[2] as i32,
993 d[3] as i32, d[6] as i32, d[9] as i32, d[12] as i32, d[13] as i32, d[10] as i32,
994 d[7] as i32, d[11] as i32, d[14] as i32, d[15] as i32,
995 ]
996}
997
998
999pub struct FrameEncoder {
1002 mb_w: usize,
1003 mb_h: usize,
1004 qp: u8, qpc: u8, cur_qp: u8,
1009 bi_w: (i32, i32),
1013 cw: usize, ccw: usize, rec_y: AlignedBytes,
1017 rec_u: AlignedBytes,
1018 rec_v: AlignedBytes,
1019 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)>,
1030 ref_idx1_y: Vec<i32>,
1031 idz: i64, rdoq_strength: f64, transform_8x8: bool, sub8x8: bool, me_wide: bool, sadfp: bool,
1039 mv_smooth: bool,
1043 do_splits: bool,
1045 me_wide_var: u64, me_rescue: i64, me_wide_coh: f64, me_range: i32, me_fast: bool, me_learn: u32,
1060 me_payoff_pct: u32,
1061 sp_single_pass: bool,
1072 sp_defer: std::cell::Cell<bool>,
1078 sp_learn_n: std::cell::Cell<u32>,
1079 sp_ring1: std::cell::Cell<i64>,
1080 sp_total: std::cell::Cell<i64>,
1081 sp_1pass: std::cell::Cell<bool>,
1082 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,
1092 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],
1108 nnz_c_cache: [[u8; 9]; 2],
1110 mb_skip_sad: Vec<u32>,
1116 mb_was_skip: Vec<bool>,
1117}
1118
1119type InterChoice = (u8, Vec<(i32, (i32, i32))>);
1122
1123const SKIP_RATE_BITS: f64 = 1.0;
1126
1127
1128
1129pub static EXT_MV: std::sync::Mutex<Vec<Vec<(i32, i32)>>> = std::sync::Mutex::new(Vec::new());
1134pub static MVCMP: [std::sync::atomic::AtomicU64; 7] = {
1136 const Z: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1137 [Z; 7]
1138};
1139pub static MVCMP_FRAME: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1140fn mv_force_on() -> bool {
1146 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1147 *ON.get_or_init(|| std::env::var("RFF_MV_FORCE").map_or(false, |v| v != "0"))
1148}
1149fn mv_cmp_on() -> bool {
1150 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1151 *ON.get_or_init(|| std::env::var("RFF_MV_CMP").map_or(false, |v| v != "0"))
1152}
1153
1154pub static MC_COUNT: [std::sync::atomic::AtomicU64; 2] = [
1158 std::sync::atomic::AtomicU64::new(0),
1159 std::sync::atomic::AtomicU64::new(0),
1160];
1161fn mc_count_on() -> bool {
1162 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1163 *ON.get_or_init(|| std::env::var("RFF_MC_COUNT").map_or(false, |v| v != "0"))
1164}
1165
1166pub static ME_PROBE: [std::sync::atomic::AtomicU64; 7] = {
1168 const Z: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1169 [Z; 7]
1170};
1171
1172fn me_oracle_on() -> bool {
1174 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1175 *ON.get_or_init(|| std::env::var("RFF_ME_ORACLE").map_or(false, |v| v != "0"))
1176}
1177
1178const SPLIT_GATE_BITS: f64 = 60.0;
1184
1185const FAST_INTRA_PENALTY_BITS: f64 = 24.0;
1189
1190#[derive(Default)]
1198struct MbState {
1199 rec_y: Vec<u8>,
1200 rec_u: Vec<u8>,
1201 rec_v: Vec<u8>,
1202 nnz_y: Vec<u8>,
1203 nnz_c: [Vec<u8>; 2],
1204 mv_y: Vec<(i32, i32)>,
1205 inter_y: Vec<bool>,
1206 ref_idx_y: Vec<i32>,
1207 coded_y: Vec<bool>,
1208 modes_y: Vec<u8>,
1209 cur_qp: u8,
1215}
1216
1217fn fast_intra_enabled() -> bool {
1223 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1224 *ON.get_or_init(|| std::env::var("RUSTY_FAST_INTRA").map_or(true, |v| v != "0"))
1225}
1226
1227fn coded_source(cfg: &EncoderConfig, frame: &YuvFrame) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
1228 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncSource);
1229 let cw = cfg.mb_width() * 16;
1230 let ch = cfg.mb_height() * 16;
1231 if frame.width == cw && frame.height == ch {
1234 return (frame.y.clone(), frame.u.clone(), frame.v.clone());
1235 }
1236 let y = clamp_plane(&frame.y, frame.width, frame.height, cw, ch);
1237 let u = clamp_plane(&frame.u, frame.chroma_width(), frame.chroma_height(), cw / 2, ch / 2);
1238 let v = clamp_plane(&frame.v, frame.chroma_width(), frame.chroma_height(), cw / 2, ch / 2);
1239 (y, u, v)
1240}
1241
1242fn clamp_plane(plane: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
1256 let mut out = vec![0u8; ow * oh];
1257 for y in 0..oh {
1258 let sy = y.min(h - 1);
1259 let src = &plane[sy * w..sy * w + w];
1260 let dst = &mut out[y * ow..y * ow + ow];
1261 if ow <= w {
1262 dst.copy_from_slice(&src[..ow]);
1263 } else {
1264 dst[..w].copy_from_slice(src);
1265 dst[w..].fill(src[w - 1]);
1266 }
1267 }
1268 out
1269}
1270
1271#[cfg(test)]
1274fn clamp_plane_per_pixel(plane: &[u8], w: usize, h: usize, ow: usize, oh: usize) -> Vec<u8> {
1275 let mut out = vec![0u8; ow * oh];
1276 for y in 0..oh {
1277 for x in 0..ow {
1278 out[y * ow + x] = plane[y.min(h - 1) * w + x.min(w - 1)];
1279 }
1280 }
1281 out
1282}
1283
1284#[cfg(test)]
1285mod source_tests {
1286 use super::*;
1287
1288 #[test]
1289 fn clamp_plane_matches_per_pixel_oracle() {
1290 let mut s: u32 = 0xDEAD_BEEF;
1291 let mut rnd = || {
1292 s = s.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
1293 (s >> 24) as u8
1294 };
1295 let cases = [
1298 (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), ];
1306 for (w, h, ow, oh) in cases {
1307 let plane: Vec<u8> = (0..w * h).map(|_| rnd()).collect();
1308 assert_eq!(
1309 clamp_plane(&plane, w, h, ow, oh),
1310 clamp_plane_per_pixel(&plane, w, h, ow, oh),
1311 "clamp mismatch for {w}x{h} -> {ow}x{oh}"
1312 );
1313 }
1314 }
1315}
1316
1317impl FrameEncoder {
1318 fn new(cfg: &EncoderConfig) -> Self {
1319 let (mb_w, mb_h) = (cfg.mb_width(), cfg.mb_height());
1320 let (cw, ch) = (mb_w * 16, mb_h * 16);
1321 let (ccw, cch) = (cw / 2, ch / 2);
1322 Self {
1323 mb_w,
1324 mb_h,
1325 qp: cfg.qp,
1326 qpc: chroma_qp(cfg.qp),
1327 cur_qp: cfg.qp,
1328 bi_w: (32, 32),
1329 cw,
1330 ccw,
1331 rec_y: AlignedBytes::zeroed(cw * ch),
1332 rec_u: AlignedBytes::zeroed(ccw * cch),
1333 rec_v: AlignedBytes::zeroed(ccw * cch),
1334 nnz_y: vec![0; (mb_w * 4) * (mb_h * 4)],
1335 nnz_c: [vec![0; (mb_w * 2) * (mb_h * 2)], vec![0; (mb_w * 2) * (mb_h * 2)]],
1336 modes_y: vec![2; (mb_w * 4) * (mb_h * 4)],
1337 coded_y: vec![false; (mb_w * 4) * (mb_h * 4)],
1338 mv_y: vec![(0, 0); (mb_w * 4) * (mb_h * 4)],
1339 inter_y: vec![false; (mb_w * 4) * (mb_h * 4)],
1340 ref_idx_y: vec![-1; (mb_w * 4) * (mb_h * 4)],
1341 mv1_y: vec![(0, 0); (mb_w * 4) * (mb_h * 4)],
1342 ref_idx1_y: vec![-1; (mb_w * 4) * (mb_h * 4)],
1343 idz: if cfg.gop_size <= 1 { 2 } else { 3 },
1346 rdoq_strength: 0.0, transform_8x8: cfg.transform_8x8,
1348 sub8x8: std::env::var("RFF_SUB8X8").ok().map(|s| s == "1")
1357 .or(cfg.sub_8x8)
1358 .unwrap_or(cfg.preset == crate::config::Preset::Quality),
1359 sadfp: me_sadfp_mode() == 2,
1379 mv_smooth: false,
1380 do_splits: true,
1381 me_wide: std::env::var("RFF_ME_WIDE").ok().map(|s| s == "1")
1382 .or(cfg.me_wide)
1383 .unwrap_or(cfg.preset == crate::config::Preset::Quality),
1384 me_wide_var: std::env::var("RFF_ME_WIDE_VAR").ok().and_then(|s| s.parse().ok()).unwrap_or(800),
1385 me_rescue: std::env::var("RFF_ME_RESCUE").ok().and_then(|s| s.parse().ok()).unwrap_or(3),
1386 me_wide_coh: std::env::var("RFF_ME_COH").ok().and_then(|s| s.parse().ok()).unwrap_or(4.0),
1387 me_range: std::env::var("RFF_ME_RANGE").ok().and_then(|s| s.parse().ok()).unwrap_or(24),
1388 me_fast: std::env::var("RFF_ME_FASTMO").map(|s| s != "0").unwrap_or(true),
1389 me_learn: std::env::var("RFF_ME_LEARN").ok().and_then(|s| s.parse().ok()).unwrap_or(40),
1390 me_payoff_pct: std::env::var("RFF_ME_PAYOFF").ok().and_then(|s| s.parse().ok()).unwrap_or(15),
1391 sp_single_pass: cfg.preset == crate::config::Preset::Balanced,
1397 sp_defer: std::cell::Cell::new({
1398 let a = DEFER_SUBPEL.load(std::sync::atomic::Ordering::Relaxed) != 0
1399 || std::env::var("RFF_DEFER_SUBPEL").map(|v| v != "0").unwrap_or(false);
1400 a && cfg.preset == crate::config::Preset::Quality
1405 }),
1406 sp_learn_n: std::cell::Cell::new(0),
1407 sp_ring1: std::cell::Cell::new(0),
1408 sp_total: std::cell::Cell::new(0),
1409 sp_1pass: std::cell::Cell::new(false),
1410 resc_n: std::cell::Cell::new(0),
1411 resc_big: std::cell::Cell::new(0),
1412 resc_off: std::cell::Cell::new(false),
1413 inter8x8: std::env::var("RFF_INTER8")
1414 .ok()
1415 .and_then(|s| s.parse().ok())
1416 .unwrap_or(1),
1417 inter8_pen: std::env::var("RFF_INTER8_PEN")
1422 .ok()
1423 .and_then(|s| s.parse().ok())
1424 .unwrap_or(8),
1425 fast: cfg.preset != crate::config::Preset::Quality,
1427 skip_accel_check: cfg.tune_skip_accel_check,
1428 coded_path_v2: cfg.coded_path_v2,
1429 aq_strength: cfg.aq_strength,
1430 tune_lambda_scale: cfg.tune_lambda_scale,
1431 tune_intra_penalty: cfg.tune_intra_penalty,
1432 satd_q: cfg.tune_satd_q,
1433 subpel_force: cfg.tune_subpel || cfg.preset == crate::config::Preset::Balanced,
1434 me_snap: cfg.tune_me_snap,
1435 me_subpel_iter: cfg.tune_me_subpel_iter,
1436 greedy_skip: cfg.tune_greedy_skip,
1437 greedy_min_free: cfg.tune_greedy_skip_min_free.unwrap_or(85),
1438 rd_skip: cfg.tune_rd_skip,
1439 rd_skip_fast_t: cfg.tune_rd_skip_fast_t.unwrap_or(0.0),
1440 rd_skip_min_free: cfg.tune_rd_skip_min_free.unwrap_or(
1441 if cfg.preset == crate::config::Preset::Fast { 60 } else { 90 },
1442 ),
1443 satd_var_thresh: i64::MAX,
1444 mb_use_satd: false,
1445 nnz_l_cache: [0x80; 25],
1446 nnz_c_cache: [[0x80; 9]; 2],
1447 mb_skip_sad: vec![0; mb_w * mb_h],
1448 mb_was_skip: vec![false; mb_w * mb_h],
1449 }
1450 }
1451
1452 fn pred_skip_sad(&self, mb_x: usize, mb_y: usize) -> u32 {
1458 let mbw = self.mb_w;
1459 let at = |x: isize, y: isize| -> Option<(bool, u32)> {
1460 if x < 0 || y < 0 || x >= mbw as isize {
1461 return None;
1462 }
1463 let i = y as usize * mbw + x as usize;
1464 Some((self.mb_was_skip[i], self.mb_skip_sad[i]))
1465 };
1466 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);
1471 let (sa, sb, sc) = (sad(a), sad(b), sad(c));
1472 if b.is_none() && c.is_none() && a.is_some() {
1474 return sa;
1475 }
1476 match (
1477 a.is_some_and(|(s, _)| s),
1478 b.is_some_and(|(s, _)| s),
1479 c.is_some_and(|(s, _)| s),
1480 ) {
1481 (true, false, false) => sa,
1482 (false, true, false) => sb,
1483 (false, false, true) => sc,
1484 _ => sb.max(sa.min(sc)).min(sa.max(sc)), }
1486 }
1487
1488 fn qp_delta(&mut self) -> i32 {
1493 let d = self.qp as i32 - self.cur_qp as i32;
1494 self.cur_qp = self.qp;
1495 d
1496 }
1497
1498 fn mv_neighbors(&self, mb_x: usize, mb_y: usize) -> [MvNeighbor; 3] {
1501 let w4 = self.mb_w * 4;
1502 let get = |avail: bool, bx: isize, by: isize| {
1503 if avail {
1504 let idx = by as usize * w4 + bx as usize;
1505 MvNeighbor {
1506 available: true,
1507 mv: self.mv_y[idx],
1508 ref_idx: self.ref_idx_y[idx],
1509 }
1510 } else {
1511 MvNeighbor::NONE
1512 }
1513 };
1514 let (bx, by) = (mb_x as isize * 4, mb_y as isize * 4);
1515 let a = get(mb_x > 0, bx - 1, by);
1516 let b = get(mb_y > 0, bx, by - 1);
1517 let c = if mb_y > 0 && mb_x + 1 < self.mb_w {
1519 get(true, bx + 4, by - 1)
1520 } else {
1521 get(mb_x > 0 && mb_y > 0, bx - 1, by - 1)
1522 };
1523 [a, b, c]
1524 }
1525
1526 fn skip_mv(&self, mb_x: usize, mb_y: usize) -> (i32, i32) {
1529 let [a, b, c] = self.mv_neighbors(mb_x, mb_y);
1530 if !a.available
1531 || !b.available
1532 || (a.ref_idx == 0 && a.mv == (0, 0))
1533 || (b.ref_idx == 0 && b.mv == (0, 0))
1534 {
1535 (0, 0)
1536 } else {
1537 predict_mv(a, b, c, 0)
1538 }
1539 }
1540
1541 fn set_mb_mv(&mut self, mb_x: usize, mb_y: usize, mv: (i32, i32), inter: bool, refi: i32) {
1544 let w4 = self.mb_w * 4;
1545 for dy in 0..4 {
1546 for dx in 0..4 {
1547 let idx = (mb_y * 4 + dy) * w4 + (mb_x * 4 + dx);
1548 self.mv_y[idx] = mv;
1549 self.inter_y[idx] = inter;
1550 self.ref_idx_y[idx] = if inter { refi } else { -1 };
1551 }
1552 }
1553 }
1554
1555 fn mv_neighbors_block(&self, pbx: isize, pby: isize, pwb: isize) -> [MvNeighbor; 3] {
1559 let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
1560 let get = |bx: isize, by: isize| -> MvNeighbor {
1561 if bx < 0 || by < 0 || bx >= w4 || by >= h4 || !self.coded_y[(by * w4 + bx) as usize] {
1562 MvNeighbor::NONE
1563 } else {
1564 let idx = (by * w4 + bx) as usize;
1565 MvNeighbor { available: true, mv: self.mv_y[idx], ref_idx: self.ref_idx_y[idx] }
1566 }
1567 };
1568 let a = get(pbx - 1, pby);
1569 let b = get(pbx, pby - 1);
1570 let mut c = get(pbx + pwb, pby - 1);
1571 if !c.available {
1572 c = get(pbx - 1, pby - 1); }
1574 [a, b, c]
1575 }
1576
1577 fn mv_neighbors_block_list(&self, pbx: isize, pby: isize, pwb: isize, list: usize) -> [MvNeighbor; 3] {
1583 let (w4, h4) = ((self.mb_w * 4) as isize, (self.mb_h * 4) as isize);
1584 let (mvg, refg): (&[(i32, i32)], &[i32]) = if list == 0 {
1585 (&self.mv_y, &self.ref_idx_y)
1586 } else {
1587 (&self.mv1_y, &self.ref_idx1_y)
1588 };
1589 let get = |bx: isize, by: isize| -> MvNeighbor {
1590 if bx < 0 || by < 0 || bx >= w4 || by >= h4 || !self.coded_y[(by * w4 + bx) as usize] {
1591 MvNeighbor::NONE
1592 } else {
1593 let idx = (by * w4 + bx) as usize;
1594 MvNeighbor { available: true, mv: mvg[idx], ref_idx: refg[idx] }
1595 }
1596 };
1597 let a = get(pbx - 1, pby);
1598 let b = get(pbx, pby - 1);
1599 let mut c = get(pbx + pwb, pby - 1);
1600 if !c.available {
1601 c = get(pbx - 1, pby - 1); }
1603 [a, b, c]
1604 }
1605
1606 #[allow(clippy::too_many_arguments)]
1616 #[inline]
1617 fn mc_satd_hp(
1618 &self,
1619 reference: &crate::RefFrame,
1620 hp: Option<&rusty_h264_common::inter::HpelPlanes>,
1621 hr_on: bool,
1622 sa_on: bool,
1626 src_row: &[u8],
1627 lx: usize,
1628 ly: usize,
1629 rw: usize,
1630 rh: usize,
1631 mv: (i32, i32),
1632 ) -> i64 {
1633 #[cfg(not(accel))]
1634 let _ = sa_on;
1635 #[cfg(feature = "profile")]
1636 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1637 let ch = self.mb_h * 16;
1638 let cw = self.cw;
1639 let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1640 let interior_fullpel = mv.0 & 3 == 0
1641 && mv.1 & 3 == 0
1642 && ix0 >= 0
1643 && iy0 >= 0
1644 && ix0 + rw as isize <= cw as isize
1645 && iy0 + rh as isize <= ch as isize;
1646 #[cfg(feature = "profile")]
1647 {
1648 let fullpel = mv.0 & 3 == 0 && mv.1 & 3 == 0;
1649 satdpath::bump(if interior_fullpel { 0 } else if fullpel { 1 } else { 2 });
1650 }
1651 if interior_fullpel {
1652 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1653 let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1654 return satd_px(src_row, cw, &reference.y[ry0 * cw + rx0..], cw, rw, rh);
1655 }
1656 if let Some(hp) = hp {
1657 if hr_on {
1658 if let Some((plane, base, stride)) =
1659 rusty_h264_common::inter::hpel_ref(hp, lx, ly, rw, rh, mv.0, mv.1)
1660 {
1661 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1662 return satd_px(src_row, cw, &plane[base..], stride, rw, rh);
1663 }
1664 }
1665 #[cfg(accel)]
1672 if sa_on {
1673 if let Some((pa, ba, pb, bb, stride)) =
1674 rusty_h264_common::inter::hpel_qpel_refs(hp, lx, ly, rw, rh, mv.0, mv.1)
1675 {
1676 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeCost);
1677 if let Some(v) = rusty_h264_accel::satd_avg(
1678 src_row, cw, &pa[ba..], &pb[bb..], stride, rw, rh,
1679 ) {
1680 return v as i64;
1681 }
1682 }
1683 }
1684 let mut pred = [0u8; 256];
1685 if rusty_h264_common::inter::hpel_block(hp, lx, ly, rw, rh, mv.0, mv.1, &mut pred) {
1686 return satd_px(src_row, cw, &pred, rw, rw, rh);
1687 }
1688 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1689 return satd_px(src_row, cw, &pred, rw, rw, rh);
1690 }
1691 let mut pred = [0u8; 256];
1692 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1693 satd_px(src_row, cw, &pred, rw, rw, rh)
1694 }
1695
1696 #[allow(clippy::too_many_arguments)]
1704 #[inline]
1705 fn mc_sad_hp(
1706 &self,
1707 reference: &crate::RefFrame,
1708 hp: Option<&rusty_h264_common::inter::HpelPlanes>,
1709 hr_on: bool,
1710 src_row: &[u8],
1711 lx: usize,
1712 ly: usize,
1713 rw: usize,
1714 rh: usize,
1715 mv: (i32, i32),
1716 _asrc: Option<&[u8; 256]>,
1717 ) -> i64 {
1718 #[cfg(feature = "profile")]
1719 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1720 let ch = self.mb_h * 16;
1721 let cw = self.cw;
1722 let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1723 let interior_fullpel = mv.0 & 3 == 0
1724 && mv.1 & 3 == 0
1725 && ix0 >= 0
1726 && iy0 >= 0
1727 && ix0 + rw as isize <= cw as isize
1728 && iy0 + rh as isize <= ch as isize;
1729 if interior_fullpel {
1730 let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1731 #[cfg(accel)]
1732 if rw == 16 && rh == 16 {
1733 if let Some(src) = _asrc {
1734 return rusty_h264_accel::sad_16x16(src, 16, &reference.y[ry0 * cw + rx0..], cw)
1735 as i64;
1736 }
1737 }
1738 return sad_strided(src_row, cw, &reference.y[ry0 * cw + rx0..], cw, rw, rh);
1739 }
1740 if let Some(hp) = hp {
1741 if hr_on {
1742 if let Some((plane, base, stride)) =
1745 rusty_h264_common::inter::hpel_ref(hp, lx, ly, rw, rh, mv.0, mv.1)
1746 {
1747 return sad_strided(src_row, cw, &plane[base..], stride, rw, rh);
1748 }
1749 if let Some((pa, ba, pb, bb, stride)) =
1751 rusty_h264_common::inter::hpel_qpel_refs(hp, lx, ly, rw, rh, mv.0, mv.1)
1752 {
1753 return sad_avg_strided(src_row, cw, &pa[ba..], &pb[bb..], stride, rw, rh);
1754 }
1755 }
1756 let mut pred = [0u8; 256];
1757 if rusty_h264_common::inter::hpel_block(hp, lx, ly, rw, rh, mv.0, mv.1, &mut pred) {
1758 return sad_strided(src_row, cw, &pred, rw, rw, rh);
1759 }
1760 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1761 return sad_strided(src_row, cw, &pred, rw, rw, rh);
1762 }
1763 let mut pred = [0u8; 256];
1764 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1765 sad_strided(src_row, cw, &pred, rw, rw, rh)
1766 }
1767
1768 #[allow(clippy::too_many_arguments)]
1777 fn mc_sad(
1778 &self,
1779 reference: &crate::RefFrame,
1780 sy: &[u8],
1781 lx: usize,
1782 ly: usize,
1783 rw: usize,
1784 rh: usize,
1785 mv: (i32, i32),
1786 _asrc: Option<&[u8; 256]>,
1789 ) -> i64 {
1790 #[cfg(feature = "profile")]
1794 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(2);
1795 let ch = self.mb_h * 16;
1796 let cw = self.cw;
1797 let (ix0, iy0) = (lx as isize + (mv.0 >> 2) as isize, ly as isize + (mv.1 >> 2) as isize);
1798 let interior_fullpel = mv.0 & 3 == 0
1799 && mv.1 & 3 == 0
1800 && ix0 >= 0
1801 && iy0 >= 0
1802 && ix0 + rw as isize <= cw as isize
1803 && iy0 + rh as isize <= ch as isize;
1804 #[cfg(accel)]
1808 if interior_fullpel && rw == 16 && rh == 16 {
1809 if let Some(src) = _asrc {
1810 let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1811 return rusty_h264_accel::sad_16x16(src, 16, &reference.y[ry0 * cw + rx0..], cw)
1812 as i64;
1813 }
1814 }
1815 let mut sad = 0u32;
1816 if interior_fullpel {
1817 let (rx0, ry0) = (ix0 as usize, iy0 as usize);
1819 let refy = &reference.y;
1820 for dy in 0..rh {
1821 let s = &sy[(ly + dy) * cw + lx..][..rw];
1822 let r = &refy[(ry0 + dy) * cw + rx0..][..rw];
1823 sad += s.iter().zip(r).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1824 }
1825 } else {
1826 let mut pred = [0u8; 256];
1827 let from_planes = !self.fast
1829 && rusty_h264_common::inter::hpel_block(
1830 reference.hpel(cw, ch),
1831 lx,
1832 ly,
1833 rw,
1834 rh,
1835 mv.0,
1836 mv.1,
1837 &mut pred,
1838 );
1839 if !from_planes {
1840 mc_luma(&reference.y, cw, ch, lx, ly, rw, rh, mv.0, mv.1, &mut pred);
1841 }
1842 for dy in 0..rh {
1843 let s = &sy[(ly + dy) * cw + lx..][..rw];
1844 let p = &pred[dy * rw..][..rw];
1845 sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1846 }
1847 }
1848 sad as i64
1849 }
1850
1851 #[allow(clippy::too_many_arguments)]
1855 fn bi_dist_rect(
1856 &self,
1857 l0: &crate::RefFrame,
1858 l1: &crate::RefFrame,
1859 sy: &[u8],
1860 lx: usize,
1861 ly: usize,
1862 rw: usize,
1863 rh: usize,
1864 mv0: (i32, i32),
1865 mv1: (i32, i32),
1866 ) -> i64 {
1867 let ch = self.mb_h * 16;
1868 let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
1869 mc_luma(&l0.y, self.cw, ch, lx, ly, rw, rh, mv0.0, mv0.1, &mut a);
1870 mc_luma(&l1.y, self.cw, ch, lx, ly, rw, rh, mv1.0, mv1.1, &mut b);
1871 let n = rw * rh;
1872 let mut avg = [0u8; 256];
1873 for i in 0..n {
1874 avg[i] = bi_blend(a[i] as i32, b[i] as i32, self.bi_w);
1875 }
1876 if self.fast && !self.mb_use_satd {
1877 let mut sad = 0u32;
1878 for dy in 0..rh {
1879 let s = &sy[(ly + dy) * self.cw + lx..][..rw];
1880 let p = &avg[dy * rw..][..rw];
1881 sad += s.iter().zip(p).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
1882 }
1883 sad as i64
1884 } else {
1885 satd_px(&sy[ly * self.cw + lx..], self.cw, &avg, rw, rw, rh)
1886 }
1887 }
1888
1889 fn bi_dist(
1895 &self,
1896 l0: &crate::RefFrame,
1897 l1: &crate::RefFrame,
1898 sy: &[u8],
1899 lx: usize,
1900 ly: usize,
1901 mv0: (i32, i32),
1902 mv1: (i32, i32),
1903 ) -> i64 {
1904 #[cfg(feature = "profile")]
1906 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(4);
1907 let ch = self.mb_h * 16;
1908 let (mut a, mut b) = ([0u8; 256], [0u8; 256]);
1909 mc_luma(&l0.y, self.cw, ch, lx, ly, 16, 16, mv0.0, mv0.1, &mut a);
1910 mc_luma(&l1.y, self.cw, ch, lx, ly, 16, 16, mv1.0, mv1.1, &mut b);
1911 let mut avg = [0u8; 256];
1912 for i in 0..256 {
1913 avg[i] = bi_blend(a[i] as i32, b[i] as i32, self.bi_w);
1914 }
1915 if self.fast && !self.mb_use_satd {
1916 let mut sad = 0u32;
1917 for dy in 0..16 {
1918 let s = &sy[(ly + dy) * self.cw + lx..][..16];
1919 let p = &avg[dy * 16..][..16];
1920 sad += s.iter().zip(p).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
1921 }
1922 sad as i64
1923 } else {
1924 satd_px(&sy[ly * self.cw + lx..], self.cw, &avg, 16, 16, 16)
1925 }
1926 }
1927
1928 fn pred_dist(&self, sy: &[u8], lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
1932 if self.fast && !self.mb_use_satd {
1933 let mut sad = 0u32;
1934 for dy in 0..16 {
1935 let s = &sy[(ly + dy) * self.cw + lx..][..16];
1936 let p = &pred[dy * 16..][..16];
1937 sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
1938 }
1939 sad as i64
1940 } else {
1941 satd_px(&sy[ly * self.cw + lx..], self.cw, pred, 16, 16, 16)
1942 }
1943 }
1944
1945 fn col_zero(&self, l1: &crate::RefFrame, bx: usize, by: usize) -> bool {
1950 if l1.w4 == 0 {
1951 return false;
1952 }
1953 let idx = by * l1.w4 + bx;
1954 if idx >= l1.ref_idx.len() {
1955 return false;
1956 }
1957 l1.ref_idx[idx] == 0 && l1.mv[idx].0.abs() <= 1 && l1.mv[idx].1.abs() <= 1
1958 }
1959
1960 #[allow(clippy::too_many_arguments)]
1964 fn b_mc_block(
1965 &self,
1966 l0: &crate::RefFrame,
1967 l1: &crate::RefFrame,
1968 mb_x: usize,
1969 mb_y: usize,
1970 dx: usize,
1971 dy: usize,
1972 refi0: i32,
1973 m0: (i32, i32),
1974 refi1: i32,
1975 m1: (i32, i32),
1976 pred_y: &mut [u8; 256],
1977 c_pred: &mut [[u8; 64]; 2],
1978 ) {
1979 #[cfg(feature = "profile")]
1981 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(4);
1982 let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
1983 let (px, py) = (mb_x * 16 + dx, mb_y * 16 + dy);
1984 let (mut a, mut b) = ([0u8; 16], [0u8; 16]);
1985 let mc4 = |r: &crate::RefFrame, mv: (i32, i32), out: &mut [u8; 16]| {
1993 if !self.fast
1994 && bdirect_planes_enabled()
1995 && rusty_h264_common::inter::hpel_block(
1996 r.hpel(self.cw, ch), px, py, 4, 4, mv.0, mv.1, out,
1997 )
1998 {
1999 return;
2000 }
2001 mc_luma(&r.y, self.cw, ch, px, py, 4, 4, mv.0, mv.1, out);
2002 };
2003 if refi0 >= 0 {
2004 mc4(l0, m0, &mut a);
2005 }
2006 if refi1 >= 0 {
2007 mc4(l1, m1, &mut b);
2008 }
2009 for yy in 0..4 {
2010 for xx in 0..4 {
2011 let i = yy * 4 + xx;
2012 let v = match (refi0 >= 0, refi1 >= 0) {
2013 (true, true) => bi_blend(a[i] as i32, b[i] as i32, self.bi_w),
2014 (true, false) => a[i],
2015 _ => b[i],
2016 };
2017 pred_y[(dy + yy) * 16 + (dx + xx)] = v;
2018 }
2019 }
2020 let (cpx, cpy) = (mb_x * 8 + dx / 2, mb_y * 8 + dy / 2);
2022 for c in 0..2 {
2023 let (r0, r1) = if c == 0 { (&l0.u, &l1.u) } else { (&l0.v, &l1.v) };
2024 let (mut ca, mut cb) = ([0u8; 4], [0u8; 4]);
2025 if refi0 >= 0 {
2026 mc_chroma(r0, self.ccw, cch, cpx, cpy, 2, 2, m0.0, m0.1, &mut ca);
2027 }
2028 if refi1 >= 0 {
2029 mc_chroma(r1, self.ccw, cch, cpx, cpy, 2, 2, m1.0, m1.1, &mut cb);
2030 }
2031 for yy in 0..2 {
2032 for xx in 0..2 {
2033 let i = yy * 2 + xx;
2034 let v = match (refi0 >= 0, refi1 >= 0) {
2035 (true, true) => bi_blend(ca[i] as i32, cb[i] as i32, self.bi_w),
2036 (true, false) => ca[i],
2037 _ => cb[i],
2038 };
2039 c_pred[c][(dy / 2 + yy) * 8 + (dx / 2 + xx)] = v;
2040 }
2041 }
2042 }
2043 }
2044
2045 fn b_direct(
2050 &self,
2051 l0: &crate::RefFrame,
2052 l1: &crate::RefFrame,
2053 mb_x: usize,
2054 mb_y: usize,
2055 ) -> ([u8; 256], [[u8; 64]; 2], [(i32, (i32, i32), i32, (i32, i32)); 16]) {
2056 let (nbx, nby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
2057 let n0 = self.mv_neighbors_block_list(nbx, nby, 4, 0);
2058 let n1 = self.mv_neighbors_block_list(nbx, nby, 4, 1);
2059 let min_pos = |a: i32, b: i32| if a < 0 { b } else if b < 0 { a } else { a.min(b) };
2060 let rid = |n: &[MvNeighbor; 3]| min_pos(min_pos(n[0].ref_idx, n[1].ref_idx), n[2].ref_idx);
2061 let (mut refi0, mut refi1) = (rid(&n0), rid(&n1));
2062 let direct_zero = refi0 < 0 && refi1 < 0;
2063 if direct_zero {
2064 refi0 = 0;
2065 refi1 = 0;
2066 }
2067 let mv0 = if refi0 >= 0 && !direct_zero { predict_mv(n0[0], n0[1], n0[2], refi0) } else { (0, 0) };
2068 let mv1 = if refi1 >= 0 && !direct_zero { predict_mv(n1[0], n1[1], n1[2], refi1) } else { (0, 0) };
2069 let mut pred_y = [0u8; 256];
2070 let mut c_pred = [[0u8; 64]; 2];
2071 let mut motion = [(0i32, (0i32, 0i32), 0i32, (0i32, 0i32)); 16];
2072 for sby in 0..4 {
2073 for sbx in 0..4 {
2074 let cz = !direct_zero && self.col_zero(l1, mb_x * 4 + sbx, mb_y * 4 + sby);
2075 let m0 = if refi0 == 0 && cz { (0, 0) } else { mv0 };
2076 let m1 = if refi1 == 0 && cz { (0, 0) } else { mv1 };
2077 motion[sby * 4 + sbx] = (refi0, m0, refi1, m1);
2078 self.b_mc_block(l0, l1, mb_x, mb_y, sbx * 4, sby * 4, refi0, m0, refi1, m1, &mut pred_y, &mut c_pred);
2079 }
2080 }
2081 (pred_y, c_pred, motion)
2082 }
2083
2084 fn commit_direct_motion(&mut self, mb_x: usize, mb_y: usize, motion: &[(i32, (i32, i32), i32, (i32, i32)); 16]) {
2087 let w4 = self.mb_w * 4;
2088 for sby in 0..4 {
2089 for sbx in 0..4 {
2090 let (refi0, m0, refi1, m1) = motion[sby * 4 + sbx];
2091 let idx = (mb_y * 4 + sby) * w4 + (mb_x * 4 + sbx);
2092 self.inter_y[idx] = true;
2093 self.coded_y[idx] = true;
2094 self.mv_y[idx] = m0;
2095 self.ref_idx_y[idx] = refi0;
2096 self.mv1_y[idx] = m1;
2097 self.ref_idx1_y[idx] = refi1;
2098 }
2099 }
2100 }
2101
2102 #[allow(clippy::too_many_arguments)]
2111 fn motion_search(
2117 &self,
2118 reference: &crate::RefFrame,
2119 sy: &[u8],
2120 lx: usize,
2121 ly: usize,
2122 rw: usize,
2123 rh: usize,
2124 predictors: &[(i32, i32)],
2125 lambda_me: f64,
2126 start: Option<(i32, i32)>,
2130 ) -> ((i32, i32), i64) {
2131 let mvk = mv_cost_kind(self.mv_smooth);
2137 let mvbits = |d: i32| -> u32 {
2138 match mvk {
2148 1 => {
2149 let a = d.unsigned_abs().min(4095) as usize;
2150 MV_COST_TAB.get_or_init(build_mv_cost)[a] as u32
2151 }
2152 2 => {
2153 let a = d.unsigned_abs().min(4095) as usize;
2154 MV_TRUE_BIASED.get_or_init(build_true_biased)[a] as u32
2155 }
2156 _ => {
2157 let codenum = if d > 0 { (2 * d - 1) as u32 } else { (-2 * d) as u32 };
2158 1 + 2 * (31 - (codenum + 1).leading_zeros())
2159 }
2160 }
2161 };
2162 let center = predictors[0];
2163 let probe = me_oracle_on();
2164 let sadfp = !self.fast && start.is_none() && self.sadfp;
2170 #[cfg(accel)]
2175 let asrc_buf = if (self.fast || sadfp) && rw == 16 && rh == 16 {
2176 let mut a = AlignedMb([0u8; 256]);
2177 for dy in 0..16 {
2178 a.0[dy * 16..dy * 16 + 16].copy_from_slice(&sy[(ly + dy) * self.cw + lx..][..16]);
2179 }
2180 Some(a)
2181 } else {
2182 None
2183 };
2184 #[cfg(accel)]
2185 let asrc: Option<&[u8; 256]> = asrc_buf.as_ref().map(|a| &a.0);
2186 #[cfg(not(accel))]
2187 let asrc: Option<&[u8; 256]> = None;
2188 let use_sad = self.fast && !self.mb_use_satd;
2196 let cw = self.cw;
2197 let hp: Option<&rusty_h264_common::inter::HpelPlanes> =
2201 if !self.fast { Some(reference.hpel(cw, self.mb_h * 16)) } else { None };
2202 let hr_on = hpel_ref_enabled();
2203 let sa_on = cfg!(accel) && hr_on && satd_avg_enabled();
2206 let src_row = &sy[ly * cw + lx..];
2207 #[cfg(accel)]
2213 let mectx = if !use_sad && mectx_enabled() {
2214 hp.and_then(|p| {
2215 rusty_h264_accel::MeCtx::new(
2216 src_row, cw, &p.f, &p.h, &p.v, &p.c, p.stride, p.pad, p.pw, p.ph,
2217 lx, ly, rw, rh,
2218 )
2219 })
2220 } else {
2221 None
2222 };
2223 let cost = |mv: (i32, i32)| -> i64 {
2224 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2225 let lam_r = if mvk != 0 { lambda_me * 0.25 } else { lambda_me };
2228 let dist = if use_sad {
2231 self.mc_sad(reference, sy, lx, ly, rw, rh, mv, asrc)
2232 } else {
2233 #[cfg(accel)]
2234 {
2235 match mectx.as_ref().and_then(|c| c.eval(mv.0, mv.1)) {
2236 Some(d) => d as i64,
2237 None => {
2238 self.mc_satd_hp(reference, hp, hr_on, sa_on, src_row, lx, ly, rw, rh, mv)
2239 }
2240 }
2241 }
2242 #[cfg(not(accel))]
2243 {
2244 self.mc_satd_hp(reference, hp, hr_on, sa_on, src_row, lx, ly, rw, rh, mv)
2245 }
2246 };
2247 dist + (lam_r * rate as f64) as i64
2248 };
2249 let lam_fp = lambda_me * if sadfp { me_sadfp_lambda() } else { 1.0 };
2253 let cost_fp = |mv: (i32, i32)| -> i64 {
2254 if !sadfp {
2255 return cost(mv);
2256 }
2257 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2258 self.mc_sad_hp(reference, hp, hr_on, src_row, lx, ly, rw, rh, mv, asrc)
2259 + (lam_fp * rate as f64) as i64
2260 };
2261 let refine_only = start.is_some();
2263 let (mut best, mut best_c) = match start {
2264 Some(mv) => (mv, cost(mv)),
2265 None => {
2266 let mut b = (0, 0);
2267 let mut bc = cost_fp(b);
2268 for &p in predictors {
2269 let pc = cost_fp(p);
2270 if pc < bc {
2271 bc = pc;
2272 b = p;
2273 }
2274 }
2275 (b, bc)
2276 }
2277 };
2278 let (seed_mv, mut seed_c) = (best, best_c);
2286 if !refine_only && self.me_snap && (best.0 & 3 != 0 || best.1 & 3 != 0) {
2287 let snapped = ((best.0 + 2).div_euclid(4) * 4, (best.1 + 2).div_euclid(4) * 4);
2288 best_c = cost_fp(snapped);
2289 best = snapped;
2290 }
2291 let mut ladder = [0i32; 5];
2305 let mut nladder = 0usize;
2306 let steps: &[i32] = if self.fast {
2307 &[16, 4]
2308 } else {
2309 let m = dia_mask();
2310 for (i, r) in DIA_RUNGS.iter().enumerate() {
2311 if m & (1 << i) != 0 {
2312 ladder[nladder] = *r;
2313 nladder += 1;
2314 }
2315 }
2316 &ladder[..nladder]
2317 };
2318 let _gd = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeDiamond);
2319 let fc = !self.fast && cfg!(accel) && me_fc_enabled()
2327 && matches!((rw, rh), (16, 16) | (16, 8) | (8, 16) | (8, 8));
2328 let ch_px = self.mb_h as isize * 16;
2329 for (_si, &step) in steps.iter().enumerate() {
2330 if refine_only {
2331 break;
2332 }
2333 loop {
2334 #[cfg(accel)]
2335 if fc && best.0 & 3 == 0 && best.1 & 3 == 0 {
2336 let s = (step >> 2) as isize;
2338 let (bx, by) = (lx as isize + (best.0 >> 2) as isize, ly as isize + (best.1 >> 2) as isize);
2339 if bx - s >= 0 && by - s >= 0 && bx + s + rw as isize <= cw as isize && by + s + rh as isize <= ch_px {
2340 let offs = [
2341 (by * cw as isize + bx + s) as usize,
2342 (by * cw as isize + bx - s) as usize,
2343 ((by + s) * cw as isize + bx) as usize,
2344 ((by - s) * cw as isize + bx) as usize,
2345 ];
2346 let batch = if rw != 16 {
2352 None
2353 } else if sadfp {
2354 rusty_h264_accel::sad_x4(src_row, cw, &reference.y, offs, cw, rw, rh)
2355 } else {
2356 rusty_h264_accel::satd_x4(src_row, cw, &reference.y, offs, cw, rw, rh)
2357 };
2358 {
2359 let ring = [(step, 0), (-step, 0), (0, step), (0, -step)];
2360 let (mut bi, mut bc) = (usize::MAX, best_c);
2361 for (i, &(dx, dy)) in ring.iter().enumerate() {
2362 let mv = (best.0 + dx, best.1 + dy);
2363 let cc = match batch {
2364 Some(sads) => {
2365 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2366 sads[i] as i64 + (lam_fp * rate as f64) as i64
2367 }
2368 None => cost_fp(mv),
2369 };
2370 #[cfg(feature = "profile")]
2371 diastats::ev(_si);
2372 if cc < bc {
2373 bc = cc;
2374 bi = i;
2375 }
2376 }
2377 if bi == usize::MAX {
2378 break;
2379 }
2380 best_c = bc;
2381 best = (best.0 + ring[bi].0, best.1 + ring[bi].1);
2382 #[cfg(feature = "profile")]
2383 diastats::imp(_si);
2384 continue;
2385 }
2386 }
2387 }
2388 let mut improved = false;
2389 for &(dx, dy) in &[(step, 0), (-step, 0), (0, step), (0, -step)] {
2390 let c = (best.0 + dx, best.1 + dy);
2391 let cc = cost_fp(c);
2392 #[cfg(feature = "profile")]
2393 diastats::ev(_si);
2394 if cc < best_c {
2395 best_c = cc;
2396 best = c;
2397 improved = true;
2398 #[cfg(feature = "profile")]
2399 diastats::imp(_si);
2400 }
2401 }
2402 if !improved {
2403 break;
2404 }
2405 }
2406 }
2407 drop(_gd);
2420 if sadfp {
2425 best_c = cost(best);
2426 seed_c = cost(seed_mv);
2427 }
2428 let _gr = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeRescue);
2429 let flat = |sself: &Self| {
2435 !refine_only && {
2436 let (mut s, mut ss) = (0u64, 0u64);
2437 for dy in 0..rh {
2438 for dx in 0..rw {
2439 let v = sy[(ly + dy) * sself.cw + lx + dx] as u64;
2440 s += v;
2441 ss += v * v;
2442 }
2443 }
2444 let n = (rw * rh) as u64;
2445 (ss - s * s / n) / n < sself.me_wide_var
2446 }
2447 };
2448 if self.me_wide && !self.fast && (self.me_fast || flat(self)) && !self.resc_off.get() {
2459 let rate_b = mvbits(best.0 - center.0) + mvbits(best.1 - center.1);
2466 let dist = best_c - (lambda_me * rate_b as f64) as i64;
2467 if dist / (rw * rh).max(1) as i64 > self.me_rescue {
2468 let pre_c = best_c;
2478 let (cx, cy) = ((best.0 + 2).div_euclid(4) * 4, (best.1 + 2).div_euclid(4) * 4);
2479 let mut gb = best;
2480 let cw = self.cw;
2489 let r = self.me_range;
2490 let batched = rw == 16 && rh == 16 && cfg!(accel) && {
2491 let (icdx, icdy) = (cx >> 2, cy >> 2);
2492 lx as i32 + icdx >= r
2493 && lx as i32 + icdx + r + 16 <= cw as i32
2494 && ly as i32 + icdy >= r
2495 && ly as i32 + icdy + r + 16 <= (self.mb_h * 16) as i32
2496 && me_batch_enabled()
2497 };
2498 #[cfg(accel)]
2499 if batched {
2500 let (icdx, icdy) = ((cx >> 2), (cy >> 2));
2501 let src = &sy[ly * cw + lx..];
2502 let mut dy = -r;
2503 while dy <= r {
2504 let rby = (ly as i32 + icdy + dy) as usize;
2505 let mut dx = -r;
2506 while dx <= r {
2507 let rbx = (lx as i32 + icdx + dx) as usize;
2508 let satd =
2509 2 * rusty_h264_accel::satd_16x16(src, cw, &reference.y[rby * cw + rbx..], cw) as i64;
2510 let mv = (cx + dx * 4, cy + dy * 4);
2511 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2512 let cc = satd + (lambda_me * rate as f64) as i64;
2513 if cc < best_c {
2514 best_c = cc;
2515 gb = mv;
2516 }
2517 dx += 2;
2518 }
2519 dy += 2;
2520 }
2521 }
2522 if !batched {
2523 let mut dy = -r;
2524 while dy <= r {
2525 let mut dx = -r;
2526 while dx <= r {
2527 let cc = cost((cx + dx * 4, cy + dy * 4));
2528 if cc < best_c {
2529 best_c = cc;
2530 gb = (cx + dx * 4, cy + dy * 4);
2531 }
2532 dx += 2;
2533 }
2534 dy += 2;
2535 }
2536 }
2537 best = gb;
2538 for dy in -1..=1 {
2539 for dx in -1..=1 {
2540 let c = (best.0 + dx * 4, best.1 + dy * 4);
2541 let cc = cost(c);
2542 if cc < best_c {
2543 best_c = cc;
2544 best = c;
2545 }
2546 }
2547 }
2548 let n = self.resc_n.get();
2557 if n < self.me_learn {
2558 self.resc_n.set(n + 1);
2559 if best_c * 16 <= pre_c * 15 {
2560 self.resc_big.set(self.resc_big.get() + 1);
2561 }
2562 if n + 1 == self.me_learn
2563 && self.resc_big.get() * 100 < self.me_learn * self.me_payoff_pct
2564 {
2565 self.resc_off.set(true);
2566 }
2567 }
2568 }
2569 }
2570 drop(_gr);
2571 let _gs = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MeSubpel);
2572 if probe {
2578 let mut ob = center;
2581 let mut oc = i64::MAX;
2582 for gy in -24i32..=24 {
2583 for gx in -24i32..=24 {
2584 let c = (center.0 + gx * 4, center.1 + gy * 4);
2585 let cc = cost(c);
2586 if cc < oc {
2587 oc = cc;
2588 ob = c;
2589 }
2590 }
2591 }
2592 let fullpel_best = ob;
2593 for &st in &[2i32, 1] {
2594 for &(dx, dy) in &[(st, 0), (-st, 0), (0, st), (0, -st)] {
2595 let c = (ob.0 + dx, ob.1 + dy);
2596 let cc = cost(c);
2597 if cc < oc {
2598 oc = cc;
2599 ob = c;
2600 }
2601 }
2602 }
2603 let mut oc_sp = oc;
2607 for dy in -3i32..=3 {
2608 for dx in -3i32..=3 {
2609 let c = (fullpel_best.0 + dx, fullpel_best.1 + dy);
2610 let cc = cost(c);
2611 if cc < oc_sp {
2612 oc_sp = cc;
2613 }
2614 }
2615 }
2616 let (mut mb_, mut mc_) = (best, best_c);
2618 for &st in &[2i32, 1] {
2619 for &(dx, dy) in &[(st, 0), (-st, 0), (0, st), (0, -st)] {
2620 let c = (mb_.0 + dx, mb_.1 + dy);
2621 let cc = cost(c);
2622 if cc < mc_ {
2623 mc_ = cc;
2624 mb_ = c;
2625 }
2626 }
2627 }
2628 use std::sync::atomic::Ordering::Relaxed;
2629 ME_PROBE[0].fetch_add(1, Relaxed);
2630 ME_PROBE[1].fetch_add(mc_.max(0) as u64, Relaxed);
2631 ME_PROBE[2].fetch_add(oc.max(0) as u64, Relaxed);
2632 ME_PROBE[3].fetch_add((mc_ > oc) as u64, Relaxed);
2633 ME_PROBE[5].fetch_add(oc_sp.max(0) as u64, Relaxed);
2634 ME_PROBE[6].fetch_add((mc_ > oc_sp) as u64, Relaxed);
2635 }
2636 let subpel: &[i32] = if (self.fast && !self.subpel_force) || (self.sp_defer.get() && !refine_only) {
2637 &[]
2638 } else {
2639 &[2, 1]
2640 };
2641 let (hv_pre, mut hv_evals) = (best_c, 0u32);
2643 let (mut hv_to_best, mut hv_ring1) = (0u32, i64::MIN);
2647 let mut pat = subpel_pattern_override()
2648 .unwrap_or(if self.sp_single_pass { 2 } else { 0 });
2649 let (sp_learn, sp_t) = sp_dispatch_cfg();
2650 let sp_dispatching = sp_learn > 0 && pat == 0 && !subpel.is_empty();
2652 if sp_dispatching && self.sp_learn_n.get() >= sp_learn && self.sp_1pass.get() {
2653 pat = 2;
2654 }
2655 const SP_MEMO_N: usize = 64;
2668 #[inline(always)]
2669 fn sp_slot(mv: (i32, i32)) -> usize {
2670 ((mv.0 & 7) as usize) | (((mv.1 & 7) as usize) << 3)
2671 }
2672 let mut memo_mv = [(i32::MIN, i32::MIN); SP_MEMO_N];
2673 let mut memo_c = [0i64; SP_MEMO_N];
2674 if !subpel.is_empty() {
2675 let s0 = sp_slot(best);
2676 memo_mv[s0] = best;
2677 memo_c[s0] = best_c;
2678 }
2679 #[cfg(feature = "profile")]
2684 let mut seen: Vec<(i32, i32)> = Vec::with_capacity(64);
2685 #[cfg(feature = "profile")]
2686 {
2687 seen.push(best);
2688 }
2689 let sp_cap = sp_maxit();
2697 let sp_fc = sp_fc_enabled() && !self.fast && cfg!(accel)
2699 && matches!((rw, rh), (16, 16) | (16, 8) | (8, 16) | (8, 8));
2700 for &step in subpel {
2701 let ring8 = [
2706 (step, 0), (-step, 0), (0, step), (0, -step),
2707 (step, step), (-step, -step), (step, -step), (-step, step),
2708 ];
2709 let ring4 = [(step, 0), (-step, 0), (0, step), (0, -step)];
2710 let ring: &[(i32, i32)] = if pat & 1 != 0 { &ring4 } else { &ring8 };
2711 let mut _iter = 0u32;
2712 loop {
2713 #[cfg(accel)]
2722 if sp_fc && step == 1 && pat & 1 == 0 {
2723 _iter += 1;
2724 let hp8 = hp.expect("sp_fc implies non-fast, which resolves hp");
2725 let ring8 = [
2726 (1, 0), (-1, 0), (0, 1), (0, -1),
2727 (1, 1), (-1, -1), (1, -1), (-1, 1),
2728 ];
2729 let mut prs: [Option<(&[u8], usize, &[u8], usize, usize)>; 8] = [None; 8];
2730 let mut all = true;
2731 for (i, &(dx, dy)) in ring8.iter().enumerate() {
2732 prs[i] = rusty_h264_common::inter::hpel_qpel_refs(
2733 hp8, lx, ly, rw, rh, best.0 + dx, best.1 + dy,
2734 );
2735 all &= prs[i].is_some();
2736 }
2737 if all {
2738 let stride = prs[0].unwrap().4;
2739 let pack = |a: usize, b: usize, c2: usize, d: usize| {
2743 if rw != 16 {
2744 return None;
2745 }
2746 let g = |i: usize| {
2747 let (pa, oa, pb, ob, _) = prs[i].unwrap();
2748 (pa, oa, pb, ob)
2749 };
2750 rusty_h264_accel::satd_avg_x4(
2751 src_row, cw, [g(a), g(b), g(c2), g(d)], stride, rw, rh,
2752 )
2753 };
2754 {
2755 let (ax, di) = (pack(0, 1, 2, 3), pack(4, 5, 6, 7));
2756 let (mut bi, mut bc) = (usize::MAX, best_c);
2757 for i in 0..8 {
2758 let (dx, dy) = ring8[i];
2759 let mv = (best.0 + dx, best.1 + dy);
2760 let cc = match (i < 4, &ax, &di) {
2761 (true, Some(ax), _) => {
2762 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2763 ax[i] as i64 + (lambda_me * rate as f64) as i64
2764 }
2765 (false, _, Some(di)) => {
2766 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2767 di[i - 4] as i64 + (lambda_me * rate as f64) as i64
2768 }
2769 _ => cost(mv),
2770 };
2771 hv_evals += 1;
2772 if cc < bc {
2773 bc = cc;
2774 bi = i;
2775 }
2776 }
2777 if hv_ring1 == i64::MIN {
2778 hv_ring1 = if bi == usize::MAX { best_c } else { bc };
2779 }
2780 if bi == usize::MAX
2781 || !self.me_subpel_iter
2782 || pat & 2 != 0
2783 || (sp_cap != 0 && _iter >= sp_cap)
2784 {
2785 if bi != usize::MAX {
2786 best_c = bc;
2787 best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2788 hv_to_best = hv_evals;
2789 }
2790 break;
2791 }
2792 best_c = bc;
2793 best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2794 hv_to_best = hv_evals;
2795 continue;
2796 }
2797 }
2798 _iter -= 1;
2799 }
2800 #[cfg(accel)]
2801 if sp_fc && step == 2 && best.0 & 3 == 0 && best.1 & 3 == 0 && pat & 1 == 0 {
2802 _iter += 1;
2803 let hp8 = hp.expect("sp_fc implies non-fast, which resolves hp");
2804 let ring8 = [
2805 (step, 0), (-step, 0), (0, step), (0, -step),
2806 (step, step), (-step, -step), (step, -step), (-step, step),
2807 ];
2808 let mut refs8: [Option<(&[u8], usize, usize)>; 8] = [None; 8];
2809 let mut all = true;
2810 for (i, &(dx, dy)) in ring8.iter().enumerate() {
2811 refs8[i] = rusty_h264_common::inter::hpel_ref(
2812 hp8, lx, ly, rw, rh, best.0 + dx, best.1 + dy,
2813 );
2814 all &= refs8[i].is_some();
2815 }
2816 if all {
2817 let stride = refs8[0].unwrap().2;
2818 let pack = |a: usize, b: usize, c2: usize, d: usize| {
2821 if rw != 16 {
2822 return None;
2823 }
2824 let g = |i: usize| {
2825 let (p, o, _) = refs8[i].unwrap();
2826 (p, o)
2827 };
2828 rusty_h264_accel::satd_x4p(
2829 src_row, cw, [g(a), g(b), g(c2), g(d)], stride, rw, rh,
2830 )
2831 };
2832 {
2833 let (ax, di) = (pack(0, 1, 2, 3), pack(4, 5, 6, 7));
2834 let (mut bi, mut bc) = (usize::MAX, best_c);
2835 for i in 0..8 {
2836 let (dx, dy) = ring8[i];
2837 let mv = (best.0 + dx, best.1 + dy);
2838 let cc = match (i < 4, &ax, &di) {
2839 (true, Some(ax), _) => {
2840 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2841 ax[i] as i64 + (lambda_me * rate as f64) as i64
2842 }
2843 (false, _, Some(di)) => {
2844 let rate = mvbits(mv.0 - center.0) + mvbits(mv.1 - center.1);
2845 di[i - 4] as i64 + (lambda_me * rate as f64) as i64
2846 }
2847 _ => cost(mv),
2848 };
2849 hv_evals += 1;
2850 if cc < bc {
2851 bc = cc;
2852 bi = i;
2853 }
2854 }
2855 if hv_ring1 == i64::MIN {
2856 hv_ring1 = if bi == usize::MAX { best_c } else { bc };
2857 }
2858 if bi == usize::MAX
2859 || !self.me_subpel_iter
2860 || pat & 2 != 0
2861 || (sp_cap != 0 && _iter >= sp_cap)
2862 {
2863 if bi != usize::MAX {
2864 best_c = bc;
2865 best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2866 hv_to_best = hv_evals;
2867 }
2868 break;
2869 }
2870 best_c = bc;
2871 best = (best.0 + ring8[bi].0, best.1 + ring8[bi].1);
2872 hv_to_best = hv_evals;
2873 continue;
2874 }
2875 }
2876 _iter -= 1; }
2878 let mut improved = false;
2879 _iter += 1;
2880 for (_pi, &(dx, dy)) in ring.iter().enumerate() {
2881 let c = (best.0 + dx, best.1 + dy);
2882 let slot = sp_slot(c);
2883 let cc = if memo_mv[slot] == c {
2884 memo_c[slot]
2885 } else {
2886 let v = cost(c);
2887 memo_mv[slot] = c;
2888 memo_c[slot] = v;
2889 v
2890 };
2891 hv_evals += 1;
2892 #[cfg(feature = "profile")]
2896 {
2897 spstats::ev(if step == 2 { 0 } else { 1 }, _pi, _iter);
2898 if seen.contains(&c) {
2899 spstats::redundant();
2900 } else {
2901 seen.push(c);
2902 }
2903 }
2904 if cc < best_c {
2905 best_c = cc;
2906 best = c;
2907 improved = true;
2908 hv_to_best = hv_evals;
2909 #[cfg(feature = "profile")]
2910 spstats::imp(if step == 2 { 0 } else { 1 }, _pi, _iter);
2911 }
2912 }
2913 if hv_ring1 == i64::MIN {
2914 hv_ring1 = best_c;
2915 }
2916 if !improved
2917 || !self.me_subpel_iter
2918 || pat & 2 != 0
2919 || (sp_cap != 0 && _iter >= sp_cap)
2920 {
2921 break;
2922 }
2923 }
2924 }
2925 if sp_dispatching {
2926 let n = self.sp_learn_n.get();
2927 if n < sp_learn {
2928 self.sp_learn_n.set(n + 1);
2929 if hv_ring1 != i64::MIN {
2930 self.sp_ring1.set(self.sp_ring1.get() + (hv_pre - hv_ring1).max(0));
2931 self.sp_total.set(self.sp_total.get() + (hv_pre - best_c).max(0));
2932 }
2933 if n + 1 == sp_learn {
2934 let tot = self.sp_total.get();
2935 self.sp_1pass.set(tot > 0 && self.sp_ring1.get() * 100 >= tot * sp_t);
2937 }
2938 }
2939 }
2940 if !subpel.is_empty() && subpel_harvest::enabled() {
2941 subpel_harvest::record(hv_pre, best_c, lambda_me, rw, rh, hv_evals, hv_to_best, hv_ring1);
2942 }
2943 if self.me_snap && seed_c < best_c {
2946 best = seed_mv;
2947 best_c = seed_c;
2948 }
2949 (best, best_c)
2950 }
2951
2952 #[allow(clippy::too_many_arguments)]
2957 #[allow(clippy::too_many_arguments)]
2962 fn encode_inter_mb(
2963 &mut self,
2964 w: &mut BitWriter,
2965 refs: &[crate::RefFrame],
2966 sy: &[u8],
2967 su: &[u8],
2968 sv: &[u8],
2969 mb_x: usize,
2970 mb_y: usize,
2971 mode: u8,
2972 parts: &[(i32, (i32, i32))],
2973 ) {
2974 if self.coded_path_v2 {
2975 self.encode_inter_mb_v2(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2976 } else {
2977 self.encode_inter_mb_v1(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
2978 }
2979 }
2980
2981 #[allow(clippy::too_many_arguments)]
2989 fn encode_inter_mb_v2(
2990 &mut self,
2991 w: &mut BitWriter,
2992 refs: &[crate::RefFrame],
2993 sy: &[u8],
2994 su: &[u8],
2995 sv: &[u8],
2996 mb_x: usize,
2997 mb_y: usize,
2998 mode: u8,
2999 parts: &[(i32, (i32, i32))],
3000 ) {
3001 #[cfg(feature = "profile")]
3003 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(1);
3004 #[cfg(not(accel))]
3005 {
3006 self.encode_inter_mb_v1(w, refs, sy, su, sv, mb_x, mb_y, mode, parts);
3007 }
3008 #[cfg(accel)]
3009 {
3010 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncInterCode);
3011 let (qp, qpc) = (self.qp, self.qpc);
3012 let w4 = self.mb_w * 4;
3013 let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
3014
3015 let mut pred_y = [0u8; 256];
3017 let mut c_pred = [[0u8; 64]; 2];
3018 let mut mvds = [(0i32, 0i32); 4];
3019 let mut n_mvd = 0;
3020 let _g_mc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
3021 for (part, &(rx, ry, rw, rh)) in inter_partitions(mode).iter().enumerate() {
3022 let (refi, mv) = parts[part];
3023 let reference = &refs[refi as usize];
3024 let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
3025 let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
3026 let pmv = predict_partition_mv(mode, part, a, b, c, refi);
3027 mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
3028 n_mvd += 1;
3029 for by in ry / 4..ry / 4 + rh / 4 {
3030 for bx in rx / 4..rx / 4 + rw / 4 {
3031 let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
3032 self.mv_y[idx] = mv;
3033 self.inter_y[idx] = true;
3034 self.ref_idx_y[idx] = refi;
3035 self.coded_y[idx] = true;
3036 }
3037 }
3038 if rw == 16 && rh == 16 {
3039 self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
3040 } else {
3041 let mut tmp = [0u8; 256];
3042 self.mc_luma_cached(reference, mb_x * 16 + rx, mb_y * 16 + ry, rw, rh, mv.0, mv.1, &mut tmp);
3043 if rw == 8 {
3047 for dy in 0..rh {
3048 pred_y[(ry + dy) * 16 + rx..][..8].copy_from_slice(&tmp[dy * 8..][..8]);
3049 }
3050 } else {
3051 for dy in 0..rh {
3052 pred_y[(ry + dy) * 16 + rx..][..16].copy_from_slice(&tmp[dy * 16..][..16]);
3053 }
3054 }
3055 }
3056 let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
3057 for cc in 0..2 {
3058 let rc = if cc == 0 { &reference.u } else { &reference.v };
3059 if crw == 8 && crh == 8 {
3060 mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut c_pred[cc]);
3061 } else {
3062 let mut tc = [0u8; 64];
3063 mc_chroma(rc, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv.0, mv.1, &mut tc);
3064 if crw == 4 {
3066 for dy in 0..crh {
3067 c_pred[cc][(cry + dy) * 8 + crx..][..4].copy_from_slice(&tc[dy * 4..][..4]);
3068 }
3069 } else {
3070 for dy in 0..crh {
3071 c_pred[cc][(cry + dy) * 8 + crx..][..8].copy_from_slice(&tc[dy * 8..][..8]);
3072 }
3073 }
3074 }
3075 }
3076 }
3077
3078 let mut dctw = AlignedDct([0i16; 256]);
3080 let dct = &mut dctw.0;
3081 let mut cbp_luma = 0u32;
3082 drop(_g_mc);
3083 let _g_tq = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncTq);
3084 let base = mb_y * 16 * self.cw + mb_x * 16;
3085 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3086 rusty_h264_accel::dct_four_t4(
3087 &mut dct[qi * 64..qi * 64 + 64],
3088 &sy[base + qy * self.cw + qx..],
3089 self.cw,
3090 &pred_y[qy * 16 + qx..],
3091 16,
3092 );
3093 }
3094 let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
3095 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
3096 for qi in 0..4 {
3097 rusty_h264_accel::quant_four_4x4(&mut dct[qi * 64..qi * 64 + 64], &ff, mf);
3098 }
3099 for blk in 0..16 {
3101 if dct[blk * 16..blk * 16 + 16].iter().any(|&v| v != 0) {
3102 cbp_luma |= 1 << (blk / 4);
3103 }
3104 }
3105
3106 let mut c_dc_levels = [[0i32; 4]; 2];
3108 let mut c_recon_dc = [[0i32; 4]; 2];
3109 let mut c_q = [[[0i32; 16]; 4]; 2];
3110 let (mut any_ac, mut any_dc) = (false, false);
3111 for c in 0..2 {
3112 let src = if c == 0 { su } else { sv };
3113 let dc2x2 = {
3114 #[repr(align(16))]
3115 struct A([i16; 64]);
3116 let mut cdct = A([0i16; 64]);
3117 rusty_h264_accel::dct_four_t4(
3118 &mut cdct.0,
3119 &src[(mb_y * 8) * self.ccw + mb_x * 8..],
3120 self.ccw,
3121 &c_pred[c],
3122 8,
3123 );
3124 let dc = [cdct.0[0] as i32, cdct.0[16] as i32, cdct.0[32] as i32, cdct.0[48] as i32];
3125 let ffc = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
3126 let mfc = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
3127 rusty_h264_accel::quant_four_4x4(&mut cdct.0, &ffc, mfc);
3128 for i in 0..4 {
3129 let q = &mut c_q[c][i];
3130 q[0] = 0;
3131 for j in 1..16 {
3132 let v = cdct.0[i * 16 + j] as i32;
3133 q[j] = v;
3134 if v != 0 {
3135 any_ac = true;
3136 }
3137 }
3138 }
3139 dc
3140 };
3141 let dl = forward_quant_chroma_dc(&dc2x2, qpc, false);
3142 if dl.iter().any(|&v| v != 0) {
3143 any_dc = true;
3144 }
3145 c_recon_dc[c] = inverse_quant_chroma_dc(&dl, qpc);
3146 c_dc_levels[c] = dl;
3147 }
3148 let cbp_chroma: u32 = if any_ac { 2 } else if any_dc { 1 } else { 0 };
3149 let cbp = cbp_luma | (cbp_chroma << 4);
3150
3151 drop(_g_tq);
3153 let _g_syn = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Syntax);
3154 w.write_ue(mode as u32);
3155 let num_refs = refs.len();
3156 if num_refs > 1 {
3157 for &(refi, _) in parts {
3158 write_ref_idx(w, refi, num_refs);
3159 }
3160 }
3161 for &(mvdx, mvdy) in &mvds[..n_mvd] {
3162 w.write_se(mvdx);
3163 w.write_se(mvdy);
3164 }
3165 write_cbp_inter(w, cbp);
3166 if cbp != 0 {
3167 w.write_se(self.qp_delta()); }
3169 self.nnz_cache_load(mb_x, mb_y);
3170 drop(_g_syn);
3171
3172 let _g_scan = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Scatter);
3174 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3175 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
3176 let total = if cbp_luma & (1 << (blk / 4)) != 0 {
3177 let nc = self.nc_pred(lbx, lby);
3178 let scan16 = scan_4x4_dcac_i16(&dct[blk * 16..blk * 16 + 16]);
3179 encode_residual_block(w, &scan16, 16, nc) as u8
3180 } else {
3181 0
3182 };
3183 self.nnz_cache_set(lbx, lby, total);
3184 self.nnz_y[by * w4 + bx] = total;
3185 }
3186 if cbp_chroma != 0 {
3187 for c in 0..2 {
3188 encode_residual_block(w, &c_dc_levels[c], 4, -1);
3189 }
3190 }
3191 if cbp_chroma == 2 {
3192 self.chroma_cache_load(mb_x, mb_y);
3193 let w2 = self.mb_w * 2;
3194 for c in 0..2 {
3195 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
3196 let nc = self.chroma_nc_pred(c, bx, by);
3197 let ac = scan_4x4_ac(&c_q[c][by * 2 + bx]);
3198 let total = encode_residual_block(w, &ac, 15, nc) as u8;
3199 self.chroma_nnz_cache_set(c, bx, by, total);
3200 self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
3201 }
3202 }
3203 }
3204 drop(_g_scan);
3205
3206 let _g_rec = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
3208 #[repr(align(16))]
3209 struct Align16([i16; 64]);
3210 let mut dct_in = Align16([0i16; 64]);
3211 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3212 let rec_off = base + qy * self.cw + qx;
3213 if cbp_luma & (1 << qi) == 0 {
3214 for r in 0..8 {
3215 let (dsti, srci) = (rec_off + r * self.cw, (qy + r) * 16 + qx);
3216 self.rec_y[dsti..dsti + 8].copy_from_slice(&pred_y[srci..srci + 8]);
3217 }
3218 continue;
3219 }
3220 for k in 0..4 {
3221 let blk = qi * 4 + k;
3222 let mut lvl = [0i32; 16];
3223 for i in 0..16 {
3224 lvl[i] = dct[blk * 16 + i] as i32;
3225 }
3226 let deq = dequantize(&lvl, qp);
3227 for i in 0..16 {
3228 dct_in.0[k * 16 + i] = deq[i] as i16;
3229 }
3230 }
3231 rusty_h264_accel::idct_four_t4_rec(
3232 &mut self.rec_y[rec_off..],
3233 self.cw,
3234 &pred_y[qy * 16 + qx..],
3235 16,
3236 &dct_in.0,
3237 );
3238 }
3239 for c in 0..2 {
3241 let base_c = (mb_y * 8) * self.ccw + mb_x * 8;
3242 let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3243 if cbp_chroma == 0 {
3244 for r in 0..8 {
3245 let dsti = base_c + r * self.ccw;
3246 plane[dsti..dsti + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
3247 }
3248 } else {
3249 #[repr(align(16))]
3250 struct A([i16; 64]);
3251 let mut d = A([0i16; 64]);
3252 for i in 0..4 {
3253 let deq = dequantize(&c_q[c][i], qpc);
3254 for j in 0..16 {
3255 d.0[i * 16 + j] = deq[j] as i16;
3256 }
3257 d.0[i * 16] = c_recon_dc[c][i] as i16;
3258 }
3259 rusty_h264_accel::idct_four_t4_rec(&mut plane[base_c..], self.ccw, &c_pred[c], 8, &d.0);
3260 }
3261 }
3262 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3263 self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
3264 }
3265 }
3266 }
3267
3268 #[allow(clippy::too_many_arguments)]
3269 fn encode_inter_mb_v1(
3270 &mut self,
3271 w: &mut BitWriter,
3272 refs: &[crate::RefFrame],
3273 sy: &[u8],
3274 su: &[u8],
3275 sv: &[u8],
3276 mb_x: usize,
3277 mb_y: usize,
3278 mode: u8,
3279 parts: &[(i32, (i32, i32))],
3280 ) {
3281 self.encode_inter_mb_v1_b(w, refs, sy, su, sv, mb_x, mb_y, mode, parts, None);
3282 }
3283
3284 #[allow(clippy::too_many_arguments)]
3295 fn plan_inter_mb(
3296 &mut self,
3297 refs: &[crate::RefFrame],
3298 sy: &[u8],
3299 su: &[u8],
3300 sv: &[u8],
3301 mb_x: usize,
3302 mb_y: usize,
3303 mode: u8,
3304 parts: &[(i32, (i32, i32))],
3305 bspec: Option<BInter>,
3306 ) -> InterPlan {
3307 #[cfg(feature = "profile")]
3309 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(1);
3310 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncInterCode);
3311 let (qp, qpc) = (self.qp, self.qpc);
3312 let w4 = self.mb_w * 4;
3313 let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
3314
3315 let mut pred_y = [0u8; 256];
3317 let mut c_pred = [[0u8; 64]; 2];
3318 let mut mvds = [(0i32, 0i32); 4]; let mut plan_refs = [0i32; 4]; let mut n_mvd = 0;
3321 let _g_mc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::PredBuf);
3322 if let Some(b) = bspec.filter(|b| b.mvmode > 0) {
3332 let (rects, _) = b_part_layout(b.mvmode);
3338 let (ch, cch) = (self.mb_h * 16, self.mb_h * 8);
3339 let mut pm: [[(i32, i32); 2]; 2] = [[(0, 0); 2]; 2]; for (part, &(rx, ry, rw, rh)) in rects.iter().enumerate() {
3341 let (pred, mv0, mv1) = b.parts2[part];
3342 let (u0, u1) = (pred == 1 || pred == 3, pred == 2 || pred == 3);
3343 let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
3344 if u0 {
3345 let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, (rw / 4) as isize, 0);
3346 let p = predict_partition_mv(b.mvmode, part, a, c0, c1, 0);
3347 pm[part][0] = (mv0.0 - p.0, mv0.1 - p.1);
3348 }
3349 if u1 {
3350 let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, (rw / 4) as isize, 1);
3351 let p = predict_partition_mv(b.mvmode, part, a, c0, c1, 0);
3352 pm[part][1] = (mv1.0 - p.0, mv1.1 - p.1);
3353 }
3354 let (lx, ly) = (mb_x * 16 + rx, mb_y * 16 + ry);
3356 let (cx, cy) = (mb_x * 8 + rx / 2, mb_y * 8 + ry / 2);
3357 let (cw2, ch2) = (rw / 2, rh / 2);
3358 let mut ay = [0u8; 256];
3359 let mut by_ = [0u8; 256];
3360 let mut ac = [[0u8; 64]; 2];
3361 let mut bc = [[0u8; 64]; 2];
3362 if u0 {
3363 mc_luma(&refs[0].y, self.cw, ch, lx, ly, rw, rh, mv0.0, mv0.1, &mut ay);
3364 mc_chroma(&refs[0].u, self.ccw, cch, cx, cy, cw2, ch2, mv0.0, mv0.1, &mut ac[0]);
3365 mc_chroma(&refs[0].v, self.ccw, cch, cx, cy, cw2, ch2, mv0.0, mv0.1, &mut ac[1]);
3366 }
3367 if u1 {
3368 mc_luma(&b.l1.y, self.cw, ch, lx, ly, rw, rh, mv1.0, mv1.1, &mut by_);
3369 mc_chroma(&b.l1.u, self.ccw, cch, cx, cy, cw2, ch2, mv1.0, mv1.1, &mut bc[0]);
3370 mc_chroma(&b.l1.v, self.ccw, cch, cx, cy, cw2, ch2, mv1.0, mv1.1, &mut bc[1]);
3371 }
3372 for r in 0..rh {
3373 for c in 0..rw {
3374 let d = (ry + r) * 16 + rx + c;
3375 let sidx = r * rw + c;
3376 pred_y[d] = match (u0, u1) {
3377 (true, true) => bi_blend(ay[sidx] as i32, by_[sidx] as i32, self.bi_w),
3378 (true, false) => ay[sidx],
3379 _ => by_[sidx],
3380 };
3381 }
3382 }
3383 for cc in 0..2 {
3384 for r in 0..ch2 {
3385 for c in 0..cw2 {
3386 let d = (ry / 2 + r) * 8 + rx / 2 + c;
3387 let sidx = r * cw2 + c;
3388 c_pred[cc][d] = match (u0, u1) {
3389 (true, true) => bi_blend(ac[cc][sidx] as i32, bc[cc][sidx] as i32, self.bi_w),
3390 (true, false) => ac[cc][sidx],
3391 _ => bc[cc][sidx],
3392 };
3393 }
3394 }
3395 }
3396 for by2 in ry / 4..(ry + rh) / 4 {
3398 for bx2 in rx / 4..(rx + rw) / 4 {
3399 let idx = (mb_y * 4 + by2) * w4 + (mb_x * 4 + bx2);
3400 self.inter_y[idx] = true;
3401 self.coded_y[idx] = true;
3402 self.mv_y[idx] = if u0 { mv0 } else { (0, 0) };
3403 self.ref_idx_y[idx] = if u0 { 0 } else { -1 };
3404 self.mv1_y[idx] = if u1 { mv1 } else { (0, 0) };
3405 self.ref_idx1_y[idx] = if u1 { 0 } else { -1 };
3406 }
3407 }
3408 }
3409 for list in 0..2 {
3411 for part in 0..2 {
3412 let pred = b.parts2[part].0;
3413 let used = if list == 0 { pred == 1 || pred == 3 } else { pred == 2 || pred == 3 };
3414 if used {
3415 mvds[n_mvd] = pm[part][list];
3416 n_mvd += 1;
3417 }
3418 }
3419 }
3420 } else if let Some(b) = bspec.filter(|b| b.dir == 0) {
3421 let (dp, dc, motion) = self.b_direct(&refs[0], b.l1, mb_x, mb_y);
3423 pred_y = dp;
3424 c_pred = dc;
3425 self.commit_direct_motion(mb_x, mb_y, &motion);
3426 } else if let Some(b) = bspec {
3427 let use0 = b.dir == 1 || b.dir == 3;
3429 let use1 = b.dir == 2 || b.dir == 3;
3430 let (lx, ly) = (mb_x * 16, mb_y * 16);
3431 let (cx, cy) = (mb_x * 8, mb_y * 8);
3432 let (pbx, pby) = ((mb_x * 4) as isize, (mb_y * 4) as isize);
3433 if use0 {
3435 let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, 4, 0);
3436 let p = predict_partition_mv(0, 0, a, c0, c1, 0);
3437 mvds[n_mvd] = (b.mv0.0 - p.0, b.mv0.1 - p.1);
3438 n_mvd += 1;
3439 }
3440 if use1 {
3441 let [a, c0, c1] = self.mv_neighbors_block_list(pbx, pby, 4, 1);
3442 let p = predict_partition_mv(0, 0, a, c0, c1, 0);
3443 mvds[n_mvd] = (b.mv1.0 - p.0, b.mv1.1 - p.1);
3444 n_mvd += 1;
3445 }
3446 let mut a_y = [0u8; 256];
3449 let mut b_y = [0u8; 256];
3450 let mut a_c = [[0u8; 64]; 2];
3451 let mut b_c = [[0u8; 64]; 2];
3452 if use0 {
3453 mc_luma(&refs[0].y, self.cw, ch, lx, ly, 16, 16, b.mv0.0, b.mv0.1, &mut a_y);
3454 mc_chroma(&refs[0].u, self.ccw, cch, cx, cy, 8, 8, b.mv0.0, b.mv0.1, &mut a_c[0]);
3455 mc_chroma(&refs[0].v, self.ccw, cch, cx, cy, 8, 8, b.mv0.0, b.mv0.1, &mut a_c[1]);
3456 }
3457 if use1 {
3458 mc_luma(&b.l1.y, self.cw, ch, lx, ly, 16, 16, b.mv1.0, b.mv1.1, &mut b_y);
3459 mc_chroma(&b.l1.u, self.ccw, cch, cx, cy, 8, 8, b.mv1.0, b.mv1.1, &mut b_c[0]);
3460 mc_chroma(&b.l1.v, self.ccw, cch, cx, cy, 8, 8, b.mv1.0, b.mv1.1, &mut b_c[1]);
3461 }
3462 match (use0, use1) {
3463 (true, true) => {
3464 for i in 0..256 {
3465 pred_y[i] = bi_blend(a_y[i] as i32, b_y[i] as i32, self.bi_w);
3466 }
3467 for c in 0..2 {
3468 for i in 0..64 {
3469 c_pred[c][i] = bi_blend(a_c[c][i] as i32, b_c[c][i] as i32, self.bi_w);
3470 }
3471 }
3472 }
3473 (true, false) => {
3474 pred_y = a_y;
3475 c_pred = a_c;
3476 }
3477 _ => {
3478 pred_y = b_y;
3479 c_pred = b_c;
3480 }
3481 }
3482 for by in 0..4 {
3484 for bx in 0..4 {
3485 let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
3486 self.inter_y[idx] = true;
3487 self.coded_y[idx] = true;
3488 self.mv_y[idx] = if use0 { b.mv0 } else { (0, 0) };
3489 self.ref_idx_y[idx] = if use0 { 0 } else { -1 };
3490 self.mv1_y[idx] = if use1 { b.mv1 } else { (0, 0) };
3491 self.ref_idx1_y[idx] = if use1 { 0 } else { -1 };
3492 }
3493 }
3494 } else {
3495 for (part, &(rx, ry, rw, rh)) in inter_partitions(mode).iter().enumerate() {
3496 let (refi, mv) = parts[part];
3497 plan_refs[part] = refi; let reference = &refs[refi as usize];
3499 let (pbx, pby) = ((mb_x * 4 + rx / 4) as isize, (mb_y * 4 + ry / 4) as isize);
3500 let [a, b, c] = self.mv_neighbors_block(pbx, pby, (rw / 4) as isize);
3501 let pmv = predict_partition_mv(mode, part, a, b, c, refi);
3502 mvds[n_mvd] = (mv.0 - pmv.0, mv.1 - pmv.1);
3503 n_mvd += 1;
3504 for by in ry / 4..ry / 4 + rh / 4 {
3506 for bx in rx / 4..rx / 4 + rw / 4 {
3507 let idx = (mb_y * 4 + by) * w4 + (mb_x * 4 + bx);
3508 self.mv_y[idx] = mv;
3509 self.inter_y[idx] = true;
3510 self.ref_idx_y[idx] = refi;
3511 self.coded_y[idx] = true;
3512 }
3513 }
3514 if rw == 16 && rh == 16 {
3517 self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
3518 } else {
3519 let mut tmp = [0u8; 256];
3520 self.mc_luma_cached(reference, mb_x * 16 + rx, mb_y * 16 + ry, rw, rh, mv.0, mv.1, &mut tmp);
3521 if rw == 8 {
3523 for dy in 0..rh {
3524 pred_y[(ry + dy) * 16 + rx..][..8].copy_from_slice(&tmp[dy * 8..][..8]);
3525 }
3526 } else {
3527 for dy in 0..rh {
3528 pred_y[(ry + dy) * 16 + rx..][..16].copy_from_slice(&tmp[dy * 16..][..16]);
3529 }
3530 }
3531 }
3532 let (crx, cry, crw, crh) = (rx / 2, ry / 2, rw / 2, rh / 2);
3534 for cc in 0..2 {
3535 let rc = if cc == 0 { &reference.u } else { &reference.v };
3536 if crw == 8 && crh == 8 {
3537 mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut c_pred[cc]);
3538 } else {
3539 let mut tc = [0u8; 64];
3540 mc_chroma(rc, self.ccw, cch, mb_x * 8 + crx, mb_y * 8 + cry, crw, crh, mv.0, mv.1, &mut tc);
3541 if crw == 4 {
3543 for dy in 0..crh {
3544 c_pred[cc][(cry + dy) * 8 + crx..][..4].copy_from_slice(&tc[dy * 4..][..4]);
3545 }
3546 } else {
3547 for dy in 0..crh {
3548 c_pred[cc][(cry + dy) * 8 + crx..][..8].copy_from_slice(&tc[dy * 8..][..8]);
3549 }
3550 }
3551 }
3552 }
3553 }
3554 } let mut q_blocks = [[0i32; 16]; 16]; let mut cbp_luma = 0u32;
3559 #[allow(unused_mut)]
3562 let mut t8x8 = false;
3563 #[allow(unused_mut)]
3564 let mut q8 = [[0i32; 64]; 4];
3565 drop(_g_mc);
3566 let _g_tq = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncTq);
3567 #[cfg(accel)]
3568 {
3569 let mut dctw = AlignedDct([0i16; 256]);
3574 let dct = &mut dctw.0;
3575 let base = mb_y * 16 * self.cw + mb_x * 16;
3576 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3577 rusty_h264_accel::dct_four_t4(
3578 &mut dct[qi * 64..qi * 64 + 64],
3579 &sy[base + qy * self.cw + qx..],
3580 self.cw,
3581 &pred_y[qy * 16 + qx..],
3582 16,
3583 );
3584 }
3585 let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
3586 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
3587 for qi in 0..4 {
3588 rusty_h264_accel::quant_four_4x4(&mut dct[qi * 64..qi * 64 + 64], &ff, mf);
3589 }
3590 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3591 let mut nz = false;
3592 for i in 0..16 {
3593 let v = dct[blk * 16 + i] as i32;
3594 q_blocks[lby * 4 + lbx][i] = v;
3595 nz |= v != 0;
3596 }
3597 if nz {
3598 cbp_luma |= 1 << (blk / 4);
3599 }
3600 }
3601 }
3602 #[cfg(not(accel))]
3603 {
3604 let mut res_blocks = [[0i32; 16]; 16]; for lby in 0..4 {
3607 for lbx in 0..4 {
3608 let b = &mut res_blocks[lby * 4 + lbx];
3609 for dy in 0..4 {
3610 for dx in 0..4 {
3611 let sx = mb_x * 16 + lbx * 4 + dx;
3612 let syy = mb_y * 16 + lby * 4 + dy;
3613 b[dy * 4 + dx] = sy[syy * self.cw + sx] as i32
3614 - pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3615 }
3616 }
3617 }
3618 }
3619 let mut coeffs = [[0i32; 16]; 16];
3620 forward_dct_blocks(&res_blocks, &mut coeffs);
3621 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
3622 let q = rdoq(&coeffs[lby * 4 + lbx], qp, 6, self.rdoq_strength, 0);
3623 if q.iter().any(|&v| v != 0) {
3624 cbp_luma |= 1 << (blk / 4);
3625 }
3626 q_blocks[lby * 4 + lbx] = q;
3627 }
3628 }
3629
3630 {
3636 if self.transform_8x8 && self.inter8x8 != 0 {
3637 let lambda =
3638 0.85 * self.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
3639 let mut ssd4 = 0i64;
3640 let mut rate4 = 0f64;
3641 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3642 let mut predb = [0i32; 16];
3643 for dy in 0..4 {
3644 for dx in 0..4 {
3645 predb[dy * 4 + dx] =
3646 pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3647 }
3648 }
3649 let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3650 let s = reconstruct_4x4(&deq, &predb);
3651 for dy in 0..4 {
3652 for dx in 0..4 {
3653 let sx = mb_x * 16 + lbx * 4 + dx;
3654 let syy = mb_y * 16 + lby * 4 + dy;
3655 let d = s[dy * 4 + dx] as i64 - sy[syy * self.cw + sx] as i64;
3656 ssd4 += d * d;
3657 }
3658 }
3659 for &l in &q_blocks[lby * 4 + lbx] {
3660 if l != 0 {
3661 rate4 += rdoq_rate((l as i64).abs());
3662 }
3663 }
3664 }
3665 let (q8c, cbp8, rate8, _rec8, ssd8) =
3666 plan_inter8_luma(sy, self.cw, mb_x, mb_y, &pred_y, qp);
3667 let j4 = ssd4 as f64 + lambda * (rate4 + 16.0);
3670 let j8 = ssd8 as f64 + lambda * (rate8 + 16.0 + self.inter8_pen as f64);
3671 if cbp8 > 0 && j8 < j4 {
3672 t8x8 = true;
3673 cbp_luma = cbp8;
3674 q8 = q8c;
3675 }
3676 }
3677 }
3678
3679 let mut c_dc_levels = [[0i32; 4]; 2];
3681 let mut c_recon_dc = [[0i32; 4]; 2];
3682 let mut c_q = [[[0i32; 16]; 4]; 2];
3683 let (mut any_ac, mut any_dc) = (false, false);
3684 for c in 0..2 {
3685 let src = if c == 0 { su } else { sv };
3686 #[cfg(accel)]
3692 let (mut dc2x2, applied) = {
3693 #[repr(align(16))]
3694 struct A([i16; 64]);
3695 let mut dct = A([0i16; 64]);
3696 rusty_h264_accel::dct_four_t4(
3697 &mut dct.0,
3698 &src[(mb_y * 8) * self.ccw + mb_x * 8..],
3699 self.ccw,
3700 &c_pred[c],
3701 8,
3702 );
3703 let dc = [
3704 dct.0[0] as i32,
3705 dct.0[16] as i32,
3706 dct.0[32] as i32,
3707 dct.0[48] as i32,
3708 ];
3709 let ffc = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
3710 let mfc = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
3711 rusty_h264_accel::quant_four_4x4(&mut dct.0, &ffc, mfc);
3712 for i in 0..4 {
3713 let q = &mut c_q[c][i];
3714 q[0] = 0;
3715 for j in 1..16 {
3716 let v = dct.0[i * 16 + j] as i32;
3717 q[j] = v;
3718 if v != 0 {
3719 any_ac = true;
3720 }
3721 }
3722 }
3723 (dc, true)
3724 };
3725 #[cfg(not(accel))]
3726 let (mut dc2x2, applied) = ([0i32; 4], false);
3727 if !applied {
3728 let mut res_blocks = [[0i32; 16]; 4];
3730 for by in 0..2 {
3731 for bx in 0..2 {
3732 let b = &mut res_blocks[by * 2 + bx];
3733 for dy in 0..4 {
3734 for dx in 0..4 {
3735 let sx = mb_x * 8 + bx * 4 + dx;
3736 let syy = mb_y * 8 + by * 4 + dy;
3737 b[dy * 4 + dx] = src[syy * self.ccw + sx] as i32
3738 - c_pred[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
3739 }
3740 }
3741 }
3742 }
3743 let mut coeffs = [[0i32; 16]; 4];
3744 forward_dct_blocks(&res_blocks, &mut coeffs);
3745 for i in 0..4 {
3746 dc2x2[i] = coeffs[i][0];
3747 let mut q = rdoq(&coeffs[i], qpc, 6, self.rdoq_strength, 1);
3748 q[0] = 0;
3749 if q[1..].iter().any(|&v| v != 0) {
3750 any_ac = true;
3751 }
3752 c_q[c][i] = q;
3753 }
3754 }
3755 let dl = forward_quant_chroma_dc(&dc2x2, qpc, false);
3756 if dl.iter().any(|&v| v != 0) {
3757 any_dc = true;
3758 }
3759 c_recon_dc[c] = inverse_quant_chroma_dc(&dl, qpc);
3760 c_dc_levels[c] = dl;
3761 }
3762 let cbp_chroma: u32 = if any_ac { 2 } else if any_dc { 1 } else { 0 };
3763 let cbp = cbp_luma | (cbp_chroma << 4);
3764
3765 drop(_g_tq);
3766 let _g_rec = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::SkipRecon);
3767 #[cfg(accel)]
3769 if t8x8 {
3770 let weight = [16i32; 64];
3773 for b8 in 0..4usize {
3774 let (b8x, b8y) = (b8 % 2, b8 / 2);
3775 let res_r = inverse_quant_8x8(&q8[b8], qp, &weight);
3776 let predb: [i32; 64] = std::array::from_fn(|i| {
3777 pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
3778 });
3779 let recon = add_residual_8x8(&res_r, &predb);
3780 for dy in 0..8 {
3781 for dx in 0..8 {
3782 let px = mb_x * 16 + b8x * 8 + dx;
3783 let py = mb_y * 16 + b8y * 8 + dy;
3784 self.rec_y[py * self.cw + px] = recon[dy * 8 + dx];
3785 }
3786 }
3787 }
3788 } else {
3789 #[repr(align(16))]
3798 struct Align16([i16; 64]);
3799 let mut dct_in = Align16([0i16; 64]);
3800 let base = mb_y * 16 * self.cw + mb_x * 16;
3801 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
3802 let rec_off = base + qy * self.cw + qx;
3803 if cbp_luma & (1 << qi) == 0 {
3804 for r in 0..8 {
3805 let (dsti, srci) = (rec_off + r * self.cw, (qy + r) * 16 + qx);
3806 self.rec_y[dsti..dsti + 8].copy_from_slice(&pred_y[srci..srci + 8]);
3807 }
3808 continue;
3809 }
3810 for k in 0..4 {
3811 let blk = qi * 4 + k;
3812 let (lbx, lby) = LUMA_4X4_SCAN_XY[blk];
3813 let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3814 for i in 0..16 {
3815 dct_in.0[k * 16 + i] = deq[i] as i16;
3816 }
3817 }
3818 rusty_h264_accel::idct_four_t4_rec(
3819 &mut self.rec_y[rec_off..],
3820 self.cw,
3821 &pred_y[qy * 16 + qx..],
3822 16,
3823 &dct_in.0,
3824 );
3825 }
3826 }
3827 #[cfg(not(accel))]
3828 if t8x8 {
3829 let weight = [16i32; 64];
3831 for b8 in 0..4usize {
3832 let (b8x, b8y) = (b8 % 2, b8 / 2);
3833 let res_r = inverse_quant_8x8(&q8[b8], qp, &weight);
3834 let predb: [i32; 64] = std::array::from_fn(|i| {
3835 pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32
3836 });
3837 let recon = add_residual_8x8(&res_r, &predb);
3838 for dy in 0..8 {
3839 for dx in 0..8 {
3840 let px = mb_x * 16 + b8x * 8 + dx;
3841 let py = mb_y * 16 + b8y * 8 + dy;
3842 self.rec_y[py * self.cw + px] = recon[dy * 8 + dx];
3843 }
3844 }
3845 }
3846 } else {
3847 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3848 let mut predb = [0i32; 16];
3849 for dy in 0..4 {
3850 for dx in 0..4 {
3851 predb[dy * 4 + dx] = pred_y[(lby * 4 + dy) * 16 + (lbx * 4 + dx)] as i32;
3852 }
3853 }
3854 let deq = dequantize(&q_blocks[lby * 4 + lbx], qp);
3855 let s = reconstruct_4x4(&deq, &predb);
3856 store(&mut self.rec_y, self.cw, mb_x * 16 + lbx * 4, mb_y * 16 + lby * 4, &s);
3857 }
3858 }
3859 for c in 0..2 {
3860 #[cfg(accel)]
3865 {
3866 let base = (mb_y * 8) * self.ccw + mb_x * 8;
3867 let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3868 if cbp_chroma == 0 {
3869 for r in 0..8 {
3871 let dsti = base + r * self.ccw;
3872 plane[dsti..dsti + 8].copy_from_slice(&c_pred[c][r * 8..r * 8 + 8]);
3873 }
3874 } else {
3875 #[repr(align(16))]
3876 struct A([i16; 64]);
3877 let mut d = A([0i16; 64]);
3878 for i in 0..4 {
3879 let deq = dequantize(&c_q[c][i], qpc);
3880 for j in 0..16 {
3881 d.0[i * 16 + j] = deq[j] as i16;
3882 }
3883 d.0[i * 16] = c_recon_dc[c][i] as i16;
3884 }
3885 rusty_h264_accel::idct_four_t4_rec(&mut plane[base..], self.ccw, &c_pred[c], 8, &d.0);
3886 }
3887 }
3888 #[cfg(not(accel))]
3889 {
3890 let mut deq_blocks = [[0i32; 16]; 4];
3893 for i in 0..4 {
3894 deq_blocks[i] = dequantize(&c_q[c][i], qpc);
3895 deq_blocks[i][0] = c_recon_dc[c][i];
3896 }
3897 let mut res = [[0i32; 16]; 4];
3898 inverse_dct_blocks(&deq_blocks, &mut res);
3899 let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
3900 for by in 0..2 {
3901 for bx in 0..2 {
3902 let mut predb = [0i32; 16];
3903 for dy in 0..4 {
3904 for dx in 0..4 {
3905 predb[dy * 4 + dx] = c_pred[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
3906 }
3907 }
3908 let s = add_residual_4x4(&res[by * 2 + bx], &predb);
3909 store(plane, self.ccw, mb_x * 8 + bx * 4, mb_y * 8 + by * 4, &s);
3910 }
3911 }
3912 }
3913 }
3914 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
3916 self.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
3917 }
3918 InterPlan { mvds, plan_refs, n_mvd, cbp, q_blocks, c_dc_levels, c_q, t8x8, q8 }
3919 }
3920
3921 #[allow(clippy::too_many_arguments)]
3924 fn encode_inter_mb_v1_b(
3925 &mut self,
3926 w: &mut BitWriter,
3927 refs: &[crate::RefFrame],
3928 sy: &[u8],
3929 su: &[u8],
3930 sv: &[u8],
3931 mb_x: usize,
3932 mb_y: usize,
3933 mode: u8,
3934 parts: &[(i32, (i32, i32))],
3935 bspec: Option<BInter>,
3936 ) {
3937 let plan = self.plan_inter_mb(refs, sy, su, sv, mb_x, mb_y, mode, parts, bspec);
3938 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
3939 self.emit_inter_cavlc(w, refs.len(), mb_x, mb_y, mode, parts, bspec, &plan);
3940 }
3941
3942 #[allow(clippy::too_many_arguments)]
3944 fn emit_inter_cavlc(
3945 &mut self,
3946 w: &mut BitWriter,
3947 num_refs: usize,
3948 mb_x: usize,
3949 mb_y: usize,
3950 mode: u8,
3951 parts: &[(i32, (i32, i32))],
3952 bspec: Option<BInter>,
3953 plan: &InterPlan,
3954 ) {
3955 let w4 = self.mb_w * 4;
3956 let (cbp, cbp_luma, cbp_chroma) = (plan.cbp, plan.cbp & 15, plan.cbp >> 4);
3957 w.write_ue(bspec.map_or(mode as u32, |b| b.dir as u32)); if mode == 3 {
3964 for _ in 0..4 {
3965 w.write_ue(0);
3966 }
3967 }
3968 if num_refs > 1 {
3969 for &(refi, _) in parts {
3970 write_ref_idx(w, refi, num_refs);
3971 }
3972 }
3973 for &(mvdx, mvdy) in &plan.mvds[..plan.n_mvd] {
3974 w.write_se(mvdx);
3975 w.write_se(mvdy);
3976 }
3977 write_cbp_inter(w, cbp);
3978 if cbp_luma > 0 && self.transform_8x8 {
3982 w.write_bit(plan.t8x8);
3983 }
3984 if cbp != 0 {
3985 w.write_se(self.qp_delta()); }
3987 self.nnz_cache_load(mb_x, mb_y);
3988 if plan.t8x8 {
3989 for b8 in 0..4usize {
3993 let (b8x, b8y) = (b8 % 2, b8 / 2);
3994 let scan8 = scan_8x8_fwd(&plan.q8[b8]);
3995 for sub in 0..4usize {
3996 let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
3997 let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
3998 let total = if cbp_luma & (1 << b8) != 0 {
3999 let nc = self.nc_pred(cx, cy);
4000 let blk: [i32; 16] = std::array::from_fn(|k| scan8[4 * k + sub]);
4001 encode_residual_block(w, &blk, 16, nc) as u8
4002 } else {
4003 0
4004 };
4005 self.nnz_cache_set(cx, cy, total);
4006 self.nnz_y[by * w4 + bx] = total;
4007 }
4008 }
4009 } else {
4010 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
4011 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
4012 let total = if cbp_luma & (1 << (blk / 4)) != 0 {
4013 let nc = self.nc_pred(lbx, lby);
4014 let scan16 = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
4015 encode_residual_block(w, &scan16, 16, nc) as u8
4016 } else {
4017 0
4018 };
4019 self.nnz_cache_set(lbx, lby, total);
4020 self.nnz_y[by * w4 + bx] = total;
4021 }
4022 }
4023 if cbp_chroma != 0 {
4024 for c in 0..2 {
4025 encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
4026 }
4027 }
4028 if cbp_chroma == 2 {
4029 self.chroma_cache_load(mb_x, mb_y);
4030 let w2 = self.mb_w * 2;
4031 for c in 0..2 {
4032 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
4033 let nc = self.chroma_nc_pred(c, bx, by);
4034 let ac = scan_4x4_ac(&plan.c_q[c][by * 2 + bx]);
4035 let total = encode_residual_block(w, &ac, 15, nc) as u8;
4036 self.chroma_nnz_cache_set(c, bx, by, total);
4037 self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
4038 }
4039 }
4040 }
4041 }
4042
4043 #[inline]
4053 fn mc_luma_cached(
4054 &self,
4055 reference: &crate::RefFrame,
4056 x0: usize,
4057 y0: usize,
4058 bw: usize,
4059 bh: usize,
4060 mvx: i32,
4061 mvy: i32,
4062 out: &mut [u8],
4063 ) {
4064 let ch = self.mb_h * 16;
4065 let cw = self.cw;
4066 if !self.fast {
4067 let p = reference.hpel(cw, ch);
4068 if rusty_h264_common::inter::hpel_block(p, x0, y0, bw, bh, mvx, mvy, out) {
4069 return;
4070 }
4071 if let Some((plane, base, stride)) =
4072 rusty_h264_common::inter::hpel_ref(p, x0, y0, bw, bh, mvx, mvy)
4073 {
4074 for r in 0..bh {
4075 out[r * bw..r * bw + bw].copy_from_slice(&plane[base + r * stride..][..bw]);
4076 }
4077 return;
4078 }
4079 }
4080 mc_luma(&reference.y, cw, ch, x0, y0, bw, bh, mvx, mvy, out);
4081 }
4082
4083 fn skip_predict_luma(
4089 &self,
4090 refs: &[crate::RefFrame],
4091 mb_x: usize,
4092 mb_y: usize,
4093 mv: (i32, i32),
4094 ) -> [u8; 256] {
4095 #[cfg(feature = "profile")]
4097 let _site = rusty_h264_common::inter::mcstats::SiteTag::new(3);
4098 let reference = &refs[0]; let ch = self.mb_h * 16;
4100 let mut pred_y = [0u8; 256];
4101 self.mc_luma_cached(reference, mb_x * 16, mb_y * 16, 16, 16, mv.0, mv.1, &mut pred_y);
4102 pred_y
4103 }
4104
4105 fn skip_predict_chroma(
4107 &self,
4108 refs: &[crate::RefFrame],
4109 mb_x: usize,
4110 mb_y: usize,
4111 mv: (i32, i32),
4112 ) -> [[u8; 64]; 2] {
4113 let reference = &refs[0];
4114 let cch = self.mb_h * 8;
4115 let mut pred_c = [[0u8; 64]; 2];
4116 for c in 0..2 {
4117 let rc = if c == 0 { &reference.u } else { &reference.v };
4118 mc_chroma(rc, self.ccw, cch, mb_x * 8, mb_y * 8, 8, 8, mv.0, mv.1, &mut pred_c[c]);
4119 }
4120 pred_c
4121 }
4122
4123 fn skip_luma_is_free(&self, sy: &[u8], mb_x: usize, mb_y: usize, pred_y: &[u8; 256]) -> bool {
4128 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFree);
4129 let qp = self.qp;
4130 #[cfg(accel)]
4137 if self.skip_accel_check {
4138 #[repr(align(16))]
4139 struct Align16([i16; 64]);
4140 let mut dct = Align16([0i16; 64]);
4141 let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
4142 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
4143 for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
4144 rusty_h264_accel::dct_four_t4(
4145 &mut dct.0,
4146 &sy[(mb_y * 16 + qy) * self.cw + mb_x * 16 + qx..],
4147 self.cw,
4148 &pred_y[qy * 16 + qx..],
4149 16,
4150 );
4151 rusty_h264_accel::quant_four_4x4(&mut dct.0, &ff, mf);
4152 if dct.0.iter().any(|&v| v != 0) {
4153 return false;
4154 }
4155 }
4156 return true;
4157 }
4158 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
4164 let ff = rusty_h264_common::transform::quant_dz_ff(qp, 6);
4165 let mut t_min = i32::MAX;
4166 for p in 0..8 {
4167 let t = (65536 + mf[p] as i32 - 1) / mf[p] as i32 - ff[p] as i32;
4168 t_min = t_min.min(t);
4169 }
4170 let t_dc = (65536 + mf[0] as i32 - 1) / mf[0] as i32 - ff[0] as i32;
4171 for by in 0..4 {
4176 for bx in 0..4 {
4177 let mut res = [0i32; 16];
4178 let (mut sad, mut dc) = (0i32, 0i32);
4179 for dy in 0..4 {
4180 for dx in 0..4 {
4181 let sx = mb_x * 16 + bx * 4 + dx;
4182 let syy = mb_y * 16 + by * 4 + dy;
4183 let d = sy[syy * self.cw + sx] as i32
4184 - pred_y[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
4185 res[dy * 4 + dx] = d;
4186 sad += d.abs();
4187 dc += d;
4188 }
4189 }
4190 if 4 * sad < t_min {
4191 continue; }
4193 if dc.abs() >= t_dc {
4194 return false; }
4196 if quantize(&forward_core(&res), qp, 6).iter().any(|&v| v != 0) {
4197 return false;
4198 }
4199 }
4200 }
4201 true
4202 }
4203
4204 fn skip_chroma_is_free(
4206 &self,
4207 su: &[u8],
4208 sv: &[u8],
4209 mb_x: usize,
4210 mb_y: usize,
4211 pred_c: &[[u8; 64]; 2],
4212 ) -> bool {
4213 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFree);
4214 let qpc = self.qpc;
4215 #[cfg(accel)]
4221 if self.skip_accel_check {
4222 #[repr(align(16))]
4223 struct Align16C([i16; 64]);
4224 let mut dct = Align16C([0i16; 64]);
4225 let ff = rusty_h264_common::transform::quant_dz_ff(qpc, 6);
4226 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
4227 for c in 0..2 {
4228 let src = if c == 0 { su } else { sv };
4229 rusty_h264_accel::dct_four_t4(
4230 &mut dct.0,
4231 &src[(mb_y * 8) * self.ccw + mb_x * 8..],
4232 self.ccw,
4233 &pred_c[c],
4234 8,
4235 );
4236 let dc2x2 = [
4237 dct.0[0] as i32,
4238 dct.0[16] as i32,
4239 dct.0[32] as i32,
4240 dct.0[48] as i32,
4241 ];
4242 rusty_h264_accel::quant_four_4x4(&mut dct.0, &ff, mf);
4243 for b in 0..4 {
4244 if dct.0[b * 16 + 1..b * 16 + 16].iter().any(|&v| v != 0) {
4245 return false;
4246 }
4247 }
4248 if forward_quant_chroma_dc(&dc2x2, qpc, false).iter().any(|&v| v != 0) {
4249 return false;
4250 }
4251 }
4252 return true;
4253 }
4254 for c in 0..2 {
4255 let src = if c == 0 { su } else { sv };
4256 let mut dc2x2 = [0i32; 4];
4257 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
4258 let mut res = [0i32; 16];
4259 for dy in 0..4 {
4260 for dx in 0..4 {
4261 let sx = mb_x * 8 + bx * 4 + dx;
4262 let syy = mb_y * 8 + by * 4 + dy;
4263 res[dy * 4 + dx] = src[syy * self.ccw + sx] as i32
4264 - pred_c[c][(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
4265 }
4266 }
4267 let coeffs = forward_core(&res);
4268 dc2x2[by * 2 + bx] = coeffs[0];
4269 if quantize(&coeffs, qpc, 6)[1..].iter().any(|&v| v != 0) {
4270 return false;
4271 }
4272 }
4273 if forward_quant_chroma_dc(&dc2x2, qpc, false).iter().any(|&v| v != 0) {
4274 return false;
4275 }
4276 }
4277 true
4278 }
4279
4280 #[allow(clippy::too_many_arguments)]
4282 fn pred_ssd(
4283 &self,
4284 sy: &[u8],
4285 su: &[u8],
4286 sv: &[u8],
4287 mb_x: usize,
4288 mb_y: usize,
4289 pred_y: &[u8; 256],
4290 pred_c: &[[u8; 64]; 2],
4291 ) -> i64 {
4292 let mut ssd = 0i64;
4293 for dy in 0..16 {
4294 for dx in 0..16 {
4295 let d = sy[(mb_y * 16 + dy) * self.cw + mb_x * 16 + dx] as i64
4296 - pred_y[dy * 16 + dx] as i64;
4297 ssd += d * d;
4298 }
4299 }
4300 for c in 0..2 {
4301 let src = if c == 0 { su } else { sv };
4302 for dy in 0..8 {
4303 for dx in 0..8 {
4304 let d = src[(mb_y * 8 + dy) * self.ccw + mb_x * 8 + dx] as i64
4305 - pred_c[c][dy * 8 + dx] as i64;
4306 ssd += d * d;
4307 }
4308 }
4309 }
4310 ssd
4311 }
4312
4313 fn mb_ssd(&self, sy: &[u8], su: &[u8], sv: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4315 let mut ssd = 0i64;
4316 for dy in 0..16 {
4317 for dx in 0..16 {
4318 let i = (mb_y * 16 + dy) * self.cw + mb_x * 16 + dx;
4319 let d = sy[i] as i64 - self.rec_y[i] as i64;
4320 ssd += d * d;
4321 }
4322 }
4323 for c in 0..2 {
4324 let (src, rec) = if c == 0 { (su, &self.rec_u) } else { (sv, &self.rec_v) };
4325 for dy in 0..8 {
4326 for dx in 0..8 {
4327 let i = (mb_y * 8 + dy) * self.ccw + mb_x * 8 + dx;
4328 let d = src[i] as i64 - rec[i] as i64;
4329 ssd += d * d;
4330 }
4331 }
4332 }
4333 ssd
4334 }
4335
4336 #[allow(clippy::too_many_arguments)]
4339 fn commit_skip_probe_marker(&self) {}
4340 fn commit_skip(
4341 &mut self,
4342 mb_x: usize,
4343 mb_y: usize,
4344 mv: (i32, i32),
4345 pred_y: &[u8; 256],
4346 pred_c: &[[u8; 64]; 2],
4347 ) {
4348 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::MvGrid);
4349 let base = mb_y * 16 * self.cw + mb_x * 16;
4352 for r in 0..16 {
4353 let d = base + r * self.cw;
4354 self.rec_y[d..d + 16].copy_from_slice(&pred_y[r * 16..r * 16 + 16]);
4355 }
4356 let cbase = mb_y * 8 * self.ccw + mb_x * 8;
4357 for c in 0..2 {
4358 let plane = if c == 0 { &mut self.rec_u } else { &mut self.rec_v };
4359 for r in 0..8 {
4360 let d = cbase + r * self.ccw;
4361 plane[d..d + 8].copy_from_slice(&pred_c[c][r * 8..r * 8 + 8]);
4362 }
4363 }
4364 self.set_mb_mv(mb_x, mb_y, mv, true, 0);
4365 let w4 = self.mb_w * 4;
4366 for row in 0..4 {
4367 let st = (mb_y * 4 + row) * w4 + mb_x * 4;
4368 self.modes_y[st..st + 4].fill(2);
4369 self.coded_y[st..st + 4].fill(true);
4370 }
4371 }
4372
4373 #[allow(clippy::too_many_arguments)]
4379 fn trial_inter(
4380 &mut self,
4381 refs: &[crate::RefFrame],
4382 sy: &[u8],
4383 su: &[u8],
4384 sv: &[u8],
4385 mb_x: usize,
4386 mb_y: usize,
4387 mode: u8,
4388 parts: &[(i32, (i32, i32))],
4389 ) -> (i64, usize) {
4390 let snap = self.save_mb(mb_x, mb_y);
4391 let mut scratch = BitWriter::new();
4392 self.encode_inter_mb(&mut scratch, refs, sy, su, sv, mb_x, mb_y, mode, parts);
4393 let bits = scratch.bit_len();
4394 let ssd = self.mb_ssd(sy, su, sv, mb_x, mb_y);
4395 self.load_mb(mb_x, mb_y, &snap);
4396 (ssd, bits)
4397 }
4398
4399 fn trial_intra(
4403 &mut self,
4404 sy: &[u8],
4405 su: &[u8],
4406 sv: &[u8],
4407 mb_x: usize,
4408 mb_y: usize,
4409 is_p: bool,
4410 ) -> (i64, usize) {
4411 let snap = self.save_mb(mb_x, mb_y);
4412 let mut scratch = BitWriter::new();
4413 encode_mb(self, &mut scratch, mb_x, mb_y, sy, su, sv, is_p);
4414 let bits = scratch.bit_len();
4415 let ssd = self.mb_ssd(sy, su, sv, mb_x, mb_y);
4416 self.load_mb(mb_x, mb_y, &snap);
4417 (ssd, bits)
4418 }
4419
4420 #[allow(clippy::too_many_arguments)]
4425 fn best_part(
4426 &self,
4427 refs: &[crate::RefFrame],
4428 sy: &[u8],
4429 nb: &[MvNeighbor; 3],
4430 num_refs: usize,
4431 rx: usize,
4432 ry: usize,
4433 rw: usize,
4434 rh: usize,
4435 extra: &[(i32, i32)],
4436 lme: f64,
4437 ) -> (i32, (i32, i32), i64) {
4438 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMe);
4439 let [a, b, c] = *nb;
4440 let (mut br, mut bmv, mut bc) = (0i32, (0, 0), i64::MAX);
4441 for r in 0..num_refs {
4442 let mut seeds = vec![predict_mv(a, b, c, r as i32)];
4443 seeds.extend_from_slice(extra);
4444 let (mv, cost) = self.motion_search(&refs[r], sy, rx, ry, rw, rh, &seeds, lme, None);
4445 let cost = cost + (lme * ref_bits(r, num_refs) as f64) as i64;
4446 if cost < bc {
4447 bc = cost;
4448 br = r as i32;
4449 bmv = mv;
4450 }
4451 }
4452 (br, bmv, bc)
4453 }
4454
4455 #[allow(clippy::too_many_arguments)]
4459 fn refine_part(
4460 &self,
4461 refs: &[crate::RefFrame],
4462 sy: &[u8],
4463 nb: &[MvNeighbor; 3],
4464 num_refs: usize,
4465 rx: usize,
4466 ry: usize,
4467 rw: usize,
4468 rh: usize,
4469 lme: f64,
4470 r: i32,
4471 mv: (i32, i32),
4472 ) -> ((i32, i32), i64) {
4473 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMe);
4474 let [a, b, c] = *nb;
4475 let rb = (lme * ref_bits(r as usize, num_refs) as f64) as i64;
4476 let seeds = [predict_mv(a, b, c, r)];
4477 let (m, cc) = self.motion_search(&refs[r as usize], sy, rx, ry, rw, rh, &seeds, lme, Some(mv));
4478 (m, cc + rb)
4479 }
4480
4481 fn best_i16_sad(&self, sy: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4485 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCost);
4486 let (lx, ly) = (mb_x * 16, mb_y * 16);
4487 let (avail_top, avail_left) = (mb_y > 0, mb_x > 0);
4488 let mut top = [0u8; 16];
4489 let mut left = [0u8; 16];
4490 if avail_top {
4491 for i in 0..16 {
4492 top[i] = self.rec_y[(ly - 1) * self.cw + lx + i];
4493 }
4494 }
4495 if avail_left {
4496 for i in 0..16 {
4497 left[i] = self.rec_y[(ly + i) * self.cw + lx - 1];
4498 }
4499 }
4500 let corner = if avail_top && avail_left {
4501 self.rec_y[(ly - 1) * self.cw + lx - 1]
4502 } else {
4503 0
4504 };
4505 let mut best = i64::MAX;
4506 for mode in [I16Mode::Dc, I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
4507 if !mode.available(avail_top, avail_left) {
4508 continue;
4509 }
4510 let pred = i16_pred(self, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
4511 best = best.min(sad_16x16(sy, self.cw, lx, ly, &pred));
4512 }
4513 best
4514 }
4515
4516 fn best_i16_satd(&self, sy: &[u8], mb_x: usize, mb_y: usize) -> i64 {
4519 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCost);
4520 let (lx, ly) = (mb_x * 16, mb_y * 16);
4521 let (avail_top, avail_left) = (mb_y > 0, mb_x > 0);
4522 let mut top = [0u8; 16];
4523 let mut left = [0u8; 16];
4524 if avail_top {
4525 for i in 0..16 {
4526 top[i] = self.rec_y[(ly - 1) * self.cw + lx + i];
4527 }
4528 }
4529 if avail_left {
4530 for i in 0..16 {
4531 left[i] = self.rec_y[(ly + i) * self.cw + lx - 1];
4532 }
4533 }
4534 let corner = if avail_top && avail_left {
4535 self.rec_y[(ly - 1) * self.cw + lx - 1]
4536 } else {
4537 0
4538 };
4539 let mut best = i64::MAX;
4540 for mode in [I16Mode::Dc, I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
4541 if !mode.available(avail_top, avail_left) {
4542 continue;
4543 }
4544 let pred = i16_pred(self, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
4545 best = best.min(satd_16x16(sy, self.cw, lx, ly, &pred));
4546 }
4547 best
4548 }
4549
4550 fn save_mb(&self, mb_x: usize, mb_y: usize) -> MbState {
4553 let mut d = MbState::default();
4554 self.save_mb_into(mb_x, mb_y, &mut d);
4555 d
4556 }
4557
4558 fn save_mb_into(&self, mb_x: usize, mb_y: usize, d: &mut MbState) {
4562 let w4 = self.mb_w * 4;
4563 let w2 = self.mb_w * 2;
4564 macro_rules! reg4 {
4565 ($v:expr, $o:expr) => {{
4566 $o.clear();
4567 for dy in 0..4 {
4568 for dx in 0..4 {
4569 $o.push($v[(mb_y * 4 + dy) * w4 + mb_x * 4 + dx]);
4570 }
4571 }
4572 }};
4573 }
4574 macro_rules! regn {
4575 ($v:expr, $o:expr, $n:expr, $ox:expr, $oy:expr, $stride:expr) => {{
4576 $o.clear();
4577 for dy in 0..$n {
4578 for dx in 0..$n {
4579 $o.push($v[($oy + dy) * $stride + $ox + dx]);
4580 }
4581 }
4582 }};
4583 }
4584 regn!(self.rec_y, d.rec_y, 16, mb_x * 16, mb_y * 16, self.cw);
4585 regn!(self.rec_u, d.rec_u, 8, mb_x * 8, mb_y * 8, self.ccw);
4586 regn!(self.rec_v, d.rec_v, 8, mb_x * 8, mb_y * 8, self.ccw);
4587 reg4!(self.nnz_y, d.nnz_y);
4588 regn!(self.nnz_c[0], d.nnz_c[0], 2, mb_x * 2, mb_y * 2, w2);
4589 regn!(self.nnz_c[1], d.nnz_c[1], 2, mb_x * 2, mb_y * 2, w2);
4590 reg4!(self.mv_y, d.mv_y);
4591 reg4!(self.inter_y, d.inter_y);
4592 reg4!(self.ref_idx_y, d.ref_idx_y);
4593 reg4!(self.coded_y, d.coded_y);
4594 reg4!(self.modes_y, d.modes_y);
4595 d.cur_qp = self.cur_qp;
4596 }
4597
4598 fn load_mb(&mut self, mb_x: usize, mb_y: usize, s: &MbState) {
4600 let w4 = self.mb_w * 4;
4601 let w2 = self.mb_w * 2;
4602 macro_rules! put4 {
4603 ($v:expr, $src:expr) => {
4604 for dy in 0..4 {
4605 for dx in 0..4 {
4606 $v[(mb_y * 4 + dy) * w4 + mb_x * 4 + dx] = $src[dy * 4 + dx];
4607 }
4608 }
4609 };
4610 }
4611 macro_rules! putn {
4612 ($v:expr, $src:expr, $n:expr, $ox:expr, $oy:expr, $stride:expr) => {
4613 for dy in 0..$n {
4614 for dx in 0..$n {
4615 $v[($oy + dy) * $stride + $ox + dx] = $src[dy * $n + dx];
4616 }
4617 }
4618 };
4619 }
4620 putn!(self.rec_y, s.rec_y, 16, mb_x * 16, mb_y * 16, self.cw);
4621 putn!(self.rec_u, s.rec_u, 8, mb_x * 8, mb_y * 8, self.ccw);
4622 putn!(self.rec_v, s.rec_v, 8, mb_x * 8, mb_y * 8, self.ccw);
4623 put4!(self.nnz_y, s.nnz_y);
4624 putn!(self.nnz_c[0], s.nnz_c[0], 2, mb_x * 2, mb_y * 2, w2);
4625 putn!(self.nnz_c[1], s.nnz_c[1], 2, mb_x * 2, mb_y * 2, w2);
4626 put4!(self.mv_y, s.mv_y);
4627 put4!(self.inter_y, s.inter_y);
4628 put4!(self.ref_idx_y, s.ref_idx_y);
4629 put4!(self.coded_y, s.coded_y);
4630 put4!(self.modes_y, s.modes_y);
4631 self.cur_qp = s.cur_qp;
4632 }
4633
4634 fn nnz_cache_load(&mut self, mb_x: usize, mb_y: usize) {
4639 let w4 = self.mb_w * 4;
4640 for lbx in 0..4 {
4641 self.nnz_l_cache[1 + lbx] = if mb_y == 0 {
4642 0x80
4643 } else {
4644 self.nnz_y[(mb_y * 4 - 1) * w4 + (mb_x * 4 + lbx)]
4645 };
4646 }
4647 for lby in 0..4 {
4648 self.nnz_l_cache[(lby + 1) * 5] = if mb_x == 0 {
4649 0x80
4650 } else {
4651 self.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 - 1)]
4652 };
4653 }
4654 }
4655
4656 #[inline]
4660 fn nc_pred(&self, lbx: usize, lby: usize) -> i32 {
4661 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;
4664 if r < 0x80 {
4665 (r + 1) >> 1
4666 } else {
4667 r & 0x7f
4668 }
4669 }
4670
4671 #[inline]
4673 fn nnz_cache_set(&mut self, lbx: usize, lby: usize, total: u8) {
4674 self.nnz_l_cache[(lby + 1) * 5 + (lbx + 1)] = total;
4675 }
4676
4677 fn chroma_cache_load(&mut self, mb_x: usize, mb_y: usize) {
4681 let w2 = self.mb_w * 2;
4682 for c in 0..2 {
4683 for bx in 0..2 {
4684 self.nnz_c_cache[c][1 + bx] = if mb_y == 0 {
4685 0x80
4686 } else {
4687 self.nnz_c[c][(mb_y * 2 - 1) * w2 + (mb_x * 2 + bx)]
4688 };
4689 }
4690 for by in 0..2 {
4691 self.nnz_c_cache[c][(by + 1) * 3] = if mb_x == 0 {
4692 0x80
4693 } else {
4694 self.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 - 1)]
4695 };
4696 }
4697 }
4698 }
4699
4700 #[inline]
4702 fn chroma_nc_pred(&self, c: usize, bx: usize, by: usize) -> i32 {
4703 let left = self.nnz_c_cache[c][(by + 1) * 3 + bx] as i32;
4704 let top = self.nnz_c_cache[c][by * 3 + (bx + 1)] as i32;
4705 let r = left + top;
4706 if r < 0x80 {
4707 (r + 1) >> 1
4708 } else {
4709 r & 0x7f
4710 }
4711 }
4712
4713 #[inline]
4715 fn chroma_nnz_cache_set(&mut self, c: usize, bx: usize, by: usize, total: u8) {
4716 self.nnz_c_cache[c][(by + 1) * 3 + (bx + 1)] = total;
4717 }
4718}
4719
4720#[inline(never)]
4737fn derive_mb_bs_from(
4738 fe: &FrameEncoder,
4739 mb_x: usize,
4740 mb_y: usize,
4741 kind: rusty_h264_common::deblock::MbKind,
4742) -> rusty_h264_common::deblock::MbBs {
4743 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncBs);
4744 let view = rusty_h264_common::deblock::BlockInfo {
4745 inter: &fe.inter_y,
4746 nnz: &fe.nnz_y,
4747 mv: &fe.mv_y,
4748 ref_id: &fe.ref_idx_y,
4749 mv1: &[],
4750 ref_id1: &[],
4751 w4: fe.mb_w * 4,
4752 t8x8: &[],
4753 bs: &[],
4754 };
4755 rusty_h264_common::deblock::derive_mb_kind(&view, mb_x, mb_y, kind)
4756}
4757
4758pub fn encode_slice_data(
4759 w: &mut BitWriter,
4760 cfg: &EncoderConfig,
4761 frame: &YuvFrame,
4762 qp: u8,
4763 is_p: bool,
4764 refs: &[crate::RefFrame],
4765 qpo: &[i32],
4766) -> crate::RefFrame {
4767 let _g_prep = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncPrep);
4768 let mut fe = FrameEncoder::new(cfg);
4769 let precomp = rusty_h264_common::deblock::precomputed_bs_enabled();
4770 let mut bs_grid =
4771 vec![rusty_h264_common::deblock::MbBs::UNSET; if precomp { fe.mb_w * fe.mb_h } else { 0 }];
4772 fe.qp = qp;
4773 fe.qpc = chroma_qp(qp);
4774 fe.cur_qp = qp;
4775 if cfg.cabac_dz_div > 0 {
4776 fe.idz = cfg.cabac_dz_div; } let (sy, su, sv) = coded_source(cfg, frame);
4779 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
4780 let num_refs = refs.len();
4781 if is_p && fe.me_wide && !refs.is_empty()
4786 && global_mc_residual(&sy, fe.cw, fe.mb_h * 16, &refs[0].y) < fe.me_wide_coh
4787 {
4788 fe.me_wide = false;
4789 }
4790 if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
4797 let hr = me_wide_headroom(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
4798 if me_wide_hr_dbg() {
4799 eprintln!("ME_HR qp{qp} headroom={hr:.2}");
4800 }
4801 if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
4802 fe.me_wide = false;
4803 }
4804 }
4805 if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
4810 let (mg, dc) = b2_mgain(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
4811 if me_sadt_dbg() {
4812 eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
4813 }
4814 fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
4815 if mv_smooth_mode() == 1 {
4818 fe.mv_smooth = mg >= mv_smooth_t() && dc <= me_sad_dcmax();
4821 }
4822 let smg = split_mg();
4824 if smg > 0.0 {
4825 fe.do_splits = mg >= smg;
4826 }
4827 }
4828 if is_p && fe.satd_q > 0.0 {
4835 let mut vars: Vec<i64> = (0..fe.mb_h)
4836 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
4837 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
4838 .collect();
4839 vars.sort_unstable();
4840 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
4841 fe.satd_var_thresh = vars[idx];
4842 }
4843 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
4848 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
4850 let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
4851 let mut skip_run = 0u32;
4852 if is_p && mv_cmp_on() {
4861 MVCMP_FRAME.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
4862 }
4863 let mut rdskip_snap = MbState::default();
4865 let mut rdskip_free = 0usize;
4866 let mut rdskip_seen = 0usize;
4867 let mut rdskip_on = false;
4868 let mut greedy_on = fe.greedy_min_free == 0; let rdskip_learn = (fe.mb_w * fe.mb_h / 8).max(64);
4870 let rdskip_min_free = fe.rd_skip_min_free as usize;
4871
4872 drop(_g_prep);
4873 let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
4874 for mb_y in 0..fe.mb_h {
4875 for mb_x in 0..fe.mb_w {
4876 let mb_idx = mb_y * fe.mb_w + mb_x;
4877 fe.qp = aq_qp[mb_idx];
4878 fe.qpc = chroma_qp(aq_qp[mb_idx]);
4879 let mut inter: Option<InterChoice> = None;
4882 let mut coded: Option<BitWriter> = None;
4886 if is_p {
4887 if num_refs > 0 {
4888 let _g_skip = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncSkip);
4892 let _g_smc = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::Neighbors);
4893 rdskip_seen += 1;
4894 if rdskip_seen >= rdskip_learn {
4895 rdskip_on = rdskip_free * 100 >= rdskip_seen * rdskip_min_free;
4896 greedy_on = fe.greedy_min_free == 0
4897 || rdskip_free * 100 >= rdskip_seen * fe.greedy_min_free as usize;
4898 }
4899 let mv_skip = fe.skip_mv(mb_x, mb_y);
4900 let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
4901 drop(_g_smc);
4902 let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
4903 let skip_c = if luma_free || !fe.fast {
4906 fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
4907 } else {
4908 [[0u8; 64]; 2]
4909 };
4910 let is_free =
4911 luma_free && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &skip_c);
4912 let skip_sad = if fe.fast {
4914 0
4915 } else {
4916 let (lx, ly) = (mb_x * 16, mb_y * 16);
4917 let mut s = 0u32;
4918 for dy in 0..16 {
4919 let src = &sy[(ly + dy) * fe.cw + lx..][..16];
4920 let p = &skip_y[dy * 16..][..16];
4921 s += src.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
4922 }
4923 s
4924 };
4925 if is_free {
4926 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
4927 if !fe.fast {
4928 fe.mb_was_skip[mb_idx] = true;
4929 fe.mb_skip_sad[mb_idx] = skip_sad;
4930 }
4931 mb_qpy[mb_idx] = fe.cur_qp; rdskip_free += 1;
4933 if precomp {
4934 bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, rusty_h264_common::deblock::MbKind::Skip);
4935 }
4936 skip_run += 1;
4937 continue;
4938 }
4939 drop(_g_skip);
4940 let (lx, ly) = (mb_x * 16, mb_y * 16);
4941 let nb = {
4942 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMvPred);
4943 fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
4944 };
4945 let lme = lambda.sqrt();
4946
4947 if fe.fast {
4948 fe.mb_use_satd = fe.satd_q > 0.0
4960 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
4961 let (r16, mv16, cost_inter) =
4962 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
4963 let cost_intra = if fe.mb_use_satd {
4964 fe.best_i16_satd(&sy, mb_x, mb_y)
4965 } else {
4966 fe.best_i16_sad(&sy, mb_x, mb_y)
4967 } + (lme * fe.tune_intra_penalty) as i64;
4968 inter = if cost_intra < cost_inter {
4969 None } else {
4971 Some((0, vec![(r16, mv16)]))
4972 };
4973 } else {
4974 if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
4984 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
4985 fe.mb_was_skip[mb_idx] = true;
4986 fe.mb_skip_sad[mb_idx] = skip_sad;
4987 mb_qpy[mb_idx] = fe.cur_qp; if precomp {
4989 bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, rusty_h264_common::deblock::MbKind::Skip);
4990 }
4991 skip_run += 1;
4992 continue;
4993 }
4994
4995 let (r16, mv16, c16) =
4997 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
4998 let mut best_c = c16;
4999 let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
5000
5001 const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
5004 let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
5005 let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
5006 let split_t = split_t();
5007 if fe.do_splits && c16 > split_gate && (split_t <= 0.0 || (c16 as f64) >= split_t * lme) {
5008 let (rt, mvt, ct) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 8, &[mv16], lme);
5009 let (rb, mvb, cb) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly + 8, 16, 8, &[mv16], lme);
5010 let (rl, mvl, cl) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 8, 16, &[mv16], lme);
5011 let (rr, mvr, cr) = fe.best_part(refs, &sy, &nb, num_refs, lx + 8, ly, 8, 16, &[mv16], lme);
5012 if ct + cb < best_c {
5013 best_c = ct + cb;
5014 pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
5015 }
5016 if cl + cr < best_c {
5017 best_c = cl + cr;
5018 pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
5019 }
5020
5021 if fe.sub8x8 {
5026 let mut c8 = (lme * 4.0) as i64; let mut p8 = Vec::with_capacity(4);
5028 for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
5029 let (r, mv, c) = fe.best_part(
5030 refs, &sy, &nb, num_refs, lx + qx, ly + qy, 8, 8, &[mv16], lme,
5031 );
5032 c8 += c;
5033 p8.push((r, mv));
5034 }
5035 if c8 < best_c {
5036 best_c = c8;
5037 pick = Some((3u8, p8));
5038 }
5039 }
5040 }
5041
5042 if fe.sp_defer.get() {
5047 if let Some((mode, parts)) = pick.as_mut() {
5048 let regions: &[(usize, usize, usize, usize)] = match mode {
5049 1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
5050 2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
5051 3 => &[(0, 0, 8, 8), (8, 0, 8, 8), (0, 8, 8, 8), (8, 8, 8, 8)],
5052 _ => &[(0, 0, 16, 16)],
5053 };
5054 let mut tot = if *mode == 3 { (lme * 4.0) as i64 } else { 0 };
5055 for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
5056 let (r, mv) = parts[i];
5057 let (m2, c2) = fe.refine_part(
5058 refs, &sy, &nb, num_refs, lx + qx, ly + qy, pw, ph, lme, r, mv,
5059 );
5060 parts[i] = (r, m2);
5061 tot += c2;
5062 }
5063 best_c = tot;
5064 }
5065 }
5066 if split_harvest::enabled() {
5067 let won = match pick.as_ref().map(|p| p.0) {
5068 Some(0) | None => 0u8,
5069 Some(m) => m,
5070 };
5071 split_harvest::record(c16, best_c, lme, split_gate, won);
5072 }
5073 let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
5076 + (lme * fe.tune_intra_penalty) as i64;
5077 inter = if c_intra < best_c { None } else { pick };
5078 fe.mb_was_skip[mb_idx] = false;
5079 fe.mb_skip_sad[mb_idx] = skip_sad;
5080 }
5081
5082 if fe.rd_skip && rdskip_on && inter.is_some() {
5094 let skip_cp = fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip);
5095 let ssd_s = fe.pred_ssd(&sy, &su, &sv, mb_x, mb_y, &skip_y, &skip_cp);
5101 debug_assert_eq!(ssd_s, {
5102 let snap = fe.save_mb(mb_x, mb_y);
5103 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
5104 let v = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y);
5105 fe.load_mb(mb_x, mb_y, &snap);
5106 v
5107 }, "skip prediction SSD must equal the committed-skip reconstruction SSD");
5108 let j_skip = ssd_s as f64 + lambda;
5110 let take_skip = if fe.rd_skip_fast_t > 0.0
5116 && (ssd_s as f64) <= lambda * fe.rd_skip_fast_t
5117 {
5118 true
5119 } else {
5120 fe.save_mb_into(mb_x, mb_y, &mut rdskip_snap);
5126 let mut scratch = BitWriter::new();
5127 {
5128 let (m, p) = inter.as_ref().unwrap();
5129 fe.encode_inter_mb(
5130 &mut scratch, refs, &sy, &su, &sv, mb_x, mb_y, *m, p,
5131 );
5132 }
5133 let bits_c = scratch.bit_len();
5134 let ssd_c = fe.mb_ssd(&sy, &su, &sv, mb_x, mb_y);
5135 let won = j_skip <= ssd_c as f64 + lambda * bits_c as f64;
5136 if won {
5137 fe.load_mb(mb_x, mb_y, &rdskip_snap); true
5139 } else {
5140 coded = Some(scratch); false
5142 }
5143 };
5144 if take_skip {
5145 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_cp);
5146 if !fe.fast {
5147 fe.mb_was_skip[mb_idx] = true;
5148 fe.mb_skip_sad[mb_idx] = skip_sad;
5149 }
5150 mb_qpy[mb_idx] = fe.cur_qp;
5151 if precomp {
5152 bs_grid[mb_idx] = derive_mb_bs_from(
5153 &fe, mb_x, mb_y,
5154 rusty_h264_common::deblock::MbKind::Skip,
5155 );
5156 }
5157 skip_run += 1;
5158 continue;
5159 }
5160 }
5161 }
5162 w.write_ue(skip_run); skip_run = 0;
5164 }
5165 if mv_force_on() && is_p && inter.is_some() {
5166 let fi = MVCMP_FRAME.load(std::sync::atomic::Ordering::Relaxed);
5167 let ext = EXT_MV.lock().unwrap();
5168 if let Some(field) = ext.get(fi) {
5169 let w4 = fe.mb_w * 4;
5170 let b0 = (mb_y * 4) * w4 + mb_x * 4;
5171 let uniform = (0..4).all(|r| {
5174 (0..4).all(|c| field.get(b0 + r * w4 + c) == field.get(b0))
5175 });
5176 if uniform {
5177 if let Some(&emv) = field.get(b0) {
5178 inter = Some((0, vec![(0, emv)]));
5179 MVCMP[6].fetch_add(1, std::sync::atomic::Ordering::Relaxed);
5180 }
5181 }
5182 }
5183 }
5184 if mv_cmp_on() && is_p {
5185 if let Some((mode, parts)) = inter.as_ref() {
5186 let fi = MVCMP_FRAME.load(std::sync::atomic::Ordering::Relaxed);
5187 let ext = EXT_MV.lock().unwrap();
5188 if let Some(field) = ext.get(fi) {
5189 let bidx = (mb_y * 4) * (fe.mb_w * 4) + mb_x * 4;
5190 if let Some(&emv) = field.get(bidx) {
5191 let (mode, parts) = (*mode, parts.clone());
5192 drop(ext);
5193 let (so, bo) =
5196 fe.trial_inter(refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts);
5197 let (se, be) = fe.trial_inter(
5198 refs, &sy, &su, &sv, mb_x, mb_y, 0, &[(0, emv)],
5199 );
5200 let jo = so as f64 + lambda * bo as f64;
5201 let je = se as f64 + lambda * be as f64;
5202 use std::sync::atomic::Ordering::Relaxed;
5203 MVCMP[0].fetch_add(1, Relaxed);
5204 MVCMP[1].fetch_add(bo as u64, Relaxed);
5205 MVCMP[2].fetch_add(be as u64, Relaxed);
5206 MVCMP[3].fetch_add(so.max(0) as u64, Relaxed);
5207 MVCMP[4].fetch_add(se.max(0) as u64, Relaxed);
5208 MVCMP[5].fetch_add((je < jo) as u64, Relaxed);
5209 MVCMP[6].fetch_add((parts[0].1 != emv) as u64, Relaxed);
5210 }
5211 }
5212 }
5213 }
5214 let mb_kind = match &inter {
5217 Some((_, parts)) if parts.len() == 1 => {
5220 rusty_h264_common::deblock::MbKind::InterUniform
5221 }
5222 Some(_) => rusty_h264_common::deblock::MbKind::Inter,
5223 None => rusty_h264_common::deblock::MbKind::Intra,
5224 };
5225 match inter {
5226 Some((mode, parts)) => match coded {
5227 Some(sc) => w.append(&sc),
5230 None => {
5231 fe.encode_inter_mb(w, refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts)
5232 }
5233 },
5234 None => encode_mb(&mut fe, w, mb_x, mb_y, &sy, &su, &sv, is_p),
5235 }
5236 mb_qpy[mb_idx] = fe.cur_qp; if precomp {
5238 bs_grid[mb_idx] = derive_mb_bs_from(&fe, mb_x, mb_y, mb_kind);
5239 }
5240 }
5241 }
5242 debug_assert!(
5243 !precomp || bs_grid.iter().all(|b| *b != rusty_h264_common::deblock::MbBs::UNSET),
5244 "a macroblock loop exit failed to store its boundary strengths"
5245 );
5246 if is_p && skip_run > 0 {
5247 w.write_ue(skip_run); }
5249 w.rbsp_trailing_bits();
5250
5251 drop(_g_loop);
5255 let _g_fin = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncFinal);
5256 let info = rusty_h264_common::deblock::BlockInfo {
5261 inter: &fe.inter_y,
5262 nnz: &fe.nnz_y,
5263 mv: &fe.mv_y,
5264 ref_id: &fe.ref_idx_y,
5265 mv1: &[],
5266 ref_id1: &[],
5267 w4: fe.mb_w * 4,
5268 t8x8: &[],
5269 bs: &bs_grid,
5270 };
5271 drop(_g_fin);
5274 rusty_h264_common::deblock::filter_frame(
5275 &mut fe.rec_y,
5276 &mut fe.rec_u,
5277 &mut fe.rec_v,
5278 fe.mb_w,
5279 fe.mb_h,
5280 &mb_qpy,
5281 0, 0, 0, &info,
5285 );
5286 let w4 = fe.mb_w * 4;
5287 crate::RefFrame {
5288 y: fe.rec_y,
5289 u: fe.rec_u,
5290 v: fe.rec_v,
5291 poc: 0, frame_num: 0, mv: fe.mv_y,
5295 ref_idx: fe.ref_idx_y,
5296 w4,
5297 hpel: std::sync::OnceLock::new(),
5299 }
5300}
5301
5302#[allow(clippy::too_many_arguments)]
5313#[allow(clippy::too_many_arguments)]
5314pub fn encode_slice_data_b(
5315 w: &mut BitWriter,
5316 cfg: &EncoderConfig,
5317 frame: &YuvFrame,
5318 qp: u8,
5319 poc: i32,
5320 l0: &crate::RefFrame,
5321 l1: &crate::RefFrame,
5322 qpo: &[i32],
5323) {
5324 let mut fe = FrameEncoder::new(cfg);
5325 fe.qp = qp;
5326 fe.qpc = chroma_qp(qp);
5327 fe.cur_qp = qp;
5328 if cfg.cabac_dz_div > 0 {
5329 fe.idz = cfg.cabac_dz_div; } fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
5334 let (sy, su, sv) = coded_source(cfg, frame);
5335 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
5336 let lme = lambda.sqrt();
5337 let refs = std::slice::from_ref(l0); if fe.satd_q > 0.0 {
5341 let mut vars: Vec<i64> = (0..fe.mb_h)
5342 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
5343 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
5344 .collect();
5345 vars.sort_unstable();
5346 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
5347 fe.satd_var_thresh = vars[idx];
5348 }
5349 let mut skip_run = 0u32; for mb_y in 0..fe.mb_h {
5351 for mb_x in 0..fe.mb_w {
5352 let (lx, ly) = (mb_x * 16, mb_y * 16);
5353 let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
5354 fe.mb_use_satd =
5355 fe.satd_q > 0.0 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
5356 let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
5359 let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
5360 let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
5361 let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
5362 let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
5366 if fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
5374 && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc)
5375 {
5376 fe.commit_direct_motion(mb_x, mb_y, &dmotion);
5377 skip_run += 1;
5378 continue;
5379 }
5380 let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
5381 let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
5382 let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
5383 let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
5385 let r_bi = mvd_bits(mv0.0 - pmv0.0) + mvd_bits(mv0.1 - pmv0.1)
5386 + mvd_bits(mv1.0 - pmv1.0) + mvd_bits(mv1.1 - pmv1.1);
5387 let j_bi = d_bi + (lme * r_bi as f64) as i64;
5388 let (mut dir, mut best) = (0u8, d_direct);
5393 if j0 < best { dir = 1; best = j0; }
5394 if j1 < best { dir = 2; best = j1; }
5395 if j_bi < best { dir = 3; best = j_bi; }
5396 let _ = best; w.write_ue(skip_run); skip_run = 0;
5399 let bspec = BInter { dir, l1, mv0, mv1, mvmode: 0, parts2: [(0, (0, 0), (0, 0)); 2] };
5400 fe.encode_inter_mb_v1_b(w, refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
5401 }
5402 }
5403 if skip_run > 0 {
5404 w.write_ue(skip_run); }
5406 w.rbsp_trailing_bits();
5407}
5408
5409#[inline(always)]
5413fn mvd_bits(d: i32) -> u32 {
5414 let codenum = if d > 0 { (2 * d - 1) as u32 } else { (-2 * d) as u32 };
5415 1 + 2 * (31 - (codenum + 1).leading_zeros())
5416}
5417
5418fn write_ref_idx(w: &mut BitWriter, refi: i32, num_refs: usize) {
5422 if num_refs == 2 {
5423 w.write_bit(refi == 0); } else {
5425 w.write_ue(refi as u32);
5426 }
5427}
5428
5429fn ref_bits(r: usize, num_refs: usize) -> u32 {
5432 if num_refs <= 1 {
5433 0
5434 } else if num_refs == 2 {
5435 1
5436 } else {
5437 let mut n = r as u32 + 1;
5438 let mut len = 1;
5439 while n > 1 {
5440 n >>= 1;
5441 len += 2;
5442 }
5443 len
5444 }
5445}
5446
5447fn residual(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[i32; 16]) -> [i32; 16] {
5448 let mut r = [0i32; 16];
5449 for dy in 0..4 {
5450 for dx in 0..4 {
5451 r[dy * 4 + dx] = src[(y0 + dy) * stride + (x0 + dx)] as i32 - pred[dy * 4 + dx];
5452 }
5453 }
5454 r
5455}
5456
5457fn store(plane: &mut [u8], stride: usize, x0: usize, y0: usize, s: &[u8; 16]) {
5459 for dy in 0..4 {
5460 for dx in 0..4 {
5461 plane[(y0 + dy) * stride + (x0 + dx)] = s[dy * 4 + dx];
5462 }
5463 }
5464}
5465
5466fn pred_block(pred: &[u8; 256], bx: usize, by: usize) -> [i32; 16] {
5469 let mut p = [0i32; 16];
5470 for dy in 0..4 {
5471 for dx in 0..4 {
5472 p[dy * 4 + dx] = pred[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
5473 }
5474 }
5475 p
5476}
5477
5478#[inline]
5489pub(crate) fn satd_px(src: &[u8], ss: usize, pred: &[u8], ps: usize, w: usize, h: usize) -> i64 {
5490 #[cfg(accel)]
5491 {
5492 let asm = match (w, h) {
5493 (16, 16) => Some(rusty_h264_accel::satd_16x16(src, ss, pred, ps)),
5494 (16, 8) => Some(rusty_h264_accel::satd_16x8(src, ss, pred, ps)),
5495 (8, 16) => Some(rusty_h264_accel::satd_8x16(src, ss, pred, ps)),
5496 (8, 8) => Some(rusty_h264_accel::satd_8x8(src, ss, pred, ps)),
5497 (4, 4) => Some(rusty_h264_accel::satd_4x4(src, ss, pred, ps)),
5498 _ => None,
5499 };
5500 if let Some(v) = asm {
5501 return 2 * v as i64;
5502 }
5503 }
5504 let (nbx, nby) = (w / 4, h / 4);
5506 let mut blocks = [[0i32; 16]; 16];
5507 let mut bi = 0;
5508 for by in 0..nby {
5509 for bx in 0..nbx {
5510 let blk = &mut blocks[bi];
5511 for dy in 0..4 {
5512 for dx in 0..4 {
5513 blk[dy * 4 + dx] =
5514 src[(by * 4 + dy) * ss + bx * 4 + dx] as i32 - pred[(by * 4 + dy) * ps + bx * 4 + dx] as i32;
5515 }
5516 }
5517 bi += 1;
5518 }
5519 }
5520 satd_4x4_sum(&blocks[..nbx * nby])
5521}
5522
5523#[inline]
5528fn sad_strided(src: &[u8], ss: usize, r: &[u8], rs: usize, w: usize, h: usize) -> i64 {
5529 #[cfg(accel)]
5530 {
5531 match (w, h) {
5532 (16, 16) => return rusty_h264_accel::sad_16x16(src, ss, r, rs) as i64,
5533 (16, 8) => return rusty_h264_accel::sad_16x8(src, ss, r, rs) as i64,
5534 (8, 16) => return rusty_h264_accel::sad_8x16(src, ss, r, rs) as i64,
5535 _ => {}
5536 }
5537 }
5538 let mut sad = 0u32;
5539 for dy in 0..h {
5540 let a = &src[dy * ss..][..w];
5541 let b = &r[dy * rs..][..w];
5542 sad += a.iter().zip(b).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
5543 }
5544 sad as i64
5545}
5546
5547#[inline]
5551fn sad_avg_strided(src: &[u8], ss: usize, a: &[u8], b: &[u8], rs: usize, w: usize, h: usize) -> i64 {
5552 let mut sad = 0u32;
5553 for dy in 0..h {
5554 let s = &src[dy * ss..][..w];
5555 let pa = &a[dy * rs..][..w];
5556 let pb = &b[dy * rs..][..w];
5557 for i in 0..w {
5558 let p = ((pa[i] as u16 + pb[i] as u16 + 1) >> 1) as u8;
5559 sad += s[i].abs_diff(p) as u32;
5560 }
5561 }
5562 sad as i64
5563}
5564
5565fn satd_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
5566 satd_px(&src[ly * stride + lx..], stride, pred, 16, 16, 16)
5567}
5568
5569fn sad_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
5573 let mut sad = 0u32;
5574 for dy in 0..16 {
5575 let s = &src[(ly + dy) * stride + lx..][..16];
5576 let p = &pred[dy * 16..][..16];
5577 sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
5578 }
5579 sad as i64
5580}
5581
5582fn satd_8x8(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[u8; 64]) -> i64 {
5584 satd_px(&src[y0 * stride + x0..], stride, pred, 8, 8, 8)
5585}
5586
5587fn satd_4x4(src: &[u8], stride: usize, px: usize, py: usize, pred: &[u8; 16]) -> i64 {
5589 satd_px(&src[py * stride + px..], stride, pred, 4, 4, 4)
5590}
5591
5592fn i4_mode_available(mode: u8, top: bool, left: bool) -> bool {
5594 match mode {
5595 0 | 3 | 7 => top, 1 | 8 => left, 2 => true, _ => top && left, }
5600}
5601
5602struct I4Plan {
5605 modes: [u8; 16], q: [[i32; 16]; 16], cbp_luma: u32, nonzero: i64, }
5610
5611struct MbPlan {
5617 use_i4: bool,
5618 i16_mode: I16Mode,
5621 i16_cbp15: bool,
5622 i16_dc_levels: [i32; 16],
5623 i16_q: [[i32; 16]; 16],
5624 i4: Option<I4Plan>,
5626 i8: Option<I8Plan>,
5629 chroma_mode: u8,
5631 cbp_chroma: u32,
5632 c_dc_levels: [[i32; 4]; 2],
5633 c_q_blocks: [[[i32; 16]; 4]; 2],
5634}
5635
5636struct InterPlan {
5643 mvds: [(i32, i32); 4], plan_refs: [i32; 4], n_mvd: usize,
5646 cbp: u32,
5647 q_blocks: [[i32; 16]; 16], c_dc_levels: [[i32; 4]; 2],
5649 c_q: [[[i32; 16]; 4]; 2],
5650 t8x8: bool, q8: [[i32; 64]; 4], }
5653
5654fn gather_i4(
5656 fe: &FrameEncoder,
5657 px: usize,
5658 py: usize,
5659 avail_top: bool,
5660 avail_left: bool,
5661 bx: usize,
5662 by: usize,
5663) -> ([u8; 8], [u8; 4], u8) {
5664 let (cw, w4) = (fe.cw, fe.mb_w * 4);
5665 let mut top = [0u8; 8];
5666 let mut left = [0u8; 4];
5667 let mut corner = 0;
5668 if avail_top {
5669 for i in 0..4 {
5670 top[i] = fe.rec_y[(py - 1) * cw + px + i];
5671 }
5672 let tr_avail = bx + 1 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 1)];
5673 for i in 0..4 {
5674 top[4 + i] = if tr_avail {
5675 fe.rec_y[(py - 1) * cw + px + 4 + i]
5676 } else {
5677 top[3]
5678 };
5679 }
5680 }
5681 if avail_left {
5682 for i in 0..4 {
5683 left[i] = fe.rec_y[(py + i) * cw + px - 1];
5684 }
5685 }
5686 if avail_top && avail_left {
5687 corner = fe.rec_y[(py - 1) * cw + px - 1];
5688 }
5689 (top, left, corner)
5690}
5691
5692#[inline]
5699fn modes_at(fe: &FrameEncoder, modes: &[u8; 16], lbx: usize, lby: usize, dx: isize, dy: isize, bx: usize, by: usize) -> u8 {
5700 let (nx, ny) = (lbx as isize + dx, lby as isize + dy);
5701 if (0..4).contains(&nx) && (0..4).contains(&ny) {
5702 modes[ny as usize * 4 + nx as usize]
5703 } else {
5704 let w4 = fe.mb_w * 4;
5705 let gx = (bx as isize + dx) as usize;
5706 let gy = (by as isize + dy) as usize;
5707 fe.modes_y[gy * w4 + gx]
5708 }
5709}
5710
5711fn plan_i4x4(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I4Plan {
5712 let w4 = fe.mb_w * 4;
5713 let mut modes = [2u8; 16];
5714 let mut q = [[0i32; 16]; 16];
5715 let mut cbp_luma = 0u32;
5716 let mut nonzero = 0i64;
5717
5718 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
5719 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
5720 let (px, py) = (bx * 4, by * 4);
5721 let avail_top = by > 0;
5722 let avail_left = bx > 0;
5723 let (top, left, corner) = gather_i4(fe, px, py, avail_top, avail_left, bx, by);
5724
5725 let mut best_m = 2u8;
5730 let mut best_cost = i64::MAX;
5731 if fe.fast && fast_intra_enabled() {
5732 let lm = if bx > 0 { modes_at(fe, &modes, lbx, lby, -1, 0, bx, by) } else { 2 };
5733 let tm = if by > 0 { modes_at(fe, &modes, lbx, lby, 0, -1, bx, by) } else { 2 };
5734 let mpm = lm.min(tm);
5735 let mut cands = [mpm, 2u8, 0, 1];
5736 for i in 1..4 {
5737 for j in 0..i {
5738 if cands[i] == cands[j] {
5739 cands[i] = 255;
5740 }
5741 }
5742 }
5743 for &m in cands.iter() {
5744 if m == 255 || !i4_mode_available(m, avail_top, avail_left) {
5745 continue;
5746 }
5747 let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
5748 let cost = satd_4x4(sy, fe.cw, px, py, &pred);
5749 if cost < best_cost {
5750 best_cost = cost;
5751 best_m = m;
5752 }
5753 }
5754 } else {
5755 for m in 0..9u8 {
5756 if !i4_mode_available(m, avail_top, avail_left) {
5757 continue;
5758 }
5759 let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
5760 let cost = satd_4x4(sy, fe.cw, px, py, &pred);
5761 if cost < best_cost {
5762 best_cost = cost;
5763 best_m = m;
5764 }
5765 }
5766 }
5767
5768 let pred = intra4x4_pred(best_m, avail_top, avail_left, &top, &left, corner);
5770 let mut predb = [0i32; 16];
5771 for i in 0..16 {
5772 predb[i] = pred[i] as i32;
5773 }
5774 let res = residual(sy, fe.cw, px, py, &predb);
5775 let qb = rdoq(&forward_core(&res), qp, fe.idz, fe.rdoq_strength, 0); let s = reconstruct_4x4(&dequantize(&qb, qp), &predb);
5777 store(&mut fe.rec_y, fe.cw, px, py, &s);
5778 fe.coded_y[by * w4 + bx] = true;
5779
5780 let nz = qb.iter().filter(|&&v| v != 0).count();
5781 if nz > 0 {
5782 cbp_luma |= 1 << ((lby / 2) * 2 + (lbx / 2));
5783 }
5784 nonzero += nz as i64;
5785 modes[lby * 4 + lbx] = best_m;
5786 q[lby * 4 + lbx] = qb;
5787 }
5788 I4Plan {
5789 modes,
5790 q,
5791 cbp_luma,
5792 nonzero,
5793 }
5794}
5795
5796struct I8Plan {
5800 modes: [u8; 4], q: [[i32; 64]; 4], cbp_luma: u32, nonzero: i64, }
5805
5806const ZIGZAG_8X8: [usize; 64] = [
5809 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,
5810 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
5811 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
5812];
5813
5814#[inline]
5815fn scan_8x8_fwd(raster: &[i32; 64]) -> [i32; 64] {
5816 std::array::from_fn(|i| raster[ZIGZAG_8X8[i]])
5817}
5818
5819fn gather_i8_enc(
5822 fe: &FrameEncoder,
5823 px: usize,
5824 py: usize,
5825 avail_top: bool,
5826 avail_left: bool,
5827 bx: usize,
5828 by: usize,
5829) -> ([u8; 16], [u8; 8], u8, bool) {
5830 let (cw, w4) = (fe.cw, fe.mb_w * 4);
5831 let mut top = [0u8; 16];
5832 let mut left = [0u8; 8];
5833 let mut corner = 0;
5834 if avail_top {
5835 for i in 0..8 {
5836 top[i] = fe.rec_y[(py - 1) * cw + px + i];
5837 }
5838 let tr_avail = bx + 2 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 2)];
5839 for i in 0..8 {
5840 top[8 + i] = if tr_avail {
5841 fe.rec_y[(py - 1) * cw + px + 8 + i]
5842 } else {
5843 top[7]
5844 };
5845 }
5846 }
5847 if avail_left {
5848 for i in 0..8 {
5849 left[i] = fe.rec_y[(py + i) * cw + px - 1];
5850 }
5851 }
5852 let avail_corner = avail_top && avail_left;
5853 if avail_corner {
5854 corner = fe.rec_y[(py - 1) * cw + px - 1];
5855 }
5856 (top, left, corner, avail_corner)
5857}
5858
5859fn plan_i8x8(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I8Plan {
5862 let w4 = fe.mb_w * 4;
5863 let mut modes = [2u8; 4];
5864 let mut q = [[0i32; 64]; 4];
5865 let mut cbp_luma = 0u32;
5866 let mut nonzero = 0i64;
5867 let weight = [16i32; 64];
5868
5869 for b8 in 0..4usize {
5870 let (b8x, b8y) = (b8 % 2, b8 / 2);
5871 let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
5872 let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2); let avail_top = b8y > 0 || mb_y > 0;
5874 let avail_left = b8x > 0 || mb_x > 0;
5875 let (top, left, corner, avail_corner) =
5876 gather_i8_enc(fe, px, py, avail_top, avail_left, bx, by);
5877
5878 let predicted = predict_i4_mode(fe, bx, by);
5882 let mut best_m = 2u8;
5883 let mut best_cost = i64::MAX;
5884 for m in 0..9u8 {
5885 if !i4_mode_available(m, avail_top, avail_left) {
5886 continue;
5887 }
5888 let pred = intra8x8_pred(m, avail_top, avail_left, avail_corner, &top, &left, corner);
5889 let mut cost = satd_8x8(sy, fe.cw, px, py, &pred);
5890 if m != predicted {
5891 cost += 4 * fe.qp as i64; }
5893 if cost < best_cost {
5894 best_cost = cost;
5895 best_m = m;
5896 }
5897 }
5898 modes[b8] = best_m;
5899
5900 let pred = intra8x8_pred(best_m, avail_top, avail_left, avail_corner, &top, &left, corner);
5902 let mut res = [0i32; 64];
5903 for dy in 0..8 {
5904 for dx in 0..8 {
5905 res[dy * 8 + dx] =
5906 sy[(py + dy) * fe.cw + (px + dx)] as i32 - pred[dy * 8 + dx] as i32;
5907 }
5908 }
5909 let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, fe.idz);
5910 let nz = levels.iter().filter(|&&v| v != 0).count();
5911 if nz > 0 {
5912 cbp_luma |= 1 << b8;
5913 }
5914 nonzero += nz as i64;
5915 q[b8] = levels;
5916
5917 let res_r = inverse_quant_8x8(&levels, qp, &weight);
5918 let predb: [i32; 64] = std::array::from_fn(|i| pred[i] as i32);
5919 let recon = add_residual_8x8(&res_r, &predb);
5920 for dy in 0..8 {
5921 for dx in 0..8 {
5922 fe.rec_y[(py + dy) * fe.cw + (px + dx)] = recon[dy * 8 + dx];
5923 }
5924 }
5925 for sry in 0..2 {
5928 for srx in 0..2 {
5929 fe.modes_y[(by + sry) * w4 + (bx + srx)] = best_m;
5930 fe.coded_y[(by + sry) * w4 + (bx + srx)] = true;
5931 }
5932 }
5933 }
5934 I8Plan {
5935 modes,
5936 q,
5937 cbp_luma,
5938 nonzero,
5939 }
5940}
5941
5942#[allow(clippy::too_many_arguments)]
5950fn plan_inter8_luma(
5951 sy: &[u8],
5952 cw: usize,
5953 mb_x: usize,
5954 mb_y: usize,
5955 pred_y: &[u8; 256],
5956 qp: u8,
5957) -> ([[i32; 64]; 4], u32, f64, [u8; 256], i64) {
5958 let weight = [16i32; 64];
5959 let mut q8 = [[0i32; 64]; 4];
5960 let mut cbp = 0u32;
5961 let mut rate = 0f64;
5962 let mut rec = [0u8; 256];
5963 let mut ssd = 0i64;
5964 for b8 in 0..4usize {
5965 let (b8x, b8y) = (b8 % 2, b8 / 2);
5966 let mut res = [0i32; 64];
5967 for dy in 0..8 {
5968 for dx in 0..8 {
5969 let sx = mb_x * 16 + b8x * 8 + dx;
5970 let syy = mb_y * 16 + b8y * 8 + dy;
5971 let p = pred_y[(b8y * 8 + dy) * 16 + (b8x * 8 + dx)] as i32;
5972 res[dy * 8 + dx] = sy[syy * cw + sx] as i32 - p;
5973 }
5974 }
5975 let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, 6);
5976 let mut nz = false;
5977 for &l in &levels {
5978 if l != 0 {
5979 nz = true;
5980 rate += rdoq_rate((l as i64).abs());
5981 }
5982 }
5983 if nz {
5984 cbp |= 1 << b8;
5985 }
5986 q8[b8] = levels;
5987
5988 let res_r = inverse_quant_8x8(&levels, qp, &weight);
5989 let predb: [i32; 64] =
5990 std::array::from_fn(|i| pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32);
5991 let recon = add_residual_8x8(&res_r, &predb);
5992 for dy in 0..8 {
5993 for dx in 0..8 {
5994 let ri = (b8y * 8 + dy) * 16 + (b8x * 8 + dx);
5995 rec[ri] = recon[dy * 8 + dx];
5996 let sx = mb_x * 16 + b8x * 8 + dx;
5997 let syy = mb_y * 16 + b8y * 8 + dy;
5998 let d = recon[dy * 8 + dx] as i64 - sy[syy * cw + sx] as i64;
5999 ssd += d * d;
6000 }
6001 }
6002 }
6003 (q8, cbp, rate, rec, ssd)
6004}
6005
6006#[inline]
6011fn i16_pred(
6012 fe: &FrameEncoder,
6013 mode: I16Mode,
6014 avail_top: bool,
6015 avail_left: bool,
6016 top: &[u8; 16],
6017 left: &[u8; 16],
6018 corner: u8,
6019 lx: usize,
6020 ly: usize,
6021) -> [u8; 256] {
6022 #[cfg(accel)]
6023 if avail_top && avail_left {
6024 let mode_n = match mode {
6025 I16Mode::Vertical => 0,
6026 I16Mode::Horizontal => 1,
6027 I16Mode::Dc => 2,
6028 I16Mode::Plane => 3,
6029 };
6030 let mut p = AlignedMb([0; 256]);
6031 rusty_h264_accel::i16x16_luma_pred(mode_n, &mut p.0, &fe.rec_y[..], ly * fe.cw + lx, fe.cw);
6032 return p.0;
6033 }
6034 let _ = (fe, lx, ly);
6035 luma16x16_pred(mode, avail_top, avail_left, top, left, corner)
6036}
6037
6038#[inline]
6042#[allow(clippy::too_many_arguments)]
6043fn chroma_pred(
6044 fe: &FrameEncoder,
6045 mode: u8,
6046 avail_top: bool,
6047 avail_left: bool,
6048 c: usize,
6049 top: &[u8; 8],
6050 left: &[u8; 8],
6051 corner: u8,
6052 cx: usize,
6053 cy: usize,
6054) -> [u8; 64] {
6055 #[cfg(accel)]
6056 if avail_top && avail_left && (mode == 2 || mode == 3) {
6057 let plane = if c == 0 { &fe.rec_u } else { &fe.rec_v };
6058 let mut p = AlignedMb([0; 256]);
6059 rusty_h264_accel::chroma8x8_pred(mode, &mut p.0[..64], &plane[..], cy * fe.ccw + cx, fe.ccw);
6060 let mut out = [0u8; 64];
6061 out.copy_from_slice(&p.0[..64]);
6062 return out;
6063 }
6064 let _ = (fe, c, cx, cy);
6065 chroma8x8_pred(mode, avail_top, avail_left, top, left, corner)
6066}
6067
6068fn predict_i4_mode(fe: &FrameEncoder, bx: usize, by: usize) -> u8 {
6071 if bx == 0 || by == 0 {
6072 return 2;
6073 }
6074 let w4 = fe.mb_w * 4;
6075 fe.modes_y[by * w4 + (bx - 1)].min(fe.modes_y[(by - 1) * w4 + bx])
6076}
6077
6078#[allow(clippy::too_many_arguments)]
6079const RDOQ_ZZ: [usize; 16] = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
6081
6082#[inline]
6087fn rdoq_rate(level: i64) -> f64 {
6088 if level == 0 {
6089 1.0
6090 } else if level == 1 {
6091 3.0 } else {
6093 3.0 + (level - 1).min(13) as f64
6095 }
6096}
6097
6098fn rdoq(coeffs: &[i32; 16], qp: u8, dz_div: i64, strength: f64, first: usize) -> [i32; 16] {
6105 let mut q = quantize(coeffs, qp, dz_div);
6106 if strength <= 0.0 {
6107 return q;
6108 }
6109 let lambda = strength * 2f64.powf((qp as f64 - 12.0) / 3.0);
6110 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
6114 const POS: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7];
6115 let dist = |p: usize, level: i64| -> f64 {
6116 let e = coeffs[p].unsigned_abs() as f64 - level as f64 * (65536.0 / mf[POS[p]] as f64);
6117 e * e
6118 };
6119 for i in first..16 {
6121 let p = RDOQ_ZZ[i];
6122 let m = q[p].unsigned_abs() as i64;
6123 if m == 0 {
6124 continue;
6125 }
6126 let j_keep = dist(p, m) + lambda * rdoq_rate(m);
6127 let j_down = dist(p, m - 1) + lambda * rdoq_rate(m - 1);
6128 if j_down < j_keep {
6129 let nl = (m - 1) as i32;
6130 q[p] = if q[p] < 0 { -nl } else { nl };
6131 }
6132 }
6133 loop {
6138 let Some(li) = (first..16).rev().find(|&i| q[RDOQ_ZZ[i]] != 0) else {
6139 break;
6140 };
6141 let p = RDOQ_ZZ[li];
6142 let m = q[p].unsigned_abs() as i64;
6143 let prev = (first..li).rev().find(|&i| q[RDOQ_ZZ[i]] != 0);
6144 let base = prev.map_or(first, |j| j + 1);
6145 let bits = rdoq_rate(m) + 1.0 + (li - base) as f64; let d_add = dist(p, 0) - dist(p, m);
6147 if d_add < lambda * bits {
6148 q[p] = 0;
6149 } else {
6150 break;
6151 }
6152 }
6153 q
6154}
6155
6156fn plan_mb(
6161 fe: &mut FrameEncoder,
6162 mb_x: usize,
6163 mb_y: usize,
6164 sy: &[u8],
6165 su: &[u8],
6166 sv: &[u8],
6167) -> MbPlan {
6168 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCode);
6169 let qp = fe.qp;
6170 let qpc = fe.qpc;
6171 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
6173
6174 let (lx, ly) = (mb_x * 16, mb_y * 16);
6176 let avail_top = mb_y > 0;
6177 let avail_left = mb_x > 0;
6178 let mut top = [0u8; 16];
6179 let mut left = [0u8; 16];
6180 if avail_top {
6181 for i in 0..16 {
6182 top[i] = fe.rec_y[(ly - 1) * fe.cw + lx + i];
6183 }
6184 }
6185 if avail_left {
6186 for i in 0..16 {
6187 left[i] = fe.rec_y[(ly + i) * fe.cw + lx - 1];
6188 }
6189 }
6190 let corner = if avail_top && avail_left {
6191 fe.rec_y[(ly - 1) * fe.cw + lx - 1]
6192 } else {
6193 0
6194 };
6195
6196 let w4 = fe.mb_w * 4;
6197
6198 let mut i16_mode = I16Mode::Dc;
6200 let mut best_pred = i16_pred(fe, I16Mode::Dc, avail_top, avail_left, &top, &left, corner, lx, ly);
6201 let mut best_cost = satd_16x16(sy, fe.cw, lx, ly, &best_pred);
6202 for mode in [I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
6203 if !mode.available(avail_top, avail_left) {
6204 continue;
6205 }
6206 let pred = i16_pred(fe, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
6207 let cost = satd_16x16(sy, fe.cw, lx, ly, &pred);
6208 if cost < best_cost {
6209 best_cost = cost;
6210 i16_mode = mode;
6211 best_pred = pred;
6212 }
6213 }
6214 let mut dc4x4 = [0i32; 16];
6217 let mut i16_q = [[0i32; 16]; 16];
6218 #[cfg(accel)]
6223 let (i16_dc_levels, _i16_recon_dc, recon16) = {
6224 #[repr(align(16))]
6225 struct A([i16; 256]);
6226 let mut dct = A([0i16; 256]);
6227 let base = ly * fe.cw + lx;
6228 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
6229 rusty_h264_accel::dct_four_t4(
6230 &mut dct.0[qi * 64..qi * 64 + 64],
6231 &sy[base + qy * fe.cw + qx..],
6232 fe.cw,
6233 &best_pred[qy * 16 + qx..],
6234 16,
6235 );
6236 }
6237 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6238 dc4x4[lby * 4 + lbx] = dct.0[blk * 16] as i32;
6239 }
6240 if fe.rdoq_strength > 0.0 {
6241 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6245 let coeffs: [i32; 16] = std::array::from_fn(|i| dct.0[blk * 16 + i] as i32);
6246 let mut q = rdoq(&coeffs, qp, fe.idz, fe.rdoq_strength, 1);
6247 q[0] = 0;
6248 i16_q[lby * 4 + lbx] = q;
6249 }
6250 } else {
6251 let ff = rusty_h264_common::transform::quant_dz_ff(qp, fe.idz);
6252 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
6253 for qi in 0..4 {
6254 rusty_h264_accel::quant_four_4x4(&mut dct.0[qi * 64..qi * 64 + 64], &ff, mf);
6255 }
6256 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6257 let q = &mut i16_q[lby * 4 + lbx];
6258 q[0] = 0;
6259 for i in 1..16 {
6260 q[i] = dct.0[blk * 16 + i] as i32;
6261 }
6262 }
6263 }
6264 let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
6265 let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
6266 let mut recon16 = [0u8; 256];
6269 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6270 let mut deq = dequantize(&i16_q[lby * 4 + lbx], qp);
6271 deq[0] = i16_recon_dc[lby * 4 + lbx];
6272 for i in 0..16 {
6273 dct.0[blk * 16 + i] = deq[i] as i16;
6274 }
6275 }
6276 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
6277 rusty_h264_accel::idct_four_t4_rec(
6278 &mut recon16[qy * 16 + qx..],
6279 16,
6280 &best_pred[qy * 16 + qx..],
6281 16,
6282 &dct.0[qi * 64..qi * 64 + 64],
6283 );
6284 }
6285 (i16_dc_levels, i16_recon_dc, recon16)
6286 };
6287 #[cfg(not(accel))]
6288 let (i16_dc_levels, _i16_recon_dc, recon16) = {
6289 let mut res_blocks = [[0i32; 16]; 16];
6290 for by in 0..4 {
6291 for bx in 0..4 {
6292 let predb = pred_block(&best_pred, bx, by);
6293 res_blocks[by * 4 + bx] = residual(sy, fe.cw, lx + bx * 4, ly + by * 4, &predb);
6294 }
6295 }
6296 let mut coeffs = [[0i32; 16]; 16];
6297 forward_dct_blocks(&res_blocks, &mut coeffs);
6298 for i in 0..16 {
6299 dc4x4[i] = coeffs[i][0];
6300 let mut q = rdoq(&coeffs[i], qp, fe.idz, fe.rdoq_strength, 1);
6301 q[0] = 0;
6302 i16_q[i] = q;
6303 }
6304 let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
6305 let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
6306 let mut recon16 = [0u8; 256];
6307 let mut deq_blocks = [[0i32; 16]; 16];
6308 for i in 0..16 {
6309 deq_blocks[i] = dequantize(&i16_q[i], qp);
6310 deq_blocks[i][0] = i16_recon_dc[i];
6311 }
6312 let mut idct = [[0i32; 16]; 16];
6313 inverse_dct_blocks(&deq_blocks, &mut idct);
6314 for by in 0..4 {
6315 for bx in 0..4 {
6316 let s = add_residual_4x4(&idct[by * 4 + bx], &pred_block(&best_pred, bx, by));
6317 for dy in 0..4 {
6318 for dx in 0..4 {
6319 recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)] = s[dy * 4 + dx];
6320 }
6321 }
6322 }
6323 }
6324 (i16_dc_levels, i16_recon_dc, recon16)
6325 };
6326 let i16_cbp15 = i16_q.iter().any(|b| b[1..].iter().any(|&c| c != 0));
6327 let i16_dc_nz = i16_dc_levels.iter().filter(|&&v| v != 0).count() as i64;
6328 let i16_ac_nz: i64 = i16_q
6329 .iter()
6330 .map(|b| b[1..].iter().filter(|&&v| v != 0).count() as i64)
6331 .sum();
6332 let i16_rate = i16_dc_nz + i16_ac_nz + if i16_cbp15 { 16 } else { 0 };
6334 let mut ssd16 = 0i64;
6336 for dy in 0..16 {
6337 for dx in 0..16 {
6338 let d = recon16[dy * 16 + dx] as i64 - sy[(ly + dy) * fe.cw + (lx + dx)] as i64;
6339 ssd16 += d * d;
6340 }
6341 }
6342
6343 let (cx, cy) = (mb_x * 8, mb_y * 8);
6345 let mut ntop = [[0u8; 8]; 2];
6347 let mut nleft = [[0u8; 8]; 2];
6348 let mut ncorner = [0u8; 2];
6349 for c in 0..2 {
6350 let rec_c = if c == 0 { &fe.rec_u } else { &fe.rec_v };
6351 if avail_top {
6352 for i in 0..8 {
6353 ntop[c][i] = rec_c[(cy - 1) * fe.ccw + cx + i];
6354 }
6355 }
6356 if avail_left {
6357 for i in 0..8 {
6358 nleft[c][i] = rec_c[(cy + i) * fe.ccw + cx - 1];
6359 }
6360 }
6361 if avail_top && avail_left {
6362 ncorner[c] = rec_c[(cy - 1) * fe.ccw + cx - 1];
6363 }
6364 }
6365 let mut chroma_mode = 0u8;
6366 let mut best_c_cost = i64::MAX;
6367 for m in 0..4u8 {
6368 if !chroma_mode_available(m, avail_top, avail_left) {
6369 continue;
6370 }
6371 let mut cost = 0i64;
6372 for c in 0..2 {
6373 let src = if c == 0 { su } else { sv };
6374 let pred8 = chroma_pred(fe, m, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy);
6375 cost += satd_8x8(src, fe.ccw, cx, cy, &pred8);
6376 }
6377 if cost < best_c_cost {
6378 best_c_cost = cost;
6379 chroma_mode = m;
6380 }
6381 }
6382
6383 let mut c_dc_levels = [[0i32; 4]; 2];
6384 let mut c_q_blocks = [[[0i32; 16]; 4]; 2];
6385 let mut any_chroma_ac = false;
6386 let mut any_chroma_dc = false;
6387 for c in 0..2 {
6388 let src = if c == 0 { su } else { sv };
6389 let pred8 =
6390 chroma_pred(fe, chroma_mode, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy);
6391 let pblk = |bx: usize, by: usize| -> [i32; 16] {
6392 let mut predb = [0i32; 16];
6393 for dy in 0..4 {
6394 for dx in 0..4 {
6395 predb[dy * 4 + dx] = pred8[(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
6396 }
6397 }
6398 predb
6399 };
6400 let mut dc2x2 = [0i32; 4];
6404 let mut qbs = [[0i32; 16]; 4];
6405 #[cfg(accel)]
6406 let recon_dc = {
6407 #[repr(align(16))]
6408 struct A([i16; 64]);
6409 let mut d = A([0i16; 64]);
6410 rusty_h264_accel::dct_four_t4(&mut d.0, &src[cy * fe.ccw + cx..], fe.ccw, &pred8, 8);
6411 for i in 0..4 {
6412 dc2x2[i] = d.0[i * 16] as i32;
6413 }
6414 if fe.rdoq_strength > 0.0 {
6415 for i in 0..4 {
6417 let coeffs: [i32; 16] = std::array::from_fn(|j| d.0[i * 16 + j] as i32);
6418 let mut q = rdoq(&coeffs, qpc, fe.idz, fe.rdoq_strength, 1);
6419 q[0] = 0;
6420 if q[1..].iter().any(|&v| v != 0) {
6421 any_chroma_ac = true;
6422 }
6423 qbs[i] = q;
6424 }
6425 } else {
6426 let ff = rusty_h264_common::transform::quant_dz_ff(qpc, fe.idz);
6427 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
6428 rusty_h264_accel::quant_four_4x4(&mut d.0, &ff, mf);
6429 for i in 0..4 {
6430 let q = &mut qbs[i];
6431 q[0] = 0;
6432 for j in 1..16 {
6433 let v = d.0[i * 16 + j] as i32;
6434 q[j] = v;
6435 if v != 0 {
6436 any_chroma_ac = true;
6437 }
6438 }
6439 }
6440 }
6441 let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
6442 if dl.iter().any(|&v| v != 0) {
6443 any_chroma_dc = true;
6444 }
6445 let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
6446 for i in 0..4 {
6447 let deq = dequantize(&qbs[i], qpc);
6448 for j in 0..16 {
6449 d.0[i * 16 + j] = deq[j] as i16;
6450 }
6451 d.0[i * 16] = recon_dc[i] as i16;
6452 }
6453 let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
6454 rusty_h264_accel::idct_four_t4_rec(&mut plane[cy * fe.ccw + cx..], fe.ccw, &pred8, 8, &d.0);
6455 c_dc_levels[c] = dl;
6456 recon_dc
6457 };
6458 #[cfg(not(accel))]
6459 let recon_dc = {
6460 let mut res_blocks = [[0i32; 16]; 4];
6461 for by in 0..2 {
6462 for bx in 0..2 {
6463 res_blocks[by * 2 + bx] =
6464 residual(src, fe.ccw, cx + bx * 4, cy + by * 4, &pblk(bx, by));
6465 }
6466 }
6467 let mut coeffs = [[0i32; 16]; 4];
6468 forward_dct_blocks(&res_blocks, &mut coeffs);
6469 for i in 0..4 {
6470 dc2x2[i] = coeffs[i][0];
6471 let mut q = rdoq(&coeffs[i], qpc, fe.idz, fe.rdoq_strength, 1);
6472 q[0] = 0;
6473 qbs[i] = q;
6474 if q[1..].iter().any(|&v| v != 0) {
6475 any_chroma_ac = true;
6476 }
6477 }
6478 let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
6479 if dl.iter().any(|&v| v != 0) {
6480 any_chroma_dc = true;
6481 }
6482 let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
6483 let mut deq_blocks = [[0i32; 16]; 4];
6484 for i in 0..4 {
6485 deq_blocks[i] = dequantize(&qbs[i], qpc);
6486 deq_blocks[i][0] = recon_dc[i];
6487 }
6488 let mut idct = [[0i32; 16]; 4];
6489 inverse_dct_blocks(&deq_blocks, &mut idct);
6490 let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
6491 for by in 0..2 {
6492 for bx in 0..2 {
6493 let s = add_residual_4x4(&idct[by * 2 + bx], &pblk(bx, by));
6494 store(plane, fe.ccw, cx + bx * 4, cy + by * 4, &s);
6495 }
6496 }
6497 c_dc_levels[c] = dl;
6498 recon_dc
6499 };
6500 let _ = recon_dc;
6501 c_q_blocks[c] = qbs;
6502 }
6503 let cbp_chroma: u32 = if any_chroma_ac {
6504 2
6505 } else if any_chroma_dc {
6506 1
6507 } else {
6508 0
6509 };
6510
6511 let base = ly * fe.cw + lx;
6517 let i4 = if i16_rate > 2 {
6518 Some(plan_i4x4(fe, sy, mb_x, mb_y, qp))
6519 } else {
6520 None
6521 };
6522 let (j4, i4_recon) = match &i4 {
6523 Some(p) => {
6524 let mut ssd = 0i64;
6525 let mut rec = [0u8; 256];
6526 for i in 0..256 {
6527 let v = fe.rec_y[base + (i / 16) * fe.cw + i % 16];
6528 rec[i] = v;
6529 let d = v as i64 - sy[base + (i / 16) * fe.cw + i % 16] as i64;
6530 ssd += d * d;
6531 }
6532 (ssd as f64 + lambda * (p.nonzero + 16) as f64, Some(rec))
6533 }
6534 None => (f64::INFINITY, None),
6535 };
6536 let i8 = if fe.transform_8x8 {
6537 Some(plan_i8x8(fe, sy, mb_x, mb_y, qp))
6538 } else {
6539 None
6540 };
6541 let j8 = match &i8 {
6542 Some(p) => {
6543 let mut ssd = 0i64;
6544 for i in 0..256 {
6545 let d = fe.rec_y[base + (i / 16) * fe.cw + i % 16] as i64
6546 - sy[base + (i / 16) * fe.cw + i % 16] as i64;
6547 ssd += d * d;
6548 }
6549 ssd as f64 + lambda * (p.nonzero + 16) as f64
6550 }
6551 None => f64::INFINITY,
6552 };
6553 let j16 = ssd16 as f64 + lambda * i16_rate as f64;
6554
6555 let (use_i4, i4, i8) = if i8.is_some() && j8 <= j4 && j8 <= j16 {
6557 (true, None, i8)
6559 } else if i4.is_some() && j4 < j16 {
6560 let rec = i4_recon.unwrap();
6562 for i in 0..256 {
6563 fe.rec_y[base + (i / 16) * fe.cw + i % 16] = rec[i];
6564 }
6565 let modes = i4.as_ref().unwrap().modes;
6566 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6567 fe.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = modes[lby * 4 + lbx];
6568 }
6569 (true, i4, None)
6570 } else {
6571 for by in 0..4 {
6573 for bx in 0..4 {
6574 for dy in 0..4 {
6575 for dx in 0..4 {
6576 fe.rec_y[(ly + by * 4 + dy) * fe.cw + (lx + bx * 4 + dx)] =
6577 recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)];
6578 }
6579 }
6580 }
6581 }
6582 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6583 fe.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
6584 }
6585 (false, None, None)
6586 };
6587 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6589 fe.coded_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = true;
6590 }
6591
6592 MbPlan {
6593 use_i4,
6594 i16_mode,
6595 i16_cbp15,
6596 i16_dc_levels,
6597 i16_q,
6598 i4,
6599 i8,
6600 chroma_mode,
6601 cbp_chroma,
6602 c_dc_levels,
6603 c_q_blocks,
6604 }
6605}
6606
6607fn encode_mb(
6610 fe: &mut FrameEncoder,
6611 w: &mut BitWriter,
6612 mb_x: usize,
6613 mb_y: usize,
6614 sy: &[u8],
6615 su: &[u8],
6616 sv: &[u8],
6617 is_p: bool,
6618) {
6619 let plan = plan_mb(fe, mb_x, mb_y, sy, su, sv);
6620 let mb_type_offset = if is_p { 5 } else { 0 };
6622 let w4 = fe.mb_w * 4;
6623 let cbp_chroma = plan.cbp_chroma;
6624
6625 if let Some(i8) = plan.i8.as_ref().filter(|_| plan.use_i4) {
6627 let cbp = i8.cbp_luma | (cbp_chroma << 4);
6631 w.write_ue(mb_type_offset); w.write_bit(true); for b8 in 0..4usize {
6634 let (bx, by) = (mb_x * 4 + (b8 % 2) * 2, mb_y * 4 + (b8 / 2) * 2);
6635 let predicted = predict_i4_mode(fe, bx, by);
6636 let actual = i8.modes[b8];
6637 if actual == predicted {
6638 w.write_bit(true);
6639 } else {
6640 w.write_bit(false);
6641 let rem = if actual < predicted { actual } else { actual - 1 };
6642 w.write_bits(rem as u32, 3);
6643 }
6644 }
6645 w.write_ue(plan.chroma_mode as u32); write_cbp_intra(w, cbp);
6647 if cbp != 0 {
6648 w.write_se(fe.qp_delta());
6649 }
6650 fe.nnz_cache_load(mb_x, mb_y);
6651 for b8 in 0..4usize {
6652 let (b8x, b8y) = (b8 % 2, b8 / 2);
6653 let scan8 = scan_8x8_fwd(&i8.q[b8]);
6654 for sub in 0..4usize {
6655 let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
6656 let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
6657 let total = if i8.cbp_luma & (1 << b8) != 0 {
6658 let nc = fe.nc_pred(cx, cy);
6659 let blk: [i32; 16] = std::array::from_fn(|k| scan8[4 * k + sub]);
6660 encode_residual_block(w, &blk, 16, nc) as u8
6661 } else {
6662 0
6663 };
6664 fe.nnz_cache_set(cx, cy, total);
6665 fe.nnz_y[by * w4 + bx] = total;
6666 }
6667 }
6668 } else if plan.use_i4 {
6669 let i4 = plan.i4.as_ref().unwrap();
6670 let cbp = i4.cbp_luma | (cbp_chroma << 4);
6671 w.write_ue(mb_type_offset); if fe.transform_8x8 {
6673 w.write_bit(false); }
6675 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6676 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6677 let predicted = predict_i4_mode(fe, bx, by);
6678 let actual = i4.modes[lby * 4 + lbx];
6679 if actual == predicted {
6680 w.write_bit(true);
6681 } else {
6682 w.write_bit(false);
6683 let rem = if actual < predicted { actual } else { actual - 1 };
6684 w.write_bits(rem as u32, 3);
6685 }
6686 }
6687 w.write_ue(plan.chroma_mode as u32); write_cbp_intra(w, cbp);
6689 if cbp != 0 {
6690 w.write_se(fe.qp_delta()); }
6692 fe.nnz_cache_load(mb_x, mb_y);
6693 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6694 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6695 let total = if i4.cbp_luma & (1 << (blk / 4)) != 0 {
6696 let nc = fe.nc_pred(lbx, lby);
6697 let scan16 = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
6698 encode_residual_block(w, &scan16, 16, nc) as u8
6699 } else {
6700 0
6701 };
6702 fe.nnz_cache_set(lbx, lby, total);
6703 fe.nnz_y[by * w4 + bx] = total;
6704 }
6705 } else {
6706 let mb_type = 1 + plan.i16_mode as u32 + 4 * cbp_chroma + if plan.i16_cbp15 { 12 } else { 0 };
6707 w.write_ue(mb_type + mb_type_offset);
6708 w.write_ue(plan.chroma_mode as u32); w.write_se(fe.qp_delta()); fe.nnz_cache_load(mb_x, mb_y);
6711 let nc_dc = fe.nc_pred(0, 0);
6712 let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
6713 encode_residual_block(w, &dc_scan, 16, nc_dc);
6714 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6715 fe.nnz_cache_set(lbx, lby, 0);
6716 fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
6717 }
6718 if plan.i16_cbp15 {
6719 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6720 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6721 let nc = fe.nc_pred(lbx, lby);
6722 let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
6723 let total = encode_residual_block(w, &ac, 15, nc) as u8;
6724 fe.nnz_cache_set(lbx, lby, total);
6725 fe.nnz_y[by * w4 + bx] = total;
6726 }
6727 }
6728 }
6729
6730 if cbp_chroma != 0 {
6732 for c in 0..2 {
6733 encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
6734 }
6735 }
6736 if cbp_chroma == 2 {
6737 fe.chroma_cache_load(mb_x, mb_y);
6738 let w2 = fe.mb_w * 2;
6739 for c in 0..2 {
6740 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
6741 let nc = fe.chroma_nc_pred(c, bx, by);
6742 let ac = scan_4x4_ac(&plan.c_q_blocks[c][by * 2 + bx]);
6743 let total = encode_residual_block(w, &ac, 15, nc) as u8;
6744 fe.chroma_nnz_cache_set(c, bx, by, total);
6745 fe.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
6746 }
6747 }
6748 }
6749}
6750
6751const CB_NZC_CACHE: [usize; 24] = [
6762 9, 10, 17, 18, 11, 12, 19, 20, 25, 26, 33, 34, 27, 28, 35, 36, 14, 15, 22, 23, 38, 39, 46, 47, ];
6766const CB_RES_MAXPOS: [i32; 11] = [0, 15, 14, 15, 3, 14, 63, 3, 3, 14, 14];
6767const CB_RES_MAXC2: [i32; 11] = [0, 4, 4, 4, 3, 4, 4, 3, 3, 4, 4];
6768const CB_RES_CBF: [usize; 11] = [0, 0, 4, 8, 12, 16, 0, 12, 12, 16, 16];
6769const CB_RES_MAP: [usize; 11] = [0, 0, 15, 29, 44, 47, 0, 44, 44, 47, 47];
6770const CB_RES_ONE: [usize; 11] = [0, 0, 10, 20, 30, 39, 0, 30, 30, 39, 39];
6771const CB_RP_I16_DC: usize = 1;
6772const CB_RP_I16_AC: usize = 2;
6773const CB_RP_LUMA_4X4: usize = 3;
6774const CB_RP_CHROMA_DC: usize = 7;
6775const CB_RP_CHROMA_AC: usize = 9;
6776
6777fn cb_unary(cab: &mut CabacEncoder, ctx: usize, off: usize, value: u32) {
6780 if value == 0 {
6781 cab.encode_decision(ctx, 0);
6782 return;
6783 }
6784 cab.encode_decision(ctx, 1);
6785 for _ in 0..value - 1 {
6786 cab.encode_decision(ctx + off, 1);
6787 }
6788 cab.encode_decision(ctx + off, 0);
6789}
6790
6791fn cb_exp_bypass(cab: &mut CabacEncoder, mut k: i32, mut n: u32) {
6793 while n >= (1 << k) {
6794 cab.encode_bypass(1);
6795 n -= 1 << k;
6796 k += 1;
6797 }
6798 cab.encode_bypass(0);
6799 while k > 0 {
6800 k -= 1;
6801 cab.encode_bypass((n >> k) & 1);
6802 }
6803}
6804
6805fn cb_ueg_level(cab: &mut CabacEncoder, ctx: usize, value: u32) {
6808 if value == 0 {
6809 cab.encode_decision(ctx, 0);
6810 return;
6811 }
6812 let ones = value.min(13);
6813 for _ in 0..ones {
6814 cab.encode_decision(ctx, 1);
6815 }
6816 if value < 13 {
6817 cab.encode_decision(ctx, 0);
6818 } else {
6819 cb_exp_bypass(cab, 0, value - 13);
6820 }
6821}
6822
6823fn cb_mb_qp_delta(cab: &mut CabacEncoder, last_delta_qp: &mut i32, delta: i32) {
6825 const O: usize = 60;
6826 let ctx_inc = (*last_delta_qp != 0) as usize;
6827 if delta == 0 {
6828 cab.encode_decision(O + ctx_inc, 0);
6829 } else {
6830 cab.encode_decision(O + ctx_inc, 1);
6831 let code = 2 * delta.unsigned_abs() - (delta > 0) as u32;
6833 cb_unary(cab, O + 2, 1, code - 1);
6834 }
6835 *last_delta_qp = delta;
6836}
6837
6838fn cb_chroma_pred_mode(cab: &mut CabacEncoder, ctx_inc: usize, mode: u8) {
6840 const C: usize = 64;
6841 if mode == 0 {
6842 cab.encode_decision(C + ctx_inc, 0);
6843 return;
6844 }
6845 cab.encode_decision(C + ctx_inc, 1);
6846 if mode == 1 {
6847 cab.encode_decision(C + 3, 0);
6848 } else if mode == 2 {
6849 cab.encode_decision(C + 3, 1);
6850 cab.encode_decision(C + 3, 0);
6851 } else {
6852 cab.encode_decision(C + 3, 1);
6853 cab.encode_decision(C + 3, 1);
6854 }
6855}
6856
6857fn cb_mb_type_i(
6859 cab: &mut CabacEncoder,
6860 ctx_inc: usize,
6861 use_i4: bool,
6862 i16_mode: u32,
6863 cbp_chroma: u32,
6864 cbp_luma15: bool,
6865) {
6866 const O: usize = 3;
6867 if use_i4 {
6868 cab.encode_decision(O + ctx_inc, 0); return;
6870 }
6871 cab.encode_decision(O + ctx_inc, 1);
6872 cab.encode_terminate(false); cab.encode_decision(O + 3, cbp_luma15 as u32);
6874 if cbp_chroma != 0 {
6875 cab.encode_decision(O + 4, 1);
6876 cab.encode_decision(O + 5, (cbp_chroma == 2) as u32);
6877 } else {
6878 cab.encode_decision(O + 4, 0);
6879 }
6880 cab.encode_decision(O + 6, (i16_mode >> 1) & 1);
6881 cab.encode_decision(O + 7, i16_mode & 1);
6882}
6883
6884fn cb_intra4x4_pred_mode(cab: &mut CabacEncoder, predicted: u8, actual: u8) {
6886 const IPR: usize = 68;
6887 if actual == predicted {
6888 cab.encode_decision(IPR, 1);
6889 } else {
6890 cab.encode_decision(IPR, 0);
6891 let rem = if actual < predicted { actual } else { actual - 1 } as u32;
6892 cab.encode_decision(IPR + 1, rem & 1);
6893 cab.encode_decision(IPR + 1, (rem >> 1) & 1);
6894 cab.encode_decision(IPR + 1, (rem >> 2) & 1);
6895 }
6896}
6897
6898fn cb_cbp(cab: &mut CabacEncoder, top: Option<u8>, left: Option<u8>, cbp: u32) {
6900 const CBP: usize = 73;
6901 let t = |m: u32| top.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
6902 let l = |m: u32| left.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
6903 let nb = |x: u32| (x == 0) as u32;
6904 let b0 = cbp & 1;
6905 let b1 = (cbp >> 1) & 1;
6906 let b2 = (cbp >> 2) & 1;
6907 let b3 = (cbp >> 3) & 1;
6908 cab.encode_decision(CBP + (l(1 << 1) + (t(1 << 2) << 1)) as usize, b0);
6909 cab.encode_decision(CBP + (nb(b0) + (t(1 << 3) << 1)) as usize, b1);
6910 cab.encode_decision(CBP + (l(1 << 3) + (nb(b0) << 1)) as usize, b2);
6911 cab.encode_decision(CBP + (nb(b2) + (nb(b1) << 1)) as usize, b3);
6912 let cbp_chroma = cbp >> 4;
6913 let ct = top.map_or(0u32, |c| ((c >> 4) != 0) as u32);
6914 let cl = left.map_or(0u32, |c| ((c >> 4) != 0) as u32);
6915 cab.encode_decision(CBP + 4 + (cl + (ct << 1)) as usize, (cbp_chroma != 0) as u32);
6916 if cbp_chroma != 0 {
6917 let ct2 = top.map_or(0u32, |c| ((c >> 4) == 2) as u32);
6918 let cl2 = left.map_or(0u32, |c| ((c >> 4) == 2) as u32);
6919 cab.encode_decision(CBP + 8 + (cl2 + (ct2 << 1)) as usize, (cbp_chroma == 2) as u32);
6920 }
6921}
6922
6923#[allow(clippy::too_many_arguments)]
6926fn cb_residual(
6927 cab: &mut CabacEncoder,
6928 nzc: &mut [u8; 48],
6929 cbf_dc: &mut u16,
6930 iz: usize,
6931 rp: usize,
6932 is_intra: bool,
6933 ndc: (Option<u16>, Option<u16>),
6934 coeffs: &[i32],
6935) -> u32 {
6936 let is_dc = rp == CB_RP_I16_DC || rp == CB_RP_CHROMA_DC || rp == CB_RP_CHROMA_DC + 1;
6937 let (mut na, mut nb) = (is_intra as u8, is_intra as u8);
6938 let scan = CB_NZC_CACHE[iz.min(23)];
6939 if is_dc {
6940 if let Some(t) = ndc.0 {
6941 nb = ((t >> rp) & 1) as u8;
6942 }
6943 if let Some(l) = ndc.1 {
6944 na = ((l >> rp) & 1) as u8;
6945 }
6946 } else {
6947 if nzc[scan - 8] != 0xff {
6948 nb = (nzc[scan - 8] != 0) as u8;
6949 }
6950 if nzc[scan - 1] != 0xff {
6951 na = (nzc[scan - 1] != 0) as u8;
6952 }
6953 }
6954 let maxpos = CB_RES_MAXPOS[rp] as usize;
6955 let coeff_num = coeffs[..=maxpos].iter().filter(|&&c| c != 0).count() as u32;
6956 let cbf = coeff_num != 0;
6957 cab.encode_decision(85 + CB_RES_CBF[rp] + (na + (nb << 1)) as usize, cbf as u32);
6958 if !cbf {
6959 if !is_dc {
6960 nzc[scan] = 0;
6961 }
6962 return 0;
6963 }
6964 if is_dc {
6965 *cbf_dc |= 1 << rp;
6966 }
6967 let map = 105 + CB_RES_MAP[rp];
6969 let last = 166 + CB_RES_MAP[rp];
6970 let lastnz = (0..=maxpos).rev().find(|&i| coeffs[i] != 0).unwrap();
6971 for i in 0..maxpos {
6972 let s = coeffs[i] != 0;
6973 cab.encode_decision(map + i, s as u32);
6974 if s {
6975 let is_last = i == lastnz;
6976 cab.encode_decision(last + i, is_last as u32);
6977 if is_last {
6978 break;
6979 }
6980 }
6981 }
6982 let one = 227 + CB_RES_ONE[rp];
6984 let abs = 232 + CB_RES_ONE[rp];
6985 let maxc2 = CB_RES_MAXC2[rp];
6986 let (mut c1, mut c2) = (1i32, 0i32);
6987 for i in (0..=maxpos).rev() {
6988 if coeffs[i] != 0 {
6989 let av = coeffs[i].unsigned_abs();
6990 let gt1 = av > 1;
6991 cab.encode_decision(one + c1 as usize, gt1 as u32);
6992 if gt1 {
6993 cb_ueg_level(cab, abs + c2 as usize, av - 2);
6994 c2 = (c2 + 1).min(maxc2);
6995 c1 = 0;
6996 } else if c1 != 0 {
6997 c1 = (c1 + 1).min(4);
6998 }
6999 cab.encode_bypass((coeffs[i] < 0) as u32);
7000 }
7001 }
7002 if !is_dc {
7003 nzc[scan] = coeff_num as u8;
7004 }
7005 coeff_num
7006}
7007
7008fn cb_build_nzc(mb_nzc: &[[u8; 24]], top: Option<usize>, left: Option<usize>) -> [u8; 48] {
7011 let mut nzc = [0xffu8; 48];
7012 if let Some(t) = top {
7013 let tn = mb_nzc[t];
7014 nzc[1..5].copy_from_slice(&tn[12..16]);
7015 (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
7016 (nzc[6], nzc[7]) = (tn[20], tn[21]);
7017 (nzc[30], nzc[31]) = (tn[22], tn[23]);
7018 }
7019 if let Some(l) = left {
7020 let ln = mb_nzc[l];
7021 (nzc[8], nzc[16], nzc[24], nzc[32]) = (ln[3], ln[7], ln[11], ln[15]);
7022 (nzc[13], nzc[21], nzc[37], nzc[45]) = (ln[17], ln[21], ln[19], ln[23]);
7023 }
7024 nzc
7025}
7026
7027fn cb_export_nzc(nzc: &[u8; 48]) -> [u8; 24] {
7029 let mut mn = [0u8; 24];
7030 for k in 0..4 {
7031 mn[k] = nzc[9 + k];
7032 mn[4 + k] = nzc[17 + k];
7033 mn[8 + k] = nzc[25 + k];
7034 mn[12 + k] = nzc[33 + k];
7035 }
7036 (mn[16], mn[17], mn[20], mn[21]) = (nzc[14], nzc[15], nzc[22], nzc[23]);
7037 (mn[18], mn[19], mn[22], mn[23]) = (nzc[38], nzc[39], nzc[46], nzc[47]);
7038 for v in mn.iter_mut() {
7039 if *v == 0xff {
7040 *v = 0;
7041 }
7042 }
7043 mn
7044}
7045
7046struct CabacState {
7049 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,
7062}
7063
7064impl CabacState {
7065 fn new(n: usize) -> Self {
7066 CabacState {
7067 cat: vec![0; n],
7068 cmode: vec![0; n],
7069 mb_cbp: vec![0; n],
7070 cbf_dc: vec![0; n],
7071 mb_nzc: vec![[0u8; 24]; n],
7072 mb_mvd: vec![[[0i16; 2]; 16]; n],
7073 mb_ref: vec![[-1i8; 16]; n],
7074 mb_mvd1: vec![[[0i16; 2]; 16]; n],
7075 mb_ref1: vec![[-1i8; 16]; n],
7076 mb_skip: vec![false; n],
7077 mb_direct: vec![false; n],
7078 last_delta_qp: 0,
7079 }
7080 }
7081}
7082
7083fn emit_mb_cabac_i(
7087 fe: &mut FrameEncoder,
7088 cab: &mut CabacEncoder,
7089 cs: &mut CabacState,
7090 plan: &MbPlan,
7091 mb_x: usize,
7092 mb_y: usize,
7093) {
7094 let mb_w = fe.mb_w;
7095 let addr = mb_y * mb_w + mb_x;
7096 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7097 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7098
7099 let li = left.map_or(0, |a| (cs.cat[a] >= 2) as usize);
7101 let ti = top.map_or(0, |a| (cs.cat[a] >= 2) as usize);
7102 let acct = crate::bitacct::enabled();
7103 let t0 = if acct { cab.pos() } else { 0 };
7104 if plan.use_i4 {
7105 cb_mb_type_i(cab, li + ti, true, 0, 0, false);
7106 } else {
7107 cb_mb_type_i(cab, li + ti, false, plan.i16_mode as u32, plan.cbp_chroma, plan.i16_cbp15);
7108 }
7109 if acct {
7110 crate::bitacct::add(crate::bitacct::B::MbType, cab.pos() - t0);
7111 }
7112 let t1 = if acct { cab.pos() } else { 0 };
7113 emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7114 if acct {
7115 crate::bitacct::add(crate::bitacct::B::IntraBody, cab.pos() - t1);
7116 }
7117}
7118
7119#[allow(clippy::too_many_arguments)]
7123fn emit_intra_body_cabac(
7124 fe: &mut FrameEncoder,
7125 cab: &mut CabacEncoder,
7126 cs: &mut CabacState,
7127 plan: &MbPlan,
7128 mb_x: usize,
7129 mb_y: usize,
7130 addr: usize,
7131 top: Option<usize>,
7132 left: Option<usize>,
7133) {
7134 let w4 = fe.mb_w * 4;
7135 let cbp_chroma = plan.cbp_chroma;
7136 let cci = left.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize)
7138 + top.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize);
7139
7140 let mut nzc;
7141 let mut cbfdc = 0u16;
7142 let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
7143
7144 if !plan.use_i4 {
7145 cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
7147 cs.cmode[addr] = plan.chroma_mode as i32;
7148 cs.cat[addr] = 2;
7149 cs.mb_cbp[addr] = ((cbp_chroma as u8) << 4) | if plan.i16_cbp15 { 15 } else { 0 };
7150 nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7151
7152 let delta = fe.qp_delta();
7153 cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7154
7155 let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
7157 cb_residual(cab, &mut nzc, &mut cbfdc, 0, CB_RP_I16_DC, true, ndc, &dc_scan);
7158 for (iz, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
7160 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7161 let total = if plan.i16_cbp15 {
7162 let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
7163 cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_I16_AC, true, ndc, &ac)
7164 } else {
7165 nzc[CB_NZC_CACHE[iz]] = 0;
7166 0
7167 };
7168 fe.nnz_y[by * w4 + bx] = total as u8;
7169 }
7170 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);
7171 } else {
7172 let i4 = plan.i4.as_ref().unwrap();
7174 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7175 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7176 let predicted = predict_i4_mode(fe, bx, by);
7177 cb_intra4x4_pred_mode(cab, predicted, i4.modes[lby * 4 + lbx]);
7178 }
7179 cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
7180 cs.cmode[addr] = plan.chroma_mode as i32;
7181 cs.cat[addr] = 0;
7182 let cbp = i4.cbp_luma | (cbp_chroma << 4);
7183 cb_cbp(cab, top.map(|a| cs.mb_cbp[a]), left.map(|a| cs.mb_cbp[a]), cbp);
7184 cs.mb_cbp[addr] = cbp as u8;
7185 nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7186
7187 if cbp == 0 {
7188 cs.last_delta_qp = 0;
7189 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7190 fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
7191 }
7192 } else {
7193 let delta = fe.qp_delta();
7194 cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7195 for id8 in 0..4usize {
7196 for id4 in 0..4usize {
7197 let iz = id8 * 4 + id4;
7198 let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
7199 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7200 let total = if i4.cbp_luma & (1 << id8) != 0 {
7201 let sc = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
7202 cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_LUMA_4X4, true, ndc, &sc)
7203 } else {
7204 nzc[CB_NZC_CACHE[iz]] = 0;
7205 0
7206 };
7207 fe.nnz_y[by * w4 + bx] = total as u8;
7208 }
7209 }
7210 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);
7211 }
7212 }
7213
7214 cs.cbf_dc[addr] = cbfdc;
7215 cs.mb_nzc[addr] = cb_export_nzc(&nzc);
7216}
7217
7218#[allow(clippy::too_many_arguments)]
7222fn cb_emit_chroma_residual(
7223 cab: &mut CabacEncoder,
7224 fe: &mut FrameEncoder,
7225 nzc: &mut [u8; 48],
7226 cbfdc: &mut u16,
7227 ndc: (Option<u16>, Option<u16>),
7228 is_intra: bool,
7229 cbp_chroma: u32,
7230 c_dc_levels: &[[i32; 4]; 2],
7231 c_q: &[[[i32; 16]; 4]; 2],
7232 mb_x: usize,
7233 mb_y: usize,
7234) {
7235 let w2 = fe.mb_w * 2;
7236 if cbp_chroma >= 1 {
7237 for i in 0..2usize {
7238 cb_residual(cab, nzc, cbfdc, 16 + i * 4, CB_RP_CHROMA_DC + i, is_intra, ndc, &c_dc_levels[i]);
7239 }
7240 }
7241 if cbp_chroma == 2 {
7242 for i in 0..2usize {
7243 for (id4, &(bx, by)) in CHROMA_4X4_SCAN_XY.iter().enumerate() {
7244 let ac = scan_4x4_ac(&c_q[i][by * 2 + bx]);
7245 let total = cb_residual(
7246 cab, nzc, cbfdc, 16 + i * 4 + id4, CB_RP_CHROMA_AC + i, is_intra, ndc, &ac,
7247 );
7248 fe.nnz_c[i][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total as u8;
7249 }
7250 }
7251 }
7252}
7253
7254pub fn encode_slice_data_cabac_intra(
7259 w: &mut BitWriter,
7260 cfg: &EncoderConfig,
7261 frame: &YuvFrame,
7262 qp: u8,
7263 qpo: &[i32],
7264) -> crate::RefFrame {
7265 let mut fe = FrameEncoder::new(cfg);
7266 fe.qp = qp;
7267 fe.qpc = chroma_qp(qp);
7268 fe.cur_qp = qp;
7269 if cfg.cabac_dz_div > 0 {
7270 fe.idz = cfg.cabac_dz_div; }
7272 let (sy, su, sv) = coded_source(cfg, frame);
7273 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
7274 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
7276 let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
7277
7278 fe.rdoq_strength = if cfg.gop_size <= 1 { cfg.cabac_rdoq } else { 0.0 };
7284 let mut cab = CabacEncoder::new(qp as i32, 0, true);
7286 let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
7287 let total = fe.mb_w * fe.mb_h;
7288
7289 for mb_y in 0..fe.mb_h {
7290 for mb_x in 0..fe.mb_w {
7291 let mb_idx = mb_y * fe.mb_w + mb_x;
7292 fe.qp = aq_qp[mb_idx];
7293 fe.qpc = chroma_qp(aq_qp[mb_idx]);
7294 let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
7295 emit_mb_cabac_i(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
7296 mb_qpy[mb_idx] = fe.cur_qp;
7297 {
7299 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7300 cab.encode_terminate(mb_idx + 1 == total);
7301 if crate::bitacct::enabled() {
7302 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
7303 }
7304 }
7305 }
7306 }
7307
7308 while !w.is_byte_aligned() {
7310 w.write_bit(true);
7311 }
7312 for b in cab.into_bytes() {
7313 w.write_bits(b as u32, 8);
7314 }
7315
7316 let ref_id: Vec<i32> = fe.ref_idx_y.iter().map(|&r| if r >= 0 { r } else { i32::MIN }).collect();
7318 let info = rusty_h264_common::deblock::BlockInfo {
7319 inter: &fe.inter_y,
7320 nnz: &fe.nnz_y,
7321 mv: &fe.mv_y,
7322 ref_id: &ref_id,
7323 mv1: &[],
7324 ref_id1: &[],
7325 w4: fe.mb_w * 4,
7326 t8x8: &[],
7327 bs: &[],
7328 };
7329 rusty_h264_common::deblock::filter_frame(
7330 &mut fe.rec_y, &mut fe.rec_u, &mut fe.rec_v, fe.mb_w, fe.mb_h, &mb_qpy, 0, 0, 0, &info,
7331 );
7332 let w4 = fe.mb_w * 4;
7333 crate::RefFrame {
7334 y: fe.rec_y,
7335 u: fe.rec_u,
7336 v: fe.rec_v,
7337 poc: 0,
7338 frame_num: 0,
7339 mv: fe.mv_y,
7340 ref_idx: fe.ref_idx_y,
7341 w4,
7342 hpel: std::sync::OnceLock::new(),
7344 }
7345}
7346
7347const CB_CACHE30: [usize; 16] = [7, 8, 13, 14, 9, 10, 15, 16, 19, 20, 25, 26, 21, 22, 27, 28];
7357const CB_G_SCAN4: [usize; 16] = [0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15];
7359
7360fn cb_ueg_mv(cab: &mut CabacEncoder, base: usize, v: u32) {
7363 const P2C: [usize; 8] = [0, 1, 2, 3, 3, 3, 3, 3];
7364 if v == 0 {
7365 cab.encode_decision(base, 0);
7366 return;
7367 }
7368 cab.encode_decision(base, 1);
7369 if v <= 7 {
7370 let mut count = 1;
7372 for _ in 0..v - 1 {
7373 cab.encode_decision(base + P2C[count], 1);
7374 count += 1;
7375 }
7376 cab.encode_decision(base + P2C[count], 0);
7377 } else {
7378 let mut count = 1;
7380 for _ in 0..7 {
7381 cab.encode_decision(base + P2C[count], 1);
7382 count += 1;
7383 }
7384 let tb = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7385 cb_exp_bypass(cab, 3, v - 8);
7386 if crate::bitacct::enabled() {
7387 crate::bitacct::add(crate::bitacct::B::MvdBypass, cab.pos() - tb);
7388 }
7389 }
7390}
7391
7392fn cb_mvd(cab: &mut CabacEncoder, comp: usize, ctx_inc: usize, d: i32) {
7395 let th = if crate::bitacct::enabled() { cab.pos() } else { u64::MAX };
7396 let base = 40 + comp * 7;
7397 if d == 0 {
7398 cab.encode_decision(base + ctx_inc, 0);
7399 if th != u64::MAX {
7400 crate::bitacct::add_mvd_sample(0, cab.pos() - th);
7401 }
7402 return;
7403 }
7404 cab.encode_decision(base + ctx_inc, 1);
7405 cb_ueg_mv(cab, base + 3, d.unsigned_abs() - 1); let ts = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7407 cab.encode_bypass((d < 0) as u32);
7408 if crate::bitacct::enabled() {
7409 crate::bitacct::add(crate::bitacct::B::MvdSign, cab.pos() - ts);
7410 }
7411 if th != u64::MAX {
7412 crate::bitacct::add_mvd_sample(d.unsigned_abs(), cab.pos() - th);
7413 }
7414}
7415
7416fn cb_mb_skip(cab: &mut CabacEncoder, ctx_inc: usize, skip: bool) {
7418 cab.encode_decision(ctx_inc, skip as u32);
7419}
7420
7421fn cb_ref_idx(cab: &mut CabacEncoder, ctx0: usize, r: u32) {
7425 const B: usize = 54;
7426 let mut v = r;
7427 let mut bin_idx = 0u32;
7428 loop {
7429 let bin = (v > 0) as u32;
7430 let ctx = match bin_idx {
7431 0 => ctx0,
7432 1 => 4,
7433 _ => 5,
7434 };
7435 cab.encode_decision(B + ctx, bin);
7436 if bin == 0 {
7437 break;
7438 }
7439 v -= 1;
7440 bin_idx += 1;
7441 }
7442}
7443
7444fn cb_mb_type_p_inter(cab: &mut CabacEncoder, mode: u8) {
7447 const S: usize = 11;
7448 cab.encode_decision(S + 3, 0); match mode {
7450 0 => {
7451 cab.encode_decision(S + 4, 0);
7452 cab.encode_decision(S + 5, 0);
7453 }
7454 3 => {
7455 cab.encode_decision(S + 4, 0);
7457 cab.encode_decision(S + 5, 1);
7458 }
7459 1 => {
7460 cab.encode_decision(S + 4, 1);
7461 cab.encode_decision(S + 6, 1);
7462 }
7463 _ => {
7464 cab.encode_decision(S + 4, 1);
7466 cab.encode_decision(S + 6, 0);
7467 }
7468 }
7469}
7470
7471fn cb_sub_mb_type_p(cab: &mut CabacEncoder, sub_type: u8) {
7474 const S: usize = 21;
7475 match sub_type {
7476 0 => cab.encode_decision(S, 1),
7477 _ => unreachable!("only 8x8 sub_mb_type (0) emitted"),
7478 }
7479}
7480
7481fn cb_mb_type_p_intra(cab: &mut CabacEncoder, plan: &MbPlan) {
7485 const S: usize = 11;
7486 cab.encode_decision(S + 3, 1); if plan.use_i4 {
7488 cab.encode_decision(S + 6, 0); return;
7490 }
7491 cab.encode_decision(S + 6, 1); cab.encode_terminate(false); cab.encode_decision(S + 7, plan.i16_cbp15 as u32);
7494 if plan.cbp_chroma != 0 {
7495 cab.encode_decision(S + 8, 1);
7496 cab.encode_decision(S + 8, (plan.cbp_chroma == 2) as u32);
7497 } else {
7498 cab.encode_decision(S + 8, 0);
7499 }
7500 cab.encode_decision(S + 9, (plan.i16_mode as u32 >> 1) & 1);
7501 cab.encode_decision(S + 9, plan.i16_mode as u32 & 1);
7502}
7503
7504fn p_partition_layout(mode: u8) -> &'static [(usize, &'static [usize])] {
7508 match mode {
7509 1 => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7]), (8, &[8, 9, 10, 11, 12, 13, 14, 15])],
7510 2 => &[(0, &[0, 1, 2, 3, 8, 9, 10, 11]), (4, &[4, 5, 6, 7, 12, 13, 14, 15])],
7511 3 => &[(0, &[0, 1, 2, 3]), (4, &[4, 5, 6, 7]), (8, &[8, 9, 10, 11]), (12, &[12, 13, 14, 15])],
7513 _ => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])],
7514 }
7515}
7516
7517#[allow(clippy::too_many_arguments)]
7520fn cb_emit_mvd_partition(
7521 cab: &mut CabacEncoder,
7522 part_idx: usize,
7523 zblocks: &[usize],
7524 mvdc: &mut [[i16; 2]; 30],
7525 refc: &mut [i8; 30],
7526 mmvd: &mut [[i16; 2]; 16],
7527 mref: &mut [i8; 16],
7528 mvd: (i32, i32),
7529 ref_idx: i8, ) {
7531 let s = CB_CACHE30[part_idx];
7532 let ctx = |comp: usize| -> usize {
7533 let mut a = 0i32;
7534 if refc[s - 6] >= 0 {
7535 a += mvdc[s - 6][comp].unsigned_abs() as i32;
7536 }
7537 if refc[s - 1] >= 0 {
7538 a += mvdc[s - 1][comp].unsigned_abs() as i32;
7539 }
7540 if a >= 3 {
7541 1 + (a > 32) as usize
7542 } else {
7543 0
7544 }
7545 };
7546 cb_mvd(cab, 0, ctx(0), mvd.0);
7547 cb_mvd(cab, 1, ctx(1), mvd.1);
7548 let (mx, my) = (mvd.0 as i16, mvd.1 as i16);
7549 for &zb in zblocks {
7550 mvdc[CB_CACHE30[zb]] = [mx, my];
7551 refc[CB_CACHE30[zb]] = ref_idx;
7552 mmvd[CB_G_SCAN4[zb]] = [mx, my];
7553 mref[CB_G_SCAN4[zb]] = ref_idx;
7554 }
7555}
7556
7557fn emit_mb_cabac_p_inter(
7560 fe: &mut FrameEncoder,
7561 cab: &mut CabacEncoder,
7562 cs: &mut CabacState,
7563 mode: u8,
7564 plan: &InterPlan,
7565 mb_x: usize,
7566 mb_y: usize,
7567 num_refs: 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
7574 let acct = crate::bitacct::enabled();
7577 let mut t0 = if acct { cab.pos() } else { 0 };
7578 cb_mb_type_p_inter(cab, mode);
7579 if mode == 3 {
7581 for _ in 0..4 {
7582 cb_sub_mb_type_p(cab, 0);
7583 }
7584 }
7585 if acct {
7586 crate::bitacct::add(crate::bitacct::B::MbType, cab.pos() - t0);
7587 t0 = cab.pos();
7588 }
7589
7590 let mut mvdc = [[0i16; 2]; 30];
7592 let mut refc = [-1i8; 30];
7593 cb_fill_inter_cache(&cs.mb_ref, &cs.mb_mvd, &mut refc, &mut mvdc, top, left, addr, mb_w);
7594 let mut mmvd = [[0i16; 2]; 16];
7595 let mut mref = [0i8; 16];
7596 let layout = p_partition_layout(mode);
7597 if num_refs > 1 {
7600 for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
7601 let r = plan.plan_refs[part];
7602 let s = CB_CACHE30[part_idx];
7603 let ctx0 = (refc[s - 1] > 0) as usize + 2 * (refc[s - 6] > 0) as usize;
7604 cb_ref_idx(cab, ctx0, r as u32);
7605 for &zb in zblocks {
7606 refc[CB_CACHE30[zb]] = r as i8;
7607 }
7608 }
7609 }
7610 if acct {
7611 crate::bitacct::add(crate::bitacct::B::RefIdx, cab.pos() - t0);
7612 t0 = cab.pos();
7613 }
7614 for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
7616 cb_emit_mvd_partition(
7617 cab, part_idx, zblocks, &mut mvdc, &mut refc, &mut mmvd, &mut mref, plan.mvds[part],
7618 plan.plan_refs[part] as i8,
7619 );
7620 }
7621 if acct {
7622 crate::bitacct::add(crate::bitacct::B::Mvd, cab.pos() - t0);
7623 }
7624 cs.mb_mvd[addr] = mmvd;
7625 cs.mb_ref[addr] = mref;
7626 cs.cat[addr] = 100;
7627 cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7628}
7629
7630#[allow(clippy::too_many_arguments)]
7633fn cb_emit_inter_residual(
7634 fe: &mut FrameEncoder,
7635 cab: &mut CabacEncoder,
7636 cs: &mut CabacState,
7637 plan: &InterPlan,
7638 mb_x: usize,
7639 mb_y: usize,
7640 addr: usize,
7641 top: Option<usize>,
7642 left: Option<usize>,
7643) {
7644 let w4 = fe.mb_w * 4;
7645 let cbp = plan.cbp;
7646 let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
7647 let acct = crate::bitacct::enabled();
7648 let mut t0 = if acct { cab.pos() } else { 0 };
7649 cb_cbp(cab, top.map(|a| cs.mb_cbp[a]), left.map(|a| cs.mb_cbp[a]), cbp);
7650 if acct {
7651 crate::bitacct::add(crate::bitacct::B::Cbp, cab.pos() - t0);
7652 }
7653 cs.mb_cbp[addr] = cbp as u8;
7654 let mut nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7655 let mut cbfdc = 0u16;
7656 let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
7657
7658 if cbp == 0 {
7659 cs.last_delta_qp = 0;
7660 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7661 fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
7662 }
7663 } else {
7664 let delta = fe.qp_delta();
7665 if acct { t0 = cab.pos(); }
7666 cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7667 if acct {
7668 crate::bitacct::add(crate::bitacct::B::QpDelta, cab.pos() - t0);
7669 t0 = cab.pos();
7670 }
7671 for id8 in 0..4usize {
7672 for id4 in 0..4usize {
7673 let iz = id8 * 4 + id4;
7674 let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
7675 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7676 let total = if cbp_luma & (1 << id8) != 0 {
7677 let sc = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
7678 cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_LUMA_4X4, false, ndc, &sc)
7679 } else {
7680 nzc[CB_NZC_CACHE[iz]] = 0;
7681 0
7682 };
7683 fe.nnz_y[by * w4 + bx] = total as u8;
7684 }
7685 }
7686 if acct {
7687 crate::bitacct::add(crate::bitacct::B::ResidLuma, cab.pos() - t0);
7688 t0 = cab.pos();
7689 }
7690 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);
7691 if acct {
7692 crate::bitacct::add(crate::bitacct::B::ResidChroma, cab.pos() - t0);
7693 }
7694 }
7695 cs.cbf_dc[addr] = cbfdc;
7696 cs.mb_nzc[addr] = cb_export_nzc(&nzc);
7697}
7698
7699fn emit_mb_cabac_p_intra(
7702 fe: &mut FrameEncoder,
7703 cab: &mut CabacEncoder,
7704 cs: &mut CabacState,
7705 plan: &MbPlan,
7706 mb_x: usize,
7707 mb_y: usize,
7708) {
7709 let mb_w = fe.mb_w;
7710 let addr = mb_y * mb_w + mb_x;
7711 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7712 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7713 let acct = crate::bitacct::enabled();
7714 let t0 = if acct { cab.pos() } else { 0 };
7715 cb_mb_type_p_intra(cab, plan);
7716 emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7717 if acct {
7718 crate::bitacct::add(crate::bitacct::B::IntraBody, cab.pos() - t0);
7721 }
7722}
7723
7724fn emit_p_skip_cabac(cab: &mut CabacEncoder, cs: &mut CabacState, addr: usize, top: Option<usize>, left: Option<usize>) {
7729 let sctx = 11
7730 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7731 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7732 let t0 = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7733 cb_mb_skip(cab, sctx, true);
7734 if crate::bitacct::enabled() {
7735 crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - t0);
7736 }
7737 cs.mb_skip[addr] = true;
7738 cs.cat[addr] = 100;
7739 cs.last_delta_qp = 0;
7740}
7741
7742fn frame_median_mb_var(sy: &[u8], cw: usize, mb_w: usize, mb_h: usize) -> i64 {
7762 let mut vs: Vec<i64> = Vec::with_capacity(mb_w * mb_h);
7763 for my in 0..mb_h {
7764 for mx in 0..mb_w {
7765 let (mut sum, mut sq) = (0i64, 0i64);
7766 for r in (0..16).step_by(2) {
7767 let row = (my * 16 + r) * cw + mx * 16;
7768 for c in (0..16).step_by(2) {
7769 let v = sy[row + c] as i64;
7770 sum += v;
7771 sq += v * v;
7772 }
7773 }
7774 let n = 64i64;
7775 vs.push((sq - sum * sum / n) / n);
7776 }
7777 }
7778 vs.sort_unstable();
7779 vs.get(vs.len() / 2).copied().unwrap_or(0)
7780}
7781
7782fn me_lambda_scale(
7785 cfg: &EncoderConfig,
7786 sy: &[u8],
7787 cw: usize,
7788 mb_w: usize,
7789 mb_h: usize,
7790 ref_y: Option<&[u8]>,
7791) -> f64 {
7792 let hi = match cfg.tune_lme_hi {
7793 Some(v) if v > 0.0 => v,
7794 _ => return cfg.cabac_lambda_scale,
7795 };
7796 if frame_median_mb_var(sy, cw, mb_w, mb_h) >= cfg.tune_lme_tex_thresh.unwrap_or(650) {
7817 return cfg.cabac_lambda_scale;
7818 }
7819 if let Some(r) = ref_y {
7820 let mot = cfg.tune_lme_motion_thresh.unwrap_or(26.0);
7821 if global_mc_residual(sy, cw, mb_h * 16, r) >= mot {
7822 return cfg.cabac_lambda_scale;
7823 }
7824 }
7825 hi
7826}
7827
7828pub fn encode_slice_data_cabac_p(
7829 w: &mut BitWriter,
7830 cfg: &EncoderConfig,
7831 frame: &YuvFrame,
7832 qp: u8,
7833 refs: &[crate::RefFrame],
7834 qpo: &[i32],
7835) -> crate::RefFrame {
7836 let mut fe = FrameEncoder::new(cfg);
7837 fe.qp = qp;
7838 fe.qpc = chroma_qp(qp);
7839 fe.cur_qp = qp;
7840 if cfg.cabac_dz_div > 0 {
7841 fe.idz = cfg.cabac_dz_div; }
7843 let (sy, su, sv) = coded_source(cfg, frame);
7844 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
7845 let lme_scale = me_lambda_scale(cfg, &sy, fe.cw, fe.mb_w, fe.mb_h, refs.first().map(|r| &r.y[..]));
7848 let num_refs = refs.len();
7849 if fe.me_wide && !refs.is_empty() {
7851 let coh = global_mc_residual(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7852 if std::env::var("RFF_ME_COH_DBG").is_ok() {
7853 eprintln!("ME_COH qp{qp} residual={coh:.2}");
7854 }
7855 if coh < fe.me_wide_coh {
7856 fe.me_wide = false;
7857 }
7858 }
7859 if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
7866 let hr = me_wide_headroom(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7867 if me_wide_hr_dbg() {
7868 eprintln!("ME_HR qp{qp} headroom={hr:.2}");
7869 }
7870 if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
7871 fe.me_wide = false;
7872 }
7873 }
7874 if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
7877 let (mg, dc) = b2_mgain(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7878 if me_sadt_dbg() {
7879 eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
7880 }
7881 fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
7882 if mv_smooth_mode() == 1 {
7885 fe.mv_smooth = mg >= mv_smooth_t() && dc <= me_sad_dcmax();
7888 }
7889 let smg = split_mg();
7891 if smg > 0.0 {
7892 fe.do_splits = mg >= smg;
7893 }
7894 }
7895 if fe.satd_q > 0.0 {
7896 let mut vars: Vec<i64> = (0..fe.mb_h)
7897 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
7898 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
7899 .collect();
7900 vars.sort_unstable();
7901 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
7902 fe.satd_var_thresh = vars[idx];
7903 }
7904 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
7905 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
7907 let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
7908
7909 let mut greedy_free = 0usize;
7913 let mut greedy_seen = 0usize;
7914 let mut greedy_on = fe.greedy_min_free == 0;
7915 let greedy_learn = (fe.mb_w * fe.mb_h / 8).max(64);
7916 let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false); let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
7918 let total = fe.mb_w * fe.mb_h;
7919
7920 let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
7923 for mb_y in 0..fe.mb_h {
7924 for mb_x in 0..fe.mb_w {
7925 let mb_idx = mb_y * fe.mb_w + mb_x;
7926 let addr = mb_idx;
7927 let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
7928 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7929 fe.qp = aq_qp[mb_idx];
7930 fe.qpc = chroma_qp(aq_qp[mb_idx]);
7931
7932 let mut inter: Option<InterChoice> = None;
7934 let mut did_skip = false;
7935 if num_refs > 0 {
7936 let mv_skip = fe.skip_mv(mb_x, mb_y);
7937 let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
7938 let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
7939 let skip_c = if luma_free || !fe.fast {
7940 fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
7941 } else {
7942 [[0u8; 64]; 2]
7943 };
7944 let is_free = luma_free && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &skip_c);
7945 let skip_sad = if fe.fast {
7946 0
7947 } else {
7948 let (lx, ly) = (mb_x * 16, mb_y * 16);
7949 let mut s = 0u32;
7950 for dy in 0..16 {
7951 let src = &sy[(ly + dy) * fe.cw + lx..][..16];
7952 let p = &skip_y[dy * 16..][..16];
7953 s += src.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
7954 }
7955 s
7956 };
7957 greedy_seen += 1;
7958 if greedy_seen >= greedy_learn {
7959 greedy_on = fe.greedy_min_free == 0
7960 || greedy_free * 100 >= greedy_seen * fe.greedy_min_free as usize;
7961 }
7962 if is_free {
7963 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
7964 if !fe.fast {
7965 fe.mb_was_skip[mb_idx] = true;
7966 fe.mb_skip_sad[mb_idx] = skip_sad;
7967 }
7968 greedy_free += 1;
7969 did_skip = true;
7970 } else {
7971 let (lx, ly) = (mb_x * 16, mb_y * 16);
7972 let nb = {
7973 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMvPred);
7974 fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
7975 };
7976 let lme = lambda.sqrt() * lme_scale;
7977 if fe.fast {
7978 fe.mb_use_satd = fe.satd_q > 0.0
7979 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
7980 let (r16, mv16, cost_inter) =
7981 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
7982 let cost_intra = if fe.mb_use_satd {
7983 fe.best_i16_satd(&sy, mb_x, mb_y)
7984 } else {
7985 fe.best_i16_sad(&sy, mb_x, mb_y)
7986 } + (lme * fe.tune_intra_penalty) as i64;
7987 inter = if cost_intra < cost_inter {
7988 None
7989 } else {
7990 Some((0, vec![(r16, mv16)]))
7991 };
7992 } else {
7993 if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
7995 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
7996 fe.mb_was_skip[mb_idx] = true;
7997 fe.mb_skip_sad[mb_idx] = skip_sad;
7998 did_skip = true;
7999 } else {
8000 let (r16, mv16, c16) =
8001 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
8002 let mut best_c = c16;
8003 let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
8004 const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
8005 let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
8006 let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
8007 let split_t = split_t();
8008 if fe.do_splits && c16 > split_gate && (split_t <= 0.0 || (c16 as f64) >= split_t * lme) {
8009 let (rt, mvt, ct) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 8, &[mv16], lme);
8010 let (rb, mvb, cb) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly + 8, 16, 8, &[mv16], lme);
8011 let (rl, mvl, cl) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 8, 16, &[mv16], lme);
8012 let (rr, mvr, cr) = fe.best_part(refs, &sy, &nb, num_refs, lx + 8, ly, 8, 16, &[mv16], lme);
8013 if ct + cb < best_c {
8014 best_c = ct + cb;
8015 pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
8016 }
8017 if cl + cr < best_c {
8018 best_c = cl + cr;
8019 pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
8020 }
8021 if fe.sub8x8 {
8023 let mut c8 = (lme * 4.0) as i64;
8024 let mut p8 = Vec::with_capacity(4);
8025 for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
8026 let (r, mv, c) = fe.best_part(
8027 refs, &sy, &nb, num_refs, lx + qx, ly + qy, 8, 8, &[mv16], lme,
8028 );
8029 c8 += c;
8030 p8.push((r, mv));
8031 }
8032 if c8 < best_c {
8033 best_c = c8;
8034 pick = Some((3u8, p8));
8035 }
8036 }
8037 }
8038 if fe.sp_defer.get() {
8043 if let Some((mode, parts)) = pick.as_mut() {
8044 let regions: &[(usize, usize, usize, usize)] = match mode {
8045 1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
8046 2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
8047 3 => &[(0, 0, 8, 8), (8, 0, 8, 8), (0, 8, 8, 8), (8, 8, 8, 8)],
8048 _ => &[(0, 0, 16, 16)],
8049 };
8050 let mut tot = if *mode == 3 { (lme * 4.0) as i64 } else { 0 };
8051 for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
8052 let (r, mv) = parts[i];
8053 let (m2, c2) = fe.refine_part(
8054 refs, &sy, &nb, num_refs, lx + qx, ly + qy, pw, ph, lme, r, mv,
8055 );
8056 parts[i] = (r, m2);
8057 tot += c2;
8058 }
8059 best_c = tot;
8060 }
8061 }
8062 let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
8063 + (lme * fe.tune_intra_penalty) as i64;
8064 inter = if c_intra < best_c { None } else { pick };
8065 fe.mb_was_skip[mb_idx] = false;
8066 fe.mb_skip_sad[mb_idx] = skip_sad;
8067 }
8068 }
8069 }
8070 }
8071
8072 if did_skip {
8074 emit_p_skip_cabac(&mut cab, &mut cs, addr, top, left);
8075 mb_qpy[mb_idx] = fe.cur_qp;
8076 {
8077 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8078 {
8079 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8080 cab.encode_terminate(mb_idx + 1 == total);
8081 if crate::bitacct::enabled() {
8082 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8083 }
8084 }
8085 if crate::bitacct::enabled() {
8086 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8087 }
8088 }
8089 continue;
8090 }
8091 let sctx = 11
8093 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
8094 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
8095 let tskip = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8096 cb_mb_skip(&mut cab, sctx, false);
8097 if crate::bitacct::enabled() {
8098 crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - tskip);
8099 }
8100 cs.mb_skip[addr] = false;
8101 match inter {
8102 Some((mode, parts)) => {
8103 let plan = fe.plan_inter_mb(refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts, None);
8104 let _ge = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
8108 emit_mb_cabac_p_inter(&mut fe, &mut cab, &mut cs, mode, &plan, mb_x, mb_y, num_refs);
8109 }
8110 None => {
8111 let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
8112 let _ge = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
8113 emit_mb_cabac_p_intra(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
8114 }
8115 }
8116 mb_qpy[mb_idx] = fe.cur_qp;
8117 {
8118 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8119 cab.encode_terminate(mb_idx + 1 == total);
8120 if crate::bitacct::enabled() {
8121 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8122 }
8123 }
8124 }
8125 }
8126
8127 while !w.is_byte_aligned() {
8128 w.write_bit(true);
8129 }
8130 for b in cab.into_bytes() {
8131 w.write_bits(b as u32, 8);
8132 }
8133
8134 let ref_id: Vec<i32> = fe.ref_idx_y.iter().map(|&r| if r >= 0 { r } else { i32::MIN }).collect();
8136 let info = rusty_h264_common::deblock::BlockInfo {
8137 inter: &fe.inter_y,
8138 nnz: &fe.nnz_y,
8139 mv: &fe.mv_y,
8140 ref_id: &ref_id,
8141 mv1: &[],
8142 ref_id1: &[],
8143 w4: fe.mb_w * 4,
8144 t8x8: &[],
8145 bs: &[],
8146 };
8147 rusty_h264_common::deblock::filter_frame(
8148 &mut fe.rec_y, &mut fe.rec_u, &mut fe.rec_v, fe.mb_w, fe.mb_h, &mb_qpy, 0, 0, 0, &info,
8149 );
8150 let w4 = fe.mb_w * 4;
8151 crate::RefFrame {
8152 y: fe.rec_y,
8153 u: fe.rec_u,
8154 v: fe.rec_v,
8155 poc: 0,
8156 frame_num: 0,
8157 mv: fe.mv_y,
8158 ref_idx: fe.ref_idx_y,
8159 w4,
8160 hpel: std::sync::OnceLock::new(),
8162 }
8163}
8164
8165fn cb_fill_inter_cache(
8175 mb_ref: &[[i8; 16]],
8176 mb_mvd: &[[[i16; 2]; 16]],
8177 refc: &mut [i8; 30],
8178 mvdc: &mut [[i16; 2]; 30],
8179 top: Option<usize>,
8180 left: Option<usize>,
8181 addr: usize,
8182 mb_w: usize,
8183) {
8184 if let Some(l) = left {
8185 for (ci, bi) in [(6usize, 3usize), (12, 7), (18, 11), (24, 15)] {
8186 refc[ci] = mb_ref[l][bi];
8187 mvdc[ci] = mb_mvd[l][bi];
8188 }
8189 }
8190 if let Some(t) = top {
8191 for (ci, bi) in [(1usize, 12usize), (2, 13), (3, 14), (4, 15)] {
8192 refc[ci] = mb_ref[t][bi];
8193 mvdc[ci] = mb_mvd[t][bi];
8194 }
8195 }
8196 let mb_x = addr % mb_w;
8197 let mb_y = addr / mb_w;
8198 if mb_x > 0 && mb_y > 0 {
8199 let a = addr - mb_w - 1;
8200 (refc[0], mvdc[0]) = (mb_ref[a][15], mb_mvd[a][15]);
8201 }
8202 if mb_y > 0 && mb_x + 1 < mb_w {
8203 let a = addr - mb_w + 1;
8204 (refc[5], mvdc[5]) = (mb_ref[a][12], mb_mvd[a][12]);
8205 }
8206}
8207
8208pub fn b_part_mb_type(p0: u8, p1: u8, mvmode: u8) -> u32 {
8213 let base = match (p0, p1) {
8214 (1, 1) => 4,
8215 (2, 2) => 6,
8216 (1, 2) => 8,
8217 (2, 1) => 10,
8218 (1, 3) => 12,
8219 (2, 3) => 14,
8220 (3, 1) => 16,
8221 (3, 2) => 18,
8222 _ => 20, };
8224 base + if mvmode == 2 { 1 } else { 0 }
8225}
8226
8227fn b_part_layout(mvmode: u8) -> ([(usize, usize, usize, usize); 2], [(usize, &'static [usize]); 2]) {
8230 if mvmode == 1 {
8231 ([(0, 0, 16, 8), (0, 8, 16, 8)],
8232 [(0, &[0, 1, 2, 3, 4, 5, 6, 7][..]), (8, &[8, 9, 10, 11, 12, 13, 14, 15][..])])
8233 } else {
8234 ([(0, 0, 8, 16), (8, 0, 8, 16)],
8235 [(0, &[0, 1, 2, 3, 8, 9, 10, 11][..]), (4, &[4, 5, 6, 7, 12, 13, 14, 15][..])])
8236 }
8237}
8238
8239pub fn cb_mb_type_b(cab: &mut CabacEncoder, ctx_inc: usize, mb_type: u32) {
8257 const B: usize = 27;
8258 if mb_type == 0 {
8259 cab.encode_decision(B + ctx_inc, 0); return;
8261 }
8262 cab.encode_decision(B + ctx_inc, 1);
8263 if mb_type <= 2 {
8264 cab.encode_decision(B + 3, 0);
8265 cab.encode_decision(B + 5, mb_type - 1); return;
8267 }
8268 cab.encode_decision(B + 3, 1);
8269 let (m4, extra) = if mb_type <= 10 {
8270 (mb_type - 3, None)
8271 } else if mb_type == 11 {
8272 (14, None) } else if mb_type == 22 {
8274 (15, None) } else {
8276 let v = mb_type + 4;
8277 (v >> 1, Some(v & 1))
8278 };
8279 cab.encode_decision(B + 4, (m4 >> 3) & 1);
8280 cab.encode_decision(B + 5, (m4 >> 2) & 1);
8281 cab.encode_decision(B + 5, (m4 >> 1) & 1);
8282 cab.encode_decision(B + 5, m4 & 1);
8283 if let Some(e) = extra {
8284 cab.encode_decision(B + 5, e);
8285 }
8286}
8287
8288const CB_ALL16: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
8289
8290fn emit_mb_cabac_b(
8293 fe: &mut FrameEncoder,
8294 cab: &mut CabacEncoder,
8295 cs: &mut CabacState,
8296 dir: u8,
8297 bsplit: Option<(u8, [(u8, (i32, i32), (i32, i32)); 2])>,
8298 plan: &InterPlan,
8299 mb_x: usize,
8300 mb_y: usize,
8301) {
8302 let mb_w = fe.mb_w;
8303 let addr = mb_y * mb_w + mb_x;
8304 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
8305 let left = if mb_x > 0 { Some(addr - 1) } else { None };
8306
8307 let bci = left.map_or(0, |a| (!cs.mb_direct[a]) as usize)
8308 + top.map_or(0, |a| (!cs.mb_direct[a]) as usize);
8309 let bmt = match bsplit {
8310 Some((mvmode, parts2)) => b_part_mb_type(parts2[0].0, parts2[1].0, mvmode),
8311 None => dir as u32,
8312 };
8313 cb_mb_type_b(cab, bci, bmt);
8314
8315 let mut mvdc0 = [[0i16; 2]; 30];
8317 let mut refc0 = [-1i8; 30];
8318 let mut mvdc1 = [[0i16; 2]; 30];
8319 let mut refc1 = [-1i8; 30];
8320 cb_fill_inter_cache(&cs.mb_ref, &cs.mb_mvd, &mut refc0, &mut mvdc0, top, left, addr, mb_w);
8321 cb_fill_inter_cache(&cs.mb_ref1, &cs.mb_mvd1, &mut refc1, &mut mvdc1, top, left, addr, mb_w);
8322 let mut mmvd0 = [[0i16; 2]; 16];
8323 let mut mref0 = [-1i8; 16];
8324 let mut mmvd1 = [[0i16; 2]; 16];
8325 let mut mref1 = [-1i8; 16];
8326 let (use0, use1) = (dir == 1 || dir == 3, dir == 2 || dir == 3);
8327 if let Some((mvmode, parts2)) = bsplit {
8328 let (_, zb) = b_part_layout(mvmode);
8334 let mut k = 0;
8335 for list in 0..2 {
8336 for part in 0..2 {
8337 let pred = parts2[part].0;
8338 let used = if list == 0 { pred == 1 || pred == 3 } else { pred == 2 || pred == 3 };
8339 if !used {
8340 continue;
8341 }
8342 let (pidx, blocks) = zb[part];
8343 if list == 0 {
8344 cb_emit_mvd_partition(cab, pidx, blocks, &mut mvdc0, &mut refc0, &mut mmvd0, &mut mref0, plan.mvds[k], 0);
8345 } else {
8346 cb_emit_mvd_partition(cab, pidx, blocks, &mut mvdc1, &mut refc1, &mut mmvd1, &mut mref1, plan.mvds[k], 0);
8347 }
8348 k += 1;
8349 }
8350 }
8351 } else if dir == 0 {
8352 mref0 = [0i8; 16];
8355 mref1 = [0i8; 16];
8356 } else {
8357 let mut k = 0;
8359 if use0 {
8360 cb_emit_mvd_partition(cab, 0, &CB_ALL16, &mut mvdc0, &mut refc0, &mut mmvd0, &mut mref0, plan.mvds[k], 0);
8361 k += 1;
8362 }
8363 if use1 {
8364 cb_emit_mvd_partition(cab, 0, &CB_ALL16, &mut mvdc1, &mut refc1, &mut mmvd1, &mut mref1, plan.mvds[k], 0);
8365 }
8366 }
8367 cs.mb_mvd[addr] = mmvd0;
8368 cs.mb_ref[addr] = mref0;
8369 cs.mb_mvd1[addr] = mmvd1;
8370 cs.mb_ref1[addr] = mref1;
8371 cs.mb_direct[addr] = dir == 0 && bsplit.is_none();
8372 cs.cat[addr] = 100;
8373 cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
8374}
8375
8376fn emit_b_skip_cabac(cab: &mut CabacEncoder, cs: &mut CabacState, addr: usize, top: Option<usize>, left: Option<usize>) {
8380 let sctx = 24
8381 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
8382 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
8383 let t0 = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8384 cb_mb_skip(cab, sctx, true);
8385 if crate::bitacct::enabled() {
8386 crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - t0);
8387 }
8388 cs.mb_skip[addr] = true;
8389 cs.cat[addr] = 100;
8390 cs.mb_direct[addr] = true;
8391 cs.mb_ref[addr] = [0i8; 16];
8392 cs.mb_ref1[addr] = [0i8; 16];
8393 cs.last_delta_qp = 0;
8394}
8395
8396#[allow(clippy::too_many_arguments)]
8400pub mod bstats {
8404 use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
8405 pub static SKIP: AtomicU64 = AtomicU64::new(0);
8406 pub static CODED: AtomicU64 = AtomicU64::new(0);
8407 pub static DIRWIN: AtomicU64 = AtomicU64::new(0);
8412 pub static SPLIT: AtomicU64 = AtomicU64::new(0);
8416 pub fn on() -> bool {
8417 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8418 *E.get_or_init(|| std::env::var_os("RFF_BSTATS").is_some())
8419 }
8420 pub fn bump(c: &AtomicU64) {
8421 if on() {
8422 c.fetch_add(1, Relaxed);
8423 }
8424 }
8425 pub fn dump() {
8429 let (s, c) = (SKIP.load(Relaxed), CODED.load(Relaxed));
8430 let t = (s + c).max(1) as f64;
8431 eprintln!(
8432 "B-slice census: B_Skip {:.1}% not-skipped {:.1}% direct-wins-of-coded {:.1}% 16x8/8x16-of-coded {:.1}% (n={})",
8433 s as f64 * 100.0 / t, c as f64 * 100.0 / t,
8434 DIRWIN.load(Relaxed) as f64 * 100.0 / c.max(1) as f64,
8435 SPLIT.load(Relaxed) as f64 * 100.0 / c.max(1) as f64, s + c
8436 );
8437 }
8438}
8439
8440pub fn encode_slice_data_cabac_b(
8441 w: &mut BitWriter,
8442 cfg: &EncoderConfig,
8443 frame: &YuvFrame,
8444 qp: u8,
8445 poc: i32,
8446 l0: &crate::RefFrame,
8447 l1: &crate::RefFrame,
8448 qpo: &[i32],
8449) {
8450 let mut fe = FrameEncoder::new(cfg);
8451 fe.qp = qp;
8452 fe.qpc = chroma_qp(qp);
8453 fe.cur_qp = qp;
8454 if cfg.cabac_dz_div > 0 {
8455 fe.idz = cfg.cabac_dz_div; }
8457 fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
8458 let (sy, su, sv) = coded_source(cfg, frame);
8459 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
8460 let lme = lambda.sqrt() * me_lambda_scale(cfg, &sy, fe.cw, fe.mb_w, fe.mb_h, Some(&l0.y[..]));
8461 let refs = std::slice::from_ref(l0);
8462 if fe.satd_q > 0.0 {
8463 let mut vars: Vec<i64> = (0..fe.mb_h)
8464 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
8465 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
8466 .collect();
8467 vars.sort_unstable();
8468 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
8469 fe.satd_var_thresh = vars[idx];
8470 }
8471 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
8472 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
8474
8475 let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false);
8476 let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
8477 let total = fe.mb_w * fe.mb_h;
8478 let bskip_t = std::env::var("RFF_BSKIP_T").ok().and_then(|v| v.parse::<f64>().ok())
8480 .or(cfg.tune_bskip_rd)
8481 .unwrap_or(0.0);
8482 let bskip_busy_pct = std::env::var("RFF_BSKIP_BUSY").ok().and_then(|v| v.parse::<usize>().ok())
8483 .or(cfg.tune_bskip_busy_pct)
8484 .unwrap_or(60);
8485 let (mut b_seen, mut b_free) = (0usize, 0usize);
8486 let bsplit_env = std::env::var("RFF_BSPLIT").ok().and_then(|v| v.parse::<u32>().ok());
8488 let bsplit_on = bsplit_env.map(|v| v == 1).unwrap_or(cfg.tune_b_split);
8489 let bsplit_probe = bsplit_env.filter(|&v| v >= 2).unwrap_or(0);
8490 let (mut b_coded, mut b_dirwin) = (0usize, 0usize);
8494 let bskip_dirwin_pct = std::env::var("RFF_BSKIP_DIRWIN").ok().and_then(|v| v.parse::<usize>().ok())
8495 .or(cfg.tune_bskip_dirwin_pct)
8496 .unwrap_or(10);
8497
8498 for mb_y in 0..fe.mb_h {
8499 for mb_x in 0..fe.mb_w {
8500 let mb_idx = mb_y * fe.mb_w + mb_x;
8501 let addr = mb_idx;
8502 let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
8503 let left = if mb_x > 0 { Some(addr - 1) } else { None };
8504 fe.qp = aq_qp[mb_idx];
8505 fe.qpc = chroma_qp(aq_qp[mb_idx]);
8506 let (lx, ly) = (mb_x * 16, mb_y * 16);
8507 let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
8508 fe.mb_use_satd =
8509 fe.satd_q > 0.0 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
8510 let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
8511 let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
8512 let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
8513 let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
8514 let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
8515 let free_skip = fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
8517 && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc);
8518 b_seen += 1;
8519 if free_skip {
8520 b_free += 1;
8521 }
8522 if free_skip {
8523 bstats::bump(&bstats::SKIP);
8524 fe.commit_direct_motion(mb_x, mb_y, &dmotion);
8525 emit_b_skip_cabac(&mut cab, &mut cs, addr, top, left);
8526 {
8527 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8528 {
8529 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8530 cab.encode_terminate(mb_idx + 1 == total);
8531 if crate::bitacct::enabled() {
8532 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8533 }
8534 }
8535 if crate::bitacct::enabled() {
8536 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8537 }
8538 }
8539 continue;
8540 }
8541 bstats::bump(&bstats::CODED);
8542 let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
8543 let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
8544 let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
8545 let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
8546 let r_bi = mvd_bits(mv0.0 - pmv0.0) + mvd_bits(mv0.1 - pmv0.1)
8547 + mvd_bits(mv1.0 - pmv1.0) + mvd_bits(mv1.1 - pmv1.1);
8548 let j_bi = d_bi + (lme * r_bi as f64) as i64;
8549 let (mut dir, mut best) = (0u8, d_direct);
8550 if j0 < best { dir = 1; best = j0; }
8551 if j1 < best { dir = 2; best = j1; }
8552 if j_bi < best { dir = 3; best = j_bi; }
8553 if dir == 0 { bstats::bump(&bstats::DIRWIN); }
8554 b_coded += 1;
8555 if dir == 0 {
8556 b_dirwin += 1;
8557 }
8558 if bskip_t > 0.0
8575 && dir == 0
8576 && b_seen >= 32
8577 && b_free * 100 < b_seen * bskip_busy_pct
8578 && b_coded >= 32
8590 && b_dirwin * 100 >= b_coded * bskip_dirwin_pct
8591 && (d_direct as f64) <= bskip_t * lambda
8592 {
8593 bstats::bump(&bstats::SKIP);
8594 fe.commit_direct_motion(mb_x, mb_y, &dmotion);
8595 emit_b_skip_cabac(&mut cab, &mut cs, addr, top, left);
8596 cab.encode_terminate(mb_idx + 1 == total);
8597 continue;
8598 }
8599 let sctx = 24
8601 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
8602 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
8603 let tskip = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8604 cb_mb_skip(&mut cab, sctx, false);
8605 if crate::bitacct::enabled() {
8606 crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - tskip);
8607 }
8608 cs.mb_skip[addr] = false;
8609 let mut bsplit: Option<(u8, [(u8, (i32, i32), (i32, i32)); 2])> = None;
8616 if bsplit_probe > 0 && dir != 0 {
8624 let m = if bsplit_probe == 2 { 1u8 } else { 2u8 };
8625 bsplit = Some((m, [(dir, mv0, mv1); 2]));
8626 } else if bsplit_on {
8627 for mvmode in 1u8..=2 {
8628 let (rects, _) = b_part_layout(mvmode);
8629 let mut cand = [(0u8, (0i32, 0i32), (0i32, 0i32)); 2];
8630 let mut jsum = 0i64;
8631 for (part, &(rx, ry, rw, rh)) in rects.iter().enumerate() {
8632 let (px, py) = (lx + rx, ly + ry);
8633 let (m0, c0) = fe.motion_search(l0, &sy, px, py, rw, rh, &[pmv0], lme, None);
8634 let (m1, c1) = fe.motion_search(l1, &sy, px, py, rw, rh, &[pmv1], lme, None);
8635 let dbi = fe.bi_dist_rect(l0, l1, &sy, px, py, rw, rh, m0, m1);
8637 let rbi = mvd_bits(m0.0 - pmv0.0) + mvd_bits(m0.1 - pmv0.1)
8638 + mvd_bits(m1.0 - pmv1.0) + mvd_bits(m1.1 - pmv1.1);
8639 let jbi = dbi + (lme * rbi as f64) as i64;
8640 let (mut bp, mut bj) = (1u8, c0);
8641 if c1 < bj { bp = 2; bj = c1; }
8642 if jbi < bj { bp = 3; bj = jbi; }
8643 cand[part] = (bp, m0, m1);
8644 jsum += bj;
8645 }
8646 let jsplit = jsum + (lme * 4.0) as i64;
8648 if jsplit < best {
8649 best = jsplit;
8650 bsplit = Some((mvmode, cand));
8651 }
8652 }
8653 if bsplit.is_some() {
8654 bstats::bump(&bstats::SPLIT);
8655 }
8656 }
8657 let bspec = if let Some((mvmode, parts2)) = bsplit {
8658 BInter { dir, l1, mv0, mv1, mvmode, parts2 }
8659 } else {
8660 BInter { dir, l1, mv0, mv1, mvmode: 0, parts2: [(0, (0, 0), (0, 0)); 2] }
8661 };
8662 let plan = fe.plan_inter_mb(refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
8663 emit_mb_cabac_b(&mut fe, &mut cab, &mut cs, dir, bsplit, &plan, mb_x, mb_y);
8664 {
8665 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8666 cab.encode_terminate(mb_idx + 1 == total);
8667 if crate::bitacct::enabled() {
8668 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8669 }
8670 }
8671 }
8672 }
8673
8674 while !w.is_byte_aligned() {
8675 w.write_bit(true);
8676 }
8677 for b in cab.into_bytes() {
8678 w.write_bits(b as u32, 8);
8679 }
8680 }
8682
8683pub fn encode_all_skip_b_cabac(w: &mut BitWriter, cfg: &EncoderConfig, qp: u8, n: usize) {
8687 let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false);
8688 for i in 0..n {
8689 cab.encode_decision(24, 1);
8692 cab.encode_terminate(i + 1 == n);
8693 }
8694 while !w.is_byte_aligned() {
8695 w.write_bit(true);
8696 }
8697 for b in cab.into_bytes() {
8698 w.write_bits(b as u32, 8);
8699 }
8700}