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 | |