use thiserror::Error;
use rayon::{ThreadPool, ThreadPoolBuilder};
use std::sync::Arc;
use crate::api::{ChromaSampling, Context, ContextInner, PixelRange};
use crate::util::Pixel;
mod encoder;
pub use encoder::*;
pub use av1_grain::*;
use crate::levels::*;
mod rate;
pub use rate::Error as RateControlError;
pub use rate::{RateControlConfig, RateControlSummary};
mod speedsettings;
pub use speedsettings::*;
pub use crate::tiling::TilingInfo;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
#[non_exhaustive]
pub enum InvalidConfig {
#[error("invalid width {0} (expected >= 16, <= 65535)")]
InvalidWidth(usize),
#[error("invalid height {0} (expected >= 16, <= 65535)")]
InvalidHeight(usize),
#[error("invalid aspect ratio numerator {0} (expected > 0)")]
InvalidAspectRatioNum(usize),
#[error("invalid aspect ratio denominator {0} (expected > 0)")]
InvalidAspectRatioDen(usize),
#[error("invalid render width {0} (expected >= 1, <= 65535")]
InvalidRenderWidth(usize),
#[error("invalid render height {0} (expected >= 1, <= 65535")]
InvalidRenderHeight(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("invalid option {0} specified with still picture mode")]
InvalidOptionWithStillPicture(&'static str),
#[error("The rate control requires a target bitrate")]
TargetBitrateNeeded,
#[error("Mismatch in the rate control configuration")]
RateControlConfigurationMismatch,
#[error("Mismatch in the color configuration")]
ColorConfigurationMismatch,
#[error("Specified level is undefined")]
LevelUndefined,
#[error("Constraints exceeded for specified level")]
LevelConstraintsExceeded,
}
#[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>>,
#[cfg(feature = "unstable")]
pub(crate) slots: usize,
}
impl Config {
pub fn new() -> Self {
Config::default()
}
pub fn with_encoder_config(mut self, enc: EncoderConfig) -> Self {
self.enc = enc;
self
}
pub const fn with_threads(mut self, threads: usize) -> Self {
self.threads = threads;
self
}
pub const 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
}
#[cfg(feature = "unstable")]
pub const fn with_parallel_gops(mut self, slots: usize) -> Self {
self.slots = slots;
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()?;
let mut config = self.enc.clone();
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.transform.rdo_tx_decision = false;
}
let mut inner = ContextInner::new(&config);
if let Some(ref s) = self.rate_control.summary {
inner.rc_state.init_second_pass();
inner.rc_state.setup_second_pass(s);
}
if self.rate_control.emit_pass_data {
let maybe_pass1_log_base_q = (self.rate_control.summary.is_none())
.then(|| inner.rc_state.select_pass1_log_base_q(&inner, 0));
inner.rc_state.init_first_pass(maybe_pass1_log_base_q);
}
Ok(inner)
}
pub(crate) fn new_thread_pool(&self) -> Option<Arc<ThreadPool>> {
if let Some(ref p) = self.pool {
Some(p.clone())
} else if self.threads != 0 {
let pool =
ThreadPoolBuilder::new().num_threads(self.threads).build().unwrap();
Some(Arc::new(pool))
} else {
None
}
}
pub fn new_context<T: Pixel>(&self) -> Result<Context<T>, InvalidConfig> {
let inner = self.new_inner()?;
let config = (*inner.config).clone();
let pool = self.new_thread_pool();
Ok(Context { is_flushing: false, inner, pool, config })
}
pub fn validate(&self) -> Result<(), InvalidConfig> {
use InvalidConfig::*;
let config = &self.enc;
if (config.still_picture && config.width < 1)
|| (!config.still_picture && config.width < 16)
|| config.width > u16::MAX as usize
{
return Err(InvalidWidth(config.width));
}
if (config.still_picture && config.height < 1)
|| (!config.still_picture && config.height < 16)
|| config.height > u16::MAX 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,
));
}
let (render_width, render_height) = config.render_size();
if render_width == 0 || render_width > u16::MAX as usize {
return Err(InvalidRenderWidth(render_width));
}
if render_height == 0 || render_height > u16::MAX as usize {
return Err(InvalidRenderHeight(render_height));
}
if config.speed_settings.rdo_lookahead_frames > MAX_RDO_LOOKAHEAD_FRAMES
|| config.speed_settings.rdo_lookahead_frames < 1
{
return Err(InvalidRdoLookaheadFrames {
actual: config.speed_settings.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 as u64 {
return Err(InvalidFrameRateNum {
actual: config.time_base.num,
max: u32::MAX as u64,
});
}
if config.time_base.den == 0 || config.time_base.den > u32::MAX as u64 {
return Err(InvalidFrameRateDen {
actual: config.time_base.den,
max: u32::MAX as u64,
});
}
if let Some(delay) = config.reservoir_frame_delay {
if !(12..=131_072).contains(&delay) {
return Err(InvalidReservoirFrameDelay(delay));
}
}
if config.switch_frame_interval > 0 && !config.low_latency {
return Err(InvalidSwitchFrameInterval(config.switch_frame_interval));
}
if config.enable_timing_info && config.still_picture {
return Err(InvalidOptionWithStillPicture("enable_timing_info"));
}
if let Some(color_description) = config.color_description {
if config.chroma_sampling != ChromaSampling::Cs400
&& color_description.is_srgb_triple()
{
if config.pixel_range != PixelRange::Full {
return Err(ColorConfigurationMismatch);
}
if config.chroma_sampling != ChromaSampling::Cs444 {
return Err(ColorConfigurationMismatch);
}
}
}
if let Some(level_idx) = config.level_idx {
if level_idx > 31 {
return Err(LevelUndefined);
}
if level_idx < 31 {
if !AV1_LEVEL_DEFINED[level_idx as usize] {
return Err(LevelUndefined);
}
if config.width * config.height
> AV1_LEVEL_MAX_PIC_SIZE[level_idx as usize]
{
return Err(LevelConstraintsExceeded);
}
if config.width > AV1_LEVEL_MAX_H_SIZE[level_idx as usize] {
return Err(LevelConstraintsExceeded);
}
if config.height > AV1_LEVEL_MAX_V_SIZE[level_idx as usize] {
return Err(LevelConstraintsExceeded);
}
if ((config.width * config.height) as u64 * config.time_base.num
+ config.time_base.den
- 1)
/ config.time_base.den
> AV1_LEVEL_MAX_DISPLAY_RATE[level_idx as usize] as u64
{
return Err(LevelConstraintsExceeded);
}
}
}
let rc = &self.rate_control;
if (rc.emit_pass_data || rc.summary.is_some()) && config.bitrate == 0 {
return Err(TargetBitrateNeeded);
}
Ok(())
}
pub fn tiling_info(&self) -> Result<TilingInfo, InvalidConfig> {
self.validate()?;
let seq = crate::encoder::Sequence::new(&self.enc);
Ok(seq.tiling)
}
}