| 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 | |
| 10 | use thiserror::Error; |
| 11 | |
| 12 | use rayon::{ThreadPool, ThreadPoolBuilder}; |
| 13 | use std::sync::Arc; |
| 14 | |
| 15 | use crate::api::{ChromaSampling, Context, ContextInner, PixelRange}; |
| 16 | use crate::util::Pixel; |
| 17 | |
| 18 | mod encoder; |
| 19 | pub use encoder::*; |
| 20 | |
| 21 | pub use av1_grain::*; |
| 22 | |
| 23 | use crate::levels::*; |
| 24 | |
| 25 | mod rate; |
| 26 | pub use rate::Error as RateControlError; |
| 27 | pub use rate::{RateControlConfig, RateControlSummary}; |
| 28 | |
| 29 | mod speedsettings; |
| 30 | pub use speedsettings::*; |
| 31 | |
| 32 | pub use crate::tiling::TilingInfo; |
| 33 | |
| 34 | /// Enumeration of possible invalid configuration errors. |
| 35 | #[derive (Debug, Clone, Copy, Eq, PartialEq, Error)] |
| 36 | #[non_exhaustive ] |
| 37 | pub 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)] |
| 134 | pub 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 | |
| 148 | impl 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 | |
| 204 | fn 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 | |
| 214 | impl 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 | |