From 66eaae2a32c44261620357f6a02c84c2f88f95bf Mon Sep 17 00:00:00 2001 From: lifegpc Date: Tue, 14 Mar 2023 12:57:59 +0000 Subject: [PATCH] Add new option: x264-crf ( #330 Fix typo in x264-profile option handle --- src/download.rs | 6 +++++ src/opt/crf.rs | 8 +++++++ src/opt/mod.rs | 3 +++ src/opthelper.rs | 18 ++++++++++++++- src/opts.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++ src/settings_list.rs | 4 ++++ ugoira/src/ugoira.c | 8 +++---- 7 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 src/opt/crf.rs diff --git a/src/download.rs b/src/download.rs index 3dbe883..75f4f9d 100644 --- a/src/download.rs +++ b/src/download.rs @@ -360,6 +360,12 @@ pub async fn download_artwork( if !profile.is_auto() { options.set("profile", profile.as_str(), None)?; } + match helper.x264_crf() { + Some(crf) => { + options.set("crf", format!("{}", crf), None)?; + } + None => {} + } let frames = UgoiraFrames::from_json(&ugoira_data["frames"])?; let output_file_name = base.join(format!("{}.mp4", id)); convert_ugoira_to_mp4( diff --git a/src/opt/crf.rs b/src/opt/crf.rs new file mode 100644 index 0000000..db3eecf --- /dev/null +++ b/src/opt/crf.rs @@ -0,0 +1,8 @@ +use json::JsonValue; + +pub fn check_crf(obj: &JsonValue) -> bool { + match obj.as_f32() { + Some(crf) => crf >= -1f32, + None => false, + } +} diff --git a/src/opt/mod.rs b/src/opt/mod.rs index 6bc3a08..f4b0100 100644 --- a/src/opt/mod.rs +++ b/src/opt/mod.rs @@ -1,5 +1,8 @@ /// Author name filters pub mod author_name_filter; +#[cfg(feature = "ugoira")] +/// libx264 Constant Rate Factor settings +pub mod crf; /// Proxy settings pub mod proxy; pub mod size; diff --git a/src/opthelper.rs b/src/opthelper.rs index bb00aef..0ddb037 100644 --- a/src/opthelper.rs +++ b/src/opthelper.rs @@ -439,6 +439,22 @@ impl OptHelper { false } + #[cfg(feature = "ugoira")] + /// The Constant Rate Factor when converting ugoira(GIF) to video. + pub fn x264_crf(&self) -> Option { + match self.opt.get_ref().x264_crf { + Some(r) => { + return Some(r); + } + None => {} + } + if self.settings.get_ref().have("x264-crf") { + let v = self.settings.get_ref().get("x264-crf").unwrap(); + return v.as_f32(); + } + None + } + #[cfg(feature = "ugoira")] /// Return the x264 profile when converting ugoira(GIF) to video. pub fn x264_profile(&self) -> X264Profile { @@ -449,7 +465,7 @@ impl OptHelper { None => {} } if self.settings.get_ref().have("x264-profile") { - let v = self.settings.get_ref().get("server").unwrap(); + let v = self.settings.get_ref().get("x264-profile").unwrap(); return X264Profile::from_str(v.as_str().unwrap()).unwrap(); } X264Profile::default() diff --git a/src/opts.rs b/src/opts.rs index 7abff50..ee539cf 100644 --- a/src/opts.rs +++ b/src/opts.rs @@ -13,6 +13,8 @@ use std::convert::TryFrom; use std::env; #[cfg(feature = "server")] use std::net::SocketAddr; +#[cfg(feature = "ugoira")] +use std::num::ParseFloatError; use std::num::ParseIntError; use std::num::TryFromIntError; use std::str::FromStr; @@ -113,6 +115,9 @@ pub struct CommandOpts { pub user_agent: Option, /// Urls want to download pub urls: Option>, + #[cfg(feature = "ugoira")] + /// The Constant Rate Factor when converting ugoira(GIF) to video. + pub x264_crf: Option, } impl CommandOpts { @@ -151,6 +156,8 @@ impl CommandOpts { download_base: None, user_agent: None, urls: None, + #[cfg(feature = "ugoira")] + x264_crf: None, } } @@ -260,6 +267,20 @@ pub fn parse_bool>(s: Option) -> Result, String> { } } +#[cfg(feature = "ugoira")] +/// Parse [f32] from string +pub fn parse_f32>(s: Option) -> Result, ParseFloatError> { + match s { + Some(s) => { + let s = s.as_ref(); + let s = s.trim(); + let c = s.parse::()?; + Ok(Some(c)) + } + None => Ok(None), + } +} + /// Prase [i64] from string pub fn parse_i64>(s: Option) -> Result, ParseIntError> { match s { @@ -533,6 +554,13 @@ pub fn parse_cmd() -> Option { "DIR", ); opts.optopt("", "user-agent", gettext("The User-Agent header."), "UA"); + #[cfg(feature = "ugoira")] + opts.optopt( + "", + "x264-crf", + gettext("The Constant Rate Factor when converting ugoira(GIF) to video."), + "float", + ); let result = match opts.parse(&argv[1..]) { Ok(m) => m, Err(err) => { @@ -830,6 +858,30 @@ pub fn parse_cmd() -> Option { } re.as_mut().unwrap().download_base = result.opt_str("download-base"); re.as_mut().unwrap().user_agent = result.opt_str("user-agent"); + #[cfg(feature = "ugoira")] + match parse_optional_opt(&result, "x264-crf", -1f32, parse_f32) { + Ok(r) => match r { + Some(crf) => { + if crf < -1f32 { + println!("{}", gettext("x264-crf can not less than -1.")); + return None; + } else { + re.as_mut().unwrap().x264_crf.replace(crf); + } + } + None => {} + }, + Err(e) => { + println!( + "{} {}", + ("Failed to parse :") + .replace("", "x264-crf") + .as_str(), + e + ); + return None; + } + } re } diff --git a/src/settings_list.rs b/src/settings_list.rs index b5f88a9..3081a84 100644 --- a/src/settings_list.rs +++ b/src/settings_list.rs @@ -7,6 +7,8 @@ use crate::retry_interval::check_retry_interval; use crate::settings::SettingDes; use crate::settings::JsonValueType; use crate::opt::author_name_filter::check_author_name_filters; +#[cfg(feature = "ugoira")] +use crate::opt::crf::check_crf; use crate::opt::proxy::check_proxy; use crate::opt::size::parse_u32_size; #[cfg(feature = "server")] @@ -55,6 +57,8 @@ pub fn get_settings_list() -> Vec { SettingDes::new("db", gettext("Database settings."), JsonValueType::Object, Some(check_db_config)).unwrap(), SettingDes::new("download-base", gettext("The base directory to save downloaded files."), JsonValueType::Str, None).unwrap(), SettingDes::new("user-agent", gettext("The User-Agent header."), JsonValueType::Str, None).unwrap(), + #[cfg(feature = "ugoira")] + SettingDes::new("x264-crf", gettext("The Constant Rate Factor when converting ugoira(GIF) to video."), JsonValueType::Number, Some(check_crf)).unwrap(), ] } diff --git a/ugoira/src/ugoira.c b/ugoira/src/ugoira.c index a5313de..4ef6558 100644 --- a/ugoira/src/ugoira.c +++ b/ugoira/src/ugoira.c @@ -164,7 +164,7 @@ UgoiraError convert_ugoira_to_mp4(const char* src, const char* dest, const Ugoir int err = UGOIRA_OK; int zip_err = 0; zip_t* zip = NULL; - int dcrf = 18; + float dcrf = 18.0; AVDictionaryEntry* tmp_ent = NULL; if (max_fps <= 0) { return RERR(UGOIRA_INVALID_MAX_FPS); @@ -174,8 +174,8 @@ UgoiraError convert_ugoira_to_mp4(const char* src, const char* dest, const Ugoir } tmp_ent = av_dict_get(opts, "crf", NULL, 0); if (tmp_ent) { - int tmp = 0; - if (sscanf(tmp_ent->value, "%i", &tmp) != 1) { + float tmp = 0; + if (sscanf(tmp_ent->value, "%f", &tmp) != 1) { return RERR(UGOIRA_INVALID_CRF); } dcrf = tmp; @@ -331,7 +331,7 @@ UgoiraError convert_ugoira_to_mp4(const char* src, const char* dest, const Ugoir } else { av_opt_set(eoc->priv_data, "preset", "slow", 0); } - av_opt_set_int(eoc->priv_data, "crf", dcrf, 0); + av_opt_set_double(eoc->priv_data, "crf", dcrf, 0); if (opts) { tmp = av_dict_get(opts, "level", NULL, 0); }