Commit 40b2b021 authored by Derek Buitenhuis's avatar Derek Buitenhuis
Browse files

api: Expose the reservoir frame delay as a settable option

There's no reason to explicitly bind this to the max keyframe interval.
It's useful to, for example, set it larger than 1.5x the keyframe
interval for short intervals (1-3s) to prevent bitrate starvation after
larger than expected (complex) keyframes.
Signed-off-by: default avatarDerek Buitenhuis <>
parent 323e0338
......@@ -74,6 +74,8 @@ pub struct EncoderConfig {
pub min_key_frame_interval: u64,
/// The *maximum* interval between two keyframes
pub max_key_frame_interval: u64,
/// The number of frames over which to distribute the reservoir usage.
pub reservoir_frame_delay: Option<i32>,
pub low_latency: bool,
pub quantizer: usize,
pub bitrate: i32,
......@@ -122,6 +124,7 @@ impl EncoderConfig {
time_base: Rational { num: 1, den: 30 },
min_key_frame_interval: 12,
max_key_frame_interval: 240,
reservoir_frame_delay: None,
low_latency: false,
quantizer: 100,
bitrate: 0,
......@@ -681,7 +684,8 @@ impl<T: Pixel> ContextInner<T> {
enc.time_base.num as i64,
enc.max_key_frame_interval as i32
enc.max_key_frame_interval as i32,
maybe_prev_log_base_q: None,
first_pass_data: FirstPassData { frames: Vec::new() },
......@@ -159,6 +159,13 @@ pub fn parse_cli() -> CliOptions {
.help("Number of frames over which rate control should distribute the reservoir [default: max(240, 1.5x keyint)]\n\
A minimum value of 12 is enforced.")
.help("Low latency mode; disables frame reordering\n\
......@@ -433,6 +440,7 @@ fn parse_config(matches: &ArgMatches<'_>) -> EncoderConfig {
cfg.quantizer = quantizer;
cfg.bitrate = bitrate.checked_mul(1000).expect("Bitrate too high");
cfg.reservoir_frame_delay = matches.value_of("RESERVOIR_FRAME_DELAY").map(|reservior_frame_delay| reservior_frame_delay.parse().unwrap());
cfg.show_psnr = matches.is_present("PSNR");
cfg.pass = matches.value_of("PASS").map(|pass| pass.parse().unwrap());
cfg.stats_file = if cfg.pass.is_some() {
......@@ -478,16 +478,21 @@ 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>,
max_key_frame_interval: i32
max_key_frame_interval: i32, maybe_reservoir_frame_delay: Option<i32>
) -> RCState {
// The buffer size is set equal to 1.5x the keyframe interval, with a minimum
// of 12.
// The default buffer size is set equal to 1.5x the keyframe interval, or 240
// frames; whichsever is smaller.
// For user set values, we enforce a minimum of 12.
// The interval is short enough to allow reaction, but long enough to allow
// looking into the next GOP (avoiding the case where the last frames
// before an I-frame get starved).
// before an I-frame get starved), in most cases.
// The 12 frame minimum gives us some chance to distribute bit estimation
// errors in the worst case.
let reservoir_frame_delay = ((max_key_frame_interval*3) >> 1).max(12);
let reservoir_frame_delay = if maybe_reservoir_frame_delay.is_some() {
} else {
((max_key_frame_interval*3) >> 1).max(240)
// TODO: What are the limits on these?
let npixels = (frame_width as i64)*(frame_height as i64);
// Insane framerates or frame sizes mean insane bitrates.
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