use std::collections::VecDeque;
use crate::{Error, Result};
use super::frameenc::FrameEncoder;
struct RateCtl {
target_per_frame: f64, q: f64, }
impl RateCtl {
fn qindex(&self) -> u32 {
self.q.round().clamp(4.0, 220.0) as u32
}
fn update(&mut self, actual_bits: f64) {
let err = (actual_bits - self.target_per_frame) / self.target_per_frame;
self.q = (self.q + 10.0 * err.clamp(-1.0, 4.0)).clamp(4.0, 220.0);
}
}
fn pack_superframe(frames: &[Vec<u8>]) -> Vec<u8> {
let max = frames.iter().map(|f| f.len()).max().unwrap_or(0);
let mag: usize = if max < (1 << 8) {
1
} else if max < (1 << 16) {
2
} else if max < (1 << 24) {
3
} else {
4
};
let marker = 0xc0u8 | (((mag - 1) as u8) << 3) | ((frames.len() - 1) as u8);
let mut out = Vec::new();
for f in frames {
out.extend_from_slice(f);
}
out.push(marker);
for f in frames {
let sz = f.len();
for b in 0..mag {
out.push(((sz >> (8 * b)) & 0xff) as u8);
}
}
out.push(marker);
out
}
#[allow(clippy::too_many_arguments)]
fn tf_sad(
anchor: &[u16],
neigh: &[u16],
w: usize,
h: usize,
bx: usize,
by: usize,
bs: usize,
mvr: i32,
mvc: i32,
) -> u64 {
let mut sad = 0u64;
for dy in 0..bs {
let ay = by + dy;
if ay >= h {
break;
}
for dx in 0..bs {
let ax = bx + dx;
if ax >= w {
break;
}
let sx = (ax as i32 + mvc).clamp(0, w as i32 - 1) as usize;
let sy = (ay as i32 + mvr).clamp(0, h as i32 - 1) as usize;
let d = anchor[ay * w + ax] as i64 - neigh[sy * w + sx] as i64;
sad += d.unsigned_abs();
}
}
sad
}
#[allow(clippy::too_many_arguments)]
fn tf_search(
anchor: &[u16],
neigh: &[u16],
w: usize,
h: usize,
bx: usize,
by: usize,
bs: usize,
range: i32,
) -> (i32, i32) {
let mut best = (0i32, 0i32);
let mut best_sad = tf_sad(anchor, neigh, w, h, bx, by, bs, 0, 0);
for mvr in -range..=range {
for mvc in -range..=range {
let sad = tf_sad(anchor, neigh, w, h, bx, by, bs, mvr, mvc);
let shorter = mvr.abs() + mvc.abs() < best.0.abs() + best.1.abs();
if sad < best_sad || (sad == best_sad && shorter) {
best_sad = sad;
best = (mvr, mvc);
}
}
}
best
}
fn group_motion(frames: &[([Vec<u16>; 3], u32, u32)]) -> f64 {
if frames.len() < 2 {
return 0.0;
}
let (mut tot, mut cnt) = (0u64, 0u64);
for w in frames.windows(2) {
let (a, b) = (&w[0].0[0], &w[1].0[0]);
let n = a.len().min(b.len());
let mut i = 0;
while i < n {
tot += (a[i] as i64 - b[i] as i64).unsigned_abs();
cnt += 1;
i += 37; }
}
tot as f64 / cnt.max(1) as f64
}
fn temporal_filter(
anchor: &[Vec<u16>; 3],
neighbors: &[&[Vec<u16>; 3]],
cw: usize,
ch: usize,
strength: f64,
) -> [Vec<u16>; 3] {
const BS: usize = 16;
const RANGE: i32 = 8;
const MAX_W: f64 = 16.0;
let (cwc, chc) = (cw / 2, ch / 2);
let sigma = (strength * 6.0).max(1.0);
let two_sig2 = 2.0 * sigma * sigma;
let weight = |mc: u16, a: u16| -> f64 {
let d = mc as f64 - a as f64;
MAX_W * (-(d * d) / two_sig2).exp()
};
let mut out = [anchor[0].clone(), anchor[1].clone(), anchor[2].clone()];
let mut by = 0;
while by < ch {
let mut bx = 0;
while bx < cw {
let mut acc = [0f64; BS * BS];
let mut wsum = [0f64; BS * BS];
let mut cacc = [[0f64; 64]; 2];
let mut cwsum = [[0f64; 64]; 2];
for dy in 0..BS {
for dx in 0..BS {
let (ax, ay) = (bx + dx, by + dy);
if ax < cw && ay < ch {
acc[dy * BS + dx] = anchor[0][ay * cw + ax] as f64 * MAX_W;
wsum[dy * BS + dx] = MAX_W;
}
}
}
let (cbx, cby) = (bx / 2, by / 2);
for dy in 0..8 {
for dx in 0..8 {
let (ax, ay) = (cbx + dx, cby + dy);
if ax < cwc && ay < chc {
for p in 0..2 {
cacc[p][dy * 8 + dx] = anchor[p + 1][ay * cwc + ax] as f64 * MAX_W;
cwsum[p][dy * 8 + dx] = MAX_W;
}
}
}
}
for nb in neighbors {
let (mvr, mvc) = tf_search(&anchor[0], &nb[0], cw, ch, bx, by, BS, RANGE);
for dy in 0..BS {
for dx in 0..BS {
let (ax, ay) = (bx + dx, by + dy);
if ax >= cw || ay >= ch {
continue;
}
let sx = (ax as i32 + mvc).clamp(0, cw as i32 - 1) as usize;
let sy = (ay as i32 + mvr).clamp(0, ch as i32 - 1) as usize;
let mc = nb[0][sy * cw + sx];
let wgt = weight(mc, anchor[0][ay * cw + ax]);
acc[dy * BS + dx] += mc as f64 * wgt;
wsum[dy * BS + dx] += wgt;
}
}
let (cmr, cmc) = (mvr / 2, mvc / 2);
for dy in 0..8 {
for dx in 0..8 {
let (ax, ay) = (cbx + dx, cby + dy);
if ax >= cwc || ay >= chc {
continue;
}
let sx = (ax as i32 + cmc).clamp(0, cwc as i32 - 1) as usize;
let sy = (ay as i32 + cmr).clamp(0, chc as i32 - 1) as usize;
for p in 0..2 {
let mc = nb[p + 1][sy * cwc + sx];
let wgt = weight(mc, anchor[p + 1][ay * cwc + ax]);
cacc[p][dy * 8 + dx] += mc as f64 * wgt;
cwsum[p][dy * 8 + dx] += wgt;
}
}
}
}
for dy in 0..BS {
for dx in 0..BS {
let (ax, ay) = (bx + dx, by + dy);
if ax < cw && ay < ch {
out[0][ay * cw + ax] =
(acc[dy * BS + dx] / wsum[dy * BS + dx]).round() as u16;
}
}
}
for dy in 0..8 {
for dx in 0..8 {
let (ax, ay) = (cbx + dx, cby + dy);
if ax < cwc && ay < chc {
for p in 0..2 {
out[p + 1][ay * cwc + ax] =
(cacc[p][dy * 8 + dx] / cwsum[p][dy * 8 + dx]).round() as u16;
}
}
}
}
bx += BS;
}
by += BS;
}
out
}
#[derive(Debug, Clone)]
pub struct EncodedPacket {
pub data: Vec<u8>,
pub pts: Option<i64>,
pub keyframe: bool,
}
fn packet(data: Vec<u8>) -> EncodedPacket {
let keyframe = data.first().is_some_and(|&b| b & 0x0c == 0);
EncodedPacket {
data,
pts: None,
keyframe,
}
}
#[derive(Debug, Clone, Default)]
pub struct Vp9EncoderConfig {
pub qindex: Option<u32>,
pub crf: Option<u32>,
pub q: Option<u32>,
pub bitrate_bps: Option<f64>,
pub fps: Option<f64>,
pub lag: Option<usize>,
pub arnr_strength: Option<f64>,
pub dispatch_budget_ms: Option<f64>,
pub two_pass: bool,
pub speed: Option<u32>,
}
pub struct Vp9Encoder {
qindex: u32,
packets: VecDeque<EncodedPacket>,
eof: bool,
reference: Option<([Vec<u16>; 3], u32, u32)>,
golden: Option<([Vec<u16>; 3], u32, u32)>,
rc: Option<RateCtl>,
lag: usize,
twopass: bool,
tf_strength: f64,
lookahead: VecDeque<([Vec<u16>; 3], u32, u32)>,
slots: [Option<[Vec<u16>; 3]>; 3],
golden_slot: usize,
arf_slot: usize,
group_dims: Option<(u32, u32)>,
chain: bool,
companion: Option<Box<crate::Vp9Decoder>>,
chain_prev_p: bool,
last_recon: Option<[Vec<u16>; 3]>,
recon_check_n: usize,
recon_check_dec: Option<Box<crate::Vp9Decoder>>,
group_chain: bool,
pass2_chaining: bool,
lag_motion_thresh: f64,
arf_qscale: f64,
speed: u32,
dispatch_budget_us: Option<u64>,
dispatch_q_state: f64,
}
impl Default for Vp9Encoder {
fn default() -> Vp9Encoder {
Vp9Encoder {
qindex: 64, packets: VecDeque::new(),
eof: false,
reference: None,
golden: None,
rc: None,
lag: 0,
twopass: false,
tf_strength: 3.0, lookahead: VecDeque::new(),
slots: [None, None, None],
golden_slot: 1,
arf_slot: 2,
group_dims: None,
speed: 3,
chain: std::env::var("VP9_NO_CHAIN").is_err(), companion: None,
chain_prev_p: false,
last_recon: None,
recon_check_n: 0,
recon_check_dec: None,
group_chain: false,
pass2_chaining: false,
lag_motion_thresh: std::env::var("VP9_LAG_MOTION_THRESH")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(8.0),
arf_qscale: std::env::var("VP9_ARF_QSCALE")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(0.5),
dispatch_budget_us: std::env::var("VP9_DISPATCH_BUDGET")
.ok()
.and_then(|v| v.parse::<f64>().ok())
.filter(|ms| *ms > 0.0)
.map(|ms| (ms * 1000.0) as u64),
dispatch_q_state: 0.5,
}
}
}
fn to_coded(plane: &[u8], stride: usize, dw: usize, dh: usize, cw: usize, ch: usize) -> Vec<u16> {
let mut out = vec![0u16; cw * ch];
for y in 0..ch {
let sy = y.min(dh - 1);
for x in 0..cw {
let sx = x.min(dw - 1);
out[y * cw + x] = plane[sy * stride + sx] as u16;
}
}
out
}
impl Vp9Encoder {
pub fn configure(&mut self, cfg: &Vp9EncoderConfig) -> Result<()> {
if let Some(qp) = cfg.qindex {
self.qindex = qp.min(255);
} else if let Some(crf) = cfg.crf {
self.qindex = (crf * 4).clamp(1, 255);
} else if let Some(q) = cfg.q {
self.qindex = q.min(255);
}
if let Some(bps) = cfg.bitrate_bps {
let fps = cfg.fps.filter(|&f| f > 0.0).unwrap_or(30.0);
self.rc = Some(RateCtl {
target_per_frame: bps / fps,
q: self.qindex as f64,
});
}
if let Some(lag) = cfg.lag {
self.lag = lag.min(32);
}
if let Some(s) = cfg.arnr_strength {
self.tf_strength = s.max(0.0);
}
if let Some(ms) = cfg.dispatch_budget_ms {
self.dispatch_budget_us = if ms > 0.0 {
Some((ms * 1000.0) as u64)
} else {
None
};
}
if cfg.two_pass {
self.twopass = true;
}
if let Some(sp) = cfg.speed {
self.speed = sp.min(6);
}
Ok(())
}
pub fn push_frame(
&mut self,
planes: [&[u8]; 3],
strides: [usize; 3],
width: u32,
height: u32,
) -> Result<()> {
let (w, h) = (width as usize, height as usize);
let mi_cols = (w + 7) >> 3;
let mi_rows = (h + 7) >> 3;
let (cw, ch) = (mi_cols * 8, mi_rows * 8);
let (cwc, chc) = (cw / 2, ch / 2);
let (dwc, dhc) = (w.div_ceil(2), h.div_ceil(2));
let y = to_coded(planes[0], strides[0], w, h, cw, ch);
let u = to_coded(planes[1], strides[1], dwc, dhc, cwc, chc);
let v = to_coded(planes[2], strides[2], dwc, dhc, cwc, chc);
if self.twopass {
self.lookahead.push_back(([y, u, v], width, height));
return Ok(());
}
if self.lag > 1 {
self.lookahead.push_back(([y, u, v], width, height));
if self.lookahead.len() >= self.lag {
let group: Vec<_> = self.lookahead.drain(..).collect();
self.code_altref_group(group);
}
return Ok(());
}
let bytes = self.code_frame([y, u, v], width, height, None);
self.packets.push_back(packet(bytes));
Ok(())
}
pub fn next_packet(&mut self) -> Result<EncodedPacket> {
if let Some(p) = self.packets.pop_front() {
Ok(p)
} else if self.eof {
Err(Error::Eof)
} else {
Err(Error::Again)
}
}
pub fn flush(&mut self) {
if !self.lookahead.is_empty() {
let group: Vec<_> = self.lookahead.drain(..).collect();
if self.twopass {
self.two_pass_encode(group);
} else {
self.code_altref_group(group);
}
}
self.eof = true;
}
}
impl Vp9Encoder {
fn next_qindex(&self) -> u32 {
match &self.rc {
Some(rc) => rc.qindex(),
None => self.qindex,
}
}
fn budget_apply(&self, enc: &mut FrameEncoder) {
if self.dispatch_budget_us.is_some() {
enc.set_dispatch_q(self.dispatch_q_state);
}
}
fn budget_update(&mut self, enc: &FrameEncoder, is_key: bool) {
if let Some(budget) = self.dispatch_budget_us {
if !is_key {
let err = (enc.decision_us() as f64 - budget as f64) / budget as f64;
const K: f64 = 0.4; let prev = self.dispatch_q_state;
self.dispatch_q_state = (prev + K * err).clamp(0.0, 0.95);
if std::env::var("VP9_BUDGET_DEBUG").is_ok() {
eprintln!(
"BUDGET decision={:.1}ms target={:.1}ms q {:.3}->{:.3}",
enc.decision_us() as f64 / 1000.0,
budget as f64 / 1000.0,
prev,
self.dispatch_q_state
);
}
}
}
}
fn code_frame(
&mut self,
coded: [Vec<u16>; 3],
w: u32,
h: u32,
altref: Option<&[Vec<u16>; 3]>,
) -> Vec<u8> {
let q = self.next_qindex();
let bytes = self.code_frame_q(coded, w, h, altref, q);
if let Some(rc) = &mut self.rc {
rc.update(bytes.len() as f64 * 8.0);
}
bytes
}
fn code_frame_q(
&mut self,
coded: [Vec<u16>; 3],
w: u32,
h: u32,
altref: Option<&[Vec<u16>; 3]>,
qindex: u32,
) -> Vec<u8> {
let reference = match self.reference.take() {
Some((planes, rw, rh)) if rw == w && rh == h => Some(planes),
_ => None,
};
let is_key = reference.is_none();
let mut enc = FrameEncoder::new(w, h, qindex, coded, reference);
enc.set_speed(self.speed);
self.budget_apply(&mut enc);
let chaining = self.chain && ((self.lag == 0 && !self.twopass) || self.pass2_chaining);
if chaining && !is_key {
if let Some(c) = &self.companion {
let mvs = if self.chain_prev_p {
c.prev_mvs.clone()
} else {
None
};
enc.set_chain(c.frame_contexts[0].clone(), mvs);
}
}
if !is_key {
if let Some((g, gw, gh)) = &self.golden {
if *gw == w && *gh == h {
enc.set_golden(g.clone());
}
}
if let Some(a) = altref {
enc.set_altref(a.clone());
}
}
let bytes = enc.encode_frame();
self.budget_update(&enc, is_key);
if chaining {
let comp = self
.companion
.get_or_insert_with(|| Box::new(crate::Vp9Decoder::default()));
let ok = comp
.push(&bytes, None)
.and_then(|_| comp.next_frame())
.is_ok();
if ok {
self.chain_prev_p = !is_key;
} else {
self.chain = false;
self.companion = None;
self.chain_prev_p = false;
}
}
let recon = enc.recon_owned();
if std::env::var("VP9_RECON_CHECK").is_ok() {
self.recon_check(&bytes, &recon, w, h);
}
if is_key {
self.golden = Some((recon.clone(), w, h));
}
self.reference = Some((recon, w, h));
bytes
}
fn recon_verify(&mut self, bytes: &[u8], expect: Option<&[Vec<u16>; 3]>, w: u32, h: u32) {
if std::env::var("VP9_RECON_CHECK").is_err() {
return;
}
match expect {
Some(r) => self.recon_check(bytes, r, w, h),
None => {
let dec = self
.recon_check_dec
.get_or_insert_with(|| Box::new(crate::Vp9Decoder::default()));
let _ = dec.push(bytes, None);
while dec.next_frame().is_ok() {}
eprintln!("RECON_CHECK frame {}: hidden (fed, not compared)", self.recon_check_n);
self.recon_check_n += 1;
}
}
}
fn recon_check(&mut self, bytes: &[u8], recon: &[Vec<u16>; 3], w: u32, h: u32) {
let dec = self
.recon_check_dec
.get_or_insert_with(|| Box::new(crate::Vp9Decoder::default()));
let got = dec.push(bytes, None).ok().and_then(|_| dec.next_frame().ok());
let Some(vf) = got else {
eprintln!("RECON_CHECK frame {}: DECODE FAILED", self.recon_check_n);
self.recon_check_n += 1;
return;
};
let mut bad = 0usize;
let mut first = None;
for p in 0..3 {
let (pw, ph) = if p == 0 {
(w as usize, h as usize)
} else {
((w as usize).div_ceil(2), (h as usize).div_ceil(2))
};
let cw = recon[p].len() / ph.max(1);
for y in 0..ph {
for x in 0..pw {
let e = recon[p].get(y * cw + x).copied().unwrap_or(0) as u8;
let d = vf.planes[p].get(y * vf.strides[p] + x).copied().unwrap_or(0);
if e != d {
bad += 1;
if first.is_none() {
first = Some((p, x, y, e, d));
}
}
}
}
}
if bad > 0 {
let (p, x, y, e, d) = first.unwrap();
eprintln!(
"RECON_CHECK frame {}: {} samples DIVERGE (first plane{} ({},{}) enc={} dec={})",
self.recon_check_n, bad, p, x, y, e, d
);
if std::env::var("VP9_RECON_CHECK").ok().as_deref() == Some("2") {
let (pw, ph) = (w as usize, h as usize);
let cw = recon[0].len() / ph.max(1);
let (sbx, sby) = (pw.div_ceil(64), ph.div_ceil(64));
let mut map = vec![0u32; sbx * sby];
for yy in 0..ph {
for xx in 0..pw {
let e = recon[0].get(yy * cw + xx).copied().unwrap_or(0) as u8;
let dv = vf.planes[0].get(yy * vf.strides[0] + xx).copied().unwrap_or(0);
if e != dv {
map[(yy / 64) * sbx + xx / 64] += 1;
}
}
}
eprintln!(" SB map ({}x{} superblocks, '.'=clean, #=count/410):", sbx, sby);
for r in 0..sby {
let row: String = (0..sbx)
.map(|c| match map[r * sbx + c] {
0 => '.',
n if n < 410 => '1',
n if n < 1640 => '2',
n if n < 3277 => '3',
_ => '#',
})
.collect();
eprintln!(" {r:2} {row}");
}
}
} else {
eprintln!("RECON_CHECK frame {}: ok", self.recon_check_n);
}
self.recon_check_n += 1;
}
fn two_pass_encode(&mut self, frames: Vec<([Vec<u16>; 3], u32, u32)>) {
let n = frames.len();
if n == 0 {
return;
}
let target_per_frame = self.rc.as_ref().map(|rc| rc.target_per_frame);
let Some(tpf) = target_per_frame else {
for (c, w, h) in frames {
let b = self.code_frame_q(c, w, h, None, self.qindex);
self.packets.push_back(packet(b));
}
return;
};
const Q_PROBE: u32 = 128;
const Q_PER_2X: f64 = 100.0; const KEY_BONUS: f64 = 16.0;
self.reference = None;
self.golden = None;
let mut probe_bits = 0.0f64;
for (c, w, h) in &frames {
let b = self.code_frame_q(c.clone(), *w, *h, None, Q_PROBE);
probe_bits += b.len() as f64 * 8.0;
}
let target_total = tpf * n as f64;
let ratio = (probe_bits / target_total).clamp(1.0 / 32.0, 32.0);
let q2 = (Q_PROBE as f64 + Q_PER_2X * ratio.log2()).clamp(4.0, 220.0);
let q_key = (q2 - KEY_BONUS).clamp(4.0, 220.0).round() as u32;
let q_inter = q2.round() as u32;
if std::env::var("VP9_2PASS_DBG").is_ok() {
eprintln!(
"2pass: probe_bits={probe_bits:.0} target_total={target_total:.0} ratio={ratio:.3} q2={q2:.1} q_key={q_key} q_inter={q_inter}"
);
}
self.reference = None;
self.golden = None;
self.companion = None;
self.chain_prev_p = false;
self.pass2_chaining = self.chain;
for (i, (c, w, h)) in frames.into_iter().enumerate() {
let q = if i == 0 { q_key } else { q_inter };
let b = self.code_frame_q(c, w, h, None, q);
self.packets.push_back(packet(b));
}
self.pass2_chaining = false;
}
fn chain_args(
&self,
) -> Option<(
crate::decode::FrameContext,
Option<std::sync::Arc<Vec<crate::mv::MvRef>>>,
)> {
let c = self.companion.as_ref()?;
let temporal_ok = !c.last_intra_only && c.last_show_frame && !c.last_frame_key;
let mvs = if temporal_ok { c.prev_mvs.clone() } else { None };
Some((c.frame_contexts[0].clone(), mvs))
}
fn companion_feed(&mut self, bytes: &[u8]) {
if !self.group_chain {
return;
}
let comp = self
.companion
.get_or_insert_with(|| Box::new(crate::Vp9Decoder::default()));
if comp.push(bytes, None).is_err() {
self.group_chain = false;
self.chain = false;
self.companion = None;
return;
}
loop {
match comp.next_frame() {
Ok(_) => {}
Err(Error::Again) | Err(Error::Eof) => break,
Err(_) => {
self.group_chain = false;
self.chain = false;
self.companion = None;
return;
}
}
}
}
fn code_altref_group(&mut self, frames: Vec<([Vec<u16>; 3], u32, u32)>) {
let n = frames.len();
if n == 0 {
return;
}
let (w, h) = (frames[0].1, frames[0].2);
let need_key = self.slots[0].is_none() || self.group_dims != Some((w, h));
self.group_dims = Some((w, h));
self.group_chain = self.chain && !self.twopass;
let plain = n <= 2 || group_motion(&frames) < self.lag_motion_thresh;
if plain {
for (i, (c, fw, fh)) in frames.into_iter().enumerate() {
let b = if need_key && i == 0 {
self.code_key_slotted(c, fw, fh)
} else {
self.code_p_slotted(c, fw, fh, false)
};
self.companion_feed(&b);
let r = self.last_recon.take();
self.recon_verify(&b, r.as_ref(), fw, fh);
self.packets.push_back(packet(b));
}
return;
}
let mut i0 = 0;
if need_key {
self.golden_slot = 1;
self.arf_slot = 2;
let (c0, kw, kh) = frames[0].clone();
let kb = self.code_key_slotted(c0, kw, kh);
self.companion_feed(&kb);
let r = self.last_recon.clone();
self.recon_verify(&kb, r.as_ref(), kw, kh);
self.packets.push_back(packet(kb));
i0 = 1;
}
let (aw, ah) = (frames[n - 1].1, frames[n - 1].2);
let carf = if self.tf_strength > 0.0 {
let window = (n - 1).saturating_sub(4).max(i0);
let neighbors: Vec<&[Vec<u16>; 3]> =
frames[window..n - 1].iter().map(|f| &f.0).collect();
let cw = (aw as usize).div_ceil(8) * 8;
let ch = (ah as usize).div_ceil(8) * 8;
temporal_filter(&frames[n - 1].0, &neighbors, cw, ch, self.tf_strength)
} else {
frames[n - 1].0.clone()
};
let ab = self.code_arf_slotted(carf, aw, ah);
self.companion_feed(&ab);
let arf_recon = self.last_recon.take();
self.recon_verify(&ab, None, aw, ah);
let (c1, w1, h1) = frames[i0].clone();
let pb1 = self.code_p_slotted(c1, w1, h1, true);
self.companion_feed(&pb1);
let r = self.last_recon.take();
self.recon_verify(&pb1, r.as_ref(), w1, h1);
self.packets.push_back(packet(pack_superframe(&[ab, pb1])));
for f in frames.iter().take(n - 1).skip(i0 + 1) {
let (ci, iw, ih) = f.clone();
let pb = self.code_p_slotted(ci, iw, ih, true);
self.companion_feed(&pb);
let r = self.last_recon.take();
self.recon_verify(&pb, r.as_ref(), iw, ih);
self.packets.push_back(packet(pb));
}
let se = FrameEncoder::encode_show_existing_frame(self.arf_slot as u32);
self.companion_feed(&se);
self.recon_verify(&se, arf_recon.as_ref(), aw, ah);
self.packets.push_back(packet(se));
std::mem::swap(&mut self.golden_slot, &mut self.arf_slot);
}
fn code_key_slotted(&mut self, coded: [Vec<u16>; 3], w: u32, h: u32) -> Vec<u8> {
let q = self.next_qindex();
let mut enc = FrameEncoder::new(w, h, q, coded, None);
enc.set_speed(self.speed);
self.budget_apply(&mut enc);
let bytes = enc.encode_frame();
self.budget_update(&enc, true);
let recon = enc.recon_owned();
self.last_recon = Some(recon.clone());
self.slots = [Some(recon.clone()), Some(recon.clone()), Some(recon)];
bytes
}
fn code_arf_slotted(&mut self, coded: [Vec<u16>; 3], w: u32, h: u32) -> Vec<u8> {
let base = self.next_qindex() as f64;
let qs = if std::env::var("VP9_ARF_QSCALE_FLAT").is_ok() {
self.arf_qscale
} else {
const Q_MAX: f64 = 220.0; let headroom = ((Q_MAX - base) / Q_MAX).clamp(0.0, 1.0);
1.0 - (1.0 - self.arf_qscale) * headroom
};
let q = ((base * qs).round() as u32).clamp(4, 255);
let idx = [0, self.golden_slot, self.arf_slot];
let mut enc = FrameEncoder::new(w, h, q, coded, self.slots[0].clone());
enc.set_speed(self.speed);
self.budget_apply(&mut enc);
enc.set_compound(self.speed <= 3);
if self.group_chain {
if let Some((fc, mvs)) = self.chain_args() {
enc.set_chain(fc, mvs);
}
}
enc.set_golden(self.slots[self.golden_slot].clone().unwrap());
enc.set_ref_frame_idx(idx);
enc.set_hidden_altref(self.arf_slot);
let bytes = enc.encode_frame();
self.budget_update(&enc, false);
let arf_recon = enc.recon_owned();
self.last_recon = Some(arf_recon.clone());
self.slots[self.arf_slot] = Some(arf_recon);
bytes
}
fn code_p_slotted(
&mut self,
coded: [Vec<u16>; 3],
w: u32,
h: u32,
with_altref: bool,
) -> Vec<u8> {
let q = self.next_qindex();
let idx = [0, self.golden_slot, self.arf_slot];
let mut enc = FrameEncoder::new(w, h, q, coded, self.slots[0].clone());
enc.set_speed(self.speed);
self.budget_apply(&mut enc);
enc.set_compound(self.speed <= 3);
if self.group_chain && self.slots[0].is_some() {
if let Some((fc, mvs)) = self.chain_args() {
enc.set_chain(fc, mvs);
}
}
if self.slots[0].is_some() {
enc.set_golden(self.slots[self.golden_slot].clone().unwrap());
if with_altref {
if let Some(a) = &self.slots[self.arf_slot] {
enc.set_altref(a.clone());
}
}
enc.set_ref_frame_idx(idx);
enc.set_refresh_frame_flags(1); }
let bytes = enc.encode_frame();
self.budget_update(&enc, self.slots[0].is_none());
let recon = enc.recon_owned();
self.last_recon = Some(recon.clone());
self.slots[0] = Some(recon);
bytes
}
}