1// Copyright (c) 2020-2022, The rav1e contributors. All rights reserved
2//
3// This source code is subject to the terms of the BSD 2 Clause License and
4// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
5// was not distributed with this source code in the LICENSE file, you can
6// obtain it at www.aomedia.org/license/software. If the Alliance for Open
7// Media Patent License 1.0 was not distributed with this source code in the
8// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
9
10use 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/// Enumeration of possible invalid configuration errors.
35#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
36#[non_exhaustive]
37pub enum InvalidConfig {
38 /// The width is invalid.
39 #[error("invalid width {0} (expected >= 16, <= 65535)")]
40 InvalidWidth(usize),
41 /// The height is invalid.
42 #[error("invalid height {0} (expected >= 16, <= 65535)")]
43 InvalidHeight(usize),
44 /// Aspect ratio numerator is invalid.
45 #[error("invalid aspect ratio numerator {0} (expected > 0)")]
46 InvalidAspectRatioNum(usize),
47 /// Aspect ratio denominator is invalid.
48 #[error("invalid aspect ratio denominator {0} (expected > 0)")]
49 InvalidAspectRatioDen(usize),
50 /// The render width (width adjusted based on the aspect ratio) is invalid.
51 #[error("invalid render width {0} (expected >= 1, <= 65535")]
52 InvalidRenderWidth(usize),
53 /// The render height (height adjusted based on the aspect ratio) is invalid.
54 #[error("invalid render height {0} (expected >= 1, <= 65535")]
55 InvalidRenderHeight(usize),
56 /// RDO lookahead frame count is invalid.
57 #[error(
58 "invalid rdo lookahead frames {actual} (expected <= {max} and >= {min})"
59 )]
60 InvalidRdoLookaheadFrames {
61 /// The actual value.
62 actual: usize,
63 /// The maximal supported value.
64 max: usize,
65 /// The minimal supported value.
66 min: usize,
67 },
68 /// Maximal keyframe interval is invalid.
69 #[error("invalid max keyframe interval {actual} (expected <= {max})")]
70 InvalidMaxKeyFrameInterval {
71 /// The actual value.
72 actual: u64,
73 /// The maximal supported value.
74 max: u64,
75 },
76 /// Tile columns is invalid.
77 #[error("invalid tile cols {0} (expected power of 2)")]
78 InvalidTileCols(usize),
79 /// Tile rows is invalid.
80 #[error("invalid tile rows {0} (expected power of 2)")]
81 InvalidTileRows(usize),
82 /// Framerate numerator is invalid.
83 #[error("invalid framerate numerator {actual} (expected > 0, <= {max})")]
84 InvalidFrameRateNum {
85 /// The actual value.
86 actual: u64,
87 /// The maximal supported value.
88 max: u64,
89 },
90 /// Framerate denominator is invalid.
91 #[error("invalid framerate denominator {actual} (expected > 0, <= {max})")]
92 InvalidFrameRateDen {
93 /// The actual value.
94 actual: u64,
95 /// The maximal supported value.
96 max: u64,
97 },
98 /// Reservoir frame delay is invalid.
99 #[error("invalid reservoir frame delay {0} (expected >= 12, <= 131072)")]
100 InvalidReservoirFrameDelay(i32),
101 /// Reservoir frame delay is invalid.
102 #[error(
103 "invalid switch frame interval {0} (must only be used with low latency mode)"
104 )]
105 InvalidSwitchFrameInterval(u64),
106
107 /// An option unsupported in still picture mode was enabled along with it.
108 #[error("invalid option {0} specified with still picture mode")]
109 InvalidOptionWithStillPicture(&'static str),
110
111 /// The rate control needs a target bitrate in order to produce results
112 #[error("The rate control requires a target bitrate")]
113 TargetBitrateNeeded,
114
115 /// The configuration
116 #[error("Mismatch in the rate control configuration")]
117 RateControlConfigurationMismatch,
118
119 /// The color configuration mismatches AV1 constraints.
120 #[error("Mismatch in the color configuration")]
121 ColorConfigurationMismatch,
122
123 /// The specified level is undefined in the current version of AV1.
124 #[error("Specified level is undefined")]
125 LevelUndefined,
126
127 /// The configuration exceeded the specified level constraints.
128 #[error("Constraints exceeded for specified level")]
129 LevelConstraintsExceeded,
130}
131
132/// Contains the encoder configuration.
133#[derive(Clone, Debug, Default)]
134pub struct Config {
135 /// Settings which impact the produced bitstream.
136 pub(crate) enc: EncoderConfig,
137 /// Rate control configuration
138 pub(crate) rate_control: RateControlConfig,
139 /// The number of threads in the threadpool.
140 pub(crate) threads: usize,
141 /// Shared thread pool
142 pub(crate) pool: Option<Arc<ThreadPool>>,
143 #[cfg(feature = "unstable")]
144 /// Number of parallel encoding slots
145 pub(crate) slots: usize,
146}
147
148impl Config {
149 /// Create a default configuration
150 ///
151 /// same as `Default::default()`
152 pub fn new() -> Self {
153 Config::default()
154 }
155
156 /// Set the encoder configuration
157 ///
158 /// `EncoderConfig` contains the settings impacting the
159 /// codec features used in the produced bitstream.
160 pub fn with_encoder_config(mut self, enc: EncoderConfig) -> Self {
161 self.enc = enc;
162 self
163 }
164
165 /// Set the number of workers in the threadpool
166 ///
167 /// The threadpool is shared across all the different parallel
168 /// components in the encoder.
169 ///
170 /// If it is left unset, the encoder will use the default global
171 /// threadpool provided by Rayon instead.
172 pub const fn with_threads(mut self, threads: usize) -> Self {
173 self.threads = threads;
174 self
175 }
176
177 /// Set the rate control configuration
178 ///
179 /// The default configuration is single pass
180 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 /// Use the provided threadpool
189 ///
190 /// It takes priority over `with_threads()`
191 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 /// Set the maximum number of GOPs to encode in parallel
198 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: Option = TilingInfo::tile_log2(blk_size:1, target:n);
206 if tile_log2.is_none() {
207 return false;
208 }
209 let tile_log2: usize = 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 // FIXME: inter unsupported with 4:2:2 and 4:4:4 chroma sampling
234 let chroma_sampling = config.chroma_sampling;
235
236 // FIXME: tx partition for intra not supported for chroma 422
237 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 // First-pass parameters depend on whether second-pass is in effect.
249 // So `init_first_pass` must follow `init_second_pass`.
250 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 /// Create a new threadpool with this configuration if set,
260 /// or return `None` if global threadpool should be used instead.
261 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 /// Creates a [`Context`] with this configuration.
274 ///
275 /// # Errors
276 ///
277 /// Returns `InvalidConfig` if the config is invalid.
278 ///
279 /// # Examples
280 ///
281 /// ```
282 /// use rav1e::prelude::*;
283 ///
284 /// # fn main() -> Result<(), InvalidConfig> {
285 /// let cfg = Config::default();
286 /// let ctx: Context<u8> = cfg.new_context()?;
287 /// # Ok(())
288 /// # }
289 /// ```
290 ///
291 /// [`Context`]: struct.Context.html
292 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 /// Validates the configuration.
301 ///
302 /// # Errors
303 ///
304 /// - Returns `InvalidConfig` if the tiling config is invalid.
305 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 // <https://aomediacodec.github.io/av1-spec/#color-config-syntax>
393 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 // TODO: add more validation
437 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 /// Provide the tiling information for the current Config
447 ///
448 /// Useful for reporting and debugging.
449 ///
450 /// # Errors
451 ///
452 /// - Returns `InvalidConfig` if the tiling config is invalid.
453 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}
461