use itertools::*;
use crate::api::color::*;
use crate::api::config::GrainTableSegment;
use crate::api::{Rational, SpeedSettings};
use crate::encoder::Tune;
use crate::serialize::{Deserialize, Serialize};
use std::fmt;
pub(crate) const MAX_RDO_LOOKAHEAD_FRAMES: usize = usize::MAX - 1;
pub(crate) const MAX_MAX_KEY_FRAME_INTERVAL: u64 = i32::MAX as u64 / 3;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EncoderConfig {
pub width: usize,
pub height: usize,
pub sample_aspect_ratio: Rational,
pub time_base: Rational,
pub bit_depth: usize,
pub chroma_sampling: ChromaSampling,
pub chroma_sample_position: ChromaSamplePosition,
pub pixel_range: PixelRange,
pub color_description: Option<ColorDescription>,
pub mastering_display: Option<MasteringDisplay>,
pub content_light: Option<ContentLight>,
pub level_idx: Option<u8>,
pub enable_timing_info: bool,
pub still_picture: bool,
pub error_resilient: bool,
pub switch_frame_interval: u64,
pub min_key_frame_interval: u64,
pub max_key_frame_interval: u64,
pub reservoir_frame_delay: Option<i32>,
pub low_latency: bool,
pub quantizer: usize,
pub min_quantizer: u8,
pub bitrate: i32,
pub tune: Tune,
pub film_grain_params: Option<Vec<GrainTableSegment>>,
pub tile_cols: usize,
pub tile_rows: usize,
pub tiles: usize,
pub speed_settings: SpeedSettings,
}
impl Default for EncoderConfig {
fn default() -> Self {
const DEFAULT_SPEED: u8 = 6;
Self::with_speed_preset(DEFAULT_SPEED)
}
}
impl EncoderConfig {
pub fn with_speed_preset(speed: u8) -> Self {
EncoderConfig {
width: 640,
height: 480,
sample_aspect_ratio: Rational { num: 1, den: 1 },
time_base: Rational { num: 1, den: 30 },
bit_depth: 8,
chroma_sampling: ChromaSampling::Cs420,
chroma_sample_position: ChromaSamplePosition::Unknown,
pixel_range: Default::default(),
color_description: None,
mastering_display: None,
content_light: None,
level_idx: None,
enable_timing_info: false,
still_picture: false,
error_resilient: false,
switch_frame_interval: 0,
min_key_frame_interval: 12,
max_key_frame_interval: 240,
min_quantizer: 0,
reservoir_frame_delay: None,
low_latency: false,
quantizer: 100,
bitrate: 0,
tune: Tune::default(),
film_grain_params: None,
tile_cols: 0,
tile_rows: 0,
tiles: 0,
speed_settings: SpeedSettings::from_preset(speed),
}
}
pub fn set_key_frame_interval(
&mut self, min_interval: u64, max_interval: u64,
) {
self.min_key_frame_interval = min_interval;
self.max_key_frame_interval = if max_interval == 0 {
MAX_MAX_KEY_FRAME_INTERVAL
} else {
max_interval
};
}
pub fn frame_rate(&self) -> f64 {
Rational::from_reciprocal(self.time_base).as_f64()
}
pub fn render_size(&self) -> (usize, usize) {
let sar = self.sample_aspect_ratio.as_f64();
if sar > 1.0 {
((self.width as f64 * sar).round() as usize, self.height)
} else {
(self.width, (self.height as f64 / sar).round() as usize)
}
}
#[inline]
pub const fn temporal_rdo(&self) -> bool {
!self.speed_settings.transform.tx_domain_distortion
}
pub fn is_hdr(&self) -> bool {
self
.color_description
.map(|colors| {
colors.transfer_characteristics == TransferCharacteristics::SMPTE2084
})
.unwrap_or(false)
}
pub(crate) fn get_film_grain_at(
&self, timestamp: u64,
) -> Option<&GrainTableSegment> {
self.film_grain_params.as_ref().and_then(|entries| {
entries.iter().find(|entry| {
timestamp >= entry.start_time && timestamp < entry.end_time
})
})
}
pub(crate) fn get_film_grain_mut_at(
&mut self, timestamp: u64,
) -> Option<&mut GrainTableSegment> {
self.film_grain_params.as_mut().and_then(|entries| {
entries.iter_mut().find(|entry| {
timestamp >= entry.start_time && timestamp < entry.end_time
})
})
}
}
impl fmt::Display for EncoderConfig {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let pairs = [
("keyint_min", self.min_key_frame_interval.to_string()),
("keyint_max", self.max_key_frame_interval.to_string()),
("quantizer", self.quantizer.to_string()),
("bitrate", self.bitrate.to_string()),
("min_quantizer", self.min_quantizer.to_string()),
("low_latency", self.low_latency.to_string()),
("tune", self.tune.to_string()),
(
"rdo_lookahead_frames",
self.speed_settings.rdo_lookahead_frames.to_string(),
),
(
"multiref",
(!self.low_latency || self.speed_settings.multiref).to_string(),
),
("fast_deblock", self.speed_settings.fast_deblock.to_string()),
(
"scene_detection_mode",
self.speed_settings.scene_detection_mode.to_string(),
),
("cdef", self.speed_settings.cdef.to_string()),
("lrf", self.speed_settings.lrf.to_string()),
("enable_timing_info", self.enable_timing_info.to_string()),
(
"min_block_size",
self.speed_settings.partition.partition_range.min.to_string(),
),
(
"max_block_size",
self.speed_settings.partition.partition_range.max.to_string(),
),
(
"encode_bottomup",
self.speed_settings.partition.encode_bottomup.to_string(),
),
(
"non_square_partition_max_threshold",
self
.speed_settings
.partition
.non_square_partition_max_threshold
.to_string(),
),
(
"reduced_tx_set",
self.speed_settings.transform.reduced_tx_set.to_string(),
),
(
"tx_domain_distortion",
self.speed_settings.transform.tx_domain_distortion.to_string(),
),
(
"tx_domain_rate",
self.speed_settings.transform.tx_domain_rate.to_string(),
),
(
"rdo_tx_decision",
self.speed_settings.transform.rdo_tx_decision.to_string(),
),
(
"prediction_modes",
self.speed_settings.prediction.prediction_modes.to_string(),
),
(
"fine_directional_intra",
self.speed_settings.prediction.fine_directional_intra.to_string(),
),
(
"include_near_mvs",
self.speed_settings.motion.include_near_mvs.to_string(),
),
(
"use_satd_subpel",
self.speed_settings.motion.use_satd_subpel.to_string(),
),
];
write!(
f,
"{}",
pairs.iter().map(|pair| format!("{}={}", pair.0, pair.1)).join(" ")
)
}
}