rav1e/api/config/
mod.rs

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 = 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    // 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}