use thiserror::Error;
use std::sync::Arc;
use crate::api::{ChromaSampling, Context, ContextInner};
use crate::cpu_features::CpuFeatureLevel;
use crate::rayon::{ThreadPool, ThreadPoolBuilder};
use crate::tiling::TilingInfo;
use crate::util::Pixel;
mod encoder;
pub use encoder::*;
mod rate;
pub use rate::Error as RateControlError;
pub use rate::{RateControlConfig, RateControlSummary};
mod speedsettings;
pub use speedsettings::*;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
#[non_exhaustive]
pub enum InvalidConfig {
#[error("invalid width {0} (expected >= 16, <= 32767)")]
InvalidWidth(usize),
#[error("invalid height {0} (expected >= 16, <= 32767)")]
InvalidHeight(usize),
#[error("invalid aspect ratio numerator {0} (expected > 0)")]
InvalidAspectRatioNum(usize),
#[error("invalid aspect ratio denominator {0} (expected > 0)")]
InvalidAspectRatioDen(usize),
#[error(
"invalid rdo lookahead frames {actual} (expected <= {max} and >= {min})"
)]
InvalidRdoLookaheadFrames {
actual: usize,
max: usize,
min: usize,
},
#[error("invalid max keyframe interval {actual} (expected <= {max})")]
InvalidMaxKeyFrameInterval {
actual: u64,
max: u64,
},
#[error("invalid tile cols {0} (expected power of 2)")]
InvalidTileCols(usize),
#[error("invalid tile rows {0} (expected power of 2)")]
InvalidTileRows(usize),
#[error("invalid framerate numerator {actual} (expected > 0, <= {max})")]
InvalidFrameRateNum {
actual: u64,
max: u64,
},
#[error("invalid framerate denominator {actual} (expected > 0, <= {max})")]
InvalidFrameRateDen {
actual: u64,
max: u64,
},
#[error("invalid reservoir frame delay {0} (expected >= 12, <= 131072)")]
InvalidReservoirFrameDelay(i32),
#[error(
"invalid switch frame interval {0} (must only be used with low latency mode)"
)]
InvalidSwitchFrameInterval(u64),
#[error("The rate control requires a target bitrate")]
TargetBitrateNeeded,
#[error("Mismatch in the rate control configuration")]
RateControlConfigurationMismatch,
}
#[derive(Clone, Debug, Default)]
pub struct Config {
pub(crate) enc: EncoderConfig,
pub(crate) rate_control: RateControlConfig,
pub(crate) threads: usize,
pub(crate) pool: Option<Arc<ThreadPool>>,
}
impl Config {
pub fn new() -> Self {
Config::default()
}
pub fn with_encoder_config(mut self, enc: EncoderConfig) -> Self {
self.enc = enc;
self
}
pub fn with_threads(mut self, threads: usize) -> Self {
self.threads = threads;
self
}
pub fn with_rate_control(mut self, rate_control: RateControlConfig) -> Self {
self.rate_control = rate_control;
self
}
#[cfg(feature = "unstable")]
pub fn with_thread_pool(mut self, pool: Arc<ThreadPool>) -> Self {
self.pool = Some(pool);
self
}
}
fn check_tile_log2(n: usize) -> bool {
let tile_log2 = TilingInfo::tile_log2(1, n);
if tile_log2.is_none() {
return false;
}
let tile_log2 = tile_log2.unwrap();
((1 << tile_log2) - n) == 0 || n == 0
}
impl Config {
pub(crate) fn new_inner<T: Pixel>(
&self,
) -> Result<ContextInner<T>, InvalidConfig> {
assert!(
8 * std::mem::size_of::<T>() >= self.enc.bit_depth,
"The Pixel u{} does not match the Config bit_depth {}",
8 * std::mem::size_of::<T>(),
self.enc.bit_depth
);
self.validate()?;
info!("CPU Feature Level: {}", CpuFeatureLevel::default());
let mut config = self.enc;
config.set_key_frame_interval(
config.min_key_frame_interval,
config.max_key_frame_interval,
);
let chroma_sampling = config.chroma_sampling;
if chroma_sampling == ChromaSampling::Cs422 {
config.speed_settings.rdo_tx_decision = false;
}
let mut inner = ContextInner::new(&config);
if self.rate_control.emit_pass_data {
let params = inner.rc_state.get_twopass_out_params(&inner, 0);
inner.rc_state.init_first_pass(params.pass1_log_base_q);
}
if let Some(ref s) = self.rate_control.summary {
inner.rc_state.init_second_pass();
inner.rc_state.setup_second_pass(s);
}
Ok(inner)
}
pub fn new_context<T: Pixel>(&self) -> Result<Context<T>, InvalidConfig> {
let inner = self.new_inner()?;
let config = inner.config;
let pool = if let Some(ref p) = self.pool {
p.clone()
} else {
let pool =
ThreadPoolBuilder::new().num_threads(self.threads).build().unwrap();
Arc::new(pool)
};
Ok(Context { is_flushing: false, inner, pool, config })
}
pub fn validate(&self) -> Result<(), InvalidConfig> {
use InvalidConfig::*;
let config = &self.enc;
if config.width < 16 || config.width > u16::max_value() as usize {
return Err(InvalidWidth(config.width));
}
if config.height < 16 || config.height > u16::max_value() as usize {
return Err(InvalidHeight(config.height));
}
if config.sample_aspect_ratio.num == 0 {
return Err(InvalidAspectRatioNum(
config.sample_aspect_ratio.num as usize,
));
}
if config.sample_aspect_ratio.den == 0 {
return Err(InvalidAspectRatioDen(
config.sample_aspect_ratio.den as usize,
));
}
if config.rdo_lookahead_frames > MAX_RDO_LOOKAHEAD_FRAMES
|| config.rdo_lookahead_frames < 1
{
return Err(InvalidRdoLookaheadFrames {
actual: config.rdo_lookahead_frames,
max: MAX_RDO_LOOKAHEAD_FRAMES,
min: 1,
});
}
if config.max_key_frame_interval > MAX_MAX_KEY_FRAME_INTERVAL {
return Err(InvalidMaxKeyFrameInterval {
actual: config.max_key_frame_interval,
max: MAX_MAX_KEY_FRAME_INTERVAL,
});
}
if !check_tile_log2(config.tile_cols) {
return Err(InvalidTileCols(config.tile_cols));
}
if !check_tile_log2(config.tile_rows) {
return Err(InvalidTileRows(config.tile_rows));
}
if config.time_base.num == 0
|| config.time_base.num > u32::max_value() as u64
{
return Err(InvalidFrameRateNum {
actual: config.time_base.num,
max: u32::max_value() as u64,
});
}
if config.time_base.den == 0
|| config.time_base.den > u32::max_value() as u64
{
return Err(InvalidFrameRateDen {
actual: config.time_base.den,
max: u32::max_value() as u64,
});
}
if let Some(delay) = config.reservoir_frame_delay {
if delay < 12 || delay > 131_072 {
return Err(InvalidReservoirFrameDelay(delay));
}
}
if config.switch_frame_interval > 0 && !config.low_latency {
return Err(InvalidSwitchFrameInterval(config.switch_frame_interval));
}
let rc = &self.rate_control;
if (rc.emit_pass_data || rc.summary.is_some()) && config.bitrate == 0 {
return Err(TargetBitrateNeeded);
}
Ok(())
}
}