Commit ea4bf00c authored by Timothy B. Terriberry's avatar Timothy B. Terriberry Committed by David Michael Barr

Add a skeleton rate control API.

This only implements the "constant quantizer" strategy, and uses
 the same per-frame-type QP index adjustments as the current code.
However, it is one step towards the true quantizer-based
 adjustments required by the rate control model.

This should result in no change in encoded quality.
parent a6132e1b
......@@ -11,6 +11,7 @@ use bitstream_io::*;
use crate::encoder::*;
use crate::metrics::calculate_frame_psnr;
use crate::partition::*;
use crate::rate::RCState;
use crate::scenechange::SceneChangeDetector;
use self::EncoderStatus::*;
......@@ -354,6 +355,7 @@ impl Config {
segment_start_frame: 0,
keyframe_detector: SceneChangeDetector::new(self.enc.bit_depth),
config: *self,
rc_state: RCState::new()
}
}
}
......@@ -377,6 +379,7 @@ pub struct Context {
segment_start_frame: u64,
keyframe_detector: SceneChangeDetector,
pub config: Config,
rc_state: RCState
}
#[derive(Clone, Copy, Debug)]
......@@ -611,6 +614,9 @@ impl Context {
self.idx += 1;
if let Some(frame) = f.clone() {
let fti = fi.get_frame_subtype();
let qps = self.rc_state.select_qi(fi, fti);
fi.set_quantizers(&qps);
let mut fs = FrameState::new_with_frame(fi, frame.clone());
let data = encode_frame(fi, &mut fs);
......
......@@ -18,6 +18,9 @@ use crate::me::*;
use crate::partition::*;
use crate::plane::*;
use crate::quantize::*;
use crate::rate::QuantizerParameters;
use crate::rate::FRAME_SUBTYPE_I;
use crate::rate::FRAME_SUBTYPE_P;
use crate::rdo::*;
use crate::segmentation::*;
use crate::transform::*;
......@@ -584,6 +587,17 @@ pub struct FrameInvariants {
pub enable_early_exit: bool,
}
fn pos_to_lvl(pos: u64, pyramid_depth: u64) -> u64 {
// Derive level within pyramid for a frame with a given coding order position
// For example, with a pyramid of depth 2, the 2 least significant bits of the
// position determine the level:
// 00 -> 0
// 01 -> 2
// 10 -> 1
// 11 -> 2
pyramid_depth - (pos | (1 << pyramid_depth)).trailing_zeros() as u64
}
impl FrameInvariants {
pub fn new(config: EncoderConfig, sequence: Sequence) -> FrameInvariants {
// Speed level decides the minimum partition size, i.e. higher speed --> larger min partition size,
......@@ -660,10 +674,6 @@ impl FrameInvariants {
fi.show_frame = true;
fi.show_existing_frame = false;
fi.frame_to_show_map_idx = 0;
let q_boost = 15;
let qi = (fi.config.quantizer.max(1 + q_boost).min(255 + q_boost)
- q_boost) as u8;
fi.set_quantizer(qi);
fi.primary_ref_frame = PRIMARY_REF_NONE;
fi.number = segment_start_frame;
for i in 0..INTER_REFS_PER_FRAME {
......@@ -721,17 +731,6 @@ impl FrameInvariants {
return (fi, false);
}
fn pos_to_lvl(pos: u64, pyramid_depth: u64) -> u64 {
// Derive level within pyramid for a frame with a given coding order position
// For example, with a pyramid of depth 2, the 2 least significant bits of the
// position determine the level:
// 00 -> 0
// 01 -> 2
// 10 -> 1
// 11 -> 2
pyramid_depth - (pos | (1 << pyramid_depth)).trailing_zeros() as u64
}
let lvl = if !inter_cfg.reorder {
0
} else if inter_cfg.idx_in_group < inter_cfg.pyramid_depth {
......@@ -758,9 +757,6 @@ impl FrameInvariants {
1 << slot_idx
};
let q_drop = 15 * lvl as usize;
let qi = (fi.config.quantizer.min(255 - q_drop) + q_drop) as u8;
fi.set_quantizer(qi);
let second_ref_frame = if !inter_cfg.multiref {
NONE_FRAME
} else if !inter_cfg.reorder || inter_cfg.idx_in_group == 0 {
......@@ -821,21 +817,43 @@ impl FrameInvariants {
(fi, true)
}
pub fn set_quantizer(&mut self, qi: u8) {
self.base_q_idx = qi;
pub fn get_frame_subtype(&self) -> usize {
if self.frame_type == FrameType::KEY {
FRAME_SUBTYPE_I
} else {
let inter_cfg = self.inter_cfg.unwrap();
let lvl = if !inter_cfg.reorder {
0
} else if inter_cfg.idx_in_group < inter_cfg.pyramid_depth {
inter_cfg.idx_in_group
} else {
pos_to_lvl(
inter_cfg.idx_in_group - inter_cfg.pyramid_depth + 1,
inter_cfg.pyramid_depth
)
};
FRAME_SUBTYPE_P + (lvl as usize)
}
}
pub fn set_quantizers(&mut self, qps: &QuantizerParameters) {
self.base_q_idx = qps.ac_qi;
// TODO: Separate qi values for each color plane.
if self.frame_type != FrameType::KEY {
self.cdef_bits = 3 - ((self.base_q_idx.max(128) - 128) >> 5);
} else {
self.cdef_bits = 3;
}
let q = dc_q(self.base_q_idx, self.dc_delta_q[0], self.sequence.bit_depth)
as f64;
// Convert q into Q0 precision, given that libaom quantizers are Q3
let q0 = q / 8.0;
// Lambda formula from doc/theoretical_results.lyx in the daala repo.
// Use Q0 quantizer since lambda will be applied to Q0 pixel domain.
self.lambda = q0 * q0 * std::f64::consts::LN_2 / 6.0;
self.base_q_idx = qps.ac_qi;
// TODO: Separate qi values for each color plane.
debug_assert!(qps.dc_qi as i32 - qps.ac_qi as i32 >= -128);
debug_assert!((qps.dc_qi as i32 - qps.ac_qi as i32) < 128);
for pi in 0..3 {
self.dc_delta_q[pi] = (qps.dc_qi as i32 - qps.ac_qi as i32) as i8;
self.ac_delta_q[pi] = 0;
}
self.lambda =
qps.lambda * ((1 << 2 * (self.sequence.bit_depth - 8)) as f64);
self.me_lambda = self.lambda.sqrt();
}
}
......
......@@ -38,6 +38,7 @@ pub mod me;
pub mod metrics;
pub mod scan_order;
pub mod scenechange;
pub mod rate;
mod api;
......
......@@ -49,6 +49,41 @@ pub fn ac_q(qindex: u8, delta_q: i8, bit_depth: usize) -> i16 {
table[(qindex as isize + delta_q as isize).max(0).min(255) as usize]
}
// TODO: Handle lossless properly.
fn select_qi(quantizer: i64, qlookup: &[i16; QINDEX_RANGE]) -> u8 {
if quantizer < qlookup[MINQ] as i64 {
MINQ as u8
} else if quantizer >= qlookup[MAXQ] as i64 {
MAXQ as u8
} else {
match qlookup.binary_search(&(quantizer as i16)) {
Ok(qi) => qi as u8,
Err(qi) => {
debug_assert!(qi > MINQ);
debug_assert!(qi <= MAXQ);
// Pick the closest quantizer in the log domain.
let qthresh = (qlookup[qi - 1] as i32) * (qlookup[qi] as i32);
let q2_i32 = (quantizer as i32) * (quantizer as i32);
if q2_i32 < qthresh {
(qi - 1) as u8
} else {
qi as u8
}
}
}
}
}
pub fn select_ac_qi(quantizer: i64, bit_depth: usize) -> u8 {
let qlookup = match bit_depth {
8 => &ac_qlookup_Q3,
10 => &ac_qlookup_10_Q3,
12 => &ac_qlookup_12_Q3,
_ => unimplemented!()
};
select_qi(quantizer, qlookup)
}
#[derive(Debug, Default, Clone, Copy)]
pub struct QuantizationContext {
log_tx_scale: usize,
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment