feat(video2x): improve the CLI help message structure and clarity

Signed-off-by: k4yt3x <i@k4yt3x.com>
This commit is contained in:
k4yt3x 2024-11-22 00:00:00 +00:00
parent 3215c89870
commit d3de1ded96
No known key found for this signature in database
2 changed files with 77 additions and 47 deletions

View File

@ -10,9 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Automatic selection of the most suitable pixel format for the output video. - Automatic selection of the most suitable pixel format for the output video.
- Support for specifying arbitrary `AVOptions` for the encoder. - Support for specifying arbitrary `AVOptions` for the encoder (#1232).
- More `AVCodecContext` options. - More `AVCodecContext` options.
### Changed
- Improve the CLI help message structure and clarity.
### Fixed ### Fixed
- Timestamp errors processing frames with PTS equal to 0 (#1222). - Timestamp errors processing frames with PTS equal to 0 (#1222).
@ -38,16 +42,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Vulkan device selection for libplacebo. - Vulkan device selection for libplacebo.
- Status bar and processing statistics. (Video2X Qt6) - Status bar and processing statistics. (Video2X Qt6)
### Fixed
- Wide character string paths support on Windows systems without UTF-8 support enabled (#1201).
### Changed ### Changed
- Automatically detect if options `colorspace` and `range` are supported by the buffer filter. - Automatically detect if options `colorspace` and `range` are supported by the buffer filter.
- Resource file missing error messages. - Resource file missing error messages.
- Rewritten the CLI with C++. - Rewritten the CLI with C++.
### Fixed
- Wide character string paths support on Windows systems without UTF-8 support enabled (#1201).
## [6.0.0] - 2024-10-29 ## [6.0.0] - 2024-10-29
### Added ### Added

View File

@ -56,14 +56,14 @@ std::mutex proc_ctx_mutex;
// Structure to hold parsed arguments // Structure to hold parsed arguments
struct Arguments { struct Arguments {
StringType loglevel = STR("info"); StringType log_level = STR("info");
bool noprogress = false; bool no_progress = false;
// General options // General options
std::filesystem::path in_fname; std::filesystem::path in_fname;
std::filesystem::path out_fname; std::filesystem::path out_fname;
StringType filter_type; StringType filter_type;
uint32_t gpuid = 0; uint32_t gpu_id = 0;
StringType hwaccel = STR("none"); StringType hwaccel = STR("none");
bool no_copy_streams = false; bool no_copy_streams = false;
bool benchmark = false; bool benchmark = false;
@ -293,7 +293,7 @@ void process_video_thread(
EncoderConfig *encoder_config, EncoderConfig *encoder_config,
VideoProcessingContext *proc_ctx VideoProcessingContext *proc_ctx
) { ) {
enum Libvideo2xLogLevel log_level = parse_log_level(arguments->loglevel); enum Libvideo2xLogLevel log_level = parse_log_level(arguments->log_level);
StringType in_fname_string; StringType in_fname_string;
StringType out_fname_string; StringType out_fname_string;
@ -314,7 +314,7 @@ void process_video_thread(
out_fname, out_fname,
log_level, log_level,
arguments->benchmark, arguments->benchmark,
arguments->gpuid, arguments->gpu_id,
hw_device_type, hw_device_type,
filter_config, filter_config,
encoder_config, encoder_config,
@ -346,70 +346,84 @@ int main(int argc, char **argv) {
// Parse command line arguments using Boost.Program_options // Parse command line arguments using Boost.Program_options
try { try {
po::options_description desc("Allowed options"); // clang-format off
po::options_description all_opts("General options");
desc.add_options() all_opts.add_options()
("help", "Display this help page") ("help", "Display this help page")
("version,v", "Print program version") ("version,V", "Print program version and exit")
("loglevel", PO_STR_VALUE<StringType>(&arguments.loglevel)->default_value(STR("info"), ("verbose,v", PO_STR_VALUE<StringType>(&arguments.log_level)->default_value(STR("info"),
"info"), "Set log level (trace, debug, info, warn, error, critical, none)") "info"), "Set verbosity level (trace, debug, info, warn, error, critical, none)")
("noprogress", po::bool_switch(&arguments.noprogress), ("no-progress", po::bool_switch(&arguments.no_progress),
"Do not display the progress bar") "Do not display the progress bar")
("listgpus", "List the available GPUs") ("list-gpus,l", "List the available GPUs")
// General Processing Options // General Processing Options
("input,i", PO_STR_VALUE<StringType>(), "Input video file path") ("input,i", PO_STR_VALUE<StringType>(), "Input video file path")
("output,o", PO_STR_VALUE<StringType>(), "Output video file path") ("output,o", PO_STR_VALUE<StringType>(), "Output video file path")
("filter,f", PO_STR_VALUE<StringType>(&arguments.filter_type), ("filter,f", PO_STR_VALUE<StringType>(&arguments.filter_type),
"Filter to use: 'libplacebo' or 'realesrgan'") "Filter to use: 'libplacebo' or 'realesrgan'")
("gpuid,g", po::value<uint32_t>(&arguments.gpuid)->default_value(0), "Vulkan GPU ID") ("gpu,g", po::value<uint32_t>(&arguments.gpu_id)->default_value(0),
"GPU ID (Vulkan device index)")
("hwaccel,a", PO_STR_VALUE<StringType>(&arguments.hwaccel)->default_value(STR("none"), ("hwaccel,a", PO_STR_VALUE<StringType>(&arguments.hwaccel)->default_value(STR("none"),
"none"), "Hardware acceleration method") "none"), "Hardware acceleration method (mostly for decoding)")
("benchmark", po::bool_switch(&arguments.benchmark), ("benchmark,b", po::bool_switch(&arguments.benchmark),
"Discard processed frames and calculate average FPS") "Discard processed frames and calculate average FPS; "
"useful for detecting encoder bottlenecks")
;
po::options_description encoder_opts("Encoder options");
encoder_opts.add_options()
// Encoder options // Encoder options
("codec,c", PO_STR_VALUE<StringType>(&arguments.codec)->default_value(STR("libx264"), ("codec,c", PO_STR_VALUE<StringType>(&arguments.codec)->default_value(STR("libx264"),
"libx264"), "Output codec") "libx264"), "Output codec")
("no_copy_streams", po::bool_switch(&arguments.no_copy_streams), ("no-copy-streams", po::bool_switch(&arguments.no_copy_streams),
"Do not copy audio and subtitle streams") "Do not copy audio and subtitle streams")
("pix_fmt", PO_STR_VALUE<StringType>(&arguments.pix_fmt), "Output pixel format") ("pix-fmt", PO_STR_VALUE<StringType>(&arguments.pix_fmt), "Output pixel format")
("bit_rate", po::value<int64_t>(&arguments.bit_rate)->default_value(0), ("bit-rate", po::value<int64_t>(&arguments.bit_rate)->default_value(0),
"Bitrate in bits per second") "Bitrate in bits per second")
("rc_buffer_size", po::value<int>(&arguments.rc_buffer_size)->default_value(0), ("rc-buffer-size", po::value<int>(&arguments.rc_buffer_size)->default_value(0),
"Rate control buffer size in bits") "Rate control buffer size in bits")
("rc_min_rate", po::value<int>(&arguments.rc_min_rate)->default_value(0), ("rc-min-rate", po::value<int>(&arguments.rc_min_rate)->default_value(0),
"Minimum rate control") "Minimum rate control")
("rc_max_rate", po::value<int>(&arguments.rc_max_rate)->default_value(0), ("rc-max-rate", po::value<int>(&arguments.rc_max_rate)->default_value(0),
"Maximum rate control") "Maximum rate control")
("qmin", po::value<int>(&arguments.qmin)->default_value(-1), "Minimum quantizer") ("qmin", po::value<int>(&arguments.qmin)->default_value(-1), "Minimum quantizer")
("qmax", po::value<int>(&arguments.qmax)->default_value(-1), "Maximum quantizer") ("qmax", po::value<int>(&arguments.qmax)->default_value(-1), "Maximum quantizer")
("gop_size", po::value<int>(&arguments.gop_size)->default_value(-1), ("gop-size", po::value<int>(&arguments.gop_size)->default_value(-1),
"Group of pictures structure size") "Group of pictures structure size")
("max_b_frames", po::value<int>(&arguments.max_b_frames)->default_value(-1), ("max-b-frames", po::value<int>(&arguments.max_b_frames)->default_value(-1),
"Maximum number of B-frames") "Maximum number of B-frames")
("keyint_min", po::value<int>(&arguments.keyint_min)->default_value(-1), ("keyint-min", po::value<int>(&arguments.keyint_min)->default_value(-1),
"Minimum interval between keyframes") "Minimum interval between keyframes")
("refs", po::value<int>(&arguments.refs)->default_value(-1), ("refs", po::value<int>(&arguments.refs)->default_value(-1),
"Number of reference frames") "Number of reference frames")
("thread_count", po::value<int>(&arguments.thread_count)->default_value(0), ("thread-count", po::value<int>(&arguments.thread_count)->default_value(0),
"Number of threads for encoding") "Number of threads for encoding")
("delay", po::value<int>(&arguments.delay)->default_value(0), ("delay", po::value<int>(&arguments.delay)->default_value(0),
"Delay in milliseconds for encoder") "Delay in milliseconds for encoder")
// Extra encoder options (key-value pairs) // Extra encoder options (key-value pairs)
("extra_option,e", PO_STR_VALUE<std::vector<StringType>>()->multitoken(), ("extra-encoder-option,e", PO_STR_VALUE<std::vector<StringType>>()->multitoken(),
"Additional AVOption(s) for codec settings (-e key=value)") "Additional AVOption(s) for the encoder (format: -e key=value)")
;
// libplacebo options po::options_description libplacebo_opts("libplacebo options");
("shader,s", PO_STR_VALUE<StringType>(), "Name or path of the GLSL shader file to use") libplacebo_opts.add_options()
("shader,s", PO_STR_VALUE<StringType>(), "Name/path of the GLSL shader file to use")
("width,w", po::value<int>(&arguments.width), "Output width") ("width,w", po::value<int>(&arguments.width), "Output width")
("height,h", po::value<int>(&arguments.height), "Output height") ("height,h", po::value<int>(&arguments.height), "Output height")
;
// RealESRGAN options // RealESRGAN options
po::options_description realesrgan_opts("RealESRGAN options");
realesrgan_opts.add_options()
("model,m", PO_STR_VALUE<StringType>(&arguments.model_name), "Name of the model to use") ("model,m", PO_STR_VALUE<StringType>(&arguments.model_name), "Name of the model to use")
("scale,r", po::value<int>(&arguments.scaling_factor), "Scaling factor (2, 3, or 4)") ("scale,r", po::value<int>(&arguments.scaling_factor), "Scaling factor (2, 3, or 4)")
; ;
// clang-format on
// Combine all options
all_opts.add(encoder_opts).add(libplacebo_opts).add(realesrgan_opts);
// Positional arguments // Positional arguments
po::positional_options_description p; po::positional_options_description p;
@ -417,15 +431,27 @@ int main(int argc, char **argv) {
#ifdef _WIN32 #ifdef _WIN32
po::variables_map vm; po::variables_map vm;
po::store(po::wcommand_line_parser(argc, argv).options(desc).positional(p).run(), vm); po::store(po::wcommand_line_parser(argc, argv).options(all_opts).positional(p).run(), vm);
#else #else
po::variables_map vm; po::variables_map vm;
po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm); po::store(po::command_line_parser(argc, argv).options(all_opts).positional(p).run(), vm);
#endif #endif
po::notify(vm); po::notify(vm);
if (vm.count("help")) { if (vm.count("help")) {
std::cout << desc << std::endl; std::cout << all_opts << std::endl;
std::cout
<< "Examples:" << std::endl
<< " Upscale an anime video to 4K using libplacebo:" << std::endl
<< " video2x -i input.mp4 -o output.mp4 -f libplacebo -s anime4k-v4-a+a "
"-w 3840 -h 2160"
<< std::endl
<< std::endl
<< " Upscale a film video by 4x using RealESRGAN with custom encoder options"
<< std::endl
<< " video2x -i input.mkv -o output.mkv -f realesrgan -m realesrgan-plus -r 4 \\"
<< std::endl
<< " -c libx264rgb -e crf=17 -e preset=veryslow -e tune=film" << std::endl;
return 0; return 0;
} }
@ -434,7 +460,7 @@ int main(int argc, char **argv) {
return 0; return 0;
} }
if (vm.count("listgpus")) { if (vm.count("list-gpus")) {
return list_gpus(); return list_gpus();
} }
@ -459,8 +485,8 @@ int main(int argc, char **argv) {
} }
// Parse avoptions // Parse avoptions
if (vm.count("extra_option")) { if (vm.count("extra-encoder-option")) {
for (const auto &opt : vm["extra_option"].as<std::vector<StringType>>()) { for (const auto &opt : vm["extra-encoder-option"].as<std::vector<StringType>>()) {
size_t eq_pos = opt.find('='); size_t eq_pos = opt.find('=');
if (eq_pos != StringType::npos) { if (eq_pos != StringType::npos) {
StringType key = opt.substr(0, eq_pos); StringType key = opt.substr(0, eq_pos);
@ -519,10 +545,10 @@ int main(int argc, char **argv) {
} }
// Validate GPU ID // Validate GPU ID
int gpu_status = is_valid_gpu_id(arguments.gpuid); int gpu_status = is_valid_gpu_id(arguments.gpu_id);
if (gpu_status < 0) { if (gpu_status < 0) {
spdlog::warn("Unable to validate GPU ID."); spdlog::warn("Unable to validate GPU ID.");
} else if (arguments.gpuid > 0 && gpu_status == 0) { } else if (arguments.gpu_id > 0 && gpu_status == 0) {
spdlog::critical("Invalid GPU ID specified."); spdlog::critical("Invalid GPU ID specified.");
return 1; return 1;
} }
@ -551,7 +577,7 @@ int main(int argc, char **argv) {
} }
// Set spdlog log level // Set spdlog log level
auto log_level = parse_log_level(arguments.loglevel); auto log_level = parse_log_level(arguments.log_level);
switch (log_level) { switch (log_level) {
case LIBVIDEO2X_LOG_LEVEL_TRACE: case LIBVIDEO2X_LOG_LEVEL_TRACE:
spdlog::set_level(spdlog::level::trace); spdlog::set_level(spdlog::level::trace);
@ -757,7 +783,7 @@ int main(int argc, char **argv) {
} }
// Display progress // Display progress
if (!arguments.noprogress) { if (!arguments.no_progress) {
int64_t processed_frames, total_frames; int64_t processed_frames, total_frames;
bool pause; bool pause;
{ {