Skip to content

Commit c49b9d5

Browse files
committed
Add zones support
Currently implemented: - Overriding the selected encoder & number of passes - Overriding or adding video params - Overriding photon noise setting - Overriding min/max scene length Closes #267
1 parent e9ad35b commit c49b9d5

File tree

12 files changed

+995
-195
lines changed

12 files changed

+995
-195
lines changed

Cargo.lock

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,6 @@ overflow-checks = true
4848

4949
[patch.crates-io]
5050
rav1e = { git = "https://github.com/xiph/rav1e", rev = "de97f3bbcc0336e1e4195ebaeb2c74fda2018dc5" }
51-
av-scenechange = { git = "https://github.com/rust-av/av-scenechange", rev = "9425e3597b27c79cd970756b7cdb9c24cb2c4d03" }
51+
av-scenechange = { git = "https://github.com/rust-av/av-scenechange", rev = "0c63f493200eeccc21193d129fc63cb1265e40fa" }
5252
# TODO: switch to release version once the fix for av_get_best_stream is published on crates.io.
5353
ffmpeg-next = { git = "https://github.com/zmwangx/rust-ffmpeg", rev = "0054b0e51b35ed240b193c7a93455714b4d75726" }

av1an-cli/src/lib.rs

+42
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,47 @@ pub struct CliOpts {
353353
#[clap(long, default_value = "yuv420p10le", help_heading = "ENCODING")]
354354
pub pix_format: Pixel,
355355

356+
/// Path to a file specifying zones within the video with differing encoder settings.
357+
///
358+
/// The zones file should include one zone per line, with each arg within a zone space-separated.
359+
/// No quotes or escaping are needed around the encoder args, as these are assumed to be the last argument.
360+
/// The zone args on each line should be in this order:
361+
///
362+
/// start_frame end_frame encoder reset(opt) video_params
363+
///
364+
/// For example:
365+
///
366+
/// ```ignore
367+
/// 136 169 aom --photon-noise 4 --cq-level=32
368+
/// 169 1330 rav1e reset -s 3 -q 42
369+
/// ```
370+
///
371+
/// Example line 1 will encode frames 136-168 using aomenc with the argument `--cq-level=32`
372+
/// and enable av1an's `--photon-noise` option.
373+
/// Note that the end frame number is *exclusive*. The start and end frame will both be forced
374+
/// to be scenecuts. Additional scene detection will still be applied within the zones.
375+
/// `-1` can be used to refer to the last frame in the video.
376+
///
377+
/// The default behavior as shown on line 1 is to preserve any options passed to
378+
/// `--video-params` or `--photon-noise` in av1an, and append or overwrite
379+
/// the additional zone settings.
380+
///
381+
/// Example line 2 will encode frames 169-1329 using rav1e. The `reset` keyword instructs
382+
/// av1an to ignore any settings which affect the encoder, and use only the
383+
/// parameters from this zone.
384+
///
385+
/// For segments where no zone is specified, the settings passed to av1an itself will be used.
386+
///
387+
/// The video params which may be specified include any parameters that are allowed by
388+
/// the encoder, as well as the following av1an options:
389+
///
390+
/// - `-x`/`--extra-split`
391+
/// - `--min-scene-len`
392+
/// - `--passes`
393+
/// - `--photon-noise` (aomenc only)
394+
#[clap(long, parse(from_os_str), help_heading = "ENCODING")]
395+
pub zones: Option<PathBuf>,
396+
356397
/// Plot an SVG of the VMAF for the encode
357398
///
358399
/// This option is independent of --target-quality, i.e. it can be used with or without it.
@@ -605,6 +646,7 @@ pub fn parse_cli(args: CliOpts) -> anyhow::Result<Vec<EncodeArgs>> {
605646
workers: args.workers,
606647
set_thread_affinity: args.set_thread_affinity,
607648
vs_script: None,
649+
zones: args.zones.clone(),
608650
};
609651

610652
arg.startup_check()?;

av1an-core/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ paste = "1.0.5"
4444
simdutf8 = "0.1.3"
4545
parking_lot = "0.12.0"
4646
cfg-if = "1.0.0"
47+
nom = "7.1.1"
4748
# TODO: move all of this CLI stuff to av1an-cli
4849
ansi_term = "0.12.1"
4950

av1an-core/src/broker.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,21 @@ impl<'a> Broker<'a> {
218218
let padding = printable_base10_digits(self.total_chunks - 1) as usize;
219219

220220
// Run all passes for this chunk
221+
let encoder = chunk
222+
.overrides
223+
.as_ref()
224+
.map_or(self.project.encoder, |ovr| ovr.encoder);
225+
let passes = chunk
226+
.overrides
227+
.as_ref()
228+
.map_or(self.project.passes, |ovr| ovr.passes);
221229
let mut tpl_crash_workaround = false;
222230
for current_pass in 1..=self.project.passes {
223231
for r#try in 1..=self.max_tries {
224232
let res = self.project.create_pipes(
225233
chunk,
234+
encoder,
235+
passes,
226236
current_pass,
227237
worker_id,
228238
padding,
@@ -246,7 +256,7 @@ impl<'a> Broker<'a> {
246256
// since `Broker::encoding_loop` will print the error message as well
247257
warn!("Encoder failed (on chunk {}):\n{}", chunk.index, e);
248258

249-
if self.project.encoder == Encoder::aom
259+
if encoder == Encoder::aom
250260
&& !tpl_crash_workaround
251261
&& memmem::rfind(e.stderr.as_bytes(), b"av1_tpl_stats_ready").is_some()
252262
{

av1an-core/src/chunk.rs

+5-18
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::scenes::ZoneOptions;
12
use serde::{Deserialize, Serialize};
23
use std::{ffi::OsString, path::Path};
34

@@ -8,31 +9,14 @@ pub struct Chunk {
89
pub source: Vec<OsString>,
910
pub output_ext: String,
1011
pub frames: usize,
12+
pub overrides: Option<ZoneOptions>,
1113
// do not break compatibility with output produced by older versions of av1an
1214
/// Optional target quality CQ level
1315
#[serde(rename = "per_shot_target_quality_cq")]
1416
pub tq_cq: Option<u32>,
1517
}
1618

1719
impl Chunk {
18-
pub fn new(
19-
temp: String,
20-
index: usize,
21-
source: Vec<OsString>,
22-
output_ext: &'static str,
23-
frames: usize,
24-
per_shot_target_quality_cq: Option<u32>,
25-
) -> Result<Self, anyhow::Error> {
26-
Ok(Self {
27-
temp,
28-
index,
29-
source,
30-
output_ext: output_ext.to_owned(),
31-
frames,
32-
tq_cq: per_shot_target_quality_cq,
33-
})
34-
}
35-
3620
/// Returns numeric name of chunk `00001`
3721
pub fn name(&self) -> String {
3822
format!("{:05}", self.index)
@@ -62,6 +46,7 @@ mod tests {
6246
output_ext: "ivf".to_owned(),
6347
frames: 5,
6448
tq_cq: None,
49+
overrides: None,
6550
};
6651
assert_eq!("00001", ch.name());
6752
}
@@ -74,6 +59,7 @@ mod tests {
7459
output_ext: "ivf".to_owned(),
7560
frames: 5,
7661
tq_cq: None,
62+
overrides: None,
7763
};
7864
assert_eq!("10000", ch.name());
7965
}
@@ -87,6 +73,7 @@ mod tests {
8773
output_ext: "ivf".to_owned(),
8874
frames: 5,
8975
tq_cq: None,
76+
overrides: None,
9077
};
9178
assert_eq!("d/encode/00001.ivf", ch.output());
9279
}

av1an-core/src/encoder.rs

+9
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,15 @@ impl Encoder {
435435
}
436436
}
437437

438+
pub const fn format(self) -> &'static str {
439+
match self {
440+
Self::aom | Self::rav1e | Self::svt_av1 => "av1",
441+
Self::vpx => "vpx",
442+
Self::x264 => "h264",
443+
Self::x265 => "h265",
444+
}
445+
}
446+
438447
/// Get the default output extension for the encoder
439448
pub const fn output_extension(&self) -> &'static str {
440449
match &self {

av1an-core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ mod grain;
5757
pub(crate) mod parse;
5858
pub mod progress_bar;
5959
pub mod scene_detect;
60+
mod scenes;
6061
pub mod settings;
6162
pub mod split;
6263
pub mod target_quality;

0 commit comments

Comments
 (0)