1use thiserror::Error;
11
12use rayon::{ThreadPool, ThreadPoolBuilder};
13use std::sync::Arc;
14
15use crate::api::{ChromaSampling, Context, ContextInner, PixelRange};
16use crate::util::Pixel;
17
18mod encoder;
19pub use encoder::*;
20
21pub use av1_grain::*;
22
23use crate::levels::*;
24
25mod rate;
26pub use rate::Error as RateControlError;
27pub use rate::{RateControlConfig, RateControlSummary};
28
29mod speedsettings;
30pub use speedsettings::*;
31
32pub use crate::tiling::TilingInfo;
33
34#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
36#[non_exhaustive]
37pub enum InvalidConfig {
38 #[error("invalid width {0} (expected >= 16, <= 65535)")]
40 InvalidWidth(usize),
41 #[error("invalid height {0} (expected >= 16, <= 65535)")]
43 InvalidHeight(usize),
44 #[error("invalid aspect ratio numerator {0} (expected > 0)")]
46 InvalidAspectRatioNum(usize),
47 #[error("invalid aspect ratio denominator {0} (expected > 0)")]
49 InvalidAspectRatioDen(usize),
50 #[error("invalid render width {0} (expected >= 1, <= 65535")]
52 InvalidRenderWidth(usize),
53 #[error("invalid render height {0} (expected >= 1, <= 65535")]
55 InvalidRenderHeight(usize),
56 #[error(
58 "invalid rdo lookahead frames {actual} (expected <= {max} and >= {min})"
59 )]
60 InvalidRdoLookaheadFrames {
61 actual: usize,
63 max: usize,
65 min: usize,
67 },
68 #[error("invalid max keyframe interval {actual} (expected <= {max})")]
70 InvalidMaxKeyFrameInterval {
71 actual: u64,
73 max: u64,
75 },
76 #[error("invalid tile cols {0} (expected power of 2)")]
78 InvalidTileCols(usize),
79 #[error("invalid tile rows {0} (expected power of 2)")]
81 InvalidTileRows(usize),
82 #[error("invalid framerate numerator {actual} (expected > 0, <= {max})")]
84 InvalidFrameRateNum {
85 actual: u64,
87 max: u64,
89 },
90 #[error("invalid framerate denominator {actual} (expected > 0, <= {max})")]
92 InvalidFrameRateDen {
93 actual: u64,
95 max: u64,
97 },
98 #[error("invalid reservoir frame delay {0} (expected >= 12, <= 131072)")]
100 InvalidReservoirFrameDelay(i32),
101 #[error(
103 "invalid switch frame interval {0} (must only be used with low latency mode)"
104 )]
105 InvalidSwitchFrameInterval(u64),
106
107 #[error("invalid option {0} specified with still picture mode")]
109 InvalidOptionWithStillPicture(&'static str),
110
111 #[error("The rate control requires a target bitrate")]
113 TargetBitrateNeeded,
114
115 #[error("Mismatch in the rate control configuration")]
117 RateControlConfigurationMismatch,
118
119 #[error("Mismatch in the color configuration")]
121 ColorConfigurationMismatch,
122
123 #[error("Specified level is undefined")]
125 LevelUndefined,
126
127 #[error("Constraints exceeded for specified level")]
129 LevelConstraintsExceeded,
130}
131
132#[derive(Clone, Debug, Default)]
134pub struct Config {
135 pub(crate) enc: EncoderConfig,
137 pub(crate) rate_control: RateControlConfig,
139 pub(crate) threads: usize,
141 pub(crate) pool: Option<Arc<ThreadPool>>,
143 #[cfg(feature = "unstable")]
144 pub(crate) slots: usize,
146}
147
148impl Config {
149 pub fn new() -> Self {
153 Config::default()
154 }
155
156 pub fn with_encoder_config(mut self, enc: EncoderConfig) -> Self {
161 self.enc = enc;
162 self
163 }
164
165 pub const fn with_threads(mut self, threads: usize) -> Self {
173 self.threads = threads;
174 self
175 }
176
177 pub const fn with_rate_control(
181 mut self, rate_control: RateControlConfig,
182 ) -> Self {
183 self.rate_control = rate_control;
184 self
185 }
186
187 #[cfg(feature = "unstable")]
188 pub fn with_thread_pool(mut self, pool: Arc<ThreadPool>) -> Self {
192 self.pool = Some(pool);
193 self
194 }
195
196 #[cfg(feature = "unstable")]
197 pub const fn with_parallel_gops(mut self, slots: usize) -> Self {
199 self.slots = slots;
200 self
201 }
202}
203
204fn check_tile_log2(n: usize) -> bool {
205 let tile_log2 = TilingInfo::tile_log2(1, n);
206 if tile_log2.is_none() {
207 return false;
208 }
209 let tile_log2 = tile_log2.unwrap();
210
211 ((1 << tile_log2) - n) == 0 || n == 0
212}
213
214impl Config {
215 pub(crate) fn new_inner<T: Pixel>(
216 &self,
217 ) -> Result<ContextInner<T>, InvalidConfig> {
218 assert!(
219 8 * std::mem::size_of::<T>() >= self.enc.bit_depth,
220 "The Pixel u{} does not match the Config bit_depth {}",
221 8 * std::mem::size_of::<T>(),
222 self.enc.bit_depth
223 );
224
225 self.validate()?;
226
227 let mut config = self.enc.clone();
228 config.set_key_frame_interval(
229 config.min_key_frame_interval,
230 config.max_key_frame_interval,
231 );
232
233 let chroma_sampling = config.chroma_sampling;
235
236 if chroma_sampling == ChromaSampling::Cs422 {
238 config.speed_settings.transform.rdo_tx_decision = false;
239 }
240
241 let mut inner = ContextInner::new(&config);
242
243 if let Some(ref s) = self.rate_control.summary {
244 inner.rc_state.init_second_pass();
245 inner.rc_state.setup_second_pass(s);
246 }
247
248 if self.rate_control.emit_pass_data {
251 let maybe_pass1_log_base_q = (self.rate_control.summary.is_none())
252 .then(|| inner.rc_state.select_pass1_log_base_q(&inner, 0));
253 inner.rc_state.init_first_pass(maybe_pass1_log_base_q);
254 }
255
256 Ok(inner)
257 }
258
259 pub(crate) fn new_thread_pool(&self) -> Option<Arc<ThreadPool>> {
262 if let Some(ref p) = self.pool {
263 Some(p.clone())
264 } else if self.threads != 0 {
265 let pool =
266 ThreadPoolBuilder::new().num_threads(self.threads).build().unwrap();
267 Some(Arc::new(pool))
268 } else {
269 None
270 }
271 }
272
273 pub fn new_context<T: Pixel>(&self) -> Result<Context<T>, InvalidConfig> {
293 let inner = self.new_inner()?;
294 let config = (*inner.config).clone();
295 let pool = self.new_thread_pool();
296
297 Ok(Context { is_flushing: false, inner, pool, config })
298 }
299
300 pub fn validate(&self) -> Result<(), InvalidConfig> {
306 use InvalidConfig::*;
307
308 let config = &self.enc;
309
310 if (config.still_picture && config.width < 1)
311 || (!config.still_picture && config.width < 16)
312 || config.width > u16::MAX as usize
313 {
314 return Err(InvalidWidth(config.width));
315 }
316 if (config.still_picture && config.height < 1)
317 || (!config.still_picture && config.height < 16)
318 || config.height > u16::MAX as usize
319 {
320 return Err(InvalidHeight(config.height));
321 }
322
323 if config.sample_aspect_ratio.num == 0 {
324 return Err(InvalidAspectRatioNum(
325 config.sample_aspect_ratio.num as usize,
326 ));
327 }
328 if config.sample_aspect_ratio.den == 0 {
329 return Err(InvalidAspectRatioDen(
330 config.sample_aspect_ratio.den as usize,
331 ));
332 }
333
334 let (render_width, render_height) = config.render_size();
335 if render_width == 0 || render_width > u16::MAX as usize {
336 return Err(InvalidRenderWidth(render_width));
337 }
338 if render_height == 0 || render_height > u16::MAX as usize {
339 return Err(InvalidRenderHeight(render_height));
340 }
341
342 if config.speed_settings.rdo_lookahead_frames > MAX_RDO_LOOKAHEAD_FRAMES
343 || config.speed_settings.rdo_lookahead_frames < 1
344 {
345 return Err(InvalidRdoLookaheadFrames {
346 actual: config.speed_settings.rdo_lookahead_frames,
347 max: MAX_RDO_LOOKAHEAD_FRAMES,
348 min: 1,
349 });
350 }
351 if config.max_key_frame_interval > MAX_MAX_KEY_FRAME_INTERVAL {
352 return Err(InvalidMaxKeyFrameInterval {
353 actual: config.max_key_frame_interval,
354 max: MAX_MAX_KEY_FRAME_INTERVAL,
355 });
356 }
357
358 if !check_tile_log2(config.tile_cols) {
359 return Err(InvalidTileCols(config.tile_cols));
360 }
361 if !check_tile_log2(config.tile_rows) {
362 return Err(InvalidTileRows(config.tile_rows));
363 }
364
365 if config.time_base.num == 0 || config.time_base.num > u32::MAX as u64 {
366 return Err(InvalidFrameRateNum {
367 actual: config.time_base.num,
368 max: u32::MAX as u64,
369 });
370 }
371 if config.time_base.den == 0 || config.time_base.den > u32::MAX as u64 {
372 return Err(InvalidFrameRateDen {
373 actual: config.time_base.den,
374 max: u32::MAX as u64,
375 });
376 }
377
378 if let Some(delay) = config.reservoir_frame_delay {
379 if !(12..=131_072).contains(&delay) {
380 return Err(InvalidReservoirFrameDelay(delay));
381 }
382 }
383
384 if config.switch_frame_interval > 0 && !config.low_latency {
385 return Err(InvalidSwitchFrameInterval(config.switch_frame_interval));
386 }
387
388 if config.enable_timing_info && config.still_picture {
389 return Err(InvalidOptionWithStillPicture("enable_timing_info"));
390 }
391
392 if let Some(color_description) = config.color_description {
394 if config.chroma_sampling != ChromaSampling::Cs400
395 && color_description.is_srgb_triple()
396 {
397 if config.pixel_range != PixelRange::Full {
398 return Err(ColorConfigurationMismatch);
399 }
400 if config.chroma_sampling != ChromaSampling::Cs444 {
401 return Err(ColorConfigurationMismatch);
402 }
403 }
404 }
405
406 if let Some(level_idx) = config.level_idx {
407 if level_idx > 31 {
408 return Err(LevelUndefined);
409 }
410 if level_idx < 31 {
411 if !AV1_LEVEL_DEFINED[level_idx as usize] {
412 return Err(LevelUndefined);
413 }
414 if config.width * config.height
415 > AV1_LEVEL_MAX_PIC_SIZE[level_idx as usize]
416 {
417 return Err(LevelConstraintsExceeded);
418 }
419 if config.width > AV1_LEVEL_MAX_H_SIZE[level_idx as usize] {
420 return Err(LevelConstraintsExceeded);
421 }
422 if config.height > AV1_LEVEL_MAX_V_SIZE[level_idx as usize] {
423 return Err(LevelConstraintsExceeded);
424 }
425 if ((config.width * config.height) as u64 * config.time_base.num
426 + config.time_base.den
427 - 1)
428 / config.time_base.den
429 > AV1_LEVEL_MAX_DISPLAY_RATE[level_idx as usize] as u64
430 {
431 return Err(LevelConstraintsExceeded);
432 }
433 }
434 }
435
436 let rc = &self.rate_control;
438
439 if (rc.emit_pass_data || rc.summary.is_some()) && config.bitrate == 0 {
440 return Err(TargetBitrateNeeded);
441 }
442
443 Ok(())
444 }
445
446 pub fn tiling_info(&self) -> Result<TilingInfo, InvalidConfig> {
454 self.validate()?;
455
456 let seq = crate::encoder::Sequence::new(&self.enc);
457
458 Ok(seq.tiling)
459 }
460}