Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crf-search: 2nd iteration 20/80 & disable on small min/max crf ranges #234

Merged
merged 3 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Unreleased
* crf-search: Tweak 2nd iteration logic that slices the crf range at the 25% or 75% crf point.
- Widen to 20%/80% to account for searches of the "middle" two subranges being more optimal.
- Disable when using custom min/max crf ranges under half the default.

# v0.7.19
* Fix stdin handling sometimes breaking bash shells.

Expand Down
48 changes: 24 additions & 24 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 19 additions & 7 deletions src/command/crf_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use std::{
};

const BAR_LEN: u64 = 1_000_000_000;
const DEFAULT_MIN_CRF: f32 = 10.0;

/// Interpolated binary search using sample-encode to find the best crf
/// value delivering min-vmaf & max-encoded-percent.
Expand All @@ -46,12 +47,12 @@ pub struct Args {
pub max_encoded_percent: f32,

/// Minimum (highest quality) crf value to try.
#[arg(long, default_value_t = 10.0)]
#[arg(long, default_value_t = DEFAULT_MIN_CRF)]
pub min_crf: f32,

/// Maximum (lowest quality) crf value to try.
///
/// [default: 55, 46 for x264,x265, 255 for rav1e]
/// [default: 55, 46 for x264,x265, 255 for rav1e,av1_vaapi]
#[arg(long)]
pub max_crf: Option<f32>,

Expand Down Expand Up @@ -144,9 +145,20 @@ async fn _run(
input_probe: Arc<Ffprobe>,
bar: ProgressBar,
) -> Result<Sample, Error> {
let max_crf = max_crf.unwrap_or_else(|| args.encoder.default_max_crf());
let default_max_crf = args.encoder.default_max_crf();
let max_crf = max_crf.unwrap_or(default_max_crf);
ensure_other!(*min_crf < max_crf, "Invalid --min-crf & --max-crf");

// Whether to make the 2nd iteration on the ~20%/~80% crf point instead of the min/max to
// improve interpolation by narrowing the crf range a 20% (or 30%) subrange.
//
// 20/80% is preferred to 25/75% to account for searches in the "middle" benefitting from
// having both bounds computed after the 2nd iteration, whereas the two edges must compute
// the min/max crf on the 3rd iter.
//
// If a custom crf range is being used under half the default, this 2nd cut is not needed.
let cut_on_iter2 = (max_crf - *min_crf) > (default_max_crf - DEFAULT_MIN_CRF) * 0.5;

let crf_increment = crf_increment
.unwrap_or_else(|| args.encoder.default_crf_increment())
.max(0.001);
Expand Down Expand Up @@ -230,8 +242,8 @@ async fn _run(
ensure_or_no_good_crf!(sample_small_enough, sample);
return Ok(sample);
}
None if run == 1 && sample.q + 1 < max_q => {
q = (sample.q + max_q) / 2;
None if cut_on_iter2 && run == 1 && sample.q + 1 < max_q => {
q = (sample.q as f32 * 0.4 + max_q as f32 * 0.6).round() as _;
}
None => q = max_q,
};
Expand All @@ -257,8 +269,8 @@ async fn _run(
Some(lower) => {
q = vmaf_lerp_q(*min_vmaf, &sample, lower);
}
None if run == 1 && sample.q > min_q + 1 => {
q = (min_q + sample.q) / 2;
None if cut_on_iter2 && run == 1 && sample.q > min_q + 1 => {
q = (sample.q as f32 * 0.4 + min_q as f32 * 0.6).round() as _;
}
None => q = min_q,
};
Expand Down