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: &[], kind: &[],
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 kind: &[],
5271 };
5272 drop(_g_fin);
5275 rusty_h264_common::deblock::filter_frame(
5276 &mut fe.rec_y,
5277 &mut fe.rec_u,
5278 &mut fe.rec_v,
5279 fe.mb_w,
5280 fe.mb_h,
5281 &mb_qpy,
5282 0, 0, 0, &info,
5286 );
5287 let w4 = fe.mb_w * 4;
5288 crate::RefFrame {
5289 y: fe.rec_y,
5290 u: fe.rec_u,
5291 v: fe.rec_v,
5292 poc: 0, frame_num: 0, mv: fe.mv_y,
5296 ref_idx: fe.ref_idx_y,
5297 w4,
5298 hpel: std::sync::OnceLock::new(),
5300 }
5301}
5302
5303#[allow(clippy::too_many_arguments)]
5314#[allow(clippy::too_many_arguments)]
5315pub fn encode_slice_data_b(
5316 w: &mut BitWriter,
5317 cfg: &EncoderConfig,
5318 frame: &YuvFrame,
5319 qp: u8,
5320 poc: i32,
5321 l0: &crate::RefFrame,
5322 l1: &crate::RefFrame,
5323 qpo: &[i32],
5324) {
5325 let mut fe = FrameEncoder::new(cfg);
5326 fe.qp = qp;
5327 fe.qpc = chroma_qp(qp);
5328 fe.cur_qp = qp;
5329 if cfg.cabac_dz_div > 0 {
5330 fe.idz = cfg.cabac_dz_div; } fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
5335 let (sy, su, sv) = coded_source(cfg, frame);
5336 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
5337 let lme = lambda.sqrt();
5338 let refs = std::slice::from_ref(l0); if fe.satd_q > 0.0 {
5342 let mut vars: Vec<i64> = (0..fe.mb_h)
5343 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
5344 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
5345 .collect();
5346 vars.sort_unstable();
5347 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
5348 fe.satd_var_thresh = vars[idx];
5349 }
5350 let mut skip_run = 0u32; for mb_y in 0..fe.mb_h {
5352 for mb_x in 0..fe.mb_w {
5353 let (lx, ly) = (mb_x * 16, mb_y * 16);
5354 let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
5355 fe.mb_use_satd =
5356 fe.satd_q > 0.0 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
5357 let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
5360 let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
5361 let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
5362 let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
5363 let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
5367 if fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
5375 && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc)
5376 {
5377 fe.commit_direct_motion(mb_x, mb_y, &dmotion);
5378 skip_run += 1;
5379 continue;
5380 }
5381 let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
5382 let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
5383 let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
5384 let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
5386 let r_bi = mvd_bits(mv0.0 - pmv0.0) + mvd_bits(mv0.1 - pmv0.1)
5387 + mvd_bits(mv1.0 - pmv1.0) + mvd_bits(mv1.1 - pmv1.1);
5388 let j_bi = d_bi + (lme * r_bi as f64) as i64;
5389 let (mut dir, mut best) = (0u8, d_direct);
5394 if j0 < best { dir = 1; best = j0; }
5395 if j1 < best { dir = 2; best = j1; }
5396 if j_bi < best { dir = 3; best = j_bi; }
5397 let _ = best; w.write_ue(skip_run); skip_run = 0;
5400 let bspec = BInter { dir, l1, mv0, mv1, mvmode: 0, parts2: [(0, (0, 0), (0, 0)); 2] };
5401 fe.encode_inter_mb_v1_b(w, refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
5402 }
5403 }
5404 if skip_run > 0 {
5405 w.write_ue(skip_run); }
5407 w.rbsp_trailing_bits();
5408}
5409
5410#[inline(always)]
5414fn mvd_bits(d: i32) -> u32 {
5415 let codenum = if d > 0 { (2 * d - 1) as u32 } else { (-2 * d) as u32 };
5416 1 + 2 * (31 - (codenum + 1).leading_zeros())
5417}
5418
5419fn write_ref_idx(w: &mut BitWriter, refi: i32, num_refs: usize) {
5423 if num_refs == 2 {
5424 w.write_bit(refi == 0); } else {
5426 w.write_ue(refi as u32);
5427 }
5428}
5429
5430fn ref_bits(r: usize, num_refs: usize) -> u32 {
5433 if num_refs <= 1 {
5434 0
5435 } else if num_refs == 2 {
5436 1
5437 } else {
5438 let mut n = r as u32 + 1;
5439 let mut len = 1;
5440 while n > 1 {
5441 n >>= 1;
5442 len += 2;
5443 }
5444 len
5445 }
5446}
5447
5448fn residual(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[i32; 16]) -> [i32; 16] {
5449 let mut r = [0i32; 16];
5450 for dy in 0..4 {
5451 for dx in 0..4 {
5452 r[dy * 4 + dx] = src[(y0 + dy) * stride + (x0 + dx)] as i32 - pred[dy * 4 + dx];
5453 }
5454 }
5455 r
5456}
5457
5458fn store(plane: &mut [u8], stride: usize, x0: usize, y0: usize, s: &[u8; 16]) {
5460 for dy in 0..4 {
5461 for dx in 0..4 {
5462 plane[(y0 + dy) * stride + (x0 + dx)] = s[dy * 4 + dx];
5463 }
5464 }
5465}
5466
5467fn pred_block(pred: &[u8; 256], bx: usize, by: usize) -> [i32; 16] {
5470 let mut p = [0i32; 16];
5471 for dy in 0..4 {
5472 for dx in 0..4 {
5473 p[dy * 4 + dx] = pred[(by * 4 + dy) * 16 + (bx * 4 + dx)] as i32;
5474 }
5475 }
5476 p
5477}
5478
5479#[inline]
5490pub(crate) fn satd_px(src: &[u8], ss: usize, pred: &[u8], ps: usize, w: usize, h: usize) -> i64 {
5491 #[cfg(accel)]
5492 {
5493 let asm = match (w, h) {
5494 (16, 16) => Some(rusty_h264_accel::satd_16x16(src, ss, pred, ps)),
5495 (16, 8) => Some(rusty_h264_accel::satd_16x8(src, ss, pred, ps)),
5496 (8, 16) => Some(rusty_h264_accel::satd_8x16(src, ss, pred, ps)),
5497 (8, 8) => Some(rusty_h264_accel::satd_8x8(src, ss, pred, ps)),
5498 (4, 4) => Some(rusty_h264_accel::satd_4x4(src, ss, pred, ps)),
5499 _ => None,
5500 };
5501 if let Some(v) = asm {
5502 return 2 * v as i64;
5503 }
5504 }
5505 let (nbx, nby) = (w / 4, h / 4);
5507 let mut blocks = [[0i32; 16]; 16];
5508 let mut bi = 0;
5509 for by in 0..nby {
5510 for bx in 0..nbx {
5511 let blk = &mut blocks[bi];
5512 for dy in 0..4 {
5513 for dx in 0..4 {
5514 blk[dy * 4 + dx] =
5515 src[(by * 4 + dy) * ss + bx * 4 + dx] as i32 - pred[(by * 4 + dy) * ps + bx * 4 + dx] as i32;
5516 }
5517 }
5518 bi += 1;
5519 }
5520 }
5521 satd_4x4_sum(&blocks[..nbx * nby])
5522}
5523
5524#[inline]
5529fn sad_strided(src: &[u8], ss: usize, r: &[u8], rs: usize, w: usize, h: usize) -> i64 {
5530 #[cfg(accel)]
5531 {
5532 match (w, h) {
5533 (16, 16) => return rusty_h264_accel::sad_16x16(src, ss, r, rs) as i64,
5534 (16, 8) => return rusty_h264_accel::sad_16x8(src, ss, r, rs) as i64,
5535 (8, 16) => return rusty_h264_accel::sad_8x16(src, ss, r, rs) as i64,
5536 _ => {}
5537 }
5538 }
5539 let mut sad = 0u32;
5540 for dy in 0..h {
5541 let a = &src[dy * ss..][..w];
5542 let b = &r[dy * rs..][..w];
5543 sad += a.iter().zip(b).map(|(&x, &y)| x.abs_diff(y) as u32).sum::<u32>();
5544 }
5545 sad as i64
5546}
5547
5548#[inline]
5552fn sad_avg_strided(src: &[u8], ss: usize, a: &[u8], b: &[u8], rs: usize, w: usize, h: usize) -> i64 {
5553 let mut sad = 0u32;
5554 for dy in 0..h {
5555 let s = &src[dy * ss..][..w];
5556 let pa = &a[dy * rs..][..w];
5557 let pb = &b[dy * rs..][..w];
5558 for i in 0..w {
5559 let p = ((pa[i] as u16 + pb[i] as u16 + 1) >> 1) as u8;
5560 sad += s[i].abs_diff(p) as u32;
5561 }
5562 }
5563 sad as i64
5564}
5565
5566fn satd_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
5567 satd_px(&src[ly * stride + lx..], stride, pred, 16, 16, 16)
5568}
5569
5570fn sad_16x16(src: &[u8], stride: usize, lx: usize, ly: usize, pred: &[u8; 256]) -> i64 {
5574 let mut sad = 0u32;
5575 for dy in 0..16 {
5576 let s = &src[(ly + dy) * stride + lx..][..16];
5577 let p = &pred[dy * 16..][..16];
5578 sad += s.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
5579 }
5580 sad as i64
5581}
5582
5583fn satd_8x8(src: &[u8], stride: usize, x0: usize, y0: usize, pred: &[u8; 64]) -> i64 {
5585 satd_px(&src[y0 * stride + x0..], stride, pred, 8, 8, 8)
5586}
5587
5588fn satd_4x4(src: &[u8], stride: usize, px: usize, py: usize, pred: &[u8; 16]) -> i64 {
5590 satd_px(&src[py * stride + px..], stride, pred, 4, 4, 4)
5591}
5592
5593fn i4_mode_available(mode: u8, top: bool, left: bool) -> bool {
5595 match mode {
5596 0 | 3 | 7 => top, 1 | 8 => left, 2 => true, _ => top && left, }
5601}
5602
5603struct I4Plan {
5606 modes: [u8; 16], q: [[i32; 16]; 16], cbp_luma: u32, nonzero: i64, }
5611
5612struct MbPlan {
5618 use_i4: bool,
5619 i16_mode: I16Mode,
5622 i16_cbp15: bool,
5623 i16_dc_levels: [i32; 16],
5624 i16_q: [[i32; 16]; 16],
5625 i4: Option<I4Plan>,
5627 i8: Option<I8Plan>,
5630 chroma_mode: u8,
5632 cbp_chroma: u32,
5633 c_dc_levels: [[i32; 4]; 2],
5634 c_q_blocks: [[[i32; 16]; 4]; 2],
5635}
5636
5637struct InterPlan {
5644 mvds: [(i32, i32); 4], plan_refs: [i32; 4], n_mvd: usize,
5647 cbp: u32,
5648 q_blocks: [[i32; 16]; 16], c_dc_levels: [[i32; 4]; 2],
5650 c_q: [[[i32; 16]; 4]; 2],
5651 t8x8: bool, q8: [[i32; 64]; 4], }
5654
5655fn gather_i4(
5657 fe: &FrameEncoder,
5658 px: usize,
5659 py: usize,
5660 avail_top: bool,
5661 avail_left: bool,
5662 bx: usize,
5663 by: usize,
5664) -> ([u8; 8], [u8; 4], u8) {
5665 let (cw, w4) = (fe.cw, fe.mb_w * 4);
5666 let mut top = [0u8; 8];
5667 let mut left = [0u8; 4];
5668 let mut corner = 0;
5669 if avail_top {
5670 for i in 0..4 {
5671 top[i] = fe.rec_y[(py - 1) * cw + px + i];
5672 }
5673 let tr_avail = bx + 1 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 1)];
5674 for i in 0..4 {
5675 top[4 + i] = if tr_avail {
5676 fe.rec_y[(py - 1) * cw + px + 4 + i]
5677 } else {
5678 top[3]
5679 };
5680 }
5681 }
5682 if avail_left {
5683 for i in 0..4 {
5684 left[i] = fe.rec_y[(py + i) * cw + px - 1];
5685 }
5686 }
5687 if avail_top && avail_left {
5688 corner = fe.rec_y[(py - 1) * cw + px - 1];
5689 }
5690 (top, left, corner)
5691}
5692
5693#[inline]
5700fn modes_at(fe: &FrameEncoder, modes: &[u8; 16], lbx: usize, lby: usize, dx: isize, dy: isize, bx: usize, by: usize) -> u8 {
5701 let (nx, ny) = (lbx as isize + dx, lby as isize + dy);
5702 if (0..4).contains(&nx) && (0..4).contains(&ny) {
5703 modes[ny as usize * 4 + nx as usize]
5704 } else {
5705 let w4 = fe.mb_w * 4;
5706 let gx = (bx as isize + dx) as usize;
5707 let gy = (by as isize + dy) as usize;
5708 fe.modes_y[gy * w4 + gx]
5709 }
5710}
5711
5712fn plan_i4x4(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I4Plan {
5713 let w4 = fe.mb_w * 4;
5714 let mut modes = [2u8; 16];
5715 let mut q = [[0i32; 16]; 16];
5716 let mut cbp_luma = 0u32;
5717 let mut nonzero = 0i64;
5718
5719 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
5720 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
5721 let (px, py) = (bx * 4, by * 4);
5722 let avail_top = by > 0;
5723 let avail_left = bx > 0;
5724 let (top, left, corner) = gather_i4(fe, px, py, avail_top, avail_left, bx, by);
5725
5726 let mut best_m = 2u8;
5731 let mut best_cost = i64::MAX;
5732 if fe.fast && fast_intra_enabled() {
5733 let lm = if bx > 0 { modes_at(fe, &modes, lbx, lby, -1, 0, bx, by) } else { 2 };
5734 let tm = if by > 0 { modes_at(fe, &modes, lbx, lby, 0, -1, bx, by) } else { 2 };
5735 let mpm = lm.min(tm);
5736 let mut cands = [mpm, 2u8, 0, 1];
5737 for i in 1..4 {
5738 for j in 0..i {
5739 if cands[i] == cands[j] {
5740 cands[i] = 255;
5741 }
5742 }
5743 }
5744 for &m in cands.iter() {
5745 if m == 255 || !i4_mode_available(m, avail_top, avail_left) {
5746 continue;
5747 }
5748 let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
5749 let cost = satd_4x4(sy, fe.cw, px, py, &pred);
5750 if cost < best_cost {
5751 best_cost = cost;
5752 best_m = m;
5753 }
5754 }
5755 } else {
5756 for m in 0..9u8 {
5757 if !i4_mode_available(m, avail_top, avail_left) {
5758 continue;
5759 }
5760 let pred = intra4x4_pred(m, avail_top, avail_left, &top, &left, corner);
5761 let cost = satd_4x4(sy, fe.cw, px, py, &pred);
5762 if cost < best_cost {
5763 best_cost = cost;
5764 best_m = m;
5765 }
5766 }
5767 }
5768
5769 let pred = intra4x4_pred(best_m, avail_top, avail_left, &top, &left, corner);
5771 let mut predb = [0i32; 16];
5772 for i in 0..16 {
5773 predb[i] = pred[i] as i32;
5774 }
5775 let res = residual(sy, fe.cw, px, py, &predb);
5776 let qb = rdoq(&forward_core(&res), qp, fe.idz, fe.rdoq_strength, 0); let s = reconstruct_4x4(&dequantize(&qb, qp), &predb);
5778 store(&mut fe.rec_y, fe.cw, px, py, &s);
5779 fe.coded_y[by * w4 + bx] = true;
5780
5781 let nz = qb.iter().filter(|&&v| v != 0).count();
5782 if nz > 0 {
5783 cbp_luma |= 1 << ((lby / 2) * 2 + (lbx / 2));
5784 }
5785 nonzero += nz as i64;
5786 modes[lby * 4 + lbx] = best_m;
5787 q[lby * 4 + lbx] = qb;
5788 }
5789 I4Plan {
5790 modes,
5791 q,
5792 cbp_luma,
5793 nonzero,
5794 }
5795}
5796
5797struct I8Plan {
5801 modes: [u8; 4], q: [[i32; 64]; 4], cbp_luma: u32, nonzero: i64, }
5806
5807const ZIGZAG_8X8: [usize; 64] = [
5810 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,
5811 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
5812 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
5813];
5814
5815#[inline]
5816fn scan_8x8_fwd(raster: &[i32; 64]) -> [i32; 64] {
5817 std::array::from_fn(|i| raster[ZIGZAG_8X8[i]])
5818}
5819
5820fn gather_i8_enc(
5823 fe: &FrameEncoder,
5824 px: usize,
5825 py: usize,
5826 avail_top: bool,
5827 avail_left: bool,
5828 bx: usize,
5829 by: usize,
5830) -> ([u8; 16], [u8; 8], u8, bool) {
5831 let (cw, w4) = (fe.cw, fe.mb_w * 4);
5832 let mut top = [0u8; 16];
5833 let mut left = [0u8; 8];
5834 let mut corner = 0;
5835 if avail_top {
5836 for i in 0..8 {
5837 top[i] = fe.rec_y[(py - 1) * cw + px + i];
5838 }
5839 let tr_avail = bx + 2 < w4 && fe.coded_y[(by - 1) * w4 + (bx + 2)];
5840 for i in 0..8 {
5841 top[8 + i] = if tr_avail {
5842 fe.rec_y[(py - 1) * cw + px + 8 + i]
5843 } else {
5844 top[7]
5845 };
5846 }
5847 }
5848 if avail_left {
5849 for i in 0..8 {
5850 left[i] = fe.rec_y[(py + i) * cw + px - 1];
5851 }
5852 }
5853 let avail_corner = avail_top && avail_left;
5854 if avail_corner {
5855 corner = fe.rec_y[(py - 1) * cw + px - 1];
5856 }
5857 (top, left, corner, avail_corner)
5858}
5859
5860fn plan_i8x8(fe: &mut FrameEncoder, sy: &[u8], mb_x: usize, mb_y: usize, qp: u8) -> I8Plan {
5863 let w4 = fe.mb_w * 4;
5864 let mut modes = [2u8; 4];
5865 let mut q = [[0i32; 64]; 4];
5866 let mut cbp_luma = 0u32;
5867 let mut nonzero = 0i64;
5868 let weight = [16i32; 64];
5869
5870 for b8 in 0..4usize {
5871 let (b8x, b8y) = (b8 % 2, b8 / 2);
5872 let (px, py) = (mb_x * 16 + b8x * 8, mb_y * 16 + b8y * 8);
5873 let (bx, by) = (mb_x * 4 + b8x * 2, mb_y * 4 + b8y * 2); let avail_top = b8y > 0 || mb_y > 0;
5875 let avail_left = b8x > 0 || mb_x > 0;
5876 let (top, left, corner, avail_corner) =
5877 gather_i8_enc(fe, px, py, avail_top, avail_left, bx, by);
5878
5879 let predicted = predict_i4_mode(fe, bx, by);
5883 let mut best_m = 2u8;
5884 let mut best_cost = i64::MAX;
5885 for m in 0..9u8 {
5886 if !i4_mode_available(m, avail_top, avail_left) {
5887 continue;
5888 }
5889 let pred = intra8x8_pred(m, avail_top, avail_left, avail_corner, &top, &left, corner);
5890 let mut cost = satd_8x8(sy, fe.cw, px, py, &pred);
5891 if m != predicted {
5892 cost += 4 * fe.qp as i64; }
5894 if cost < best_cost {
5895 best_cost = cost;
5896 best_m = m;
5897 }
5898 }
5899 modes[b8] = best_m;
5900
5901 let pred = intra8x8_pred(best_m, avail_top, avail_left, avail_corner, &top, &left, corner);
5903 let mut res = [0i32; 64];
5904 for dy in 0..8 {
5905 for dx in 0..8 {
5906 res[dy * 8 + dx] =
5907 sy[(py + dy) * fe.cw + (px + dx)] as i32 - pred[dy * 8 + dx] as i32;
5908 }
5909 }
5910 let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, fe.idz);
5911 let nz = levels.iter().filter(|&&v| v != 0).count();
5912 if nz > 0 {
5913 cbp_luma |= 1 << b8;
5914 }
5915 nonzero += nz as i64;
5916 q[b8] = levels;
5917
5918 let res_r = inverse_quant_8x8(&levels, qp, &weight);
5919 let predb: [i32; 64] = std::array::from_fn(|i| pred[i] as i32);
5920 let recon = add_residual_8x8(&res_r, &predb);
5921 for dy in 0..8 {
5922 for dx in 0..8 {
5923 fe.rec_y[(py + dy) * fe.cw + (px + dx)] = recon[dy * 8 + dx];
5924 }
5925 }
5926 for sry in 0..2 {
5929 for srx in 0..2 {
5930 fe.modes_y[(by + sry) * w4 + (bx + srx)] = best_m;
5931 fe.coded_y[(by + sry) * w4 + (bx + srx)] = true;
5932 }
5933 }
5934 }
5935 I8Plan {
5936 modes,
5937 q,
5938 cbp_luma,
5939 nonzero,
5940 }
5941}
5942
5943#[allow(clippy::too_many_arguments)]
5951fn plan_inter8_luma(
5952 sy: &[u8],
5953 cw: usize,
5954 mb_x: usize,
5955 mb_y: usize,
5956 pred_y: &[u8; 256],
5957 qp: u8,
5958) -> ([[i32; 64]; 4], u32, f64, [u8; 256], i64) {
5959 let weight = [16i32; 64];
5960 let mut q8 = [[0i32; 64]; 4];
5961 let mut cbp = 0u32;
5962 let mut rate = 0f64;
5963 let mut rec = [0u8; 256];
5964 let mut ssd = 0i64;
5965 for b8 in 0..4usize {
5966 let (b8x, b8y) = (b8 % 2, b8 / 2);
5967 let mut res = [0i32; 64];
5968 for dy in 0..8 {
5969 for dx in 0..8 {
5970 let sx = mb_x * 16 + b8x * 8 + dx;
5971 let syy = mb_y * 16 + b8y * 8 + dy;
5972 let p = pred_y[(b8y * 8 + dy) * 16 + (b8x * 8 + dx)] as i32;
5973 res[dy * 8 + dx] = sy[syy * cw + sx] as i32 - p;
5974 }
5975 }
5976 let levels = quantize_8x8(&forward_core_8x8(&res), qp, &weight, 6);
5977 let mut nz = false;
5978 for &l in &levels {
5979 if l != 0 {
5980 nz = true;
5981 rate += rdoq_rate((l as i64).abs());
5982 }
5983 }
5984 if nz {
5985 cbp |= 1 << b8;
5986 }
5987 q8[b8] = levels;
5988
5989 let res_r = inverse_quant_8x8(&levels, qp, &weight);
5990 let predb: [i32; 64] =
5991 std::array::from_fn(|i| pred_y[(b8y * 8 + i / 8) * 16 + (b8x * 8 + i % 8)] as i32);
5992 let recon = add_residual_8x8(&res_r, &predb);
5993 for dy in 0..8 {
5994 for dx in 0..8 {
5995 let ri = (b8y * 8 + dy) * 16 + (b8x * 8 + dx);
5996 rec[ri] = recon[dy * 8 + dx];
5997 let sx = mb_x * 16 + b8x * 8 + dx;
5998 let syy = mb_y * 16 + b8y * 8 + dy;
5999 let d = recon[dy * 8 + dx] as i64 - sy[syy * cw + sx] as i64;
6000 ssd += d * d;
6001 }
6002 }
6003 }
6004 (q8, cbp, rate, rec, ssd)
6005}
6006
6007#[inline]
6012fn i16_pred(
6013 fe: &FrameEncoder,
6014 mode: I16Mode,
6015 avail_top: bool,
6016 avail_left: bool,
6017 top: &[u8; 16],
6018 left: &[u8; 16],
6019 corner: u8,
6020 lx: usize,
6021 ly: usize,
6022) -> [u8; 256] {
6023 #[cfg(accel)]
6024 if avail_top && avail_left {
6025 let mode_n = match mode {
6026 I16Mode::Vertical => 0,
6027 I16Mode::Horizontal => 1,
6028 I16Mode::Dc => 2,
6029 I16Mode::Plane => 3,
6030 };
6031 let mut p = AlignedMb([0; 256]);
6032 rusty_h264_accel::i16x16_luma_pred(mode_n, &mut p.0, &fe.rec_y[..], ly * fe.cw + lx, fe.cw);
6033 return p.0;
6034 }
6035 let _ = (fe, lx, ly);
6036 luma16x16_pred(mode, avail_top, avail_left, top, left, corner)
6037}
6038
6039#[inline]
6043#[allow(clippy::too_many_arguments)]
6044fn chroma_pred(
6045 fe: &FrameEncoder,
6046 mode: u8,
6047 avail_top: bool,
6048 avail_left: bool,
6049 c: usize,
6050 top: &[u8; 8],
6051 left: &[u8; 8],
6052 corner: u8,
6053 cx: usize,
6054 cy: usize,
6055) -> [u8; 64] {
6056 #[cfg(accel)]
6057 if avail_top && avail_left && (mode == 2 || mode == 3) {
6058 let plane = if c == 0 { &fe.rec_u } else { &fe.rec_v };
6059 let mut p = AlignedMb([0; 256]);
6060 rusty_h264_accel::chroma8x8_pred(mode, &mut p.0[..64], &plane[..], cy * fe.ccw + cx, fe.ccw);
6061 let mut out = [0u8; 64];
6062 out.copy_from_slice(&p.0[..64]);
6063 return out;
6064 }
6065 let _ = (fe, c, cx, cy);
6066 chroma8x8_pred(mode, avail_top, avail_left, top, left, corner)
6067}
6068
6069fn predict_i4_mode(fe: &FrameEncoder, bx: usize, by: usize) -> u8 {
6072 if bx == 0 || by == 0 {
6073 return 2;
6074 }
6075 let w4 = fe.mb_w * 4;
6076 fe.modes_y[by * w4 + (bx - 1)].min(fe.modes_y[(by - 1) * w4 + bx])
6077}
6078
6079#[allow(clippy::too_many_arguments)]
6080const RDOQ_ZZ: [usize; 16] = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
6082
6083#[inline]
6088fn rdoq_rate(level: i64) -> f64 {
6089 if level == 0 {
6090 1.0
6091 } else if level == 1 {
6092 3.0 } else {
6094 3.0 + (level - 1).min(13) as f64
6096 }
6097}
6098
6099fn rdoq(coeffs: &[i32; 16], qp: u8, dz_div: i64, strength: f64, first: usize) -> [i32; 16] {
6106 let mut q = quantize(coeffs, qp, dz_div);
6107 if strength <= 0.0 {
6108 return q;
6109 }
6110 let lambda = strength * 2f64.powf((qp as f64 - 12.0) / 3.0);
6111 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
6115 const POS: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7];
6116 let dist = |p: usize, level: i64| -> f64 {
6117 let e = coeffs[p].unsigned_abs() as f64 - level as f64 * (65536.0 / mf[POS[p]] as f64);
6118 e * e
6119 };
6120 for i in first..16 {
6122 let p = RDOQ_ZZ[i];
6123 let m = q[p].unsigned_abs() as i64;
6124 if m == 0 {
6125 continue;
6126 }
6127 let j_keep = dist(p, m) + lambda * rdoq_rate(m);
6128 let j_down = dist(p, m - 1) + lambda * rdoq_rate(m - 1);
6129 if j_down < j_keep {
6130 let nl = (m - 1) as i32;
6131 q[p] = if q[p] < 0 { -nl } else { nl };
6132 }
6133 }
6134 loop {
6139 let Some(li) = (first..16).rev().find(|&i| q[RDOQ_ZZ[i]] != 0) else {
6140 break;
6141 };
6142 let p = RDOQ_ZZ[li];
6143 let m = q[p].unsigned_abs() as i64;
6144 let prev = (first..li).rev().find(|&i| q[RDOQ_ZZ[i]] != 0);
6145 let base = prev.map_or(first, |j| j + 1);
6146 let bits = rdoq_rate(m) + 1.0 + (li - base) as f64; let d_add = dist(p, 0) - dist(p, m);
6148 if d_add < lambda * bits {
6149 q[p] = 0;
6150 } else {
6151 break;
6152 }
6153 }
6154 q
6155}
6156
6157fn plan_mb(
6162 fe: &mut FrameEncoder,
6163 mb_x: usize,
6164 mb_y: usize,
6165 sy: &[u8],
6166 su: &[u8],
6167 sv: &[u8],
6168) -> MbPlan {
6169 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncIntraCode);
6170 let qp = fe.qp;
6171 let qpc = fe.qpc;
6172 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
6174
6175 let (lx, ly) = (mb_x * 16, mb_y * 16);
6177 let avail_top = mb_y > 0;
6178 let avail_left = mb_x > 0;
6179 let mut top = [0u8; 16];
6180 let mut left = [0u8; 16];
6181 if avail_top {
6182 for i in 0..16 {
6183 top[i] = fe.rec_y[(ly - 1) * fe.cw + lx + i];
6184 }
6185 }
6186 if avail_left {
6187 for i in 0..16 {
6188 left[i] = fe.rec_y[(ly + i) * fe.cw + lx - 1];
6189 }
6190 }
6191 let corner = if avail_top && avail_left {
6192 fe.rec_y[(ly - 1) * fe.cw + lx - 1]
6193 } else {
6194 0
6195 };
6196
6197 let w4 = fe.mb_w * 4;
6198
6199 let mut i16_mode = I16Mode::Dc;
6201 let mut best_pred = i16_pred(fe, I16Mode::Dc, avail_top, avail_left, &top, &left, corner, lx, ly);
6202 let mut best_cost = satd_16x16(sy, fe.cw, lx, ly, &best_pred);
6203 for mode in [I16Mode::Vertical, I16Mode::Horizontal, I16Mode::Plane] {
6204 if !mode.available(avail_top, avail_left) {
6205 continue;
6206 }
6207 let pred = i16_pred(fe, mode, avail_top, avail_left, &top, &left, corner, lx, ly);
6208 let cost = satd_16x16(sy, fe.cw, lx, ly, &pred);
6209 if cost < best_cost {
6210 best_cost = cost;
6211 i16_mode = mode;
6212 best_pred = pred;
6213 }
6214 }
6215 let mut dc4x4 = [0i32; 16];
6218 let mut i16_q = [[0i32; 16]; 16];
6219 #[cfg(accel)]
6224 let (i16_dc_levels, _i16_recon_dc, recon16) = {
6225 #[repr(align(16))]
6226 struct A([i16; 256]);
6227 let mut dct = A([0i16; 256]);
6228 let base = ly * fe.cw + lx;
6229 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
6230 rusty_h264_accel::dct_four_t4(
6231 &mut dct.0[qi * 64..qi * 64 + 64],
6232 &sy[base + qy * fe.cw + qx..],
6233 fe.cw,
6234 &best_pred[qy * 16 + qx..],
6235 16,
6236 );
6237 }
6238 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6239 dc4x4[lby * 4 + lbx] = dct.0[blk * 16] as i32;
6240 }
6241 if fe.rdoq_strength > 0.0 {
6242 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6246 let coeffs: [i32; 16] = std::array::from_fn(|i| dct.0[blk * 16 + i] as i32);
6247 let mut q = rdoq(&coeffs, qp, fe.idz, fe.rdoq_strength, 1);
6248 q[0] = 0;
6249 i16_q[lby * 4 + lbx] = q;
6250 }
6251 } else {
6252 let ff = rusty_h264_common::transform::quant_dz_ff(qp, fe.idz);
6253 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qp as usize];
6254 for qi in 0..4 {
6255 rusty_h264_accel::quant_four_4x4(&mut dct.0[qi * 64..qi * 64 + 64], &ff, mf);
6256 }
6257 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6258 let q = &mut i16_q[lby * 4 + lbx];
6259 q[0] = 0;
6260 for i in 1..16 {
6261 q[i] = dct.0[blk * 16 + i] as i32;
6262 }
6263 }
6264 }
6265 let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
6266 let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
6267 let mut recon16 = [0u8; 256];
6270 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6271 let mut deq = dequantize(&i16_q[lby * 4 + lbx], qp);
6272 deq[0] = i16_recon_dc[lby * 4 + lbx];
6273 for i in 0..16 {
6274 dct.0[blk * 16 + i] = deq[i] as i16;
6275 }
6276 }
6277 for (qi, &(qx, qy)) in [(0usize, 0usize), (8, 0), (0, 8), (8, 8)].iter().enumerate() {
6278 rusty_h264_accel::idct_four_t4_rec(
6279 &mut recon16[qy * 16 + qx..],
6280 16,
6281 &best_pred[qy * 16 + qx..],
6282 16,
6283 &dct.0[qi * 64..qi * 64 + 64],
6284 );
6285 }
6286 (i16_dc_levels, i16_recon_dc, recon16)
6287 };
6288 #[cfg(not(accel))]
6289 let (i16_dc_levels, _i16_recon_dc, recon16) = {
6290 let mut res_blocks = [[0i32; 16]; 16];
6291 for by in 0..4 {
6292 for bx in 0..4 {
6293 let predb = pred_block(&best_pred, bx, by);
6294 res_blocks[by * 4 + bx] = residual(sy, fe.cw, lx + bx * 4, ly + by * 4, &predb);
6295 }
6296 }
6297 let mut coeffs = [[0i32; 16]; 16];
6298 forward_dct_blocks(&res_blocks, &mut coeffs);
6299 for i in 0..16 {
6300 dc4x4[i] = coeffs[i][0];
6301 let mut q = rdoq(&coeffs[i], qp, fe.idz, fe.rdoq_strength, 1);
6302 q[0] = 0;
6303 i16_q[i] = q;
6304 }
6305 let i16_dc_levels = forward_quant_luma_dc(&dc4x4, qp, true);
6306 let i16_recon_dc = inverse_quant_luma_dc(&i16_dc_levels, qp);
6307 let mut recon16 = [0u8; 256];
6308 let mut deq_blocks = [[0i32; 16]; 16];
6309 for i in 0..16 {
6310 deq_blocks[i] = dequantize(&i16_q[i], qp);
6311 deq_blocks[i][0] = i16_recon_dc[i];
6312 }
6313 let mut idct = [[0i32; 16]; 16];
6314 inverse_dct_blocks(&deq_blocks, &mut idct);
6315 for by in 0..4 {
6316 for bx in 0..4 {
6317 let s = add_residual_4x4(&idct[by * 4 + bx], &pred_block(&best_pred, bx, by));
6318 for dy in 0..4 {
6319 for dx in 0..4 {
6320 recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)] = s[dy * 4 + dx];
6321 }
6322 }
6323 }
6324 }
6325 (i16_dc_levels, i16_recon_dc, recon16)
6326 };
6327 let i16_cbp15 = i16_q.iter().any(|b| b[1..].iter().any(|&c| c != 0));
6328 let i16_dc_nz = i16_dc_levels.iter().filter(|&&v| v != 0).count() as i64;
6329 let i16_ac_nz: i64 = i16_q
6330 .iter()
6331 .map(|b| b[1..].iter().filter(|&&v| v != 0).count() as i64)
6332 .sum();
6333 let i16_rate = i16_dc_nz + i16_ac_nz + if i16_cbp15 { 16 } else { 0 };
6335 let mut ssd16 = 0i64;
6337 for dy in 0..16 {
6338 for dx in 0..16 {
6339 let d = recon16[dy * 16 + dx] as i64 - sy[(ly + dy) * fe.cw + (lx + dx)] as i64;
6340 ssd16 += d * d;
6341 }
6342 }
6343
6344 let (cx, cy) = (mb_x * 8, mb_y * 8);
6346 let mut ntop = [[0u8; 8]; 2];
6348 let mut nleft = [[0u8; 8]; 2];
6349 let mut ncorner = [0u8; 2];
6350 for c in 0..2 {
6351 let rec_c = if c == 0 { &fe.rec_u } else { &fe.rec_v };
6352 if avail_top {
6353 for i in 0..8 {
6354 ntop[c][i] = rec_c[(cy - 1) * fe.ccw + cx + i];
6355 }
6356 }
6357 if avail_left {
6358 for i in 0..8 {
6359 nleft[c][i] = rec_c[(cy + i) * fe.ccw + cx - 1];
6360 }
6361 }
6362 if avail_top && avail_left {
6363 ncorner[c] = rec_c[(cy - 1) * fe.ccw + cx - 1];
6364 }
6365 }
6366 let mut chroma_mode = 0u8;
6367 let mut best_c_cost = i64::MAX;
6368 for m in 0..4u8 {
6369 if !chroma_mode_available(m, avail_top, avail_left) {
6370 continue;
6371 }
6372 let mut cost = 0i64;
6373 for c in 0..2 {
6374 let src = if c == 0 { su } else { sv };
6375 let pred8 = chroma_pred(fe, m, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy);
6376 cost += satd_8x8(src, fe.ccw, cx, cy, &pred8);
6377 }
6378 if cost < best_c_cost {
6379 best_c_cost = cost;
6380 chroma_mode = m;
6381 }
6382 }
6383
6384 let mut c_dc_levels = [[0i32; 4]; 2];
6385 let mut c_q_blocks = [[[0i32; 16]; 4]; 2];
6386 let mut any_chroma_ac = false;
6387 let mut any_chroma_dc = false;
6388 for c in 0..2 {
6389 let src = if c == 0 { su } else { sv };
6390 let pred8 =
6391 chroma_pred(fe, chroma_mode, avail_top, avail_left, c, &ntop[c], &nleft[c], ncorner[c], cx, cy);
6392 let pblk = |bx: usize, by: usize| -> [i32; 16] {
6393 let mut predb = [0i32; 16];
6394 for dy in 0..4 {
6395 for dx in 0..4 {
6396 predb[dy * 4 + dx] = pred8[(by * 4 + dy) * 8 + (bx * 4 + dx)] as i32;
6397 }
6398 }
6399 predb
6400 };
6401 let mut dc2x2 = [0i32; 4];
6405 let mut qbs = [[0i32; 16]; 4];
6406 #[cfg(accel)]
6407 let recon_dc = {
6408 #[repr(align(16))]
6409 struct A([i16; 64]);
6410 let mut d = A([0i16; 64]);
6411 rusty_h264_accel::dct_four_t4(&mut d.0, &src[cy * fe.ccw + cx..], fe.ccw, &pred8, 8);
6412 for i in 0..4 {
6413 dc2x2[i] = d.0[i * 16] as i32;
6414 }
6415 if fe.rdoq_strength > 0.0 {
6416 for i in 0..4 {
6418 let coeffs: [i32; 16] = std::array::from_fn(|j| d.0[i * 16 + j] as i32);
6419 let mut q = rdoq(&coeffs, qpc, fe.idz, fe.rdoq_strength, 1);
6420 q[0] = 0;
6421 if q[1..].iter().any(|&v| v != 0) {
6422 any_chroma_ac = true;
6423 }
6424 qbs[i] = q;
6425 }
6426 } else {
6427 let ff = rusty_h264_common::transform::quant_dz_ff(qpc, fe.idz);
6428 let mf = &rusty_h264_common::transform::QUANT_MF_OH[qpc as usize];
6429 rusty_h264_accel::quant_four_4x4(&mut d.0, &ff, mf);
6430 for i in 0..4 {
6431 let q = &mut qbs[i];
6432 q[0] = 0;
6433 for j in 1..16 {
6434 let v = d.0[i * 16 + j] as i32;
6435 q[j] = v;
6436 if v != 0 {
6437 any_chroma_ac = true;
6438 }
6439 }
6440 }
6441 }
6442 let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
6443 if dl.iter().any(|&v| v != 0) {
6444 any_chroma_dc = true;
6445 }
6446 let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
6447 for i in 0..4 {
6448 let deq = dequantize(&qbs[i], qpc);
6449 for j in 0..16 {
6450 d.0[i * 16 + j] = deq[j] as i16;
6451 }
6452 d.0[i * 16] = recon_dc[i] as i16;
6453 }
6454 let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
6455 rusty_h264_accel::idct_four_t4_rec(&mut plane[cy * fe.ccw + cx..], fe.ccw, &pred8, 8, &d.0);
6456 c_dc_levels[c] = dl;
6457 recon_dc
6458 };
6459 #[cfg(not(accel))]
6460 let recon_dc = {
6461 let mut res_blocks = [[0i32; 16]; 4];
6462 for by in 0..2 {
6463 for bx in 0..2 {
6464 res_blocks[by * 2 + bx] =
6465 residual(src, fe.ccw, cx + bx * 4, cy + by * 4, &pblk(bx, by));
6466 }
6467 }
6468 let mut coeffs = [[0i32; 16]; 4];
6469 forward_dct_blocks(&res_blocks, &mut coeffs);
6470 for i in 0..4 {
6471 dc2x2[i] = coeffs[i][0];
6472 let mut q = rdoq(&coeffs[i], qpc, fe.idz, fe.rdoq_strength, 1);
6473 q[0] = 0;
6474 qbs[i] = q;
6475 if q[1..].iter().any(|&v| v != 0) {
6476 any_chroma_ac = true;
6477 }
6478 }
6479 let dl = forward_quant_chroma_dc(&dc2x2, qpc, true);
6480 if dl.iter().any(|&v| v != 0) {
6481 any_chroma_dc = true;
6482 }
6483 let recon_dc = inverse_quant_chroma_dc(&dl, qpc);
6484 let mut deq_blocks = [[0i32; 16]; 4];
6485 for i in 0..4 {
6486 deq_blocks[i] = dequantize(&qbs[i], qpc);
6487 deq_blocks[i][0] = recon_dc[i];
6488 }
6489 let mut idct = [[0i32; 16]; 4];
6490 inverse_dct_blocks(&deq_blocks, &mut idct);
6491 let plane = if c == 0 { &mut fe.rec_u } else { &mut fe.rec_v };
6492 for by in 0..2 {
6493 for bx in 0..2 {
6494 let s = add_residual_4x4(&idct[by * 2 + bx], &pblk(bx, by));
6495 store(plane, fe.ccw, cx + bx * 4, cy + by * 4, &s);
6496 }
6497 }
6498 c_dc_levels[c] = dl;
6499 recon_dc
6500 };
6501 let _ = recon_dc;
6502 c_q_blocks[c] = qbs;
6503 }
6504 let cbp_chroma: u32 = if any_chroma_ac {
6505 2
6506 } else if any_chroma_dc {
6507 1
6508 } else {
6509 0
6510 };
6511
6512 let base = ly * fe.cw + lx;
6518 let i4 = if i16_rate > 2 {
6519 Some(plan_i4x4(fe, sy, mb_x, mb_y, qp))
6520 } else {
6521 None
6522 };
6523 let (j4, i4_recon) = match &i4 {
6524 Some(p) => {
6525 let mut ssd = 0i64;
6526 let mut rec = [0u8; 256];
6527 for i in 0..256 {
6528 let v = fe.rec_y[base + (i / 16) * fe.cw + i % 16];
6529 rec[i] = v;
6530 let d = v as i64 - sy[base + (i / 16) * fe.cw + i % 16] as i64;
6531 ssd += d * d;
6532 }
6533 (ssd as f64 + lambda * (p.nonzero + 16) as f64, Some(rec))
6534 }
6535 None => (f64::INFINITY, None),
6536 };
6537 let i8 = if fe.transform_8x8 {
6538 Some(plan_i8x8(fe, sy, mb_x, mb_y, qp))
6539 } else {
6540 None
6541 };
6542 let j8 = match &i8 {
6543 Some(p) => {
6544 let mut ssd = 0i64;
6545 for i in 0..256 {
6546 let d = fe.rec_y[base + (i / 16) * fe.cw + i % 16] as i64
6547 - sy[base + (i / 16) * fe.cw + i % 16] as i64;
6548 ssd += d * d;
6549 }
6550 ssd as f64 + lambda * (p.nonzero + 16) as f64
6551 }
6552 None => f64::INFINITY,
6553 };
6554 let j16 = ssd16 as f64 + lambda * i16_rate as f64;
6555
6556 let (use_i4, i4, i8) = if i8.is_some() && j8 <= j4 && j8 <= j16 {
6558 (true, None, i8)
6560 } else if i4.is_some() && j4 < j16 {
6561 let rec = i4_recon.unwrap();
6563 for i in 0..256 {
6564 fe.rec_y[base + (i / 16) * fe.cw + i % 16] = rec[i];
6565 }
6566 let modes = i4.as_ref().unwrap().modes;
6567 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6568 fe.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = modes[lby * 4 + lbx];
6569 }
6570 (true, i4, None)
6571 } else {
6572 for by in 0..4 {
6574 for bx in 0..4 {
6575 for dy in 0..4 {
6576 for dx in 0..4 {
6577 fe.rec_y[(ly + by * 4 + dy) * fe.cw + (lx + bx * 4 + dx)] =
6578 recon16[(by * 4 + dy) * 16 + (bx * 4 + dx)];
6579 }
6580 }
6581 }
6582 }
6583 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6584 fe.modes_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 2;
6585 }
6586 (false, None, None)
6587 };
6588 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6590 fe.coded_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = true;
6591 }
6592
6593 MbPlan {
6594 use_i4,
6595 i16_mode,
6596 i16_cbp15,
6597 i16_dc_levels,
6598 i16_q,
6599 i4,
6600 i8,
6601 chroma_mode,
6602 cbp_chroma,
6603 c_dc_levels,
6604 c_q_blocks,
6605 }
6606}
6607
6608fn encode_mb(
6611 fe: &mut FrameEncoder,
6612 w: &mut BitWriter,
6613 mb_x: usize,
6614 mb_y: usize,
6615 sy: &[u8],
6616 su: &[u8],
6617 sv: &[u8],
6618 is_p: bool,
6619) {
6620 let plan = plan_mb(fe, mb_x, mb_y, sy, su, sv);
6621 let mb_type_offset = if is_p { 5 } else { 0 };
6623 let w4 = fe.mb_w * 4;
6624 let cbp_chroma = plan.cbp_chroma;
6625
6626 if let Some(i8) = plan.i8.as_ref().filter(|_| plan.use_i4) {
6628 let cbp = i8.cbp_luma | (cbp_chroma << 4);
6632 w.write_ue(mb_type_offset); w.write_bit(true); for b8 in 0..4usize {
6635 let (bx, by) = (mb_x * 4 + (b8 % 2) * 2, mb_y * 4 + (b8 / 2) * 2);
6636 let predicted = predict_i4_mode(fe, bx, by);
6637 let actual = i8.modes[b8];
6638 if actual == predicted {
6639 w.write_bit(true);
6640 } else {
6641 w.write_bit(false);
6642 let rem = if actual < predicted { actual } else { actual - 1 };
6643 w.write_bits(rem as u32, 3);
6644 }
6645 }
6646 w.write_ue(plan.chroma_mode as u32); write_cbp_intra(w, cbp);
6648 if cbp != 0 {
6649 w.write_se(fe.qp_delta());
6650 }
6651 fe.nnz_cache_load(mb_x, mb_y);
6652 for b8 in 0..4usize {
6653 let (b8x, b8y) = (b8 % 2, b8 / 2);
6654 let scan8 = scan_8x8_fwd(&i8.q[b8]);
6655 for sub in 0..4usize {
6656 let (cx, cy) = (b8x * 2 + sub % 2, b8y * 2 + sub / 2);
6657 let (bx, by) = (mb_x * 4 + cx, mb_y * 4 + cy);
6658 let total = if i8.cbp_luma & (1 << b8) != 0 {
6659 let nc = fe.nc_pred(cx, cy);
6660 let blk: [i32; 16] = std::array::from_fn(|k| scan8[4 * k + sub]);
6661 encode_residual_block(w, &blk, 16, nc) as u8
6662 } else {
6663 0
6664 };
6665 fe.nnz_cache_set(cx, cy, total);
6666 fe.nnz_y[by * w4 + bx] = total;
6667 }
6668 }
6669 } else if plan.use_i4 {
6670 let i4 = plan.i4.as_ref().unwrap();
6671 let cbp = i4.cbp_luma | (cbp_chroma << 4);
6672 w.write_ue(mb_type_offset); if fe.transform_8x8 {
6674 w.write_bit(false); }
6676 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6677 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6678 let predicted = predict_i4_mode(fe, bx, by);
6679 let actual = i4.modes[lby * 4 + lbx];
6680 if actual == predicted {
6681 w.write_bit(true);
6682 } else {
6683 w.write_bit(false);
6684 let rem = if actual < predicted { actual } else { actual - 1 };
6685 w.write_bits(rem as u32, 3);
6686 }
6687 }
6688 w.write_ue(plan.chroma_mode as u32); write_cbp_intra(w, cbp);
6690 if cbp != 0 {
6691 w.write_se(fe.qp_delta()); }
6693 fe.nnz_cache_load(mb_x, mb_y);
6694 for (blk, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
6695 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6696 let total = if i4.cbp_luma & (1 << (blk / 4)) != 0 {
6697 let nc = fe.nc_pred(lbx, lby);
6698 let scan16 = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
6699 encode_residual_block(w, &scan16, 16, nc) as u8
6700 } else {
6701 0
6702 };
6703 fe.nnz_cache_set(lbx, lby, total);
6704 fe.nnz_y[by * w4 + bx] = total;
6705 }
6706 } else {
6707 let mb_type = 1 + plan.i16_mode as u32 + 4 * cbp_chroma + if plan.i16_cbp15 { 12 } else { 0 };
6708 w.write_ue(mb_type + mb_type_offset);
6709 w.write_ue(plan.chroma_mode as u32); w.write_se(fe.qp_delta()); fe.nnz_cache_load(mb_x, mb_y);
6712 let nc_dc = fe.nc_pred(0, 0);
6713 let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
6714 encode_residual_block(w, &dc_scan, 16, nc_dc);
6715 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6716 fe.nnz_cache_set(lbx, lby, 0);
6717 fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
6718 }
6719 if plan.i16_cbp15 {
6720 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
6721 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
6722 let nc = fe.nc_pred(lbx, lby);
6723 let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
6724 let total = encode_residual_block(w, &ac, 15, nc) as u8;
6725 fe.nnz_cache_set(lbx, lby, total);
6726 fe.nnz_y[by * w4 + bx] = total;
6727 }
6728 }
6729 }
6730
6731 if cbp_chroma != 0 {
6733 for c in 0..2 {
6734 encode_residual_block(w, &plan.c_dc_levels[c], 4, -1);
6735 }
6736 }
6737 if cbp_chroma == 2 {
6738 fe.chroma_cache_load(mb_x, mb_y);
6739 let w2 = fe.mb_w * 2;
6740 for c in 0..2 {
6741 for &(bx, by) in &CHROMA_4X4_SCAN_XY {
6742 let nc = fe.chroma_nc_pred(c, bx, by);
6743 let ac = scan_4x4_ac(&plan.c_q_blocks[c][by * 2 + bx]);
6744 let total = encode_residual_block(w, &ac, 15, nc) as u8;
6745 fe.chroma_nnz_cache_set(c, bx, by, total);
6746 fe.nnz_c[c][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total;
6747 }
6748 }
6749 }
6750}
6751
6752const CB_NZC_CACHE: [usize; 24] = [
6763 9, 10, 17, 18, 11, 12, 19, 20, 25, 26, 33, 34, 27, 28, 35, 36, 14, 15, 22, 23, 38, 39, 46, 47, ];
6767const CB_RES_MAXPOS: [i32; 11] = [0, 15, 14, 15, 3, 14, 63, 3, 3, 14, 14];
6768const CB_RES_MAXC2: [i32; 11] = [0, 4, 4, 4, 3, 4, 4, 3, 3, 4, 4];
6769const CB_RES_CBF: [usize; 11] = [0, 0, 4, 8, 12, 16, 0, 12, 12, 16, 16];
6770const CB_RES_MAP: [usize; 11] = [0, 0, 15, 29, 44, 47, 0, 44, 44, 47, 47];
6771const CB_RES_ONE: [usize; 11] = [0, 0, 10, 20, 30, 39, 0, 30, 30, 39, 39];
6772const CB_RP_I16_DC: usize = 1;
6773const CB_RP_I16_AC: usize = 2;
6774const CB_RP_LUMA_4X4: usize = 3;
6775const CB_RP_CHROMA_DC: usize = 7;
6776const CB_RP_CHROMA_AC: usize = 9;
6777
6778fn cb_unary(cab: &mut CabacEncoder, ctx: usize, off: usize, value: u32) {
6781 if value == 0 {
6782 cab.encode_decision(ctx, 0);
6783 return;
6784 }
6785 cab.encode_decision(ctx, 1);
6786 for _ in 0..value - 1 {
6787 cab.encode_decision(ctx + off, 1);
6788 }
6789 cab.encode_decision(ctx + off, 0);
6790}
6791
6792fn cb_exp_bypass(cab: &mut CabacEncoder, mut k: i32, mut n: u32) {
6794 while n >= (1 << k) {
6795 cab.encode_bypass(1);
6796 n -= 1 << k;
6797 k += 1;
6798 }
6799 cab.encode_bypass(0);
6800 while k > 0 {
6801 k -= 1;
6802 cab.encode_bypass((n >> k) & 1);
6803 }
6804}
6805
6806fn cb_ueg_level(cab: &mut CabacEncoder, ctx: usize, value: u32) {
6809 if value == 0 {
6810 cab.encode_decision(ctx, 0);
6811 return;
6812 }
6813 let ones = value.min(13);
6814 for _ in 0..ones {
6815 cab.encode_decision(ctx, 1);
6816 }
6817 if value < 13 {
6818 cab.encode_decision(ctx, 0);
6819 } else {
6820 cb_exp_bypass(cab, 0, value - 13);
6821 }
6822}
6823
6824fn cb_mb_qp_delta(cab: &mut CabacEncoder, last_delta_qp: &mut i32, delta: i32) {
6826 const O: usize = 60;
6827 let ctx_inc = (*last_delta_qp != 0) as usize;
6828 if delta == 0 {
6829 cab.encode_decision(O + ctx_inc, 0);
6830 } else {
6831 cab.encode_decision(O + ctx_inc, 1);
6832 let code = 2 * delta.unsigned_abs() - (delta > 0) as u32;
6834 cb_unary(cab, O + 2, 1, code - 1);
6835 }
6836 *last_delta_qp = delta;
6837}
6838
6839fn cb_chroma_pred_mode(cab: &mut CabacEncoder, ctx_inc: usize, mode: u8) {
6841 const C: usize = 64;
6842 if mode == 0 {
6843 cab.encode_decision(C + ctx_inc, 0);
6844 return;
6845 }
6846 cab.encode_decision(C + ctx_inc, 1);
6847 if mode == 1 {
6848 cab.encode_decision(C + 3, 0);
6849 } else if mode == 2 {
6850 cab.encode_decision(C + 3, 1);
6851 cab.encode_decision(C + 3, 0);
6852 } else {
6853 cab.encode_decision(C + 3, 1);
6854 cab.encode_decision(C + 3, 1);
6855 }
6856}
6857
6858fn cb_mb_type_i(
6860 cab: &mut CabacEncoder,
6861 ctx_inc: usize,
6862 use_i4: bool,
6863 i16_mode: u32,
6864 cbp_chroma: u32,
6865 cbp_luma15: bool,
6866) {
6867 const O: usize = 3;
6868 if use_i4 {
6869 cab.encode_decision(O + ctx_inc, 0); return;
6871 }
6872 cab.encode_decision(O + ctx_inc, 1);
6873 cab.encode_terminate(false); cab.encode_decision(O + 3, cbp_luma15 as u32);
6875 if cbp_chroma != 0 {
6876 cab.encode_decision(O + 4, 1);
6877 cab.encode_decision(O + 5, (cbp_chroma == 2) as u32);
6878 } else {
6879 cab.encode_decision(O + 4, 0);
6880 }
6881 cab.encode_decision(O + 6, (i16_mode >> 1) & 1);
6882 cab.encode_decision(O + 7, i16_mode & 1);
6883}
6884
6885fn cb_intra4x4_pred_mode(cab: &mut CabacEncoder, predicted: u8, actual: u8) {
6887 const IPR: usize = 68;
6888 if actual == predicted {
6889 cab.encode_decision(IPR, 1);
6890 } else {
6891 cab.encode_decision(IPR, 0);
6892 let rem = if actual < predicted { actual } else { actual - 1 } as u32;
6893 cab.encode_decision(IPR + 1, rem & 1);
6894 cab.encode_decision(IPR + 1, (rem >> 1) & 1);
6895 cab.encode_decision(IPR + 1, (rem >> 2) & 1);
6896 }
6897}
6898
6899fn cb_cbp(cab: &mut CabacEncoder, top: Option<u8>, left: Option<u8>, cbp: u32) {
6901 const CBP: usize = 73;
6902 let t = |m: u32| top.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
6903 let l = |m: u32| left.map_or(0u32, |c| ((c as u32 & m) == 0) as u32);
6904 let nb = |x: u32| (x == 0) as u32;
6905 let b0 = cbp & 1;
6906 let b1 = (cbp >> 1) & 1;
6907 let b2 = (cbp >> 2) & 1;
6908 let b3 = (cbp >> 3) & 1;
6909 cab.encode_decision(CBP + (l(1 << 1) + (t(1 << 2) << 1)) as usize, b0);
6910 cab.encode_decision(CBP + (nb(b0) + (t(1 << 3) << 1)) as usize, b1);
6911 cab.encode_decision(CBP + (l(1 << 3) + (nb(b0) << 1)) as usize, b2);
6912 cab.encode_decision(CBP + (nb(b2) + (nb(b1) << 1)) as usize, b3);
6913 let cbp_chroma = cbp >> 4;
6914 let ct = top.map_or(0u32, |c| ((c >> 4) != 0) as u32);
6915 let cl = left.map_or(0u32, |c| ((c >> 4) != 0) as u32);
6916 cab.encode_decision(CBP + 4 + (cl + (ct << 1)) as usize, (cbp_chroma != 0) as u32);
6917 if cbp_chroma != 0 {
6918 let ct2 = top.map_or(0u32, |c| ((c >> 4) == 2) as u32);
6919 let cl2 = left.map_or(0u32, |c| ((c >> 4) == 2) as u32);
6920 cab.encode_decision(CBP + 8 + (cl2 + (ct2 << 1)) as usize, (cbp_chroma == 2) as u32);
6921 }
6922}
6923
6924#[allow(clippy::too_many_arguments)]
6927fn cb_residual(
6928 cab: &mut CabacEncoder,
6929 nzc: &mut [u8; 48],
6930 cbf_dc: &mut u16,
6931 iz: usize,
6932 rp: usize,
6933 is_intra: bool,
6934 ndc: (Option<u16>, Option<u16>),
6935 coeffs: &[i32],
6936) -> u32 {
6937 let is_dc = rp == CB_RP_I16_DC || rp == CB_RP_CHROMA_DC || rp == CB_RP_CHROMA_DC + 1;
6938 let (mut na, mut nb) = (is_intra as u8, is_intra as u8);
6939 let scan = CB_NZC_CACHE[iz.min(23)];
6940 if is_dc {
6941 if let Some(t) = ndc.0 {
6942 nb = ((t >> rp) & 1) as u8;
6943 }
6944 if let Some(l) = ndc.1 {
6945 na = ((l >> rp) & 1) as u8;
6946 }
6947 } else {
6948 if nzc[scan - 8] != 0xff {
6949 nb = (nzc[scan - 8] != 0) as u8;
6950 }
6951 if nzc[scan - 1] != 0xff {
6952 na = (nzc[scan - 1] != 0) as u8;
6953 }
6954 }
6955 let maxpos = CB_RES_MAXPOS[rp] as usize;
6956 let coeff_num = coeffs[..=maxpos].iter().filter(|&&c| c != 0).count() as u32;
6957 let cbf = coeff_num != 0;
6958 cab.encode_decision(85 + CB_RES_CBF[rp] + (na + (nb << 1)) as usize, cbf as u32);
6959 if !cbf {
6960 if !is_dc {
6961 nzc[scan] = 0;
6962 }
6963 return 0;
6964 }
6965 if is_dc {
6966 *cbf_dc |= 1 << rp;
6967 }
6968 let map = 105 + CB_RES_MAP[rp];
6970 let last = 166 + CB_RES_MAP[rp];
6971 let lastnz = (0..=maxpos).rev().find(|&i| coeffs[i] != 0).unwrap();
6972 for i in 0..maxpos {
6973 let s = coeffs[i] != 0;
6974 cab.encode_decision(map + i, s as u32);
6975 if s {
6976 let is_last = i == lastnz;
6977 cab.encode_decision(last + i, is_last as u32);
6978 if is_last {
6979 break;
6980 }
6981 }
6982 }
6983 let one = 227 + CB_RES_ONE[rp];
6985 let abs = 232 + CB_RES_ONE[rp];
6986 let maxc2 = CB_RES_MAXC2[rp];
6987 let (mut c1, mut c2) = (1i32, 0i32);
6988 for i in (0..=maxpos).rev() {
6989 if coeffs[i] != 0 {
6990 let av = coeffs[i].unsigned_abs();
6991 let gt1 = av > 1;
6992 cab.encode_decision(one + c1 as usize, gt1 as u32);
6993 if gt1 {
6994 cb_ueg_level(cab, abs + c2 as usize, av - 2);
6995 c2 = (c2 + 1).min(maxc2);
6996 c1 = 0;
6997 } else if c1 != 0 {
6998 c1 = (c1 + 1).min(4);
6999 }
7000 cab.encode_bypass((coeffs[i] < 0) as u32);
7001 }
7002 }
7003 if !is_dc {
7004 nzc[scan] = coeff_num as u8;
7005 }
7006 coeff_num
7007}
7008
7009fn cb_build_nzc(mb_nzc: &[[u8; 24]], top: Option<usize>, left: Option<usize>) -> [u8; 48] {
7012 let mut nzc = [0xffu8; 48];
7013 if let Some(t) = top {
7014 let tn = mb_nzc[t];
7015 nzc[1..5].copy_from_slice(&tn[12..16]);
7016 (nzc[0], nzc[5], nzc[29]) = (0, 0, 0);
7017 (nzc[6], nzc[7]) = (tn[20], tn[21]);
7018 (nzc[30], nzc[31]) = (tn[22], tn[23]);
7019 }
7020 if let Some(l) = left {
7021 let ln = mb_nzc[l];
7022 (nzc[8], nzc[16], nzc[24], nzc[32]) = (ln[3], ln[7], ln[11], ln[15]);
7023 (nzc[13], nzc[21], nzc[37], nzc[45]) = (ln[17], ln[21], ln[19], ln[23]);
7024 }
7025 nzc
7026}
7027
7028fn cb_export_nzc(nzc: &[u8; 48]) -> [u8; 24] {
7030 let mut mn = [0u8; 24];
7031 for k in 0..4 {
7032 mn[k] = nzc[9 + k];
7033 mn[4 + k] = nzc[17 + k];
7034 mn[8 + k] = nzc[25 + k];
7035 mn[12 + k] = nzc[33 + k];
7036 }
7037 (mn[16], mn[17], mn[20], mn[21]) = (nzc[14], nzc[15], nzc[22], nzc[23]);
7038 (mn[18], mn[19], mn[22], mn[23]) = (nzc[38], nzc[39], nzc[46], nzc[47]);
7039 for v in mn.iter_mut() {
7040 if *v == 0xff {
7041 *v = 0;
7042 }
7043 }
7044 mn
7045}
7046
7047struct CabacState {
7050 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,
7063}
7064
7065impl CabacState {
7066 fn new(n: usize) -> Self {
7067 CabacState {
7068 cat: vec![0; n],
7069 cmode: vec![0; n],
7070 mb_cbp: vec![0; n],
7071 cbf_dc: vec![0; n],
7072 mb_nzc: vec![[0u8; 24]; n],
7073 mb_mvd: vec![[[0i16; 2]; 16]; n],
7074 mb_ref: vec![[-1i8; 16]; n],
7075 mb_mvd1: vec![[[0i16; 2]; 16]; n],
7076 mb_ref1: vec![[-1i8; 16]; n],
7077 mb_skip: vec![false; n],
7078 mb_direct: vec![false; n],
7079 last_delta_qp: 0,
7080 }
7081 }
7082}
7083
7084fn emit_mb_cabac_i(
7088 fe: &mut FrameEncoder,
7089 cab: &mut CabacEncoder,
7090 cs: &mut CabacState,
7091 plan: &MbPlan,
7092 mb_x: usize,
7093 mb_y: usize,
7094) {
7095 let mb_w = fe.mb_w;
7096 let addr = mb_y * mb_w + mb_x;
7097 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7098 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7099
7100 let li = left.map_or(0, |a| (cs.cat[a] >= 2) as usize);
7102 let ti = top.map_or(0, |a| (cs.cat[a] >= 2) as usize);
7103 let acct = crate::bitacct::enabled();
7104 let t0 = if acct { cab.pos() } else { 0 };
7105 if plan.use_i4 {
7106 cb_mb_type_i(cab, li + ti, true, 0, 0, false);
7107 } else {
7108 cb_mb_type_i(cab, li + ti, false, plan.i16_mode as u32, plan.cbp_chroma, plan.i16_cbp15);
7109 }
7110 if acct {
7111 crate::bitacct::add(crate::bitacct::B::MbType, cab.pos() - t0);
7112 }
7113 let t1 = if acct { cab.pos() } else { 0 };
7114 emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7115 if acct {
7116 crate::bitacct::add(crate::bitacct::B::IntraBody, cab.pos() - t1);
7117 }
7118}
7119
7120#[allow(clippy::too_many_arguments)]
7124fn emit_intra_body_cabac(
7125 fe: &mut FrameEncoder,
7126 cab: &mut CabacEncoder,
7127 cs: &mut CabacState,
7128 plan: &MbPlan,
7129 mb_x: usize,
7130 mb_y: usize,
7131 addr: usize,
7132 top: Option<usize>,
7133 left: Option<usize>,
7134) {
7135 let w4 = fe.mb_w * 4;
7136 let cbp_chroma = plan.cbp_chroma;
7137 let cci = left.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize)
7139 + top.map_or(0, |a| (1..=3).contains(&cs.cmode[a]) as usize);
7140
7141 let mut nzc;
7142 let mut cbfdc = 0u16;
7143 let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
7144
7145 if !plan.use_i4 {
7146 cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
7148 cs.cmode[addr] = plan.chroma_mode as i32;
7149 cs.cat[addr] = 2;
7150 cs.mb_cbp[addr] = ((cbp_chroma as u8) << 4) | if plan.i16_cbp15 { 15 } else { 0 };
7151 nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7152
7153 let delta = fe.qp_delta();
7154 cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7155
7156 let dc_scan = scan_4x4_dcac(&plan.i16_dc_levels);
7158 cb_residual(cab, &mut nzc, &mut cbfdc, 0, CB_RP_I16_DC, true, ndc, &dc_scan);
7159 for (iz, &(lbx, lby)) in LUMA_4X4_SCAN_XY.iter().enumerate() {
7161 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7162 let total = if plan.i16_cbp15 {
7163 let ac = scan_4x4_ac(&plan.i16_q[lby * 4 + lbx]);
7164 cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_I16_AC, true, ndc, &ac)
7165 } else {
7166 nzc[CB_NZC_CACHE[iz]] = 0;
7167 0
7168 };
7169 fe.nnz_y[by * w4 + bx] = total as u8;
7170 }
7171 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);
7172 } else {
7173 let i4 = plan.i4.as_ref().unwrap();
7175 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7176 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7177 let predicted = predict_i4_mode(fe, bx, by);
7178 cb_intra4x4_pred_mode(cab, predicted, i4.modes[lby * 4 + lbx]);
7179 }
7180 cb_chroma_pred_mode(cab, cci, plan.chroma_mode);
7181 cs.cmode[addr] = plan.chroma_mode as i32;
7182 cs.cat[addr] = 0;
7183 let cbp = i4.cbp_luma | (cbp_chroma << 4);
7184 cb_cbp(cab, top.map(|a| cs.mb_cbp[a]), left.map(|a| cs.mb_cbp[a]), cbp);
7185 cs.mb_cbp[addr] = cbp as u8;
7186 nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7187
7188 if cbp == 0 {
7189 cs.last_delta_qp = 0;
7190 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7191 fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
7192 }
7193 } else {
7194 let delta = fe.qp_delta();
7195 cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7196 for id8 in 0..4usize {
7197 for id4 in 0..4usize {
7198 let iz = id8 * 4 + id4;
7199 let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
7200 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7201 let total = if i4.cbp_luma & (1 << id8) != 0 {
7202 let sc = scan_4x4_dcac(&i4.q[lby * 4 + lbx]);
7203 cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_LUMA_4X4, true, ndc, &sc)
7204 } else {
7205 nzc[CB_NZC_CACHE[iz]] = 0;
7206 0
7207 };
7208 fe.nnz_y[by * w4 + bx] = total as u8;
7209 }
7210 }
7211 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);
7212 }
7213 }
7214
7215 cs.cbf_dc[addr] = cbfdc;
7216 cs.mb_nzc[addr] = cb_export_nzc(&nzc);
7217}
7218
7219#[allow(clippy::too_many_arguments)]
7223fn cb_emit_chroma_residual(
7224 cab: &mut CabacEncoder,
7225 fe: &mut FrameEncoder,
7226 nzc: &mut [u8; 48],
7227 cbfdc: &mut u16,
7228 ndc: (Option<u16>, Option<u16>),
7229 is_intra: bool,
7230 cbp_chroma: u32,
7231 c_dc_levels: &[[i32; 4]; 2],
7232 c_q: &[[[i32; 16]; 4]; 2],
7233 mb_x: usize,
7234 mb_y: usize,
7235) {
7236 let w2 = fe.mb_w * 2;
7237 if cbp_chroma >= 1 {
7238 for i in 0..2usize {
7239 cb_residual(cab, nzc, cbfdc, 16 + i * 4, CB_RP_CHROMA_DC + i, is_intra, ndc, &c_dc_levels[i]);
7240 }
7241 }
7242 if cbp_chroma == 2 {
7243 for i in 0..2usize {
7244 for (id4, &(bx, by)) in CHROMA_4X4_SCAN_XY.iter().enumerate() {
7245 let ac = scan_4x4_ac(&c_q[i][by * 2 + bx]);
7246 let total = cb_residual(
7247 cab, nzc, cbfdc, 16 + i * 4 + id4, CB_RP_CHROMA_AC + i, is_intra, ndc, &ac,
7248 );
7249 fe.nnz_c[i][(mb_y * 2 + by) * w2 + (mb_x * 2 + bx)] = total as u8;
7250 }
7251 }
7252 }
7253}
7254
7255pub fn encode_slice_data_cabac_intra(
7260 w: &mut BitWriter,
7261 cfg: &EncoderConfig,
7262 frame: &YuvFrame,
7263 qp: u8,
7264 qpo: &[i32],
7265) -> crate::RefFrame {
7266 let mut fe = FrameEncoder::new(cfg);
7267 fe.qp = qp;
7268 fe.qpc = chroma_qp(qp);
7269 fe.cur_qp = qp;
7270 if cfg.cabac_dz_div > 0 {
7271 fe.idz = cfg.cabac_dz_div; }
7273 let (sy, su, sv) = coded_source(cfg, frame);
7274 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
7275 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
7277 let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
7278
7279 fe.rdoq_strength = if cfg.gop_size <= 1 { cfg.cabac_rdoq } else { 0.0 };
7285 let mut cab = CabacEncoder::new(qp as i32, 0, true);
7287 let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
7288 let total = fe.mb_w * fe.mb_h;
7289
7290 for mb_y in 0..fe.mb_h {
7291 for mb_x in 0..fe.mb_w {
7292 let mb_idx = mb_y * fe.mb_w + mb_x;
7293 fe.qp = aq_qp[mb_idx];
7294 fe.qpc = chroma_qp(aq_qp[mb_idx]);
7295 let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
7296 emit_mb_cabac_i(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
7297 mb_qpy[mb_idx] = fe.cur_qp;
7298 {
7300 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7301 cab.encode_terminate(mb_idx + 1 == total);
7302 if crate::bitacct::enabled() {
7303 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
7304 }
7305 }
7306 }
7307 }
7308
7309 while !w.is_byte_aligned() {
7311 w.write_bit(true);
7312 }
7313 for b in cab.into_bytes() {
7314 w.write_bits(b as u32, 8);
7315 }
7316
7317 let ref_id: Vec<i32> = fe.ref_idx_y.iter().map(|&r| if r >= 0 { r } else { i32::MIN }).collect();
7319 let info = rusty_h264_common::deblock::BlockInfo {
7320 inter: &fe.inter_y,
7321 nnz: &fe.nnz_y,
7322 mv: &fe.mv_y,
7323 ref_id: &ref_id,
7324 mv1: &[],
7325 ref_id1: &[],
7326 w4: fe.mb_w * 4,
7327 t8x8: &[],
7328 bs: &[], kind: &[],
7329 };
7330 rusty_h264_common::deblock::filter_frame(
7331 &mut fe.rec_y, &mut fe.rec_u, &mut fe.rec_v, fe.mb_w, fe.mb_h, &mb_qpy, 0, 0, 0, &info,
7332 );
7333 let w4 = fe.mb_w * 4;
7334 crate::RefFrame {
7335 y: fe.rec_y,
7336 u: fe.rec_u,
7337 v: fe.rec_v,
7338 poc: 0,
7339 frame_num: 0,
7340 mv: fe.mv_y,
7341 ref_idx: fe.ref_idx_y,
7342 w4,
7343 hpel: std::sync::OnceLock::new(),
7345 }
7346}
7347
7348const CB_CACHE30: [usize; 16] = [7, 8, 13, 14, 9, 10, 15, 16, 19, 20, 25, 26, 21, 22, 27, 28];
7358const CB_G_SCAN4: [usize; 16] = [0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15];
7360
7361fn cb_ueg_mv(cab: &mut CabacEncoder, base: usize, v: u32) {
7364 const P2C: [usize; 8] = [0, 1, 2, 3, 3, 3, 3, 3];
7365 if v == 0 {
7366 cab.encode_decision(base, 0);
7367 return;
7368 }
7369 cab.encode_decision(base, 1);
7370 if v <= 7 {
7371 let mut count = 1;
7373 for _ in 0..v - 1 {
7374 cab.encode_decision(base + P2C[count], 1);
7375 count += 1;
7376 }
7377 cab.encode_decision(base + P2C[count], 0);
7378 } else {
7379 let mut count = 1;
7381 for _ in 0..7 {
7382 cab.encode_decision(base + P2C[count], 1);
7383 count += 1;
7384 }
7385 let tb = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7386 cb_exp_bypass(cab, 3, v - 8);
7387 if crate::bitacct::enabled() {
7388 crate::bitacct::add(crate::bitacct::B::MvdBypass, cab.pos() - tb);
7389 }
7390 }
7391}
7392
7393fn cb_mvd(cab: &mut CabacEncoder, comp: usize, ctx_inc: usize, d: i32) {
7396 let th = if crate::bitacct::enabled() { cab.pos() } else { u64::MAX };
7397 let base = 40 + comp * 7;
7398 if d == 0 {
7399 cab.encode_decision(base + ctx_inc, 0);
7400 if th != u64::MAX {
7401 crate::bitacct::add_mvd_sample(0, cab.pos() - th);
7402 }
7403 return;
7404 }
7405 cab.encode_decision(base + ctx_inc, 1);
7406 cb_ueg_mv(cab, base + 3, d.unsigned_abs() - 1); let ts = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7408 cab.encode_bypass((d < 0) as u32);
7409 if crate::bitacct::enabled() {
7410 crate::bitacct::add(crate::bitacct::B::MvdSign, cab.pos() - ts);
7411 }
7412 if th != u64::MAX {
7413 crate::bitacct::add_mvd_sample(d.unsigned_abs(), cab.pos() - th);
7414 }
7415}
7416
7417fn cb_mb_skip(cab: &mut CabacEncoder, ctx_inc: usize, skip: bool) {
7419 cab.encode_decision(ctx_inc, skip as u32);
7420}
7421
7422fn cb_ref_idx(cab: &mut CabacEncoder, ctx0: usize, r: u32) {
7426 const B: usize = 54;
7427 let mut v = r;
7428 let mut bin_idx = 0u32;
7429 loop {
7430 let bin = (v > 0) as u32;
7431 let ctx = match bin_idx {
7432 0 => ctx0,
7433 1 => 4,
7434 _ => 5,
7435 };
7436 cab.encode_decision(B + ctx, bin);
7437 if bin == 0 {
7438 break;
7439 }
7440 v -= 1;
7441 bin_idx += 1;
7442 }
7443}
7444
7445fn cb_mb_type_p_inter(cab: &mut CabacEncoder, mode: u8) {
7448 const S: usize = 11;
7449 cab.encode_decision(S + 3, 0); match mode {
7451 0 => {
7452 cab.encode_decision(S + 4, 0);
7453 cab.encode_decision(S + 5, 0);
7454 }
7455 3 => {
7456 cab.encode_decision(S + 4, 0);
7458 cab.encode_decision(S + 5, 1);
7459 }
7460 1 => {
7461 cab.encode_decision(S + 4, 1);
7462 cab.encode_decision(S + 6, 1);
7463 }
7464 _ => {
7465 cab.encode_decision(S + 4, 1);
7467 cab.encode_decision(S + 6, 0);
7468 }
7469 }
7470}
7471
7472fn cb_sub_mb_type_p(cab: &mut CabacEncoder, sub_type: u8) {
7475 const S: usize = 21;
7476 match sub_type {
7477 0 => cab.encode_decision(S, 1),
7478 _ => unreachable!("only 8x8 sub_mb_type (0) emitted"),
7479 }
7480}
7481
7482fn cb_mb_type_p_intra(cab: &mut CabacEncoder, plan: &MbPlan) {
7486 const S: usize = 11;
7487 cab.encode_decision(S + 3, 1); if plan.use_i4 {
7489 cab.encode_decision(S + 6, 0); return;
7491 }
7492 cab.encode_decision(S + 6, 1); cab.encode_terminate(false); cab.encode_decision(S + 7, plan.i16_cbp15 as u32);
7495 if plan.cbp_chroma != 0 {
7496 cab.encode_decision(S + 8, 1);
7497 cab.encode_decision(S + 8, (plan.cbp_chroma == 2) as u32);
7498 } else {
7499 cab.encode_decision(S + 8, 0);
7500 }
7501 cab.encode_decision(S + 9, (plan.i16_mode as u32 >> 1) & 1);
7502 cab.encode_decision(S + 9, plan.i16_mode as u32 & 1);
7503}
7504
7505fn p_partition_layout(mode: u8) -> &'static [(usize, &'static [usize])] {
7509 match mode {
7510 1 => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7]), (8, &[8, 9, 10, 11, 12, 13, 14, 15])],
7511 2 => &[(0, &[0, 1, 2, 3, 8, 9, 10, 11]), (4, &[4, 5, 6, 7, 12, 13, 14, 15])],
7512 3 => &[(0, &[0, 1, 2, 3]), (4, &[4, 5, 6, 7]), (8, &[8, 9, 10, 11]), (12, &[12, 13, 14, 15])],
7514 _ => &[(0, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])],
7515 }
7516}
7517
7518#[allow(clippy::too_many_arguments)]
7521fn cb_emit_mvd_partition(
7522 cab: &mut CabacEncoder,
7523 part_idx: usize,
7524 zblocks: &[usize],
7525 mvdc: &mut [[i16; 2]; 30],
7526 refc: &mut [i8; 30],
7527 mmvd: &mut [[i16; 2]; 16],
7528 mref: &mut [i8; 16],
7529 mvd: (i32, i32),
7530 ref_idx: i8, ) {
7532 let s = CB_CACHE30[part_idx];
7533 let ctx = |comp: usize| -> usize {
7534 let mut a = 0i32;
7535 if refc[s - 6] >= 0 {
7536 a += mvdc[s - 6][comp].unsigned_abs() as i32;
7537 }
7538 if refc[s - 1] >= 0 {
7539 a += mvdc[s - 1][comp].unsigned_abs() as i32;
7540 }
7541 if a >= 3 {
7542 1 + (a > 32) as usize
7543 } else {
7544 0
7545 }
7546 };
7547 cb_mvd(cab, 0, ctx(0), mvd.0);
7548 cb_mvd(cab, 1, ctx(1), mvd.1);
7549 let (mx, my) = (mvd.0 as i16, mvd.1 as i16);
7550 for &zb in zblocks {
7551 mvdc[CB_CACHE30[zb]] = [mx, my];
7552 refc[CB_CACHE30[zb]] = ref_idx;
7553 mmvd[CB_G_SCAN4[zb]] = [mx, my];
7554 mref[CB_G_SCAN4[zb]] = ref_idx;
7555 }
7556}
7557
7558fn emit_mb_cabac_p_inter(
7561 fe: &mut FrameEncoder,
7562 cab: &mut CabacEncoder,
7563 cs: &mut CabacState,
7564 mode: u8,
7565 plan: &InterPlan,
7566 mb_x: usize,
7567 mb_y: usize,
7568 num_refs: usize,
7569) {
7570 let mb_w = fe.mb_w;
7571 let addr = mb_y * mb_w + mb_x;
7572 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7573 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7574
7575 let acct = crate::bitacct::enabled();
7578 let mut t0 = if acct { cab.pos() } else { 0 };
7579 cb_mb_type_p_inter(cab, mode);
7580 if mode == 3 {
7582 for _ in 0..4 {
7583 cb_sub_mb_type_p(cab, 0);
7584 }
7585 }
7586 if acct {
7587 crate::bitacct::add(crate::bitacct::B::MbType, cab.pos() - t0);
7588 t0 = cab.pos();
7589 }
7590
7591 let mut mvdc = [[0i16; 2]; 30];
7593 let mut refc = [-1i8; 30];
7594 cb_fill_inter_cache(&cs.mb_ref, &cs.mb_mvd, &mut refc, &mut mvdc, top, left, addr, mb_w);
7595 let mut mmvd = [[0i16; 2]; 16];
7596 let mut mref = [0i8; 16];
7597 let layout = p_partition_layout(mode);
7598 if num_refs > 1 {
7601 for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
7602 let r = plan.plan_refs[part];
7603 let s = CB_CACHE30[part_idx];
7604 let ctx0 = (refc[s - 1] > 0) as usize + 2 * (refc[s - 6] > 0) as usize;
7605 cb_ref_idx(cab, ctx0, r as u32);
7606 for &zb in zblocks {
7607 refc[CB_CACHE30[zb]] = r as i8;
7608 }
7609 }
7610 }
7611 if acct {
7612 crate::bitacct::add(crate::bitacct::B::RefIdx, cab.pos() - t0);
7613 t0 = cab.pos();
7614 }
7615 for (part, &(part_idx, zblocks)) in layout.iter().enumerate() {
7617 cb_emit_mvd_partition(
7618 cab, part_idx, zblocks, &mut mvdc, &mut refc, &mut mmvd, &mut mref, plan.mvds[part],
7619 plan.plan_refs[part] as i8,
7620 );
7621 }
7622 if acct {
7623 crate::bitacct::add(crate::bitacct::B::Mvd, cab.pos() - t0);
7624 }
7625 cs.mb_mvd[addr] = mmvd;
7626 cs.mb_ref[addr] = mref;
7627 cs.cat[addr] = 100;
7628 cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7629}
7630
7631#[allow(clippy::too_many_arguments)]
7634fn cb_emit_inter_residual(
7635 fe: &mut FrameEncoder,
7636 cab: &mut CabacEncoder,
7637 cs: &mut CabacState,
7638 plan: &InterPlan,
7639 mb_x: usize,
7640 mb_y: usize,
7641 addr: usize,
7642 top: Option<usize>,
7643 left: Option<usize>,
7644) {
7645 let w4 = fe.mb_w * 4;
7646 let cbp = plan.cbp;
7647 let (cbp_luma, cbp_chroma) = (cbp & 15, cbp >> 4);
7648 let acct = crate::bitacct::enabled();
7649 let mut t0 = if acct { cab.pos() } else { 0 };
7650 cb_cbp(cab, top.map(|a| cs.mb_cbp[a]), left.map(|a| cs.mb_cbp[a]), cbp);
7651 if acct {
7652 crate::bitacct::add(crate::bitacct::B::Cbp, cab.pos() - t0);
7653 }
7654 cs.mb_cbp[addr] = cbp as u8;
7655 let mut nzc = cb_build_nzc(&cs.mb_nzc, top, left);
7656 let mut cbfdc = 0u16;
7657 let ndc = (top.map(|a| cs.cbf_dc[a]), left.map(|a| cs.cbf_dc[a]));
7658
7659 if cbp == 0 {
7660 cs.last_delta_qp = 0;
7661 for &(lbx, lby) in &LUMA_4X4_SCAN_XY {
7662 fe.nnz_y[(mb_y * 4 + lby) * w4 + (mb_x * 4 + lbx)] = 0;
7663 }
7664 } else {
7665 let delta = fe.qp_delta();
7666 if acct { t0 = cab.pos(); }
7667 cb_mb_qp_delta(cab, &mut cs.last_delta_qp, delta);
7668 if acct {
7669 crate::bitacct::add(crate::bitacct::B::QpDelta, cab.pos() - t0);
7670 t0 = cab.pos();
7671 }
7672 for id8 in 0..4usize {
7673 for id4 in 0..4usize {
7674 let iz = id8 * 4 + id4;
7675 let (lbx, lby) = LUMA_4X4_SCAN_XY[iz];
7676 let (bx, by) = (mb_x * 4 + lbx, mb_y * 4 + lby);
7677 let total = if cbp_luma & (1 << id8) != 0 {
7678 let sc = scan_4x4_dcac(&plan.q_blocks[lby * 4 + lbx]);
7679 cb_residual(cab, &mut nzc, &mut cbfdc, iz, CB_RP_LUMA_4X4, false, ndc, &sc)
7680 } else {
7681 nzc[CB_NZC_CACHE[iz]] = 0;
7682 0
7683 };
7684 fe.nnz_y[by * w4 + bx] = total as u8;
7685 }
7686 }
7687 if acct {
7688 crate::bitacct::add(crate::bitacct::B::ResidLuma, cab.pos() - t0);
7689 t0 = cab.pos();
7690 }
7691 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);
7692 if acct {
7693 crate::bitacct::add(crate::bitacct::B::ResidChroma, cab.pos() - t0);
7694 }
7695 }
7696 cs.cbf_dc[addr] = cbfdc;
7697 cs.mb_nzc[addr] = cb_export_nzc(&nzc);
7698}
7699
7700fn emit_mb_cabac_p_intra(
7703 fe: &mut FrameEncoder,
7704 cab: &mut CabacEncoder,
7705 cs: &mut CabacState,
7706 plan: &MbPlan,
7707 mb_x: usize,
7708 mb_y: usize,
7709) {
7710 let mb_w = fe.mb_w;
7711 let addr = mb_y * mb_w + mb_x;
7712 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
7713 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7714 let acct = crate::bitacct::enabled();
7715 let t0 = if acct { cab.pos() } else { 0 };
7716 cb_mb_type_p_intra(cab, plan);
7717 emit_intra_body_cabac(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
7718 if acct {
7719 crate::bitacct::add(crate::bitacct::B::IntraBody, cab.pos() - t0);
7722 }
7723}
7724
7725fn emit_p_skip_cabac(cab: &mut CabacEncoder, cs: &mut CabacState, addr: usize, top: Option<usize>, left: Option<usize>) {
7730 let sctx = 11
7731 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
7732 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
7733 let t0 = if crate::bitacct::enabled() { cab.pos() } else { 0 };
7734 cb_mb_skip(cab, sctx, true);
7735 if crate::bitacct::enabled() {
7736 crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - t0);
7737 }
7738 cs.mb_skip[addr] = true;
7739 cs.cat[addr] = 100;
7740 cs.last_delta_qp = 0;
7741}
7742
7743fn frame_median_mb_var(sy: &[u8], cw: usize, mb_w: usize, mb_h: usize) -> i64 {
7763 let mut vs: Vec<i64> = Vec::with_capacity(mb_w * mb_h);
7764 for my in 0..mb_h {
7765 for mx in 0..mb_w {
7766 let (mut sum, mut sq) = (0i64, 0i64);
7767 for r in (0..16).step_by(2) {
7768 let row = (my * 16 + r) * cw + mx * 16;
7769 for c in (0..16).step_by(2) {
7770 let v = sy[row + c] as i64;
7771 sum += v;
7772 sq += v * v;
7773 }
7774 }
7775 let n = 64i64;
7776 vs.push((sq - sum * sum / n) / n);
7777 }
7778 }
7779 vs.sort_unstable();
7780 vs.get(vs.len() / 2).copied().unwrap_or(0)
7781}
7782
7783fn me_lambda_scale(
7786 cfg: &EncoderConfig,
7787 sy: &[u8],
7788 cw: usize,
7789 mb_w: usize,
7790 mb_h: usize,
7791 ref_y: Option<&[u8]>,
7792) -> f64 {
7793 let hi = match cfg.tune_lme_hi {
7794 Some(v) if v > 0.0 => v,
7795 _ => return cfg.cabac_lambda_scale,
7796 };
7797 if frame_median_mb_var(sy, cw, mb_w, mb_h) >= cfg.tune_lme_tex_thresh.unwrap_or(650) {
7818 return cfg.cabac_lambda_scale;
7819 }
7820 if let Some(r) = ref_y {
7821 let mot = cfg.tune_lme_motion_thresh.unwrap_or(26.0);
7822 if global_mc_residual(sy, cw, mb_h * 16, r) >= mot {
7823 return cfg.cabac_lambda_scale;
7824 }
7825 }
7826 hi
7827}
7828
7829pub fn encode_slice_data_cabac_p(
7830 w: &mut BitWriter,
7831 cfg: &EncoderConfig,
7832 frame: &YuvFrame,
7833 qp: u8,
7834 refs: &[crate::RefFrame],
7835 qpo: &[i32],
7836) -> crate::RefFrame {
7837 let mut fe = FrameEncoder::new(cfg);
7838 fe.qp = qp;
7839 fe.qpc = chroma_qp(qp);
7840 fe.cur_qp = qp;
7841 if cfg.cabac_dz_div > 0 {
7842 fe.idz = cfg.cabac_dz_div; }
7844 let (sy, su, sv) = coded_source(cfg, frame);
7845 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
7846 let lme_scale = me_lambda_scale(cfg, &sy, fe.cw, fe.mb_w, fe.mb_h, refs.first().map(|r| &r.y[..]));
7849 let num_refs = refs.len();
7850 if fe.me_wide && !refs.is_empty() {
7852 let coh = global_mc_residual(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7853 if std::env::var("RFF_ME_COH_DBG").is_ok() {
7854 eprintln!("ME_COH qp{qp} residual={coh:.2}");
7855 }
7856 if coh < fe.me_wide_coh {
7857 fe.me_wide = false;
7858 }
7859 }
7860 if fe.me_wide && !refs.is_empty() && (me_wide_hr_thresh() > 0.0 || me_wide_hr_dbg()) {
7867 let hr = me_wide_headroom(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7868 if me_wide_hr_dbg() {
7869 eprintln!("ME_HR qp{qp} headroom={hr:.2}");
7870 }
7871 if me_wide_hr_thresh() > 0.0 && hr < me_wide_hr_thresh() {
7872 fe.me_wide = false;
7873 }
7874 }
7875 if me_sadfp_mode() == 1 && !fe.fast && !refs.is_empty() {
7878 let (mg, dc) = b2_mgain(&sy, fe.cw, fe.mb_h * 16, &refs[0].y);
7879 if me_sadt_dbg() {
7880 eprintln!("B2_MG qp{qp} mgain={mg:.3} dcfrac={dc:.3}");
7881 }
7882 fe.sadfp = mg >= me_sadt() && dc <= me_sad_dcmax();
7883 if mv_smooth_mode() == 1 {
7886 fe.mv_smooth = mg >= mv_smooth_t() && dc <= me_sad_dcmax();
7889 }
7890 let smg = split_mg();
7892 if smg > 0.0 {
7893 fe.do_splits = mg >= smg;
7894 }
7895 }
7896 if fe.satd_q > 0.0 {
7897 let mut vars: Vec<i64> = (0..fe.mb_h)
7898 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
7899 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
7900 .collect();
7901 vars.sort_unstable();
7902 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
7903 fe.satd_var_thresh = vars[idx];
7904 }
7905 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
7906 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
7908 let mut mb_qpy = vec![qp; fe.mb_w * fe.mb_h];
7909
7910 let mut greedy_free = 0usize;
7914 let mut greedy_seen = 0usize;
7915 let mut greedy_on = fe.greedy_min_free == 0;
7916 let greedy_learn = (fe.mb_w * fe.mb_h / 8).max(64);
7917 let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false); let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
7919 let total = fe.mb_w * fe.mb_h;
7920
7921 let _g_loop = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMbLoop);
7924 for mb_y in 0..fe.mb_h {
7925 for mb_x in 0..fe.mb_w {
7926 let mb_idx = mb_y * fe.mb_w + mb_x;
7927 let addr = mb_idx;
7928 let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
7929 let left = if mb_x > 0 { Some(addr - 1) } else { None };
7930 fe.qp = aq_qp[mb_idx];
7931 fe.qpc = chroma_qp(aq_qp[mb_idx]);
7932
7933 let mut inter: Option<InterChoice> = None;
7935 let mut did_skip = false;
7936 if num_refs > 0 {
7937 let mv_skip = fe.skip_mv(mb_x, mb_y);
7938 let skip_y = fe.skip_predict_luma(refs, mb_x, mb_y, mv_skip);
7939 let luma_free = fe.skip_luma_is_free(&sy, mb_x, mb_y, &skip_y);
7940 let skip_c = if luma_free || !fe.fast {
7941 fe.skip_predict_chroma(refs, mb_x, mb_y, mv_skip)
7942 } else {
7943 [[0u8; 64]; 2]
7944 };
7945 let is_free = luma_free && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &skip_c);
7946 let skip_sad = if fe.fast {
7947 0
7948 } else {
7949 let (lx, ly) = (mb_x * 16, mb_y * 16);
7950 let mut s = 0u32;
7951 for dy in 0..16 {
7952 let src = &sy[(ly + dy) * fe.cw + lx..][..16];
7953 let p = &skip_y[dy * 16..][..16];
7954 s += src.iter().zip(p).map(|(&a, &b)| a.abs_diff(b) as u32).sum::<u32>();
7955 }
7956 s
7957 };
7958 greedy_seen += 1;
7959 if greedy_seen >= greedy_learn {
7960 greedy_on = fe.greedy_min_free == 0
7961 || greedy_free * 100 >= greedy_seen * fe.greedy_min_free as usize;
7962 }
7963 if is_free {
7964 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
7965 if !fe.fast {
7966 fe.mb_was_skip[mb_idx] = true;
7967 fe.mb_skip_sad[mb_idx] = skip_sad;
7968 }
7969 greedy_free += 1;
7970 did_skip = true;
7971 } else {
7972 let (lx, ly) = (mb_x * 16, mb_y * 16);
7973 let nb = {
7974 let _g = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncMvPred);
7975 fe.mv_neighbors_block(mb_x as isize * 4, mb_y as isize * 4, 4)
7976 };
7977 let lme = lambda.sqrt() * lme_scale;
7978 if fe.fast {
7979 fe.mb_use_satd = fe.satd_q > 0.0
7980 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
7981 let (r16, mv16, cost_inter) =
7982 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
7983 let cost_intra = if fe.mb_use_satd {
7984 fe.best_i16_satd(&sy, mb_x, mb_y)
7985 } else {
7986 fe.best_i16_sad(&sy, mb_x, mb_y)
7987 } + (lme * fe.tune_intra_penalty) as i64;
7988 inter = if cost_intra < cost_inter {
7989 None
7990 } else {
7991 Some((0, vec![(r16, mv16)]))
7992 };
7993 } else {
7994 if fe.greedy_skip && greedy_on && skip_sad < fe.pred_skip_sad(mb_x, mb_y) {
7996 fe.commit_skip(mb_x, mb_y, mv_skip, &skip_y, &skip_c);
7997 fe.mb_was_skip[mb_idx] = true;
7998 fe.mb_skip_sad[mb_idx] = skip_sad;
7999 did_skip = true;
8000 } else {
8001 let (r16, mv16, c16) =
8002 fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 16, &[], lme);
8003 let mut best_c = c16;
8004 let mut pick: Option<InterChoice> = Some((0, vec![(r16, mv16)]));
8005 const QSTEP16: [i64; 6] = [10, 11, 13, 14, 16, 18];
8006 let qstep16 = QSTEP16[(fe.qp % 6) as usize] << (fe.qp / 6);
8007 let split_gate = ((30 * (qstep16 + 160)) >> 3) * 2;
8008 let split_t = split_t();
8009 if fe.do_splits && c16 > split_gate && (split_t <= 0.0 || (c16 as f64) >= split_t * lme) {
8010 let (rt, mvt, ct) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 16, 8, &[mv16], lme);
8011 let (rb, mvb, cb) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly + 8, 16, 8, &[mv16], lme);
8012 let (rl, mvl, cl) = fe.best_part(refs, &sy, &nb, num_refs, lx, ly, 8, 16, &[mv16], lme);
8013 let (rr, mvr, cr) = fe.best_part(refs, &sy, &nb, num_refs, lx + 8, ly, 8, 16, &[mv16], lme);
8014 if ct + cb < best_c {
8015 best_c = ct + cb;
8016 pick = Some((1u8, vec![(rt, mvt), (rb, mvb)]));
8017 }
8018 if cl + cr < best_c {
8019 best_c = cl + cr;
8020 pick = Some((2u8, vec![(rl, mvl), (rr, mvr)]));
8021 }
8022 if fe.sub8x8 {
8024 let mut c8 = (lme * 4.0) as i64;
8025 let mut p8 = Vec::with_capacity(4);
8026 for &(qx, qy) in &[(0usize, 0usize), (8, 0), (0, 8), (8, 8)] {
8027 let (r, mv, c) = fe.best_part(
8028 refs, &sy, &nb, num_refs, lx + qx, ly + qy, 8, 8, &[mv16], lme,
8029 );
8030 c8 += c;
8031 p8.push((r, mv));
8032 }
8033 if c8 < best_c {
8034 best_c = c8;
8035 pick = Some((3u8, p8));
8036 }
8037 }
8038 }
8039 if fe.sp_defer.get() {
8044 if let Some((mode, parts)) = pick.as_mut() {
8045 let regions: &[(usize, usize, usize, usize)] = match mode {
8046 1 => &[(0, 0, 16, 8), (0, 8, 16, 8)],
8047 2 => &[(0, 0, 8, 16), (8, 0, 8, 16)],
8048 3 => &[(0, 0, 8, 8), (8, 0, 8, 8), (0, 8, 8, 8), (8, 8, 8, 8)],
8049 _ => &[(0, 0, 16, 16)],
8050 };
8051 let mut tot = if *mode == 3 { (lme * 4.0) as i64 } else { 0 };
8052 for (i, &(qx, qy, pw, ph)) in regions.iter().enumerate() {
8053 let (r, mv) = parts[i];
8054 let (m2, c2) = fe.refine_part(
8055 refs, &sy, &nb, num_refs, lx + qx, ly + qy, pw, ph, lme, r, mv,
8056 );
8057 parts[i] = (r, m2);
8058 tot += c2;
8059 }
8060 best_c = tot;
8061 }
8062 }
8063 let c_intra = fe.best_i16_satd(&sy, mb_x, mb_y)
8064 + (lme * fe.tune_intra_penalty) as i64;
8065 inter = if c_intra < best_c { None } else { pick };
8066 fe.mb_was_skip[mb_idx] = false;
8067 fe.mb_skip_sad[mb_idx] = skip_sad;
8068 }
8069 }
8070 }
8071 }
8072
8073 if did_skip {
8075 emit_p_skip_cabac(&mut cab, &mut cs, addr, top, left);
8076 mb_qpy[mb_idx] = fe.cur_qp;
8077 {
8078 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8079 {
8080 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8081 cab.encode_terminate(mb_idx + 1 == total);
8082 if crate::bitacct::enabled() {
8083 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8084 }
8085 }
8086 if crate::bitacct::enabled() {
8087 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8088 }
8089 }
8090 continue;
8091 }
8092 let sctx = 11
8094 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
8095 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
8096 let tskip = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8097 cb_mb_skip(&mut cab, sctx, false);
8098 if crate::bitacct::enabled() {
8099 crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - tskip);
8100 }
8101 cs.mb_skip[addr] = false;
8102 match inter {
8103 Some((mode, parts)) => {
8104 let plan = fe.plan_inter_mb(refs, &sy, &su, &sv, mb_x, mb_y, mode, &parts, None);
8105 let _ge = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
8109 emit_mb_cabac_p_inter(&mut fe, &mut cab, &mut cs, mode, &plan, mb_x, mb_y, num_refs);
8110 }
8111 None => {
8112 let plan = plan_mb(&mut fe, mb_x, mb_y, &sy, &su, &sv);
8113 let _ge = rusty_h264_common::prof::scope(rusty_h264_common::prof::Stage::EncEmit);
8114 emit_mb_cabac_p_intra(&mut fe, &mut cab, &mut cs, &plan, mb_x, mb_y);
8115 }
8116 }
8117 mb_qpy[mb_idx] = fe.cur_qp;
8118 {
8119 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8120 cab.encode_terminate(mb_idx + 1 == total);
8121 if crate::bitacct::enabled() {
8122 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8123 }
8124 }
8125 }
8126 }
8127
8128 while !w.is_byte_aligned() {
8129 w.write_bit(true);
8130 }
8131 for b in cab.into_bytes() {
8132 w.write_bits(b as u32, 8);
8133 }
8134
8135 let ref_id: Vec<i32> = fe.ref_idx_y.iter().map(|&r| if r >= 0 { r } else { i32::MIN }).collect();
8137 let info = rusty_h264_common::deblock::BlockInfo {
8138 inter: &fe.inter_y,
8139 nnz: &fe.nnz_y,
8140 mv: &fe.mv_y,
8141 ref_id: &ref_id,
8142 mv1: &[],
8143 ref_id1: &[],
8144 w4: fe.mb_w * 4,
8145 t8x8: &[],
8146 bs: &[], kind: &[],
8147 };
8148 rusty_h264_common::deblock::filter_frame(
8149 &mut fe.rec_y, &mut fe.rec_u, &mut fe.rec_v, fe.mb_w, fe.mb_h, &mb_qpy, 0, 0, 0, &info,
8150 );
8151 let w4 = fe.mb_w * 4;
8152 crate::RefFrame {
8153 y: fe.rec_y,
8154 u: fe.rec_u,
8155 v: fe.rec_v,
8156 poc: 0,
8157 frame_num: 0,
8158 mv: fe.mv_y,
8159 ref_idx: fe.ref_idx_y,
8160 w4,
8161 hpel: std::sync::OnceLock::new(),
8163 }
8164}
8165
8166fn cb_fill_inter_cache(
8176 mb_ref: &[[i8; 16]],
8177 mb_mvd: &[[[i16; 2]; 16]],
8178 refc: &mut [i8; 30],
8179 mvdc: &mut [[i16; 2]; 30],
8180 top: Option<usize>,
8181 left: Option<usize>,
8182 addr: usize,
8183 mb_w: usize,
8184) {
8185 if let Some(l) = left {
8186 for (ci, bi) in [(6usize, 3usize), (12, 7), (18, 11), (24, 15)] {
8187 refc[ci] = mb_ref[l][bi];
8188 mvdc[ci] = mb_mvd[l][bi];
8189 }
8190 }
8191 if let Some(t) = top {
8192 for (ci, bi) in [(1usize, 12usize), (2, 13), (3, 14), (4, 15)] {
8193 refc[ci] = mb_ref[t][bi];
8194 mvdc[ci] = mb_mvd[t][bi];
8195 }
8196 }
8197 let mb_x = addr % mb_w;
8198 let mb_y = addr / mb_w;
8199 if mb_x > 0 && mb_y > 0 {
8200 let a = addr - mb_w - 1;
8201 (refc[0], mvdc[0]) = (mb_ref[a][15], mb_mvd[a][15]);
8202 }
8203 if mb_y > 0 && mb_x + 1 < mb_w {
8204 let a = addr - mb_w + 1;
8205 (refc[5], mvdc[5]) = (mb_ref[a][12], mb_mvd[a][12]);
8206 }
8207}
8208
8209pub fn b_part_mb_type(p0: u8, p1: u8, mvmode: u8) -> u32 {
8214 let base = match (p0, p1) {
8215 (1, 1) => 4,
8216 (2, 2) => 6,
8217 (1, 2) => 8,
8218 (2, 1) => 10,
8219 (1, 3) => 12,
8220 (2, 3) => 14,
8221 (3, 1) => 16,
8222 (3, 2) => 18,
8223 _ => 20, };
8225 base + if mvmode == 2 { 1 } else { 0 }
8226}
8227
8228fn b_part_layout(mvmode: u8) -> ([(usize, usize, usize, usize); 2], [(usize, &'static [usize]); 2]) {
8231 if mvmode == 1 {
8232 ([(0, 0, 16, 8), (0, 8, 16, 8)],
8233 [(0, &[0, 1, 2, 3, 4, 5, 6, 7][..]), (8, &[8, 9, 10, 11, 12, 13, 14, 15][..])])
8234 } else {
8235 ([(0, 0, 8, 16), (8, 0, 8, 16)],
8236 [(0, &[0, 1, 2, 3, 8, 9, 10, 11][..]), (4, &[4, 5, 6, 7, 12, 13, 14, 15][..])])
8237 }
8238}
8239
8240pub fn cb_mb_type_b(cab: &mut CabacEncoder, ctx_inc: usize, mb_type: u32) {
8258 const B: usize = 27;
8259 if mb_type == 0 {
8260 cab.encode_decision(B + ctx_inc, 0); return;
8262 }
8263 cab.encode_decision(B + ctx_inc, 1);
8264 if mb_type <= 2 {
8265 cab.encode_decision(B + 3, 0);
8266 cab.encode_decision(B + 5, mb_type - 1); return;
8268 }
8269 cab.encode_decision(B + 3, 1);
8270 let (m4, extra) = if mb_type <= 10 {
8271 (mb_type - 3, None)
8272 } else if mb_type == 11 {
8273 (14, None) } else if mb_type == 22 {
8275 (15, None) } else {
8277 let v = mb_type + 4;
8278 (v >> 1, Some(v & 1))
8279 };
8280 cab.encode_decision(B + 4, (m4 >> 3) & 1);
8281 cab.encode_decision(B + 5, (m4 >> 2) & 1);
8282 cab.encode_decision(B + 5, (m4 >> 1) & 1);
8283 cab.encode_decision(B + 5, m4 & 1);
8284 if let Some(e) = extra {
8285 cab.encode_decision(B + 5, e);
8286 }
8287}
8288
8289const CB_ALL16: [usize; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
8290
8291fn emit_mb_cabac_b(
8294 fe: &mut FrameEncoder,
8295 cab: &mut CabacEncoder,
8296 cs: &mut CabacState,
8297 dir: u8,
8298 bsplit: Option<(u8, [(u8, (i32, i32), (i32, i32)); 2])>,
8299 plan: &InterPlan,
8300 mb_x: usize,
8301 mb_y: usize,
8302) {
8303 let mb_w = fe.mb_w;
8304 let addr = mb_y * mb_w + mb_x;
8305 let top = if mb_y > 0 { Some(addr - mb_w) } else { None };
8306 let left = if mb_x > 0 { Some(addr - 1) } else { None };
8307
8308 let bci = left.map_or(0, |a| (!cs.mb_direct[a]) as usize)
8309 + top.map_or(0, |a| (!cs.mb_direct[a]) as usize);
8310 let bmt = match bsplit {
8311 Some((mvmode, parts2)) => b_part_mb_type(parts2[0].0, parts2[1].0, mvmode),
8312 None => dir as u32,
8313 };
8314 cb_mb_type_b(cab, bci, bmt);
8315
8316 let mut mvdc0 = [[0i16; 2]; 30];
8318 let mut refc0 = [-1i8; 30];
8319 let mut mvdc1 = [[0i16; 2]; 30];
8320 let mut refc1 = [-1i8; 30];
8321 cb_fill_inter_cache(&cs.mb_ref, &cs.mb_mvd, &mut refc0, &mut mvdc0, top, left, addr, mb_w);
8322 cb_fill_inter_cache(&cs.mb_ref1, &cs.mb_mvd1, &mut refc1, &mut mvdc1, top, left, addr, mb_w);
8323 let mut mmvd0 = [[0i16; 2]; 16];
8324 let mut mref0 = [-1i8; 16];
8325 let mut mmvd1 = [[0i16; 2]; 16];
8326 let mut mref1 = [-1i8; 16];
8327 let (use0, use1) = (dir == 1 || dir == 3, dir == 2 || dir == 3);
8328 if let Some((mvmode, parts2)) = bsplit {
8329 let (_, zb) = b_part_layout(mvmode);
8335 let mut k = 0;
8336 for list in 0..2 {
8337 for part in 0..2 {
8338 let pred = parts2[part].0;
8339 let used = if list == 0 { pred == 1 || pred == 3 } else { pred == 2 || pred == 3 };
8340 if !used {
8341 continue;
8342 }
8343 let (pidx, blocks) = zb[part];
8344 if list == 0 {
8345 cb_emit_mvd_partition(cab, pidx, blocks, &mut mvdc0, &mut refc0, &mut mmvd0, &mut mref0, plan.mvds[k], 0);
8346 } else {
8347 cb_emit_mvd_partition(cab, pidx, blocks, &mut mvdc1, &mut refc1, &mut mmvd1, &mut mref1, plan.mvds[k], 0);
8348 }
8349 k += 1;
8350 }
8351 }
8352 } else if dir == 0 {
8353 mref0 = [0i8; 16];
8356 mref1 = [0i8; 16];
8357 } else {
8358 let mut k = 0;
8360 if use0 {
8361 cb_emit_mvd_partition(cab, 0, &CB_ALL16, &mut mvdc0, &mut refc0, &mut mmvd0, &mut mref0, plan.mvds[k], 0);
8362 k += 1;
8363 }
8364 if use1 {
8365 cb_emit_mvd_partition(cab, 0, &CB_ALL16, &mut mvdc1, &mut refc1, &mut mmvd1, &mut mref1, plan.mvds[k], 0);
8366 }
8367 }
8368 cs.mb_mvd[addr] = mmvd0;
8369 cs.mb_ref[addr] = mref0;
8370 cs.mb_mvd1[addr] = mmvd1;
8371 cs.mb_ref1[addr] = mref1;
8372 cs.mb_direct[addr] = dir == 0 && bsplit.is_none();
8373 cs.cat[addr] = 100;
8374 cb_emit_inter_residual(fe, cab, cs, plan, mb_x, mb_y, addr, top, left);
8375}
8376
8377fn emit_b_skip_cabac(cab: &mut CabacEncoder, cs: &mut CabacState, addr: usize, top: Option<usize>, left: Option<usize>) {
8381 let sctx = 24
8382 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
8383 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
8384 let t0 = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8385 cb_mb_skip(cab, sctx, true);
8386 if crate::bitacct::enabled() {
8387 crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - t0);
8388 }
8389 cs.mb_skip[addr] = true;
8390 cs.cat[addr] = 100;
8391 cs.mb_direct[addr] = true;
8392 cs.mb_ref[addr] = [0i8; 16];
8393 cs.mb_ref1[addr] = [0i8; 16];
8394 cs.last_delta_qp = 0;
8395}
8396
8397#[allow(clippy::too_many_arguments)]
8401pub mod bstats {
8405 use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
8406 pub static SKIP: AtomicU64 = AtomicU64::new(0);
8407 pub static CODED: AtomicU64 = AtomicU64::new(0);
8408 pub static DIRWIN: AtomicU64 = AtomicU64::new(0);
8413 pub static SPLIT: AtomicU64 = AtomicU64::new(0);
8417 pub fn on() -> bool {
8418 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8419 *E.get_or_init(|| std::env::var_os("RFF_BSTATS").is_some())
8420 }
8421 pub fn bump(c: &AtomicU64) {
8422 if on() {
8423 c.fetch_add(1, Relaxed);
8424 }
8425 }
8426 pub fn dump() {
8430 let (s, c) = (SKIP.load(Relaxed), CODED.load(Relaxed));
8431 let t = (s + c).max(1) as f64;
8432 eprintln!(
8433 "B-slice census: B_Skip {:.1}% not-skipped {:.1}% direct-wins-of-coded {:.1}% 16x8/8x16-of-coded {:.1}% (n={})",
8434 s as f64 * 100.0 / t, c as f64 * 100.0 / t,
8435 DIRWIN.load(Relaxed) as f64 * 100.0 / c.max(1) as f64,
8436 SPLIT.load(Relaxed) as f64 * 100.0 / c.max(1) as f64, s + c
8437 );
8438 }
8439}
8440
8441pub fn encode_slice_data_cabac_b(
8442 w: &mut BitWriter,
8443 cfg: &EncoderConfig,
8444 frame: &YuvFrame,
8445 qp: u8,
8446 poc: i32,
8447 l0: &crate::RefFrame,
8448 l1: &crate::RefFrame,
8449 qpo: &[i32],
8450) {
8451 let mut fe = FrameEncoder::new(cfg);
8452 fe.qp = qp;
8453 fe.qpc = chroma_qp(qp);
8454 fe.cur_qp = qp;
8455 if cfg.cabac_dz_div > 0 {
8456 fe.idz = cfg.cabac_dz_div; }
8458 fe.bi_w = implicit_bi_weights(poc, l0.poc, l1.poc);
8459 let (sy, su, sv) = coded_source(cfg, frame);
8460 let lambda = 0.85 * fe.tune_lambda_scale * 2f64.powf((qp as f64 - 12.0) / 3.0);
8461 let lme = lambda.sqrt() * me_lambda_scale(cfg, &sy, fe.cw, fe.mb_w, fe.mb_h, Some(&l0.y[..]));
8462 let refs = std::slice::from_ref(l0);
8463 if fe.satd_q > 0.0 {
8464 let mut vars: Vec<i64> = (0..fe.mb_h)
8465 .flat_map(|my| (0..fe.mb_w).map(move |mx| (mx, my)))
8466 .map(|(mx, my)| mb_variance(&sy, fe.cw, mx, my))
8467 .collect();
8468 vars.sort_unstable();
8469 let idx = (((1.0 - fe.satd_q) * vars.len() as f64) as usize).min(vars.len() - 1);
8470 fe.satd_var_thresh = vars[idx];
8471 }
8472 let mut aq_qp = aq_qp_map(&sy, fe.cw, fe.mb_w, fe.mb_h, qp, fe.aq_strength);
8473 apply_mbtree_qpo(&mut aq_qp, qpo); fe.cur_qp = qp;
8475
8476 let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false);
8477 let mut cs = CabacState::new(fe.mb_w * fe.mb_h);
8478 let total = fe.mb_w * fe.mb_h;
8479 let bskip_t = std::env::var("RFF_BSKIP_T").ok().and_then(|v| v.parse::<f64>().ok())
8481 .or(cfg.tune_bskip_rd)
8482 .unwrap_or(0.0);
8483 let bskip_busy_pct = std::env::var("RFF_BSKIP_BUSY").ok().and_then(|v| v.parse::<usize>().ok())
8484 .or(cfg.tune_bskip_busy_pct)
8485 .unwrap_or(60);
8486 let (mut b_seen, mut b_free) = (0usize, 0usize);
8487 let bsplit_env = std::env::var("RFF_BSPLIT").ok().and_then(|v| v.parse::<u32>().ok());
8489 let bsplit_on = bsplit_env.map(|v| v == 1).unwrap_or(cfg.tune_b_split);
8490 let bsplit_probe = bsplit_env.filter(|&v| v >= 2).unwrap_or(0);
8491 let (mut b_coded, mut b_dirwin) = (0usize, 0usize);
8495 let bskip_dirwin_pct = std::env::var("RFF_BSKIP_DIRWIN").ok().and_then(|v| v.parse::<usize>().ok())
8496 .or(cfg.tune_bskip_dirwin_pct)
8497 .unwrap_or(10);
8498
8499 for mb_y in 0..fe.mb_h {
8500 for mb_x in 0..fe.mb_w {
8501 let mb_idx = mb_y * fe.mb_w + mb_x;
8502 let addr = mb_idx;
8503 let top = if mb_y > 0 { Some(addr - fe.mb_w) } else { None };
8504 let left = if mb_x > 0 { Some(addr - 1) } else { None };
8505 fe.qp = aq_qp[mb_idx];
8506 fe.qpc = chroma_qp(aq_qp[mb_idx]);
8507 let (lx, ly) = (mb_x * 16, mb_y * 16);
8508 let (pbx, pby) = (mb_x as isize * 4, mb_y as isize * 4);
8509 fe.mb_use_satd =
8510 fe.satd_q > 0.0 && mb_variance(&sy, fe.cw, mb_x, mb_y) >= fe.satd_var_thresh;
8511 let n0 = fe.mv_neighbors_block_list(pbx, pby, 4, 0);
8512 let n1 = fe.mv_neighbors_block_list(pbx, pby, 4, 1);
8513 let pmv0 = predict_partition_mv(0, 0, n0[0], n0[1], n0[2], 0);
8514 let pmv1 = predict_partition_mv(0, 0, n1[0], n1[1], n1[2], 0);
8515 let (dp, dc, dmotion) = fe.b_direct(l0, l1, mb_x, mb_y);
8516 let free_skip = fe.skip_luma_is_free(&sy, mb_x, mb_y, &dp)
8518 && fe.skip_chroma_is_free(&su, &sv, mb_x, mb_y, &dc);
8519 b_seen += 1;
8520 if free_skip {
8521 b_free += 1;
8522 }
8523 if free_skip {
8524 bstats::bump(&bstats::SKIP);
8525 fe.commit_direct_motion(mb_x, mb_y, &dmotion);
8526 emit_b_skip_cabac(&mut cab, &mut cs, addr, top, left);
8527 {
8528 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8529 {
8530 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8531 cab.encode_terminate(mb_idx + 1 == total);
8532 if crate::bitacct::enabled() {
8533 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8534 }
8535 }
8536 if crate::bitacct::enabled() {
8537 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8538 }
8539 }
8540 continue;
8541 }
8542 bstats::bump(&bstats::CODED);
8543 let d_direct = fe.pred_dist(&sy, lx, ly, &dp);
8544 let (mv0, j0) = fe.motion_search(l0, &sy, lx, ly, 16, 16, &[pmv0], lme, None);
8545 let (mv1, j1) = fe.motion_search(l1, &sy, lx, ly, 16, 16, &[pmv1], lme, None);
8546 let d_bi = fe.bi_dist(l0, l1, &sy, lx, ly, mv0, mv1);
8547 let r_bi = mvd_bits(mv0.0 - pmv0.0) + mvd_bits(mv0.1 - pmv0.1)
8548 + mvd_bits(mv1.0 - pmv1.0) + mvd_bits(mv1.1 - pmv1.1);
8549 let j_bi = d_bi + (lme * r_bi as f64) as i64;
8550 let (mut dir, mut best) = (0u8, d_direct);
8551 if j0 < best { dir = 1; best = j0; }
8552 if j1 < best { dir = 2; best = j1; }
8553 if j_bi < best { dir = 3; best = j_bi; }
8554 if dir == 0 { bstats::bump(&bstats::DIRWIN); }
8555 b_coded += 1;
8556 if dir == 0 {
8557 b_dirwin += 1;
8558 }
8559 if bskip_t > 0.0
8576 && dir == 0
8577 && b_seen >= 32
8578 && b_free * 100 < b_seen * bskip_busy_pct
8579 && b_coded >= 32
8591 && b_dirwin * 100 >= b_coded * bskip_dirwin_pct
8592 && (d_direct as f64) <= bskip_t * lambda
8593 {
8594 bstats::bump(&bstats::SKIP);
8595 fe.commit_direct_motion(mb_x, mb_y, &dmotion);
8596 emit_b_skip_cabac(&mut cab, &mut cs, addr, top, left);
8597 cab.encode_terminate(mb_idx + 1 == total);
8598 continue;
8599 }
8600 let sctx = 24
8602 + left.map_or(0, |a| (!cs.mb_skip[a]) as usize)
8603 + top.map_or(0, |a| (!cs.mb_skip[a]) as usize);
8604 let tskip = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8605 cb_mb_skip(&mut cab, sctx, false);
8606 if crate::bitacct::enabled() {
8607 crate::bitacct::add(crate::bitacct::B::SkipFlag, cab.pos() - tskip);
8608 }
8609 cs.mb_skip[addr] = false;
8610 let mut bsplit: Option<(u8, [(u8, (i32, i32), (i32, i32)); 2])> = None;
8617 if bsplit_probe > 0 && dir != 0 {
8625 let m = if bsplit_probe == 2 { 1u8 } else { 2u8 };
8626 bsplit = Some((m, [(dir, mv0, mv1); 2]));
8627 } else if bsplit_on {
8628 for mvmode in 1u8..=2 {
8629 let (rects, _) = b_part_layout(mvmode);
8630 let mut cand = [(0u8, (0i32, 0i32), (0i32, 0i32)); 2];
8631 let mut jsum = 0i64;
8632 for (part, &(rx, ry, rw, rh)) in rects.iter().enumerate() {
8633 let (px, py) = (lx + rx, ly + ry);
8634 let (m0, c0) = fe.motion_search(l0, &sy, px, py, rw, rh, &[pmv0], lme, None);
8635 let (m1, c1) = fe.motion_search(l1, &sy, px, py, rw, rh, &[pmv1], lme, None);
8636 let dbi = fe.bi_dist_rect(l0, l1, &sy, px, py, rw, rh, m0, m1);
8638 let rbi = mvd_bits(m0.0 - pmv0.0) + mvd_bits(m0.1 - pmv0.1)
8639 + mvd_bits(m1.0 - pmv1.0) + mvd_bits(m1.1 - pmv1.1);
8640 let jbi = dbi + (lme * rbi as f64) as i64;
8641 let (mut bp, mut bj) = (1u8, c0);
8642 if c1 < bj { bp = 2; bj = c1; }
8643 if jbi < bj { bp = 3; bj = jbi; }
8644 cand[part] = (bp, m0, m1);
8645 jsum += bj;
8646 }
8647 let jsplit = jsum + (lme * 4.0) as i64;
8649 if jsplit < best {
8650 best = jsplit;
8651 bsplit = Some((mvmode, cand));
8652 }
8653 }
8654 if bsplit.is_some() {
8655 bstats::bump(&bstats::SPLIT);
8656 }
8657 }
8658 let bspec = if let Some((mvmode, parts2)) = bsplit {
8659 BInter { dir, l1, mv0, mv1, mvmode, parts2 }
8660 } else {
8661 BInter { dir, l1, mv0, mv1, mvmode: 0, parts2: [(0, (0, 0), (0, 0)); 2] }
8662 };
8663 let plan = fe.plan_inter_mb(refs, &sy, &su, &sv, mb_x, mb_y, 0, &[], Some(bspec));
8664 emit_mb_cabac_b(&mut fe, &mut cab, &mut cs, dir, bsplit, &plan, mb_x, mb_y);
8665 {
8666 let tt = if crate::bitacct::enabled() { cab.pos() } else { 0 };
8667 cab.encode_terminate(mb_idx + 1 == total);
8668 if crate::bitacct::enabled() {
8669 crate::bitacct::add(crate::bitacct::B::Terminate, cab.pos() - tt);
8670 }
8671 }
8672 }
8673 }
8674
8675 while !w.is_byte_aligned() {
8676 w.write_bit(true);
8677 }
8678 for b in cab.into_bytes() {
8679 w.write_bits(b as u32, 8);
8680 }
8681 }
8683
8684pub fn encode_all_skip_b_cabac(w: &mut BitWriter, cfg: &EncoderConfig, qp: u8, n: usize) {
8688 let mut cab = CabacEncoder::new(qp as i32, cfg.cabac_init_idc, false);
8689 for i in 0..n {
8690 cab.encode_decision(24, 1);
8693 cab.encode_terminate(i + 1 == n);
8694 }
8695 while !w.is_byte_aligned() {
8696 w.write_bit(true);
8697 }
8698 for b in cab.into_bytes() {
8699 w.write_bits(b as u32, 8);
8700 }
8701}