use crate::api::color::ChromaSampling;
use crate::api::ContextInner;
use crate::encoder::TEMPORAL_DELIMITER;
use crate::quantize::{ac_q, dc_q, select_ac_qi, select_dc_qi};
use crate::util::{clamp, ILog, Pixel};
use std::cmp;
pub const FRAME_NSUBTYPES: usize = 4;
pub const FRAME_SUBTYPE_I: usize = 0;
pub const FRAME_SUBTYPE_P: usize = 1;
#[allow(unused)]
pub const FRAME_SUBTYPE_B0: usize = 2;
#[allow(unused)]
pub const FRAME_SUBTYPE_B1: usize = 3;
pub const FRAME_SUBTYPE_SEF: usize = 4;
const PASS_SINGLE: i32 = 0;
const PASS_1: i32 = 1;
const PASS_2: i32 = 2;
const PASS_2_PLUS_1: i32 = 3;
const TWOPASS_MAGIC: i32 = 0x50324156;
const TWOPASS_VERSION: i32 = 1;
pub(crate) const TWOPASS_HEADER_SZ: usize = 16 + FRAME_NSUBTYPES * (4 + 1 + 8);
const TWOPASS_PACKET_SZ: usize = 8;
const SEF_BITS: i64 = 24;
pub(crate) const QSCALE: i32 = 3;
const INTER_DELAY_TARGET_MIN: i32 = 10;
const MQP_Q12: &[i32; FRAME_NSUBTYPES] = &[
(1.0 * (1 << 12) as f64) as i32,
(1.0 * (1 << 12) as f64) as i32,
(1.0 * (1 << 12) as f64) as i32,
(1.0 * (1 << 12) as f64) as i32,
];
const DQP_Q57: &[i64; FRAME_NSUBTYPES] = &[
(-(33_810_170.0 / 86_043_287.0) * (1i64 << 57) as f64) as i64,
(0.0 * (1i64 << 57) as f64) as i64,
((33_810_170.0 / 86_043_287.0) * (1i64 << 57) as f64) as i64,
(2.0 * (33_810_170.0 / 86_043_287.0) * (1i64 << 57) as f64) as i64,
];
#[rustfmt::skip]
const Q_MODEL_ADD: [i64; 4] = [
-0x24_4FE7_ECB3_DD90,
-0x37_41DA_38AD_0924,
-0x70_83BD_A626_311C,
0,
];
#[rustfmt::skip]
const Q_MODEL_MUL: [i64; 4] = [
0x8A0_50DD,
0x887_7666,
0x8D4_A712,
0,
];
pub(crate) const fn q57(v: i32) -> i64 {
(v as i64) << 57
}
#[rustfmt::skip]
const ATANH_LOG2: &[i64; 32] = &[
0x32B8_0347_3F7A_D0F4, 0x2F2A_71BD_4E25_E916, 0x2E68_B244_BB93_BA06,
0x2E39_FB91_98CE_62E4, 0x2E2E_683F_6856_5C8F, 0x2E2B_850B_E207_7FC1,
0x2E2A_CC58_FE7B_78DB, 0x2E2A_9E2D_E52F_D5F2, 0x2E2A_92A3_38D5_3EEC,
0x2E2A_8FC0_8F5E_19B6, 0x2E2A_8F07_E51A_485E, 0x2E2A_8ED9_BA8A_F388,
0x2E2A_8ECE_2FE7_384A, 0x2E2A_8ECB_4D3E_4B1A, 0x2E2A_8ECA_9494_0FE8,
0x2E2A_8ECA_6669_811D, 0x2E2A_8ECA_5ADE_DD6A, 0x2E2A_8ECA_57FC_347E,
0x2E2A_8ECA_5743_8A43, 0x2E2A_8ECA_5715_5FB4, 0x2E2A_8ECA_5709_D510,
0x2E2A_8ECA_5706_F267, 0x2E2A_8ECA_5706_39BD, 0x2E2A_8ECA_5706_0B92,
0x2E2A_8ECA_5706_0008, 0x2E2A_8ECA_5705_FD25, 0x2E2A_8ECA_5705_FC6C,
0x2E2A_8ECA_5705_FC3E, 0x2E2A_8ECA_5705_FC33, 0x2E2A_8ECA_5705_FC30,
0x2E2A_8ECA_5705_FC2F, 0x2E2A_8ECA_5705_FC2F
];
pub(crate) fn bexp64(logq57: i64) -> i64 {
let ipart = (logq57 >> 57) as i32;
if ipart < 0 {
return 0;
}
if ipart >= 63 {
return 0x7FFF_FFFF_FFFF_FFFF;
}
let mut z = logq57 - q57(ipart);
let mut w: i64;
if z != 0 {
z <<= 5;
w = 0x26A3_D0E4_01DD_846D;
let mut i: i64 = 0;
loop {
let mask = -((z < 0) as i64);
w += ((w >> (i + 1)) + mask) ^ mask;
z -= (ATANH_LOG2[i as usize] + mask) ^ mask;
if i >= 3 {
break;
}
z *= 2;
i += 1;
}
loop {
let mask = -((z < 0) as i64);
w += ((w >> (i + 1)) + mask) ^ mask;
z -= (ATANH_LOG2[i as usize] + mask) ^ mask;
if i >= 12 {
break;
}
z *= 2;
i += 1;
}
while i < 32 {
let mask = -((z < 0) as i64);
w += ((w >> (i + 1)) + mask) ^ mask;
z = (z - ((ATANH_LOG2[i as usize] + mask) ^ mask)) * 2;
i += 1;
}
let mut wlo: i32 = 0;
if ipart > 30 {
loop {
let mask = -((z < 0) as i64);
wlo += (((w >> i) + mask) ^ mask) as i32;
z -= (ATANH_LOG2[31] + mask) ^ mask;
if i >= 39 {
break;
}
z *= 2;
i += 1;
}
while i < 61 {
let mask = -((z < 0) as i64);
wlo += (((w >> i) + mask) ^ mask) as i32;
z = (z - ((ATANH_LOG2[31] + mask) ^ mask)) * 2;
i += 1;
}
}
w = (w << 1) + (wlo as i64);
} else {
w = 1i64 << 62;
}
if ipart < 62 {
w = ((w >> (61 - ipart)) + 1) >> 1;
}
w
}
fn blog64(w: i64) -> i64 {
let mut w = w;
if w <= 0 {
return -1;
}
let ipart = w.ilog() as i32 - 1;
if ipart > 61 {
w >>= ipart - 61;
} else {
w <<= 61 - ipart;
}
let mut z: i64 = 0;
if (w & (w - 1)) != 0 {
let mut x = w + (1i64 << 61);
let mut y = w - (1i64 << 61);
for i in 0..4 {
let mask = -((y < 0) as i64);
z += ((ATANH_LOG2[i as usize] >> i) + mask) ^ mask;
let u = x >> (i + 1);
x -= ((y >> (i + 1)) + mask) ^ mask;
y -= (u + mask) ^ mask;
}
for i in 3..13 {
let mask = -((y < 0) as i64);
z += ((ATANH_LOG2[i as usize] >> i) + mask) ^ mask;
let u = x >> (i + 1);
x -= ((y >> (i + 1)) + mask) ^ mask;
y -= (u + mask) ^ mask;
}
for i in 12..32 {
let mask = -((y < 0) as i64);
z += ((ATANH_LOG2[i as usize] >> i) + mask) ^ mask;
let u = x >> (i + 1);
x -= ((y >> (i + 1)) + mask) ^ mask;
y -= (u + mask) ^ mask;
}
for i in 32..40 {
let mask = -((y < 0) as i64);
z += ((ATANH_LOG2[31] >> i) + mask) ^ mask;
let u = x >> (i + 1);
x -= ((y >> (i + 1)) + mask) ^ mask;
y -= (u + mask) ^ mask;
}
for i in 39..62 {
let mask = -((y < 0) as i64);
z += ((ATANH_LOG2[31] >> i) + mask) ^ mask;
let u = x >> (i + 1);
x -= ((y >> (i + 1)) + mask) ^ mask;
y -= (u + mask) ^ mask;
}
z = (z + 8) >> 4;
}
q57(ipart) + z
}
const fn q57_to_q24(v: i64) -> i32 {
(((v >> 32) + 1) >> 1) as i32
}
const fn q24_to_q57(v: i32) -> i64 {
(v as i64) << 33
}
fn bexp_q24(log_scale: i32) -> i64 {
if log_scale < 23 << 24 {
let ret = bexp64(((log_scale as i64) << 33) + q57(24));
if ret < (1i64 << 47) - 1 {
return ret;
}
}
(1i64 << 47) - 1
}
#[rustfmt::skip]
const ROUGH_TAN_LOOKUP: &[u16; 18] = &[
0, 358, 722, 1098, 1491, 1910,
2365, 2868, 3437, 4096, 4881, 5850,
7094, 8784, 11254, 15286, 23230, 46817
];
pub struct IIRBessel2 {
c: [i32; 2],
g: i32,
x: [i32; 2],
y: [i32; 2],
}
fn warp_alpha(alpha: i32) -> i32 {
let i = ((alpha * 36) >> 24).min(16);
let t0 = ROUGH_TAN_LOOKUP[i as usize];
let t1 = ROUGH_TAN_LOOKUP[i as usize + 1];
let d = alpha * 36 - (i << 24);
((((t0 as i64) << 32) + (((t1 - t0) << 8) as i64) * (d as i64)) >> 32) as i32
}
fn iir_bessel2_get_parameters(delay: i32) -> (i32, i32, i32) {
let alpha = (1 << 24) / delay;
let warp = warp_alpha(alpha).max(1) as i64;
let k1 = 3 * warp;
let k2 = k1 * warp;
let d = ((((1 << 12) + k1) << 12) + k2 + 256) >> 9;
let a = (k2 << 23) / d;
let ik2 = (1i64 << 48) / k2;
let b1 = 2 * a * (ik2 - (1i64 << 24));
let b2 = (1i64 << 56) - ((4 * a) << 24) - b1;
(
((b1 + (1i64 << 31)) >> 32) as i32,
((b2 + (1i64 << 31)) >> 32) as i32,
((a + 128) >> 8) as i32,
)
}
impl IIRBessel2 {
pub fn new(delay: i32, value: i32) -> IIRBessel2 {
let (c0, c1, g) = iir_bessel2_get_parameters(delay);
IIRBessel2 { c: [c0, c1], g, x: [value, value], y: [value, value] }
}
pub fn reinit(&mut self, delay: i32) {
let (c0, c1, g) = iir_bessel2_get_parameters(delay);
self.c[0] = c0;
self.c[1] = c1;
self.g = g;
}
pub fn update(&mut self, x: i32) -> i32 {
let c0 = self.c[0] as i64;
let c1 = self.c[1] as i64;
let g = self.g as i64;
let x0 = self.x[0] as i64;
let x1 = self.x[1] as i64;
let y0 = self.y[0] as i64;
let y1 = self.y[1] as i64;
let ya =
((((x as i64) + x0 * 2 + x1) * g + y0 * c0 + y1 * c1 + (1i64 << 23))
>> 24) as i32;
self.x[1] = self.x[0];
self.x[0] = x;
self.y[1] = self.y[0];
self.y[0] = ya;
ya
}
}
#[derive(Copy, Clone)]
struct RCFrameMetrics {
log_scale_q24: i32,
fti: usize,
show_frame: bool,
}
impl RCFrameMetrics {
const fn new() -> RCFrameMetrics {
RCFrameMetrics { log_scale_q24: 0, fti: 0, show_frame: false }
}
}
#[derive(Debug, Default, Clone)]
pub struct RCSummary {
pub(crate) ntus: i32,
nframes: [i32; FRAME_NSUBTYPES + 1],
exp: [u8; FRAME_NSUBTYPES],
scale_sum: [i64; FRAME_NSUBTYPES],
pub(crate) total: i32,
}
pub(crate) struct RCDeserialize {
pass2_buffer_pos: usize,
pass2_buffer_fill: usize,
pass2_buffer: [u8; TWOPASS_HEADER_SZ],
}
impl Default for RCDeserialize {
fn default() -> Self {
RCDeserialize {
pass2_buffer: [0; TWOPASS_HEADER_SZ],
pass2_buffer_pos: 0,
pass2_buffer_fill: 0,
}
}
}
impl RCDeserialize {
pub(crate) fn buffer_fill(
&mut self, buf: &[u8], consumed: usize, goal: usize,
) -> usize {
let mut consumed = consumed;
while self.pass2_buffer_fill < goal && consumed < buf.len() {
self.pass2_buffer[self.pass2_buffer_fill] = buf[consumed];
self.pass2_buffer_fill += 1;
consumed += 1;
}
consumed
}
fn unbuffer_val(&mut self, n: usize) -> i64 {
let mut bytes = n;
let mut ret = 0;
let mut shift = 0;
while bytes > 0 {
bytes -= 1;
ret |= (self.pass2_buffer[self.pass2_buffer_pos] as i64) << shift;
self.pass2_buffer_pos += 1;
shift += 8;
}
ret
}
fn parse_metrics(&mut self) -> Result<RCFrameMetrics, String> {
debug_assert!(self.pass2_buffer_fill >= TWOPASS_PACKET_SZ);
let ft_val = self.unbuffer_val(4);
let show_frame = (ft_val >> 31) != 0;
let fti = (ft_val & 0x7FFFFFFF) as usize;
if fti > FRAME_NSUBTYPES {
return Err("Invalid frame type".to_string());
}
let log_scale_q24 = self.unbuffer_val(4) as i32;
Ok(RCFrameMetrics { log_scale_q24, fti, show_frame })
}
pub(crate) fn parse_summary(&mut self) -> Result<RCSummary, String> {
if self.unbuffer_val(4) != TWOPASS_MAGIC as i64 {
return Err("Magic value mismatch".to_string());
}
if self.unbuffer_val(4) != TWOPASS_VERSION as i64 {
return Err("Version number mismatch".to_string());
}
let mut s =
RCSummary { ntus: self.unbuffer_val(4) as i32, ..Default::default() };
if s.ntus < 1 {
return Err("No TUs found in first pass summary".to_string());
}
let mut total: i32 = 0;
for nframes in s.nframes.iter_mut() {
let n = self.unbuffer_val(4) as i32;
if n < 0 {
return Err("Got negative frame count".to_string());
}
total = total
.checked_add(n)
.ok_or_else(|| "Frame count too large".to_string())?;
*nframes = n;
}
if s.ntus > total {
return Err("More TUs than frames".to_string());
}
s.total = total;
for exp in s.exp.iter_mut() {
*exp = self.unbuffer_val(1) as u8;
}
for scale_sum in s.scale_sum.iter_mut() {
*scale_sum = self.unbuffer_val(8);
if *scale_sum < 0 {
return Err("Got negative scale sum".to_string());
}
}
Ok(s)
}
}
pub struct RCState {
target_bitrate: i32,
reservoir_frame_delay: i32,
reservoir_frame_delay_is_set: bool,
maybe_ac_qi_max: Option<u8>,
ac_qi_min: u8,
drop_frames: bool,
cap_overflow: bool,
cap_underflow: bool,
pass1_log_base_q: i64,
twopass_state: i32,
log_npixels: i64,
bits_per_tu: i64,
reservoir_fullness: i64,
reservoir_target: i64,
reservoir_max: i64,
log_scale: [i64; FRAME_NSUBTYPES],
exp: [u8; FRAME_NSUBTYPES],
scalefilter: [IIRBessel2; FRAME_NSUBTYPES],
nframes: [i32; FRAME_NSUBTYPES + 1],
inter_delay: [i32; FRAME_NSUBTYPES - 1],
inter_delay_target: i32,
rate_bias: i64,
nencoded_frames: i64,
nsef_frames: i64,
pass1_buffer: [u8; TWOPASS_HEADER_SZ],
pub pass1_data_retrieved: bool,
pass1_summary_retrieved: bool,
pass2_data_ready: bool,
prev_metrics: RCFrameMetrics,
cur_metrics: RCFrameMetrics,
frame_metrics: Vec<RCFrameMetrics>,
nframe_metrics: usize,
frame_metrics_head: usize,
des: RCDeserialize,
ntus: i32,
ntus_total: i32,
ntus_left: i32,
nframes_total: [i32; FRAME_NSUBTYPES + 1],
nframes_total_total: i32,
nframes_left: [i32; FRAME_NSUBTYPES + 1],
scale_sum: [i64; FRAME_NSUBTYPES],
scale_window_ntus: i32,
scale_window_nframes: [i32; FRAME_NSUBTYPES + 1],
scale_window_sum: [i64; FRAME_NSUBTYPES],
}
pub struct QuantizerParameters {
pub log_base_q: i64,
pub log_target_q: i64,
pub dc_qi: [u8; 3],
pub ac_qi: [u8; 3],
pub lambda: f64,
pub dist_scale: [f64; 3],
}
const Q57_SQUARE_EXP_SCALE: f64 =
(2.0 * ::std::f64::consts::LN_2) / ((1i64 << 57) as f64);
fn chroma_offset(
log_target_q: i64, chroma_sampling: ChromaSampling,
) -> (i64, i64) {
let x = log_target_q.max(0);
let y = match chroma_sampling {
ChromaSampling::Cs400 => 0,
ChromaSampling::Cs420 => (x >> 2) + (x >> 6),
ChromaSampling::Cs422 => (x >> 3) + (x >> 4) - (x >> 7),
ChromaSampling::Cs444 => (x >> 4) + (x >> 5) + (x >> 8),
};
(0x19D_5D9F_D501_0B37 - y, 0xA4_D3C2_5E68_DC58 - y)
}
impl QuantizerParameters {
fn new_from_log_q(
log_base_q: i64, log_target_q: i64, bit_depth: usize,
chroma_sampling: ChromaSampling, is_intra: bool,
) -> QuantizerParameters {
let scale = q57(QSCALE + bit_depth as i32 - 8);
let mut log_q_y = log_target_q;
if !is_intra && bit_depth == 8 {
log_q_y = log_target_q
+ (log_target_q >> 32) * Q_MODEL_MUL[chroma_sampling as usize]
+ Q_MODEL_ADD[chroma_sampling as usize];
}
let quantizer = bexp64(log_q_y + scale);
let (offset_u, offset_v) = chroma_offset(log_q_y, chroma_sampling);
let mono = chroma_sampling == ChromaSampling::Cs400;
let log_q_u = log_q_y + offset_u;
let log_q_v = log_q_y + offset_v;
let quantizer_u = bexp64(log_q_u + scale);
let quantizer_v = bexp64(log_q_v + scale);
let lambda = (::std::f64::consts::LN_2 / 6.0)
* ((log_target_q as f64) * Q57_SQUARE_EXP_SCALE).exp();
let scale = |q| bexp64((log_target_q - q) * 2 + q57(16)) as f64 / 65536.;
let dist_scale = [scale(log_q_y), scale(log_q_u), scale(log_q_v)];
let base_q_idx = select_ac_qi(quantizer, bit_depth).max(1);
let min_qi = base_q_idx.saturating_sub(63).max(1);
let max_qi = base_q_idx.saturating_add(63).min(255);
let clamp_qi = |qi: u8| qi.max(min_qi).min(max_qi);
QuantizerParameters {
log_base_q,
log_target_q,
dc_qi: [
clamp_qi(select_dc_qi(quantizer, bit_depth)),
if mono { 0 } else { clamp_qi(select_dc_qi(quantizer_u, bit_depth)) },
if mono { 0 } else { clamp_qi(select_dc_qi(quantizer_v, bit_depth)) },
],
ac_qi: [
base_q_idx,
if mono { 0 } else { clamp_qi(select_ac_qi(quantizer_u, bit_depth)) },
if mono { 0 } else { clamp_qi(select_ac_qi(quantizer_v, bit_depth)) },
],
lambda,
dist_scale,
}
}
}
pub(crate) struct TwoPassOutParams {
pub pass1_log_base_q: i64,
done_processing: bool,
}
impl RCState {
pub fn new(
frame_width: i32, frame_height: i32, framerate_num: i64,
framerate_den: i64, target_bitrate: i32, maybe_ac_qi_max: Option<u8>,
ac_qi_min: u8, max_key_frame_interval: i32,
maybe_reservoir_frame_delay: Option<i32>,
) -> RCState {
let reservoir_frame_delay = maybe_reservoir_frame_delay
.unwrap_or_else(|| ((max_key_frame_interval * 3) >> 1).min(240))
.max(12);
let npixels = (frame_width as i64) * (frame_height as i64);
let bits_per_tu = clamp(
(target_bitrate as i64) * framerate_den / framerate_num,
40,
0x4000_0000_0000,
) - (TEMPORAL_DELIMITER.len() * 8) as i64;
let reservoir_max = bits_per_tu * (reservoir_frame_delay as i64);
let reservoir_target = (reservoir_max + 1) >> 1;
let ibpp = npixels / bits_per_tu;
let (i_exp, i_log_scale) = if ibpp < 1 {
(48u8, blog64(36) - q57(QSCALE))
} else if ibpp < 4 {
(61u8, blog64(55) - q57(QSCALE))
} else {
(77u8, blog64(129) - q57(QSCALE))
};
let (p_exp, p_log_scale) = if ibpp < 2 {
(69u8, blog64(32) - q57(QSCALE))
} else if ibpp < 139 {
(104u8, blog64(84) - q57(QSCALE))
} else {
(83u8, blog64(19) - q57(QSCALE))
};
let (b0_exp, b0_log_scale) = if ibpp < 2 {
(84u8, blog64(30) - q57(QSCALE))
} else if ibpp < 92 {
(120u8, blog64(68) - q57(QSCALE))
} else {
(68u8, blog64(4) - q57(QSCALE))
};
let (b1_exp, b1_log_scale) = if ibpp < 2 {
(87u8, blog64(27) - q57(QSCALE))
} else if ibpp < 126 {
(139u8, blog64(84) - q57(QSCALE))
} else {
(61u8, blog64(1) - q57(QSCALE))
};
RCState {
target_bitrate,
reservoir_frame_delay,
reservoir_frame_delay_is_set: maybe_reservoir_frame_delay.is_some(),
maybe_ac_qi_max,
ac_qi_min,
drop_frames: false,
cap_overflow: true,
cap_underflow: false,
pass1_log_base_q: 0,
twopass_state: PASS_SINGLE,
log_npixels: blog64(npixels),
bits_per_tu,
reservoir_fullness: reservoir_target,
reservoir_target,
reservoir_max,
log_scale: [i_log_scale, p_log_scale, b0_log_scale, b1_log_scale],
exp: [i_exp, p_exp, b0_exp, b1_exp],
scalefilter: [
IIRBessel2::new(4, q57_to_q24(i_log_scale)),
IIRBessel2::new(INTER_DELAY_TARGET_MIN, q57_to_q24(p_log_scale)),
IIRBessel2::new(INTER_DELAY_TARGET_MIN, q57_to_q24(b0_log_scale)),
IIRBessel2::new(INTER_DELAY_TARGET_MIN, q57_to_q24(b1_log_scale)),
],
nframes: [0; FRAME_NSUBTYPES + 1],
inter_delay: [INTER_DELAY_TARGET_MIN; FRAME_NSUBTYPES - 1],
inter_delay_target: reservoir_frame_delay >> 1,
rate_bias: 0,
nencoded_frames: 0,
nsef_frames: 0,
pass1_buffer: [0; TWOPASS_HEADER_SZ],
pass1_data_retrieved: true,
pass1_summary_retrieved: false,
pass2_data_ready: false,
prev_metrics: RCFrameMetrics::new(),
cur_metrics: RCFrameMetrics::new(),
frame_metrics: Vec::new(),
nframe_metrics: 0,
frame_metrics_head: 0,
ntus: 0,
ntus_total: 0,
ntus_left: 0,
nframes_total: [0; FRAME_NSUBTYPES + 1],
nframes_total_total: 0,
nframes_left: [0; FRAME_NSUBTYPES + 1],
scale_sum: [0; FRAME_NSUBTYPES],
scale_window_ntus: 0,
scale_window_nframes: [0; FRAME_NSUBTYPES + 1],
scale_window_sum: [0; FRAME_NSUBTYPES],
des: RCDeserialize::default(),
}
}
pub(crate) fn select_first_pass_qi(
&self, bit_depth: usize, fti: usize, chroma_sampling: ChromaSampling,
) -> QuantizerParameters {
let log_q = ((self.pass1_log_base_q + (1i64 << 11)) >> 12)
* (MQP_Q12[fti] as i64)
+ DQP_Q57[fti];
QuantizerParameters::new_from_log_q(
self.pass1_log_base_q,
log_q,
bit_depth,
chroma_sampling,
fti == 0,
)
}
pub(crate) fn select_qi<T: Pixel>(
&self, ctx: &ContextInner<T>, output_frameno: u64, fti: usize,
maybe_prev_log_base_q: Option<i64>,
) -> QuantizerParameters {
if self.target_bitrate <= 0 {
let bit_depth = ctx.config.bit_depth;
let chroma_sampling = ctx.config.chroma_sampling;
let (log_base_q, log_q) =
Self::calc_flat_quantizer(ctx.config.quantizer as u8, bit_depth, fti);
QuantizerParameters::new_from_log_q(
log_base_q,
log_q,
bit_depth,
chroma_sampling,
fti == 0,
)
} else {
let mut nframes: [i32; FRAME_NSUBTYPES + 1] = [0; FRAME_NSUBTYPES + 1];
let mut log_scale: [i64; FRAME_NSUBTYPES] = self.log_scale;
let mut reservoir_tus = self.reservoir_frame_delay.min(self.ntus_left);
let mut reservoir_frames = 0;
let mut log_cur_scale = (self.scalefilter[fti].y[0] as i64) << 33;
match self.twopass_state {
PASS_1 => {
return self.select_first_pass_qi(
ctx.config.bit_depth,
fti,
ctx.config.chroma_sampling,
);
}
PASS_2 | PASS_2_PLUS_1 => {
let mut scale_window_sum: [i64; FRAME_NSUBTYPES] =
self.scale_window_sum;
let mut scale_window_nframes: [i32; FRAME_NSUBTYPES + 1] =
self.scale_window_nframes;
for ftj in 0..FRAME_NSUBTYPES {
reservoir_frames += scale_window_nframes[ftj];
}
if !self.frame_metrics.is_empty() {
let mut fm_tail = self.frame_metrics_head + self.nframe_metrics;
if fm_tail >= self.frame_metrics.len() {
fm_tail -= self.frame_metrics.len();
}
let mut fmi = fm_tail;
loop {
if fmi == 0 {
fmi += self.frame_metrics.len();
}
fmi -= 1;
if fmi == self.frame_metrics_head {
break;
}
if self.frame_metrics[fmi].fti == FRAME_SUBTYPE_I {
while fmi != fm_tail {
let m = &self.frame_metrics[fmi];
let ftj = m.fti;
scale_window_nframes[ftj] -= 1;
if ftj < FRAME_NSUBTYPES {
scale_window_sum[ftj] -= bexp_q24(m.log_scale_q24);
reservoir_frames -= 1;
}
if m.show_frame {
reservoir_tus -= 1;
}
fmi += 1;
if fmi >= self.frame_metrics.len() {
fmi = 0;
}
}
break;
}
}
}
nframes = scale_window_nframes;
if self.cur_metrics.fti != fti {
scale_window_nframes[self.cur_metrics.fti] -= 1;
if self.cur_metrics.fti != FRAME_SUBTYPE_SEF {
scale_window_sum[self.cur_metrics.fti] -=
bexp_q24(self.cur_metrics.log_scale_q24);
}
} else {
log_cur_scale = (self.cur_metrics.log_scale_q24 as i64) << 33;
}
if reservoir_tus >= self.ntus_left
&& self.ntus_total as u64
> ctx.gop_input_frameno_start[&output_frameno]
{
let nfinal_gop_tus = self.ntus_total
- (ctx.gop_input_frameno_start[&output_frameno] as i32);
if ctx.config.max_key_frame_interval as i32 > nfinal_gop_tus {
let reservoir_pad = (ctx.config.max_key_frame_interval as i32
- nfinal_gop_tus)
.min(self.reservoir_frame_delay - reservoir_tus);
let (guessed_reservoir_frames, guessed_reservoir_tus) = ctx
.guess_frame_subtypes(
&mut nframes,
reservoir_tus + reservoir_pad,
);
reservoir_frames = guessed_reservoir_frames;
reservoir_tus = guessed_reservoir_tus;
}
}
for ftj in 0..FRAME_NSUBTYPES {
let scale = scale_window_sum[ftj]
+ bexp_q24(self.scalefilter[ftj].y[0])
* (nframes[ftj] - scale_window_nframes[ftj]) as i64;
log_scale[ftj] = if nframes[ftj] > 0 {
blog64(scale) - blog64(nframes[ftj] as i64) - q57(24)
} else {
-self.log_npixels
};
}
}
_ => {
let (guessed_reservoir_frames, guessed_reservoir_tus) =
ctx.guess_frame_subtypes(&mut nframes, self.reservoir_frame_delay);
reservoir_frames = guessed_reservoir_frames;
reservoir_tus = guessed_reservoir_tus;
}
}
let rate_bias = (self.rate_bias / (self.nencoded_frames as i64 + 100))
* (reservoir_frames as i64);
let rate_total = self.reservoir_fullness - self.reservoir_target
+ rate_bias
+ (reservoir_tus as i64) * self.bits_per_tu;
let bit_depth = ctx.config.bit_depth;
let chroma_sampling = ctx.config.chroma_sampling;
let mut log_qlo = blog64(ac_q(self.ac_qi_min, 0, bit_depth) as i64)
- q57(QSCALE + bit_depth as i32 - 8);
let mut log_qhi =
blog64(ac_q(self.maybe_ac_qi_max.unwrap_or(255), 0, bit_depth) as i64)
- q57(QSCALE + bit_depth as i32 - 8);
let mut log_base_q = (log_qlo + log_qhi) >> 1;
while log_qlo < log_qhi {
let mut bits = 0i64;
for ftj in 0..FRAME_NSUBTYPES {
let log_q = ((log_base_q + (1i64 << 11)) >> 12)
* (MQP_Q12[ftj] as i64)
+ DQP_Q57[ftj];
bits += (nframes[ftj] as i64)
* bexp64(
log_scale[ftj] + self.log_npixels
- ((log_q + 32) >> 6) * (self.exp[ftj] as i64),
);
}
bits += (nframes[FRAME_SUBTYPE_SEF] as i64) * SEF_BITS;
let diff = bits - rate_total;
if diff > 0 {
log_qlo = log_base_q + 1;
} else if diff < 0 {
log_qhi = log_base_q - 1;
} else {
break;
}
log_base_q = (log_qlo + log_qhi) >> 1;
}
if let Some(prev_log_base_q) = maybe_prev_log_base_q {
log_base_q = clamp(
log_base_q,
prev_log_base_q - 0xA4_D3C2_5E68_DC58,
prev_log_base_q + 0xA4_D3C2_5E68_DC58,
);
}
let mut log_q = ((log_base_q + (1i64 << 11)) >> 12)
* (MQP_Q12[fti] as i64)
+ DQP_Q57[fti];
if self.cap_overflow {
let margin = (self.reservoir_max + 31) >> 5;
let soft_limit = self.reservoir_fullness + self.bits_per_tu
- (self.reservoir_max - margin);
if soft_limit > 0 {
let log_soft_limit = blog64(soft_limit);
let log_scale_pixels = log_cur_scale + self.log_npixels;
let exp = self.exp[fti] as i64;
let mut log_q_exp = ((log_q + 32) >> 6) * exp;
if log_scale_pixels - log_q_exp < log_soft_limit {
log_q_exp += ((log_scale_pixels - log_soft_limit - log_q_exp)
>> 32)
* ((margin.min(soft_limit) << 32) / margin);
log_q = ((log_q_exp + (exp >> 1)) / exp) << 6;
}
}
}
if self.maybe_ac_qi_max.is_none() {
let log_hard_limit =
blog64(self.reservoir_fullness + (self.bits_per_tu >> 1));
let log_scale_pixels = log_cur_scale + self.log_npixels;
let exp = self.exp[fti] as i64;
let mut log_q_exp = ((log_q + 32) >> 6) * exp;
if log_scale_pixels - log_q_exp > log_hard_limit {
log_q_exp = log_scale_pixels - log_hard_limit;
log_q = ((log_q_exp + (exp >> 1)) / exp) << 6;
}
}
if let Some(qi_max) = self.maybe_ac_qi_max {
let (max_log_base_q, max_log_q) =
Self::calc_flat_quantizer(qi_max, ctx.config.bit_depth, fti);
log_base_q = cmp::min(log_base_q, max_log_base_q);
log_q = cmp::min(log_q, max_log_q);
}
if self.ac_qi_min > 0 {
let (min_log_base_q, min_log_q) =
Self::calc_flat_quantizer(self.ac_qi_min, ctx.config.bit_depth, fti);
log_base_q = cmp::max(log_base_q, min_log_base_q);
log_q = cmp::max(log_q, min_log_q);
}
QuantizerParameters::new_from_log_q(
log_base_q,
log_q,
bit_depth,
chroma_sampling,
fti == 0,
)
}
}
fn calc_flat_quantizer(
base_qi: u8, bit_depth: usize, fti: usize,
) -> (i64, i64) {
let ac_quantizer = ac_q(base_qi as u8, 0, bit_depth) as i64;
let dc_qi = select_dc_qi(ac_quantizer, bit_depth);
let dc_quantizer = dc_q(dc_qi as u8, 0, bit_depth) as i64;
let log_ac_q = blog64(ac_quantizer) - q57(QSCALE + bit_depth as i32 - 8);
let log_dc_q = blog64(dc_quantizer) - q57(QSCALE + bit_depth as i32 - 8);
let log_base_q = (log_ac_q + log_dc_q + 1) >> 1;
let log_q = ((log_base_q + (1i64 << 11)) >> 12) * (MQP_Q12[fti] as i64)
+ DQP_Q57[fti];
(log_base_q, log_q)
}
pub fn update_state(
&mut self, bits: i64, fti: usize, show_frame: bool, log_target_q: i64,
trial: bool, droppable: bool,
) -> bool {
if trial {
assert!(self.needs_trial_encode(fti));
assert!(bits > 0);
}
let mut dropped = false;
if self.target_bitrate > 0 {
let mut estimated_bits = 0;
let mut bits = bits;
let mut droppable = droppable;
let mut log_scale = q57(-64);
if !self.drop_frames
|| fti == FRAME_SUBTYPE_SEF
|| (self.twopass_state == PASS_2
|| self.twopass_state == PASS_2_PLUS_1)
&& !self.frame_metrics.is_empty()
{
droppable = false;
}
if fti == FRAME_SUBTYPE_SEF {
debug_assert!(bits == SEF_BITS);
debug_assert!(show_frame);
debug_assert!(!trial);
estimated_bits = SEF_BITS;
self.nsef_frames += 1;
} else {
let log_q_exp = ((log_target_q + 32) >> 6) * (self.exp[fti] as i64);
let prev_log_scale = self.log_scale[fti];
if bits <= 0 {
bits = 0;
dropped = true;
} else {
let log_bits = blog64(bits);
log_scale = (log_bits - self.log_npixels + log_q_exp).min(q57(16));
estimated_bits =
bexp64(prev_log_scale + self.log_npixels - log_q_exp);
if !trial {
self.nencoded_frames += 1;
}
}
}
let log_scale_q24 = q57_to_q24(log_scale);
if self.twopass_state == PASS_2 || self.twopass_state == PASS_2_PLUS_1 {
if !trial {
self.prev_metrics = self.cur_metrics;
let ftj = self.prev_metrics.fti;
self.nframes_left[ftj] -= 1;
self.scale_window_nframes[ftj] -= 1;
if ftj < FRAME_NSUBTYPES {
self.scale_window_sum[ftj] -=
bexp_q24(self.prev_metrics.log_scale_q24);
}
if self.prev_metrics.show_frame {
self.ntus_left -= 1;
self.scale_window_ntus -= 1;
}
if !self.frame_metrics.is_empty() {
self.nframe_metrics -= 1;
self.frame_metrics_head += 1;
if self.frame_metrics_head >= self.frame_metrics.len() {
self.frame_metrics_head = 0;
}
}
self.pass2_data_ready = false;
self.twopass_in(None).unwrap_or(0);
}
}
if self.twopass_state == PASS_1 || self.twopass_state == PASS_2_PLUS_1 {
self.prev_metrics.log_scale_q24 = log_scale_q24;
self.prev_metrics.fti = fti;
self.prev_metrics.show_frame = show_frame;
self.pass1_data_retrieved = false;
}
if fti != FRAME_SUBTYPE_SEF && bits > 0 {
if trial || self.nframes[fti] <= 0 {
let f = &mut self.scalefilter[fti];
let x = log_scale_q24;
f.x[0] = x;
f.x[1] = x;
f.y[0] = x;
f.y[1] = x;
self.log_scale[fti] = log_scale;
} else {
if fti > 0
&& self.inter_delay[fti - 1] < self.inter_delay_target
&& self.nframes[fti] >= self.inter_delay[fti - 1]
{
self.inter_delay[fti - 1] += 1;
self.scalefilter[fti].reinit(self.inter_delay[fti - 1]);
}
self.log_scale[fti] =
q24_to_q57(self.scalefilter[fti].update(log_scale_q24));
}
if droppable && self.reservoir_fullness + self.bits_per_tu < bits {
bits = 0;
dropped = true;
} else {
}
}
if !trial {
if !trial && self.nframes[fti] < ::std::i32::MAX {
self.nframes[fti] += 1;
}
self.reservoir_fullness -= bits;
if show_frame {
self.reservoir_fullness += self.bits_per_tu;
}
if self.cap_overflow {
self.reservoir_fullness =
self.reservoir_fullness.min(self.reservoir_max);
}
if self.cap_underflow {
self.reservoir_fullness = self.reservoir_fullness.max(0);
}
self.rate_bias += estimated_bits - bits;
}
}
dropped
}
pub fn needs_trial_encode(&self, fti: usize) -> bool {
self.target_bitrate > 0 && self.nframes[fti] == 0
}
pub(crate) fn ready(&self) -> bool {
match self.twopass_state {
PASS_SINGLE => true,
PASS_1 => self.pass1_data_retrieved,
PASS_2 => self.pass2_data_ready,
_ => self.pass1_data_retrieved && self.pass2_data_ready,
}
}
fn buffer_val(&mut self, val: i64, bytes: usize, cur_pos: usize) -> usize {
let mut val = val;
let mut bytes = bytes;
let mut cur_pos = cur_pos;
while bytes > 0 {
bytes -= 1;
self.pass1_buffer[cur_pos] = val as u8;
cur_pos += 1;
val >>= 8;
}
cur_pos
}
pub(crate) fn get_twopass_out_params<T: Pixel>(
&self, ctx: &ContextInner<T>, output_frameno: u64,
) -> TwoPassOutParams {
let mut pass1_log_base_q = 0;
let mut done_processing = false;
if !self.pass1_data_retrieved {
if self.twopass_state == PASS_SINGLE {
pass1_log_base_q = self
.select_qi(ctx, output_frameno, FRAME_SUBTYPE_I, None)
.log_base_q;
}
} else {
done_processing = ctx.done_processing();
}
TwoPassOutParams { pass1_log_base_q, done_processing }
}
pub(crate) fn init_first_pass(&mut self, pass1_log_base_q: i64) {
if self.twopass_state == PASS_SINGLE {
self.pass1_log_base_q = pass1_log_base_q;
} else {
debug_assert!(self.twopass_state == PASS_2);
}
self.twopass_state += PASS_1;
}
fn emit_placeholder_summary(&mut self) -> &[u8] {
let mut cur_pos = 0;
cur_pos = self.buffer_val(TWOPASS_MAGIC as i64, 4, cur_pos);
cur_pos = self.buffer_val(TWOPASS_VERSION as i64, 4, cur_pos);
cur_pos = self.buffer_val(0, TWOPASS_HEADER_SZ - 8, cur_pos);
debug_assert!(cur_pos == TWOPASS_HEADER_SZ);
self.pass1_data_retrieved = true;
&self.pass1_buffer[..cur_pos]
}
pub(crate) fn emit_frame_data(&mut self) -> Option<&[u8]> {
let mut cur_pos = 0;
let fti = self.prev_metrics.fti;
if fti < FRAME_NSUBTYPES {
self.scale_sum[fti] += bexp_q24(self.prev_metrics.log_scale_q24);
}
if self.prev_metrics.show_frame {
self.ntus += 1;
}
if self.nencoded_frames + self.nsef_frames >= std::i32::MAX as i64 {
None?
}
cur_pos = self.buffer_val(
(self.prev_metrics.show_frame as i64) << 31
| self.prev_metrics.fti as i64,
4,
cur_pos,
);
cur_pos =
self.buffer_val(self.prev_metrics.log_scale_q24 as i64, 4, cur_pos);
debug_assert!(cur_pos == TWOPASS_PACKET_SZ);
self.pass1_data_retrieved = true;
Some(&self.pass1_buffer[..cur_pos])
}
pub(crate) fn emit_summary(&mut self) -> &[u8] {
let mut cur_pos = 0;
cur_pos = self.buffer_val(TWOPASS_MAGIC as i64, 4, cur_pos);
cur_pos = self.buffer_val(TWOPASS_VERSION as i64, 4, cur_pos);
cur_pos = self.buffer_val(self.ntus as i64, 4, cur_pos);
for fti in 0..=FRAME_NSUBTYPES {
cur_pos = self.buffer_val(self.nframes[fti] as i64, 4, cur_pos);
}
for fti in 0..FRAME_NSUBTYPES {
cur_pos = self.buffer_val(self.exp[fti] as i64, 1, cur_pos);
}
for fti in 0..FRAME_NSUBTYPES {
cur_pos = self.buffer_val(self.scale_sum[fti], 8, cur_pos);
}
debug_assert!(cur_pos == TWOPASS_HEADER_SZ);
self.pass1_summary_retrieved = true;
&self.pass1_buffer[..cur_pos]
}
pub(crate) fn twopass_out(
&mut self, params: TwoPassOutParams,
) -> Option<&[u8]> {
if !self.pass1_data_retrieved {
if self.twopass_state != PASS_1 && self.twopass_state != PASS_2_PLUS_1 {
self.init_first_pass(params.pass1_log_base_q);
Some(self.emit_placeholder_summary())
} else {
self.emit_frame_data()
}
} else if params.done_processing && !self.pass1_summary_retrieved {
Some(self.emit_summary())
} else {
None
}
}
pub(crate) fn init_second_pass(&mut self) {
if self.twopass_state == PASS_SINGLE || self.twopass_state == PASS_1 {
self.twopass_state += PASS_2;
if self.reservoir_frame_delay_is_set {
debug_assert!(self.reservoir_frame_delay > 0);
let nmetrics = (self.reservoir_frame_delay as usize) * 2 + 8;
self.frame_metrics.reserve_exact(nmetrics);
self.frame_metrics.resize(nmetrics, RCFrameMetrics::new());
}
}
}
pub(crate) fn setup_second_pass(&mut self, s: &RCSummary) {
self.ntus_total = s.ntus;
self.ntus_left = s.ntus;
self.nframes_total = s.nframes;
self.nframes_left = s.nframes;
self.nframes_total_total = s.nframes.iter().sum();
if self.frame_metrics.is_empty() {
self.reservoir_frame_delay = s.ntus;
self.scale_window_nframes = self.nframes_total;
self.scale_window_sum = s.scale_sum;
self.reservoir_max =
self.bits_per_tu * (self.reservoir_frame_delay as i64);
self.reservoir_target = (self.reservoir_max + 1) >> 1;
self.reservoir_fullness = self.reservoir_target;
} else {
self.reservoir_frame_delay = self.reservoir_frame_delay.min(s.ntus);
}
self.exp = s.exp;
}
fn twopass_parse_summary(&mut self, buf: &[u8]) -> Result<usize, String> {
let consumed = self.des.buffer_fill(buf, 0, TWOPASS_HEADER_SZ);
if self.des.pass2_buffer_fill >= TWOPASS_HEADER_SZ {
self.des.pass2_buffer_pos = 0;
let s = self.des.parse_summary()?;
self.setup_second_pass(&s);
self.des.pass2_buffer_fill = 0;
}
Ok(consumed)
}
pub(crate) fn twopass_first_packet_size(&self) -> usize {
let frames_needed = if !self.frame_metrics.is_empty() {
self.reservoir_frame_delay as usize
} else {
1
};
TWOPASS_HEADER_SZ + frames_needed * TWOPASS_PACKET_SZ
}
pub(crate) fn twopass_in_frames_needed(&self) -> i32 {
if self.target_bitrate <= 0 {
return 0;
}
if self.frame_metrics.is_empty() {
return if self.pass2_data_ready { 0 } else { 1 };
}
let mut cur_scale_window_nframes = 0;
let mut cur_nframes_left = 0;
for fti in 0..=FRAME_NSUBTYPES {
cur_scale_window_nframes += self.scale_window_nframes[fti];
cur_nframes_left += self.nframes_left[fti];
}
(self.reservoir_frame_delay - self.scale_window_ntus)
.max(0)
.min(cur_nframes_left - cur_scale_window_nframes)
}
pub(crate) fn parse_frame_data_packet(
&mut self, buf: &[u8],
) -> Result<(), String> {
if buf.len() != TWOPASS_PACKET_SZ {
return Err("Incorrect buffer size".to_string());
}
self.des.buffer_fill(buf, 0, TWOPASS_PACKET_SZ);
self.des.pass2_buffer_pos = 0;
let m = self.des.parse_metrics()?;
self.des.pass2_buffer_fill = 0;
if self.frame_metrics.is_empty() {
self.cur_metrics = m;
self.pass2_data_ready = true;
} else {
let frames_needed = self.twopass_in_frames_needed();
if frames_needed > 0 {
if self.nframe_metrics >= self.frame_metrics.len() {
return Err(
"Read too many frames without finding enough TUs".to_string(),
);
}
let mut fmi = self.frame_metrics_head + self.nframe_metrics;
if fmi >= self.frame_metrics.len() {
fmi -= self.frame_metrics.len();
}
self.nframe_metrics += 1;
self.frame_metrics[fmi] = m;
self.scale_window_nframes[m.fti] += 1;
if m.fti < FRAME_NSUBTYPES {
self.scale_window_sum[m.fti] += bexp_q24(m.log_scale_q24);
}
if m.show_frame {
self.scale_window_ntus += 1;
}
if frames_needed == 1 {
self.pass2_data_ready = true;
self.cur_metrics = self.frame_metrics[self.frame_metrics_head];
}
} else {
return Err("No frames needed".to_string());
}
}
Ok(())
}
fn twopass_parse_frame_data(
&mut self, maybe_buf: Option<&[u8]>, mut consumed: usize,
) -> Result<usize, String> {
{
if self.frame_metrics.is_empty() {
if let Some(buf) = maybe_buf {
consumed = self.des.buffer_fill(buf, consumed, TWOPASS_PACKET_SZ);
if self.des.pass2_buffer_fill >= TWOPASS_PACKET_SZ {
self.des.pass2_buffer_pos = 0;
self.cur_metrics = self.des.parse_metrics()?;
self.des.pass2_buffer_fill = 0;
self.pass2_data_ready = true;
}
} else {
return Ok(TWOPASS_PACKET_SZ - self.des.pass2_buffer_fill);
}
} else {
let mut cur_scale_window_nframes = 0;
let mut cur_nframes_left = 0;
for fti in 0..=FRAME_NSUBTYPES {
cur_scale_window_nframes += self.scale_window_nframes[fti];
cur_nframes_left += self.nframes_left[fti];
}
let mut frames_needed = self.twopass_in_frames_needed();
while frames_needed > 0 {
if let Some(buf) = maybe_buf {
consumed = self.des.buffer_fill(buf, consumed, TWOPASS_PACKET_SZ);
if self.des.pass2_buffer_fill >= TWOPASS_PACKET_SZ {
self.des.pass2_buffer_pos = 0;
let m = self.des.parse_metrics()?;
if self.nframe_metrics >= self.frame_metrics.len() {
return Err(
"Read too many frames without finding enough TUs"
.to_string(),
);
}
let mut fmi = self.frame_metrics_head + self.nframe_metrics;
if fmi >= self.frame_metrics.len() {
fmi -= self.frame_metrics.len();
}
self.nframe_metrics += 1;
self.frame_metrics[fmi] = m;
self.scale_window_nframes[m.fti] += 1;
cur_scale_window_nframes += 1;
if m.fti < FRAME_NSUBTYPES {
self.scale_window_sum[m.fti] += bexp_q24(m.log_scale_q24);
}
if m.show_frame {
self.scale_window_ntus += 1;
}
frames_needed = (self.reservoir_frame_delay
- self.scale_window_ntus)
.max(0)
.min(cur_nframes_left - cur_scale_window_nframes);
self.des.pass2_buffer_fill = 0;
} else {
break;
}
} else {
return Ok(
TWOPASS_PACKET_SZ * (frames_needed as usize)
- self.des.pass2_buffer_fill,
);
}
}
if frames_needed <= 0 {
self.cur_metrics = self.frame_metrics[self.frame_metrics_head];
self.pass2_data_ready = true;
}
}
}
Ok(consumed)
}
pub(crate) fn twopass_in(
&mut self, maybe_buf: Option<&[u8]>,
) -> Result<usize, String> {
let mut consumed = 0;
self.init_second_pass();
if self.nframes_total[FRAME_SUBTYPE_I] == 0 {
self.pass2_data_ready = false;
if let Some(buf) = maybe_buf {
consumed = self.twopass_parse_summary(buf)?
} else {
return Ok(self.twopass_first_packet_size());
}
}
if self.nframes_total[FRAME_SUBTYPE_I] > 0 {
if self.nencoded_frames + self.nsef_frames
>= self.nframes_total_total as i64
{
self.pass2_data_ready = false;
} else if !self.pass2_data_ready {
return self.twopass_parse_frame_data(maybe_buf, consumed);
}
}
Ok(consumed)
}
}
#[cfg(test)]
mod test {
use super::{bexp64, blog64};
#[test]
fn blog64_vectors() {
assert!(blog64(1793) == 0x159dc71e24d32daf);
assert!(blog64(0x678dde6e5fd29f05) == 0x7d6373ad151ca685);
}
#[test]
fn bexp64_vectors() {
assert!(bexp64(0x159dc71e24d32daf) == 1793);
assert!((bexp64(0x7d6373ad151ca685) - 0x678dde6e5fd29f05).abs() < 29);
}
#[test]
fn blog64_bexp64_round_trip() {
for a in 1..=std::u16::MAX as i64 {
let b = std::i64::MAX / a;
let (log_a, log_b, log_ab) = (blog64(a), blog64(b), blog64(a * b));
assert!((log_a + log_b - log_ab).abs() < 4);
assert!(bexp64(log_a) == a);
assert!((bexp64(log_b) - b).abs() < 128);
assert!((bexp64(log_ab) - a * b).abs() < 128);
}
}
}