diff --git a/README.md b/README.md
index 412e131..2599b52 100644
--- a/README.md
+++ b/README.md
@@ -62,6 +62,7 @@ supported.
- **Intel Quick Sync Video H264/HEVC** (h264_qsv, hevc_qsv)
- ***Intel Arc AV1** (av1_qsv)
- **Apple Silicon H264/HEVC** (h264_videotoolbox, hevc_videotoolbox)
+- ***VAAPI H264/HEVC/AV1 on Linux** (h264_vaapi, hevc_vaapi, av1_vaapi; commonly used with AMD or Intel GPUs)
The following have code in the project to 'support it' but currently do not function correctly:
@@ -114,6 +115,10 @@ it's highly suggested to use the same version, or at least version `6.*` of ffmp
- For Mac, recommended to install via `brew install ffmpeg` (will install ffpmpeg and ffprobe)
- For Linux, recommended to install using your distro's package manager. Note: the default one may be a much lower
version than `6.0` and may require more setup
+ - To use VAAPI on Linux, install the VAAPI driver package supplied by your distribution for your GPU vendor. For
+ example, AMD commonly uses the Mesa VAAPI driver, while current Intel hardware uses `intel-media-driver` (iHD)
+ and older Intel hardware may require the legacy i965 driver
+ - Make sure your user can access the DRM render nodes under `/dev/dri`
2) 7-Zip to unzip any downloaded ffmpeg binaries
3) ffmpeg/ffprobe must be available on your path (tool will error out if it can't find
either); quick path setup guide for
@@ -156,6 +161,36 @@ benchmark is as simple as:
Kill the tool at any time by hitting `ctrl-c` in the terminal/console where the tool is running.
+### VAAPI on Linux
+
+VAAPI encoders use a DRM render node instead of the numeric `--gpu` option used by NVENC and AMF. The default device is
+`/dev/dri/renderD128`; select a different GPU with `--vaapi-device`:
+
+`./benchmark -e h264_vaapi -s 1080-60.y4m --vaapi-device /dev/dri/renderD128`
+
+On a mixed-GPU system, pass the render node belonging to the GPU you want to benchmark, for example
+`/dev/dri/renderD129`. VAAPI render-node ordering is independent of the numeric `--gpu` option.
+
+The user running the benchmark must have permission to access the render device. First inspect the selected render
+node and its permissions:
+
+`ls -l /dev/dri/renderD128`
+
+If the device belongs to a non-root group such as `render` or `video`, add your user to that group—for example,
+`sudo usermod -aG render $USER`—then log out and back in for the new group membership to take effect. If the device's
+group is `root`, do not add your user to the `root` group; instead, change the device's group to one your user belongs
+to, for example `sudo chgrp "$(id -gn)" /dev/dri/renderD128`. This change may be reset when the device or system restarts.
+You can alternatively run the benchmark with `sudo`, but this is not recommended.
+
+For 10-bit HEVC or AV1, add `--ten-bit`. Ten-bit H264 VAAPI is not supported by this tool.
+
+VAAPI is separate from vendor-specific FFmpeg backends such as Intel QSV and Nvidia NVENC. Use `h264_vaapi`,
+`hevc_vaapi`, or `av1_vaapi` to benchmark VAAPI through a DRM render node. AV1 encoding requires hardware and a VAAPI
+driver that exposes AV1 encode support.
+
+Before running, verify that the FFmpeg build exposes the requested `h264_vaapi`, `hevc_vaapi`, or `av1_vaapi` encoder
+with `ffmpeg -encoders`, and inspect available render nodes with `ls /dev/dri/renderD*`.
+
### Understanding the results
You'll be given the maximum fps statistics possible at a given resolution, with those results logged out to a
@@ -398,4 +433,4 @@ An example of what the author did for the nvenc_h264 encoder:
Error about unsupported feature
If you are getting the following: `#![feature] may not be used on the stable release channel`, this should be fixable by
-running `cargo clean`.
\ No newline at end of file
+running `cargo clean`.
diff --git a/benchmark/src/benchmark_cli.rs b/benchmark/src/benchmark_cli.rs
index b4536f7..3b7bcf6 100644
--- a/benchmark/src/benchmark_cli.rs
+++ b/benchmark/src/benchmark_cli.rs
@@ -2,6 +2,7 @@ use clap::Parser;
use cli::cli_util::{
are_all_source_files_present, error_with_ack, get_repo_url, standard_cli_check,
+ validate_vaapi_options,
};
use cli::supported::get_supported_inputs;
@@ -28,10 +29,13 @@ pub struct BenchmarkCli {
/// the GPU you wish to run the encode on; defaults to the first/only GPU found in your system
#[arg(short, long, default_value = "0")]
pub gpu: u8,
+ /// DRM render node used by VAAPI encoders
+ #[arg(long, value_name = "device", default_value = "/dev/dri/renderD128")]
+ pub vaapi_device: String,
/// whether to run decode benchmark as well; defaults to off, as this will take up more storage space
#[arg(short, long)]
pub decode: bool,
- /// adds in '-pix_fmt yuv420p10le' to force 10-bit encoding
+ /// forces 10-bit encoding; VAAPI HEVC uploads p010 hardware frames
#[arg(short, long)]
pub ten_bit: bool,
// this is an internal option not intended to be exposed to the end user
@@ -57,6 +61,7 @@ impl BenchmarkCli {
log_output_directory: String::from(""),
verbose: false,
gpu: 0,
+ vaapi_device: String::from("/dev/dri/renderD128"),
decode: false,
ten_bit: false,
was_ui_opened: false,
@@ -72,6 +77,12 @@ impl BenchmarkCli {
&self.files_directory,
self.was_ui_opened,
);
+ validate_vaapi_options(
+ &self.encoder,
+ &self.vaapi_device,
+ self.ten_bit,
+ self.was_ui_opened,
+ );
// if you did not provide a source file, we'll be running on all expected files
if self.source_file.is_empty() && !are_all_source_files_present(&self.files_directory) {
diff --git a/benchmark/src/main.rs b/benchmark/src/main.rs
index b1d4d5a..c0d1348 100644
--- a/benchmark/src/main.rs
+++ b/benchmark/src/main.rs
@@ -13,6 +13,7 @@ use codecs::get_vendor_for_codec;
use codecs::nvenc::Nvenc;
use codecs::permute::Permute;
use codecs::qsv::QSV;
+use codecs::vaapi::Vaapi;
use codecs::vendor::Vendor;
use engine::benchmark_engine::BenchmarkEngine;
use ffmpeg::metadata::MetaData;
@@ -67,6 +68,7 @@ fn benchmark() {
permutation.bitrate = bitrate;
permutation.encoder_settings = settings;
permutation.verbose = cli.verbose;
+ permutation.vaapi_device = cli.vaapi_device.clone();
// tell this encode run that we'll want to preserve the file output
if cli.decode {
@@ -282,6 +284,10 @@ fn get_benchmark_settings_for(cli: &BenchmarkCli) -> String {
apple_h264.get_benchmark_settings()
}
}
+ Vendor::Vaapi => {
+ let vaapi = Vaapi::new(&cli.encoder, cli.ten_bit);
+ vaapi.get_benchmark_settings()
+ }
Vendor::Unknown => {
// nothing to do here
String::from("")
@@ -294,6 +300,10 @@ fn get_bitrate_for(metadata: &MetaData, string: String) -> u32 {
return *Nvenc::get_resolution_to_bitrate_map(metadata.fps)
.get(&metadata.get_res())
.unwrap();
+ } else if string.contains("vaapi") {
+ return *Vaapi::get_resolution_to_bitrate_map(metadata.fps)
+ .get(&metadata.get_res())
+ .unwrap();
} else {
return *Amf::get_resolution_to_bitrate_map(metadata.fps)
.get(&metadata.get_res())
diff --git a/cli/src/cli_util.rs b/cli/src/cli_util.rs
index c981486..8492918 100644
--- a/cli/src/cli_util.rs
+++ b/cli/src/cli_util.rs
@@ -86,6 +86,32 @@ pub fn standard_cli_check(
}
}
+pub fn validate_vaapi_options(
+ encoder: &String,
+ vaapi_device: &String,
+ ten_bit: bool,
+ was_ui_opened: bool,
+) {
+ if !encoder.contains("vaapi") {
+ return;
+ }
+
+ if !cfg!(target_os = "linux") {
+ println!("Error: VAAPI encoders are currently supported only on Linux");
+ error_with_ack(was_ui_opened);
+ }
+
+ if !Path::new(vaapi_device).exists() {
+ println!("Error: VAAPI device [{}] does not exist", vaapi_device);
+ error_with_ack(was_ui_opened);
+ }
+
+ if encoder == "h264_vaapi" && ten_bit {
+ println!("Error: 10-bit encoding is not supported by h264_vaapi");
+ error_with_ack(was_ui_opened);
+ }
+}
+
pub fn error_with_ack(ack: bool) {
// want to give the user a chance to acknowledge the error
if ack {
diff --git a/cli/src/supported.rs b/cli/src/supported.rs
index 950f182..449e58d 100644
--- a/cli/src/supported.rs
+++ b/cli/src/supported.rs
@@ -1,4 +1,4 @@
-const SUPPORTED_ENCODERS: [&'static str; 10] = [
+const SUPPORTED_ENCODERS: [&'static str; 13] = [
"h264_nvenc",
"hevc_nvenc",
"h264_amf",
@@ -9,6 +9,9 @@ const SUPPORTED_ENCODERS: [&'static str; 10] = [
"h264_videotoolbox",
"hevc_videotoolbox",
"prores_videotoolbox",
+ "h264_vaapi",
+ "hevc_vaapi",
+ "av1_vaapi",
];
const ENCODE_FILES: [&'static str; 8] = [
@@ -26,10 +29,28 @@ pub fn is_encoder_supported(potential_encoder: &String) -> bool {
return SUPPORTED_ENCODERS.contains(&potential_encoder.as_str());
}
-pub fn get_supported_encoders() -> [&'static str; 10] {
+pub fn get_supported_encoders() -> [&'static str; 13] {
return SUPPORTED_ENCODERS;
}
pub fn get_supported_inputs() -> [&'static str; 8] {
return ENCODE_FILES;
}
+
+#[cfg(test)]
+mod tests {
+ use super::{get_supported_encoders, is_encoder_supported};
+
+ #[test]
+ fn av1_vaapi_is_supported() {
+ assert!(is_encoder_supported(&"av1_vaapi".to_string()));
+ }
+
+ #[test]
+ fn vaapi_encoders_are_listed_last() {
+ assert_eq!(
+ &get_supported_encoders()[10..],
+ &["h264_vaapi", "hevc_vaapi", "av1_vaapi"]
+ );
+ }
+}
diff --git a/codecs/src/lib.rs b/codecs/src/lib.rs
index 99a4833..0fcbbd3 100644
--- a/codecs/src/lib.rs
+++ b/codecs/src/lib.rs
@@ -7,6 +7,7 @@ pub mod nvenc;
pub mod permute;
pub mod qsv;
mod resolutions;
+pub mod vaapi;
pub mod vendor;
pub fn get_vendor_for_codec(codec: &String) -> Vendor {
@@ -19,6 +20,8 @@ pub fn get_vendor_for_codec(codec: &String) -> Vendor {
return Vendor::IntelQSV;
} else if codec.contains("videotoolbox") {
return Vendor::Apple;
+ } else if codec.contains("vaapi") {
+ return Vendor::Vaapi;
}
return Vendor::Unknown;
diff --git a/codecs/src/vaapi.rs b/codecs/src/vaapi.rs
new file mode 100644
index 0000000..7ea0a21
--- /dev/null
+++ b/codecs/src/vaapi.rs
@@ -0,0 +1,171 @@
+use std::collections::HashMap;
+
+use crate::permute::Permute;
+use crate::resolutions::map_res_to_bitrate;
+
+pub struct Vaapi {
+ profiles: Vec<&'static str>,
+ permutations: Vec,
+ index: i32,
+}
+
+impl Vaapi {
+ pub fn new(encoder: &str, ten_bit: bool) -> Self {
+ Self {
+ profiles: if encoder == "h264_vaapi" {
+ vec!["high", "main", "constrained_baseline"]
+ } else if encoder == "hevc_vaapi" && ten_bit {
+ vec!["main10"]
+ } else if encoder == "hevc_vaapi" {
+ vec!["main"]
+ } else {
+ // FFmpeg's VAAPI AV1 encoder currently supports Main for both
+ // 8-bit and 10-bit 4:2:0 input.
+ vec!["main"]
+ },
+ permutations: Vec::new(),
+ index: -1,
+ }
+ }
+
+ pub fn get_benchmark_settings(&self) -> String {
+ format!("-rc_mode CBR -profile:v {}", self.profiles.first().unwrap())
+ }
+
+ fn has_next(&self) -> bool {
+ self.index + 1 < self.permutations.len() as i32
+ }
+}
+
+impl Iterator for Vaapi {
+ type Item = (usize, String);
+
+ fn next(&mut self) -> Option {
+ if !self.has_next() {
+ return None;
+ }
+
+ self.index += 1;
+ let index = self.index as usize;
+ Some((index, self.permutations.get(index).unwrap().to_string()))
+ }
+}
+
+impl Permute for Vaapi {
+ fn init(&mut self) -> &Vec {
+ self.index = -1;
+ self.permutations.clear();
+ self.permutations.extend(
+ self.profiles
+ .iter()
+ .map(|profile| format!("-rc_mode CBR -profile:v {}", profile)),
+ );
+ &self.permutations
+ }
+
+ fn run_standard_only(&mut self) -> &Vec {
+ self.index = -1;
+ self.permutations.clear();
+ self.permutations.push(self.get_benchmark_settings());
+ &self.permutations
+ }
+
+ fn get_resolution_to_bitrate_map(fps: u32) -> HashMap {
+ let mut map = HashMap::new();
+ let mut bitrates: [u32; 4] = [20, 35, 50, 85];
+
+ if fps == 120 {
+ bitrates.iter_mut().for_each(|bitrate| *bitrate *= 2);
+ }
+
+ map_res_to_bitrate(&mut map, bitrates);
+ map
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::Vaapi;
+ use crate::permute::Permute;
+
+ #[test]
+ fn h264_uses_high_profile() {
+ assert_eq!(
+ Vaapi::new("h264_vaapi", false).get_benchmark_settings(),
+ "-rc_mode CBR -profile:v high"
+ );
+ }
+
+ #[test]
+ fn hevc_uses_main_profile() {
+ assert_eq!(
+ Vaapi::new("hevc_vaapi", false).get_benchmark_settings(),
+ "-rc_mode CBR -profile:v main"
+ );
+ }
+
+ #[test]
+ fn ten_bit_hevc_uses_main10_profile() {
+ assert_eq!(
+ Vaapi::new("hevc_vaapi", true).get_benchmark_settings(),
+ "-rc_mode CBR -profile:v main10"
+ );
+ }
+
+ #[test]
+ fn av1_uses_main_profile_for_eight_and_ten_bit() {
+ for ten_bit in [false, true] {
+ assert_eq!(
+ Vaapi::new("av1_vaapi", ten_bit).get_benchmark_settings(),
+ "-rc_mode CBR -profile:v main"
+ );
+ }
+ }
+
+ #[test]
+ fn standard_permutation_can_be_iterated() {
+ let mut vaapi = Vaapi::new("h264_vaapi", false);
+ vaapi.run_standard_only();
+ assert_eq!(vaapi.next().unwrap().1, vaapi.get_benchmark_settings());
+ assert!(vaapi.next().is_none());
+ }
+
+ #[test]
+ fn h264_permutations_include_all_eight_bit_profiles() {
+ let mut vaapi = Vaapi::new("h264_vaapi", false);
+ assert_eq!(
+ vaapi.init(),
+ &vec![
+ "-rc_mode CBR -profile:v high".to_string(),
+ "-rc_mode CBR -profile:v main".to_string(),
+ "-rc_mode CBR -profile:v constrained_baseline".to_string(),
+ ]
+ );
+ }
+
+ #[test]
+ fn hevc_permutations_follow_selected_bit_depth() {
+ let mut eight_bit = Vaapi::new("hevc_vaapi", false);
+ assert_eq!(
+ eight_bit.init(),
+ &vec!["-rc_mode CBR -profile:v main".to_string()]
+ );
+
+ let mut ten_bit = Vaapi::new("hevc_vaapi", true);
+ assert_eq!(
+ ten_bit.init(),
+ &vec!["-rc_mode CBR -profile:v main10".to_string()]
+ );
+ }
+
+ #[test]
+ fn av1_has_only_main_profile_permutation() {
+ for ten_bit in [false, true] {
+ let mut vaapi = Vaapi::new("av1_vaapi", ten_bit);
+ assert_eq!(
+ vaapi.init(),
+ &vec!["-rc_mode CBR -profile:v main".to_string()]
+ );
+ }
+ }
+}
diff --git a/codecs/src/vendor.rs b/codecs/src/vendor.rs
index b058b2a..756f3f0 100644
--- a/codecs/src/vendor.rs
+++ b/codecs/src/vendor.rs
@@ -3,5 +3,6 @@ pub enum Vendor {
AMD,
IntelQSV,
Apple,
+ Vaapi,
Unknown,
}
diff --git a/engine/src/engine.rs b/engine/src/engine.rs
index 78d4459..814fa4d 100644
--- a/engine/src/engine.rs
+++ b/engine/src/engine.rs
@@ -39,6 +39,7 @@ pub fn run_encode(
p.bitrate,
p.decode_run,
p.ten_bit,
+ p.vaapi_device,
);
let encode_start_time = SystemTime::now();
@@ -141,12 +142,17 @@ fn log_header(
}
println!("[Resolution:\t{}x{}]", metadata.width, metadata.height);
- println!("[Encoder:\t{}]", permutation.encoder);
+ println!("[Encoder:\t{}]", format_encoder_label(&permutation));
println!("[FPS:\t\t{}]", metadata.fps);
println!("[Bitrate:\t{}Mb/s]", permutation.bitrate);
println!("[{}]", permutation.encoder_settings);
}
+fn format_encoder_label(permutation: &Permutation) -> String {
+ let bit_depth = if permutation.ten_bit { 10 } else { 8 };
+ format!("{} ({}-bit)", permutation.encoder, bit_depth)
+}
+
pub fn spawn_ffmpeg_child(
ffmpeg_args: &FfmpegArgs,
verbose: bool,
diff --git a/engine/src/permutation_engine.rs b/engine/src/permutation_engine.rs
index 865e57a..da21b22 100644
--- a/engine/src/permutation_engine.rs
+++ b/engine/src/permutation_engine.rs
@@ -178,6 +178,7 @@ fn calc_vmaf_score(
p.bitrate.clone(),
p.decode_run,
p.ten_bit,
+ p.vaapi_device.clone(),
);
println!(
diff --git a/ffmpeg/src/args.rs b/ffmpeg/src/args.rs
index 47f42db..5c1168d 100644
--- a/ffmpeg/src/args.rs
+++ b/ffmpeg/src/args.rs
@@ -21,6 +21,7 @@ pub struct FfmpegArgs {
pub stats_period: c_float,
pub decode: bool,
pub ten_bit: bool,
+ pub vaapi_device: String,
}
impl Default for FfmpegArgs {
@@ -41,6 +42,7 @@ impl Default for FfmpegArgs {
stats_period: 0.5,
decode: false,
ten_bit: false,
+ vaapi_device: String::from("/dev/dri/renderD128"),
}
}
}
@@ -53,6 +55,7 @@ impl FfmpegArgs {
current_bitrate: u32,
decode: bool,
ten_bit: bool,
+ vaapi_device: String,
) -> FfmpegArgs {
let ffmpeg_args = FfmpegArgs {
first_input,
@@ -61,6 +64,7 @@ impl FfmpegArgs {
encoder_args: encoder_args.to_string(),
decode,
ten_bit,
+ vaapi_device,
..Default::default()
};
@@ -100,6 +104,17 @@ impl FfmpegArgs {
// always pass the '-y' flag, for output to be overwritten
output.push_str("-y ");
+ let is_vaapi = self.encoder.contains("vaapi");
+ if is_vaapi && !self.decode && !self.is_vmaf {
+ output.push_str(
+ format!(
+ "-init_hw_device vaapi=va:{} -filter_hw_device va ",
+ self.vaapi_device
+ )
+ .as_str(),
+ );
+ }
+
if self.decode {
let vendor = get_vendor_for_codec(&self.encoder);
let hwaccel = match vendor {
@@ -108,12 +123,19 @@ impl FfmpegArgs {
Vendor::IntelQSV => "qsv",
// TODO: implement this
Vendor::Apple => "apple",
+ Vendor::Vaapi => "vaapi",
Vendor::Unknown => "error",
};
output.push_str("-hwaccel ");
output.push_str(hwaccel);
- output.push_str(" ");
+ output.push(' ');
+
+ if is_vaapi {
+ output.push_str("-hwaccel_device ");
+ output.push_str(&self.vaapi_device);
+ output.push(' ');
+ }
}
if self.report {
@@ -139,6 +161,10 @@ impl FfmpegArgs {
if self.is_vmaf {
append_vmaf_only_args(&mut output);
} else {
+ if is_vaapi {
+ let upload_format = if self.ten_bit { "p010" } else { "nv12" };
+ output.push_str(format!(" -vf format={},hwupload", upload_format).as_str());
+ }
append_encode_only_args(
&mut output,
self.bitrate,
@@ -149,7 +175,7 @@ impl FfmpegArgs {
}
// add in pixel format of 10-bit if specified
- if self.ten_bit {
+ if self.ten_bit && !is_vaapi {
output.push(' ');
output.push_str("-pix_fmt yuv420p10le");
}
@@ -318,6 +344,76 @@ mod tests {
);
}
+ #[test]
+ fn vaapi_encode_initializes_device_and_uploads_frames() {
+ let args = FfmpegArgs::build_ffmpeg_args(
+ INPUT_ONE.to_string(),
+ "h264_vaapi".to_string(),
+ &"-rc_mode CBR -profile:v high".to_string(),
+ BITRATE,
+ false,
+ false,
+ "/dev/dri/renderD129".to_string(),
+ );
+
+ assert_eq!(
+ args.to_string(),
+ "-progress tcp://localhost:1234 -stats_period 0.5 -y -init_hw_device vaapi=va:/dev/dri/renderD129 -filter_hw_device va -i 1080-60.y4m -vf format=nv12,hwupload -b:v 6M -c:v h264_vaapi -rc_mode CBR -profile:v high -f null -"
+ );
+ }
+
+ #[test]
+ fn vaapi_ten_bit_encode_uploads_p010_frames() {
+ let args = FfmpegArgs::build_ffmpeg_args(
+ INPUT_ONE.to_string(),
+ "hevc_vaapi".to_string(),
+ &"-rc_mode CBR -profile:v main10".to_string(),
+ BITRATE,
+ false,
+ true,
+ "/dev/dri/renderD128".to_string(),
+ );
+
+ assert!(args.to_string().contains("-vf format=p010,hwupload"));
+ assert!(!args.to_string().contains("-pix_fmt yuv420p10le"));
+ }
+
+ #[test]
+ fn av1_vaapi_encode_uses_main_profile_and_hardware_upload() {
+ let args = FfmpegArgs::build_ffmpeg_args(
+ INPUT_ONE.to_string(),
+ "av1_vaapi".to_string(),
+ &"-rc_mode CBR -profile:v main".to_string(),
+ BITRATE,
+ false,
+ false,
+ "/dev/dri/renderD128".to_string(),
+ );
+
+ assert!(args.to_string().contains("-vf format=nv12,hwupload"));
+ assert!(args
+ .to_string()
+ .contains("-c:v av1_vaapi -rc_mode CBR -profile:v main"));
+ }
+
+ #[test]
+ fn vaapi_decode_selects_requested_device() {
+ let args = FfmpegArgs::build_ffmpeg_args(
+ INPUT_ONE.to_string(),
+ "h264_vaapi".to_string(),
+ &String::new(),
+ BITRATE,
+ true,
+ false,
+ "/dev/dri/renderD129".to_string(),
+ );
+
+ assert_eq!(
+ args.to_string(),
+ "-progress tcp://localhost:1234 -stats_period 0.5 -y -hwaccel vaapi -hwaccel_device /dev/dri/renderD129 -i 1080-60.y4m -f null -"
+ );
+ }
+
fn get_one_input_args() -> FfmpegArgs {
let args = Cli {
encoder: ENCODER.to_string(),
@@ -339,6 +435,7 @@ mod tests {
args.bitrate,
false,
false,
+ String::from("/dev/dri/renderD128"),
);
}
diff --git a/permutation/src/permutation.rs b/permutation/src/permutation.rs
index a07ce3c..cd18ccd 100644
--- a/permutation/src/permutation.rs
+++ b/permutation/src/permutation.rs
@@ -15,6 +15,7 @@ pub struct Permutation {
// determines whether this permutation is the decode run or not
pub decode_run: bool,
pub ten_bit: bool,
+ pub vaapi_device: String,
// whether we are doing any decoding at all
pub is_decoding: bool,
}
@@ -34,6 +35,7 @@ impl Permutation {
decode_run: false,
is_decoding: false,
ten_bit: false,
+ vaapi_device: String::from("/dev/dri/renderD128"),
}
}
diff --git a/permutor-cli/src/main.rs b/permutor-cli/src/main.rs
index fde74ee..f2b2fa2 100644
--- a/permutor-cli/src/main.rs
+++ b/permutor-cli/src/main.rs
@@ -8,6 +8,7 @@ use codecs::get_vendor_for_codec;
use codecs::nvenc::Nvenc;
use codecs::permute::Permute;
use codecs::qsv::QSV;
+use codecs::vaapi::Vaapi;
use codecs::vendor::Vendor;
use engine::permutation_engine::PermutationEngine;
use permutation::permutation::Permutation;
@@ -43,6 +44,9 @@ fn main() {
Vendor::Apple => {
build_apple_silicon_h264_permutations(&mut engine, &cli, bitrate);
}
+ Vendor::Vaapi => {
+ build_vaapi_setting_permutations(&mut engine, &cli, bitrate);
+ }
Vendor::Unknown => {}
}
}
@@ -130,6 +134,32 @@ fn build_amf_setting_permutations(engine: &mut PermutationEngine, cli: &Permutor
}
}
+fn build_vaapi_setting_permutations(
+ engine: &mut PermutationEngine,
+ cli: &PermutorCli,
+ bitrate: u32,
+) {
+ let mut vaapi = Vaapi::new(&cli.encoder, cli.ten_bit);
+ vaapi.init();
+
+ while let Some((_encoder_index, settings)) = vaapi.next() {
+ let mut permutation = Permutation::new(cli.source_file.clone(), cli.encoder.clone());
+ permutation.encoder_settings = settings;
+ permutation.bitrate = bitrate;
+ permutation.check_quality = cli.check_quality;
+ permutation.verbose = cli.verbose;
+ permutation.detect_overload = cli.detect_overload;
+ permutation.allow_duplicates = cli.allow_duplicate_scores;
+ permutation.ten_bit = cli.ten_bit;
+ permutation.vaapi_device = cli.vaapi_device.clone();
+ engine.add(permutation);
+
+ if cli.test_run {
+ break;
+ }
+ }
+}
+
fn build_intel_av1_permutations(engine: &mut PermutationEngine, cli: &PermutorCli, bitrate: u32) {
let mut intel_av1 = AV1QSV::new();
diff --git a/permutor-cli/src/permutor_cli.rs b/permutor-cli/src/permutor_cli.rs
index 6185171..bec40bb 100644
--- a/permutor-cli/src/permutor_cli.rs
+++ b/permutor-cli/src/permutor_cli.rs
@@ -1,6 +1,6 @@
use clap::Parser;
-use cli::cli_util::{error_with_ack, standard_cli_check};
+use cli::cli_util::{error_with_ack, standard_cli_check, validate_vaapi_options};
#[derive(Parser)]
pub struct PermutorCli {
@@ -31,7 +31,7 @@ pub struct PermutorCli {
/// runs just the first permutation for given encoder; useful for testing the tool & output
#[arg(short, long)]
pub test_run: bool,
- /// adds in '-pix_fmt yuv420p10le' to force 10-bit encoding
+ /// forces 10-bit encoding; VAAPI HEVC uploads p010 hardware frames
#[arg(long)]
pub ten_bit: bool,
/// maximum value to increase the bitrate to (in 5Mb/s intervals); if not specified, tool will not permute over bitrate values
@@ -46,6 +46,9 @@ pub struct PermutorCli {
/// the GPU you wish to run the encode on; defaults to the first/only GPU found in your system
#[arg(short, long, default_value = "0")]
pub gpu: u8,
+ /// DRM render node used by VAAPI encoders
+ #[arg(long, value_name = "device", default_value = "/dev/dri/renderD128")]
+ pub vaapi_device: String,
/// opt-out of using b frames for either H264 or HEVC encoders; currently only supported for Nvidia GPUs
#[arg(short, long)]
pub no_b_frame: bool,
@@ -60,6 +63,7 @@ impl PermutorCli {
&self.files_directory,
false,
);
+ validate_vaapi_options(&self.encoder, &self.vaapi_device, self.ten_bit, false);
if self.source_file.is_empty() {
println!("Error: No source file was provided to run on, please specify an input file");