use crate::{Template, TvaiError, TvaiResult}; use std::collections::HashMap; use serde::{Deserialize, Serialize}; /// 音频编解码器配置 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AudioCodec { pub name: String, #[serde(rename = "ffmpegOpts")] pub ffmpeg_opts: String, pub ext: Vec, pub bitrate: Option, } /// 比特率配置 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BitrateConfig { pub min: u32, #[serde(rename = "minRec")] pub min_rec: u32, pub max: u32, pub default: u32, pub suggested: Vec, #[serde(rename = "ffmpegOpt")] pub ffmpeg_opt: String, } /// 视频编码器配置 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct VideoEncoder { pub id: String, pub encoder: String, pub profile: Option, #[serde(rename = "allowsAlpha")] pub allows_alpha: u8, #[serde(rename = "ffmpegOpts")] pub ffmpeg_opts: String, pub ext: Vec, pub os: Option, #[serde(rename = "minSize")] pub min_size: Option<[u32; 2]>, #[serde(rename = "maxSize")] pub max_size: Option<[u32; 2]>, #[serde(rename = "maxBitDepth")] pub max_bit_depth: Option, #[serde(rename = "bitrateOpts")] pub bitrate_opts: Option, #[serde(rename = "cqpValues")] pub cqp_values: Option>>, pub gpu: Option, #[serde(rename = "autoBitsPerPixel")] pub auto_bits_per_pixel: Option, #[serde(rename = "maxBitRate")] pub max_bit_rate: Option, pub device: Option, pub compute: Option, #[serde(rename = "isImage")] pub is_image: Option, } /// 比特率选项 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BitrateOptions { pub cbr: Option, pub vbr: Option, } /// 模型推荐规则 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ModelRecommendationRule { pub id: String, pub property: String, #[serde(rename = "type")] pub rule_type: String, pub conditions: Vec, } /// 推荐条件 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RecommendationCondition { pub min: Option, pub max: Option, #[serde(rename = "recommendedModelValues")] pub recommended_model_values: Vec, } /// Topaz Video AI 模板的 FFmpeg 命令生成器 #[derive(Debug, Default)] pub struct FfmpegCommandGenerator { /// 从模板模型到 FFmpeg 滤镜模型的映射 model_mappings: HashMap, /// 音频编解码器配置 audio_codecs: Vec, /// 视频编码器配置 video_encoders: Vec, /// 模型推荐规则 model_recommendation_rules: Vec, } impl FfmpegCommandGenerator { /// 创建新的 FFmpeg 命令生成器 pub fn new() -> Self { let mut generator = Self { model_mappings: HashMap::new(), audio_codecs: Vec::new(), video_encoders: Vec::new(), model_recommendation_rules: Vec::new(), }; // 初始化默认模型映射 generator.init_model_mappings(); // 加载配置文件 if let Err(e) = generator.load_configurations() { eprintln!("警告:加载配置文件失败: {}", e); } generator } /// 从内置配置加载编解码器和模型推荐规则 fn load_configurations(&mut self) -> TvaiResult<()> { // 内置音频编解码器配置 const AUDIO_CODECS_JSON: &str = r#"[ { "name": "AAC", "ffmpegOpts": "aac -ac 2", "ext": [ "mp4", "mov", "mkv", "avi" ], "bitrate": { "min": 32, "minRec": 128, "max": 320, "default": 320, "suggested": [ 128, 160, 192, 256, 320 ], "ffmpegOpt": "-b:a k" } }, { "name": "AC3", "ffmpegOpts": "ac3", "ext": [ "mp4", "mov", "mkv", "avi" ], "bitrate": { "min": 160, "minRec": 160, "max": 640, "default": 448, "suggested": [ 160, 192, 256, 320, 448, 640 ], "ffmpegOpt": "-b:a k" } }, { "name": "PCM", "ffmpegOpts": "pcm_s24le", "ext": [ "mov", "mkv", "avi" ] }, { "name": "Vorbis", "ffmpegOpts": "vorbis -ac 2", "ext": [ "webm" ] } ]"#; // 解析音频编解码器配置 self.audio_codecs = serde_json::from_str(AUDIO_CODECS_JSON) .map_err(|e| TvaiError::ConfigError(format!("解析内置音频编解码器配置失败: {}", e)))?; // 内置视频编码器配置(部分常用编码器) const VIDEO_ENCODERS_JSON: &str = r#"[ { "id": "h264-high-win-nvidia", "encoder": "H264", "profile": "High", "allowsAlpha": 0, "ffmpegOpts": "-c:v h264_nvenc -profile:v high -pix_fmt yuv420p -g 30", "bitrateOpts": { "cbr": "-rc cbr -b:v -preset p6 ", "vbr": "-preset p7 -tune hq -rc constqp -qp -rc-lookahead 20 -spatial_aq 1 -aq-strength 15 -b:v 0" }, "cqpValues": { "High": [ 18 ], "Mid": [ 25 ], "Low": [ 28 ] }, "ext": [ "mov", "mkv", "mp4" ], "maxBitRate": 2000, "os": "windows|linux", "device": "nvidia|tesla", "minSize": [145,145], "maxSize": [4096,4096], "maxBitDepth": 8 }, { "id": "h265-main-win-nvidia", "encoder": "H265", "profile": "Main", "allowsAlpha": 0, "gpu": "nvidia", "ffmpegOpts": "-c:v hevc_nvenc -profile:v main -pix_fmt yuv420p -b_ref_mode disabled -tag:v hvc1 -g 30", "bitrateOpts": { "cbr": "-rc cbr -b:v -preset p6", "vbr": "-preset p7 -tune hq -rc constqp -qp -rc-lookahead 20 -spatial_aq 1 -aq-strength 15 -b:v 0" }, "cqpValues": { "High": [ 17 ], "Mid": [ 25 ], "Low": [ 28 ] }, "ext": [ "mov", "mkv", "mp4" ], "maxBitRate": 2000, "os": "windows|linux", "device": "nvidia|tesla", "minSize": [129,129], "maxSize": [8192,4320], "maxBitDepth": 12 }, { "id": "h264-high-osx", "encoder": "H264", "profile": "High", "allowsAlpha": 0, "ffmpegOpts": "-c:v h264_videotoolbox -profile:v high -pix_fmt yuv420p -allow_sw 1 -g 30", "ext": [ "mov", "mkv", "mp4" ], "gpu": "appleIntel", "autoBitsPerPixel": "1.0", "maxBitRate": 2000, "os": "osx", "minSize": [2,2], "maxSize": [7680,4320], "doNotScaleFullColorRange": "always" }, { "id": "h265-main-osx", "encoder": "H265", "profile": "Main", "allowsAlpha": 0, "ffmpegOpts": "-c:v hevc_videotoolbox -profile:v main -tag:v hvc1 -pix_fmt yuv420p -allow_sw 1 -g 30", "ext": [ "mov", "mkv", "mp4" ], "gpu": "appleIntel", "autoBitsPerPixel": "0.8", "maxBitRate": 2000, "os": "osx", "minSize": [2,2], "maxSize": [8192,4320], "doNotScaleFullColorRange": "always" }, { "id": "h264-high-win-amd", "encoder": "H264", "profile": "High", "allowsAlpha": 0, "ffmpegOpts": "-c:v h264_amf -profile:v high -pix_fmt yuv420p -g 30", "gpu": "amd", "bitrateOpts": { "cbr": "-b:v ", "vbr": "-b:v 0 -quality 0 -rc cqp -qp_i -qp_p -qp_b " }, "cqpValues": { "High": [ 19, 20, 20 ], "Mid": [ 20, 23, 23 ], "Low": [ 28, 30, 30 ] }, "ext": [ "mov", "mkv", "mp4" ], "maxBitRate": 2000, "os": "windows", "minSize": [2,2], "maxSize": [4096,4096], "device": "amd|radeon" }, { "id": "h265-main-win-amd", "encoder": "H265", "profile": "Main", "allowsAlpha": 0, "gpu": "amd", "ffmpegOpts": "-c:v hevc_amf -profile:v main -profile_tier main -tag:v hvc1 -pix_fmt yuv420p -g 30", "bitrateOpts": { "cbr": "-b:v ", "vbr": "-b:v 0 -quality 0 -rc cqp -qp_i -qp_p " }, "cqpValues": { "High": [17, 19 ], "Mid": [20, 23 ], "Low": [28, 30 ] }, "ext": [ "mov", "mkv", "mp4" ], "maxBitRate": 2000, "os": "windows", "minSize": [2,2], "maxSize": [8192,4320], "device": "amd|radeon" }, { "id": "libx264-default", "encoder": "H264", "profile": "High", "allowsAlpha": 0, "ffmpegOpts": "-c:v libx264 -profile:v high -pix_fmt yuv420p", "ext": [ "mov", "mkv", "mp4", "avi" ], "maxBitRate": 2000, "os": "windows|linux|osx", "minSize": [1,1], "maxSize": [16384,16384] }, { "id": "libx265-default", "encoder": "H265", "profile": "Main", "allowsAlpha": 0, "ffmpegOpts": "-c:v libx265 -profile:v main -pix_fmt yuv420p", "ext": [ "mov", "mkv", "mp4" ], "maxBitRate": 2000, "os": "windows|linux|osx", "minSize": [1,1], "maxSize": [16384,16384] } ]"#; // 解析视频编码器配置 self.video_encoders = serde_json::from_str(VIDEO_ENCODERS_JSON) .map_err(|e| TvaiError::ConfigError(format!("解析内置视频编码器配置失败: {}", e)))?; // 内置模型推荐规则 const MODEL_RECOMMENDATION_RULES_JSON: &str = r#"[ { "id": "target-resolution", "property": "height", "type":"range", "conditions": [ { "max": 480, "min": 1, "recommendedModelValues": ["iris", "artemis"] }, { "min": 481, "max": 720, "recommendedModelValues": ["proteus", "rhea"] }, { "min": 721, "max": 1080, "recommendedModelValues": ["proteus", "nyx", "rhea"] }, { "min": 1081, "recommendedModelValues": ["nyx", "theia", "proteus"] } ] } ]"#; // 解析模型推荐规则 self.model_recommendation_rules = serde_json::from_str(MODEL_RECOMMENDATION_RULES_JSON) .map_err(|e| TvaiError::ConfigError(format!("解析内置模型推荐规则失败: {}", e)))?; Ok(()) } /// 初始化从模板模型到 FFmpeg 滤镜模型的映射 fn init_model_mappings(&mut self) { // 增强模型 - Proteus 系列(通用) self.model_mappings.insert("prob-3".to_string(), "ahq-12".to_string()); self.model_mappings.insert("prob-4".to_string(), "ahq-12".to_string()); // 增强模型 - 专用系列 self.model_mappings.insert("nyx-3".to_string(), "nyx-3".to_string()); // Iris 系列(低光/噪声) self.model_mappings.insert("hyp-1".to_string(), "hyp-1".to_string()); // Hyperion HDR 模型 // Artemis 系列(动画/卡通) self.model_mappings.insert("art-1".to_string(), "art-1".to_string()); self.model_mappings.insert("art-2".to_string(), "art-2".to_string()); self.model_mappings.insert("alq-13".to_string(), "ahq-12".to_string()); // Artemis 基准模型 // Gaia 系列(自然场景) self.model_mappings.insert("gai-1".to_string(), "gai-1".to_string()); self.model_mappings.insert("gai-2".to_string(), "gai-2".to_string()); self.model_mappings.insert("ghq-5".to_string(), "ahq-12".to_string()); // Gaia 基准模型 // Theia 系列(细节恢复) self.model_mappings.insert("the-1".to_string(), "the-1".to_string()); self.model_mappings.insert("the-2".to_string(), "the-2".to_string()); // Iris 系列(低光/噪声) self.model_mappings.insert("iris-1".to_string(), "nyx-3".to_string()); self.model_mappings.insert("iris-2".to_string(), "nyx-3".to_string()); self.model_mappings.insert("iris-3".to_string(), "nyx-3".to_string()); // Iris 基准模型 // Nyx 系列(降噪) self.model_mappings.insert("nyx-1".to_string(), "nyx-3".to_string()); self.model_mappings.insert("nyx-2".to_string(), "nyx-3".to_string()); // Nyx 基准模型 self.model_mappings.insert("nxf-1".to_string(), "nyx-3".to_string()); // Nyx Fast 基准模型 // 专用模型 self.model_mappings.insert("rhea-1".to_string(), "ahq-12".to_string()); // Rhea 基准模型 self.model_mappings.insert("rxl-1".to_string(), "ahq-12".to_string()); // RXL 基准模型 // 帧插值模型 self.model_mappings.insert("apo-8".to_string(), "chr-2".to_string()); // Apollo self.model_mappings.insert("apf-1".to_string(), "chr-2".to_string()); // Apollo Fast self.model_mappings.insert("chr-2".to_string(), "chr-2".to_string()); // Chronos self.model_mappings.insert("chf-3".to_string(), "chr-2".to_string()); // Chronos Fast self.model_mappings.insert("aion-1".to_string(), "chr-2".to_string()); // Aion (16X) // 运动模糊模型 self.model_mappings.insert("thm-2".to_string(), "thm-2".to_string()); // 稳定化模型(映射到 ref-2 用于 tvai_stb) // 注意:模板稳定化使用与 tvai_stb 滤镜不同的方法 } /// 添加自定义模型映射 pub fn add_model_mapping(&mut self, template_model: String, ffmpeg_model: String) { self.model_mappings.insert(template_model, ffmpeg_model); } /// 从模板生成 FFmpeg 命令 pub fn generate(&self, template: &Template, input_file: &str, output_file: &str) -> TvaiResult { let mut filters = Vec::new(); let settings = &template.settings; // 生成增强滤镜(通常是链中的第一个) if settings.enhance.active { let up_filter = self.generate_enhancement_filter(&settings.enhance)?; filters.push(up_filter); } // 生成帧插值滤镜(在增强之后) if settings.slow_motion.active { let fi_filter = self.generate_frame_interpolation_filter(&settings.slow_motion, &settings.output)?; filters.push(fi_filter); } // 生成稳定化滤镜(在插值之后) if settings.stabilize.active { let stb_filter = self.generate_stabilization_filter(&settings.stabilize)?; filters.push(stb_filter); } // 生成运动模糊滤镜(如果激活) if settings.motion_blur.active { let mb_filter = self.generate_motion_blur_filter(&settings.motion_blur)?; filters.push(mb_filter); } // 生成颗粒滤镜(通常是链中的最后一个) if settings.grain.active { let grain_filter = self.generate_grain_filter(&settings.grain)?; filters.push(grain_filter); } // 如果存在滤镜管理器,处理第二次增强 if let Some(ref filter_manager) = settings.filter_manager { if filter_manager.second_enhancement_enabled && settings.enhance.active { let second_enhance_filter = self.generate_second_enhancement_filter(&settings.enhance, filter_manager)?; filters.push(second_enhance_filter); } } // 构建完整的 FFmpeg 命令 let mut command = format!("ffmpeg -i \"{}\"", input_file); if !filters.is_empty() { command.push_str(" -vf \""); command.push_str(&filters.join(",")); command.push('"'); } // 添加输出设置 command.push_str(&self.generate_output_settings(&settings.output)?); command.push_str(&format!(" \"{}\"", output_file)); Ok(command) } /// 生成稳定化滤镜 (tvai_stb) fn generate_stabilization_filter(&self, settings: &crate::StabilizeSettings) -> TvaiResult { let mut params = Vec::new(); // 使用默认的稳定化模型 params.push("model=ref-2".to_string()); // 映射稳定化方法(0: 自动裁剪, 1: 全帧稳定化) let full_frame = if settings.method == 0 { 0 } else { 1 }; params.push(format!("full={}", full_frame)); // 映射平滑度(模板中为 0-100,转换为 0-16 范围) let smoothness = (settings.smooth as f64 / 100.0 * 16.0).min(16.0); params.push(format!("smoothness={:.1}", smoothness)); // 滚动快门校正 if settings.rsc { params.push("roll=1".to_string()); } // 减少运动抖动 if settings.reduce_motion { let reduce_level = settings.reduce_motion_iteration.min(5); params.push(format!("reduce={}", reduce_level)); } // 调试信息 println!("生成 tvai_stb 滤镜参数: {:?}", params); println!("稳定化设置: smoothness={:.1}, full={}, roll={}, reduce={}", smoothness, full_frame, settings.rsc, if settings.reduce_motion { settings.reduce_motion_iteration } else { 0 }); Ok(format!("tvai_stb={}", params.join(":"))) } /// 生成增强滤镜 (tvai_up) fn generate_enhancement_filter(&self, settings: &crate::EnhanceSettings) -> TvaiResult { let mut params = Vec::new(); // 映射模型 let model = self.model_mappings.get(&settings.model) .ok_or_else(|| TvaiError::ModelMappingError(format!("未知的增强模型: {}", settings.model)))?; params.push(format!("model={}", model)); // 添加缩放参数(默认不缩放,除非指定) params.push("scale=0".to_string()); // 将增强参数映射到 tvai_up 参数(使用标准化值 0.0-1.0) if settings.compress != 0 { let compression = settings.compress as f64 / 100.0; params.push(format!("compression={:.2}", compression)); } if settings.detail != 0 { let details = settings.detail as f64 / 100.0; params.push(format!("details={:.2}", details)); } if settings.denoise != 0 { let noise = settings.denoise as f64 / 100.0; params.push(format!("noise={:.2}", noise)); } if settings.dehalo != 0 { let halo = settings.dehalo as f64 / 100.0; params.push(format!("halo={:.2}", halo)); } if settings.deblur != 0 { let blur = settings.deblur as f64 / 100.0; params.push(format!("blur={:.2}", blur)); } if settings.sharpen != 0 { let preblur = -(settings.sharpen as f64 / 100.0); params.push(format!("preblur={:.2}", preblur)); } // 如果不是逐行扫描,添加视频类型参数 if settings.video_type != 1 { match settings.video_type { 0 => params.push("interlaced=1".to_string()), 2 => params.push("telecine=1".to_string()), _ => {} } } // 如果指定了场序,添加场序参数 if settings.field_order != 0 { match settings.field_order { 1 => params.push("field_order=tff".to_string()), 2 => params.push("field_order=bff".to_string()), _ => {} } } // 如果指定了自动增强级别,添加参数 if settings.auto != 0 { let auto_level = settings.auto as f64 / 100.0; params.push(format!("auto={:.2}", auto_level)); } // 如果指定了添加噪声参数,添加参数 if settings.add_noise != 0 { let add_noise = settings.add_noise as f64 / 100.0; params.push(format!("add_noise={:.2}", add_noise)); } // 如果不是默认值,添加恢复原始细节参数 if settings.recover_original_detail_value != 20 { let recover = settings.recover_original_detail_value as f64 / 100.0; params.push(format!("recover_detail={:.2}", recover)); } // 基于 AI 引擎标志添加模型特定优化 if settings.is_artemis { // Artemis 动画/卡通内容优化 params.push("content_type=animation".to_string()); params.push("edge_enhance=1".to_string()); } else if settings.is_gaia { // Gaia 自然场景优化 params.push("content_type=natural".to_string()); params.push("texture_enhance=1".to_string()); } else if settings.is_theia { // Theia 细节恢复优化 params.push("detail_mode=high".to_string()); params.push("sharpening_boost=1".to_string()); } else if settings.is_iris { // Iris 低光/噪声优化 params.push("noise_profile=aggressive".to_string()); params.push("low_light_boost=1".to_string()); } else if settings.is_proteus { // Proteus 是默认的通用模型 params.push("content_type=general".to_string()); } // 如果指定了焦点修复级别,添加参数 if let Some(ref focus_level) = settings.focus_fix_level { if focus_level != "Off" { let focus_value = match focus_level.as_str() { "Low" => 0.25, "Medium" => 0.5, "High" => 0.75, "Maximum" => 1.0, _ => 0.0, }; if focus_value > 0.0 { params.push(format!("focus_fix={:.2}", focus_value)); } } } Ok(format!("tvai_up={}", params.join(":"))) } /// 生成帧插值滤镜 (tvai_fi) fn generate_frame_interpolation_filter(&self, slowmo_settings: &crate::SlowMotionSettings, output_settings: &crate::OutputSettings) -> TvaiResult { let mut params = Vec::new(); // 映射模型 let model = self.model_mappings.get(&slowmo_settings.model) .ok_or_else(|| TvaiError::ModelMappingError(format!("未知的帧插值模型: {}", slowmo_settings.model)))?; params.push(format!("model={}", model)); // 设置慢动作因子 if slowmo_settings.factor != 1.0 { params.push(format!("slowmo={}", slowmo_settings.factor)); } // 设置输出帧率(video_rate 格式) if output_settings.out_fps > 0.0 { // 将浮点数转换为分数格式,例如 60.0 -> "60/1" if output_settings.out_fps.fract() == 0.0 { params.push(format!("fps={}/1", output_settings.out_fps as i32)); } else { // 对于非整数帧率,使用更精确的分数表示 params.push(format!("fps={:.3}", output_settings.out_fps)); } } // 注意:当 out_fps 为 0 时,不添加 fps 参数,让 tvai_fi 使用默认的 "0" 值 // 重复帧阈值(rdt 参数) if !slowmo_settings.duplicate { params.push("rdt=-0.01".to_string()); // 禁用重复帧移除 } else if slowmo_settings.duplicate_threshold != 10 { // 仅在非默认值时添加 let threshold = slowmo_settings.duplicate_threshold as f64 / 1000.0; params.push(format!("rdt={:.3}", threshold)); } // 调试信息 println!("生成 tvai_fi 滤镜参数: {:?}", params); println!("输出 FPS: {}, 慢动作因子: {}", output_settings.out_fps, slowmo_settings.factor); Ok(format!("tvai_fi={}", params.join(":"))) } /// 生成运动模糊滤镜 (tvai_mb) fn generate_motion_blur_filter(&self, settings: &crate::MotionBlurSettings) -> TvaiResult { let mut params = Vec::new(); // 映射模型 let model = self.model_mappings.get(&settings.model) .ok_or_else(|| TvaiError::ModelMappingError(format!("未知的运动模糊模型: {}", settings.model)))?; params.push(format!("model={}", model)); Ok(format!("tvai_mb={}", params.join(":"))) } /// 生成颗粒滤镜 (tvai_grain 或噪声近似) fn generate_grain_filter(&self, settings: &crate::GrainSettings) -> TvaiResult { // 尝试使用 tvai_grain,如果不可用则回退到噪声滤镜 let mut params = Vec::new(); // 颗粒量(0-100 比例) let amount = settings.grain as f64 / 100.0; params.push(format!("amount={:.2}", amount)); // 颗粒大小(1-10 比例) params.push(format!("size={}", settings.grain_size)); // 使用 tvai_grain 滤镜 Ok(format!("tvai_grain={}", params.join(":"))) } /// 生成第二次增强滤镜 fn generate_second_enhancement_filter(&self, enhance_settings: &crate::EnhanceSettings, filter_manager: &crate::FilterManagerSettings) -> TvaiResult { let mut params = Vec::new(); // 使用与主要增强相同的模型 let model = self.model_mappings.get(&enhance_settings.model) .ok_or_else(|| TvaiError::ModelMappingError(format!("未知的增强模型: {}", enhance_settings.model)))?; params.push(format!("model={}", model)); // 设置中间分辨率缩放 let intermediate_scale = match filter_manager.second_enhancement_intermediate_resolution { 0 => 0.5, // 半分辨率 1 => 0.75, // 3/4 分辨率 2 => 1.0, // 相同分辨率 3 => 1.5, // 1.5x 分辨率 4 => 2.0, // 2x 分辨率 _ => 1.0, // 默认为相同分辨率 }; params.push(format!("scale={}", intermediate_scale)); // 为第二次处理应用较轻的增强参数 if enhance_settings.denoise != 0 { let noise = (enhance_settings.denoise as f64 / 100.0) * 0.5; // 减半 params.push(format!("noise={:.2}", noise)); } if enhance_settings.detail != 0 { let details = (enhance_settings.detail as f64 / 100.0) * 0.5; // 减半 params.push(format!("details={:.2}", details)); } Ok(format!("tvai_up={}", params.join(":"))) } /// 根据编解码器名称获取音频编解码器配置 pub fn get_audio_codec(&self, name: &str) -> Option<&AudioCodec> { self.audio_codecs.iter().find(|codec| codec.name.eq_ignore_ascii_case(name)) } /// 根据编码器ID获取视频编码器配置 pub fn get_video_encoder(&self, id: &str) -> Option<&VideoEncoder> { self.video_encoders.iter().find(|encoder| encoder.id == id) } /// 根据编码器名称获取视频编码器配置 pub fn get_video_encoder_by_name(&self, name: &str) -> Option<&VideoEncoder> { self.video_encoders.iter().find(|encoder| encoder.encoder.eq_ignore_ascii_case(name)) } /// 根据分辨率推荐模型 pub fn recommend_model_for_resolution(&self, height: u32) -> Vec { for rule in &self.model_recommendation_rules { if rule.id == "target-resolution" && rule.property == "height" { for condition in &rule.conditions { let min = condition.min.unwrap_or(0); let max = condition.max.unwrap_or(u32::MAX); if height >= min && height <= max { return condition.recommended_model_values.clone(); } } } } vec!["prob-4".to_string()] // 默认推荐 Proteus } /// 使用配置文件生成音频编解码器参数 pub fn generate_audio_codec_params(&self, codec_name: &str, bitrate: Option) -> String { if let Some(codec) = self.get_audio_codec(codec_name) { let mut params = format!("-c:a {}", codec.ffmpeg_opts); if let (Some(bitrate_config), Some(bitrate_value)) = (&codec.bitrate, bitrate) { let bitrate_param = bitrate_config.ffmpeg_opt.replace("", &bitrate_value.to_string()); params.push_str(&format!(" {}", bitrate_param)); } params } else { // 回退到默认设置 if let Some(bitrate) = bitrate { format!("-c:a {} -b:a {}k", codec_name, bitrate) } else { format!("-c:a {}", codec_name) } } } /// 使用配置文件生成视频编码器参数 pub fn generate_video_encoder_params(&self, encoder_id: &str, quality_mode: &str, quality_value: Option) -> String { if let Some(encoder) = self.get_video_encoder(encoder_id) { let mut params = encoder.ffmpeg_opts.clone(); // 添加质量设置 if let (Some(bitrate_opts), Some(quality)) = (&encoder.bitrate_opts, quality_value) { match quality_mode { "cbr" => { if let Some(cbr_template) = &bitrate_opts.cbr { let cbr_param = cbr_template.replace("", &format!("{}k", quality)); params.push_str(&format!(" {}", cbr_param)); } }, "vbr" | "cqp" => { if let Some(vbr_template) = &bitrate_opts.vbr { let mut vbr_param = vbr_template.clone(); // 处理不同的质量参数占位符 if let Some(cqp_values) = &encoder.cqp_values { if let Some(values) = cqp_values.get("Mid") { // 默认使用中等质量 for (i, value) in values.iter().enumerate() { match i { 0 => vbr_param = vbr_param.replace("", &value.to_string()) .replace("", &value.to_string()), 1 => vbr_param = vbr_param.replace("", &value.to_string()), 2 => vbr_param = vbr_param.replace("", &value.to_string()), _ => break, } } } } // 如果没有预设值,使用传入的质量值 vbr_param = vbr_param.replace("", &quality.to_string()); params.push_str(&format!(" {}", vbr_param)); } }, _ => {} } } params } else { // 回退到默认设置 format!("-c:v {}", encoder_id) } } /// 生成输出设置 fn generate_output_settings(&self, settings: &crate::OutputSettings) -> TvaiResult { let mut output_params = Vec::new(); // 检查输出设置是否激活 if !settings.active { // 如果输出设置未激活,使用配置文件中的默认设置 let audio_params = self.generate_audio_codec_params("AAC", Some(128)); output_params.push("-c:v libx264".to_string()); output_params.push(audio_params); return Ok(format!(" {}", output_params.join(" "))); } // 硬件加速 if let Some(ref hwaccel) = settings.hwaccel { output_params.push(format!("-hwaccel {}", hwaccel)); } // 基于优先级设置处理分辨率 let use_custom_resolution = if let (Some(_width), Some(_height)) = (settings.width, settings.height) { // 检查自定义分辨率优先级 let priority = settings.custom_resolution_priority.unwrap_or(0); match priority { 0 => true, // 自定义分辨率具有正常优先级 1 => true, // 自定义分辨率具有高优先级 2 => false, // 尺寸方法优先于自定义分辨率 _ => true, // 默认使用自定义分辨率 } } else { false }; if use_custom_resolution { let width = settings.width.unwrap(); let height = settings.height.unwrap(); // 处理宽高比锁定 if settings.lock_aspect_ratio.unwrap_or(true) { // 使用缩放滤镜保持宽高比 output_params.push(format!("-vf scale={}:{}:force_original_aspect_ratio=decrease", width, height)); // 如果需要,添加填充以达到精确尺寸 if settings.crop_to_fit { // 裁剪以适应精确尺寸 output_params.push(format!("-vf scale={}:{}:force_original_aspect_ratio=increase,crop={}:{}", width, height, width, height)); } else { // 填充以适应精确尺寸 output_params.push(format!("-vf scale={}:{}:force_original_aspect_ratio=decrease,pad={}:{}:(ow-iw)/2:(oh-ih)/2", width, height, width, height)); } } else { // 拉伸到精确尺寸(忽略宽高比) output_params.push(format!("-s {}x{}", width, height)); } } else { // 处理输出尺寸方法(基于 Topaz Video AI 尺寸方法) match settings.out_size_method { 0 => { // 原始尺寸 - 不缩放 }, 1 => { // SD (480p) output_params.push("-s 720x480".to_string()); }, 2 => { // DVD (576p) output_params.push("-s 720x576".to_string()); }, 3 => { // HD Ready (720p) output_params.push("-s 1280x720".to_string()); }, 4 => { // HD (1080p) output_params.push("-s 1920x1080".to_string()); }, 5 => { // QHD (1440p) output_params.push("-s 2560x1440".to_string()); }, 6 => { // FHD (1920x1080) - 与 4 相同 output_params.push("-s 1920x1080".to_string()); }, 7 => { // 4K UHD (2160p) output_params.push("-s 3840x2160".to_string()); }, 8 => { // 8K UHD (4320p) output_params.push("-s 7680x4320".to_string()); }, _ => { // 未知方法 - 保持原始尺寸 } } } // 为尺寸方法输出处理裁剪适应 if settings.crop_to_fit && !use_custom_resolution { // 为尺寸方法输出应用裁剪适应 if let Some(last_param) = output_params.last_mut() { if last_param.starts_with("-s ") { let size = &last_param[3..]; // 用缩放+裁剪替换简单缩放 *last_param = format!("-vf scale={}:force_original_aspect_ratio=increase,crop={}", size, size); } } } // 处理像素宽高比 if settings.output_par != 0 { match settings.output_par { 1 => output_params.push("-aspect 4:3".to_string()), 2 => output_params.push("-aspect 16:9".to_string()), 3 => output_params.push("-aspect 1:1".to_string()), _ => {} } } // 帧率 if settings.out_fps > 0.0 { output_params.push(format!("-r {}", settings.out_fps)); } // 视频编解码器 - 使用配置文件 if let Some(ref codec) = settings.codec { // 尝试从配置文件中查找编码器 if let Some(encoder) = self.get_video_encoder_by_name(codec) { let quality_mode = if settings.bitrate.is_some() { "cbr" } else { "vbr" }; let quality_value = settings.bitrate.map(|b| b as u32).or(settings.crf.map(|c| c as u32)); let encoder_params = self.generate_video_encoder_params(&encoder.id, quality_mode, quality_value); output_params.push(encoder_params); } else { // 回退到传统方式 output_params.push(format!("-c:v {}", codec)); // 视频质量设置 if let Some(bitrate) = settings.bitrate { output_params.push(format!("-b:v {}k", bitrate)); } else if let Some(crf) = settings.crf { // 使用 CRF 进行基于质量的编码 match codec.as_str() { "hevc_nvenc" | "h264_nvenc" => { output_params.push(format!("-cq {}", crf)); }, "h264_amf" | "hevc_amf" => { output_params.push(format!("-qp {}", crf)); }, "h264_qsv" | "hevc_qsv" => { output_params.push(format!("-global_quality {}", crf)); }, _ => { output_params.push(format!("-crf {}", crf)); } } } else { output_params.push("-crf 23".to_string()); // 默认质量 } } } else { // 使用默认的 H.264 编码器 output_params.push("-c:v libx264".to_string()); output_params.push("-crf 23".to_string()); } // 编码预设 if let Some(ref preset) = settings.preset { output_params.push(format!("-preset {}", preset)); } else { output_params.push("-preset medium".to_string()); } // 视频配置文件 if let Some(ref profile) = settings.profile { output_params.push(format!("-profile:v {}", profile)); } // 视频级别 if let Some(ref level) = settings.level { output_params.push(format!("-level {}", level)); } // GOP 大小 if let Some(gop_size) = settings.gop_size { output_params.push(format!("-g {}", gop_size)); } // B 帧 if let Some(b_frames) = settings.b_frames { output_params.push(format!("-bf {}", b_frames)); } // 像素格式 if let Some(ref pix_fmt) = settings.pixel_format { output_params.push(format!("-pix_fmt {}", pix_fmt)); } // 颜色设置 if let Some(ref colorspace) = settings.colorspace { output_params.push(format!("-colorspace {}", colorspace)); } if let Some(ref color_primaries) = settings.color_primaries { output_params.push(format!("-color_primaries {}", color_primaries)); } if let Some(ref color_trc) = settings.color_trc { output_params.push(format!("-color_trc {}", color_trc)); } // 音频设置 - 使用配置文件 if let Some(ref audio_codec) = settings.audio_codec { if audio_codec != "copy" { // 使用配置文件生成音频编解码器参数 let audio_params = self.generate_audio_codec_params(audio_codec, settings.audio_bitrate.map(|b| b as u32)); output_params.push(audio_params); // 添加额外的音频参数 if let Some(audio_sample_rate) = settings.audio_sample_rate { output_params.push(format!("-ar {}", audio_sample_rate)); } if let Some(audio_channels) = settings.audio_channels { output_params.push(format!("-ac {}", audio_channels)); } } else { output_params.push("-c:a copy".to_string()); } } else { // 使用默认音频编解码器 let default_audio = self.generate_audio_codec_params("AAC", Some(128)); output_params.push(default_audio); } // 自定义参数 if let Some(ref custom_params) = settings.custom_params { for param in custom_params { output_params.push(param.clone()); } } if output_params.is_empty() { Ok(String::new()) } else { Ok(format!(" {}", output_params.join(" "))) } } /// 获取可用的模型映射 pub fn get_model_mappings(&self) -> &HashMap { &self.model_mappings } /// 验证模板用于 FFmpeg 生成 pub fn validate_template(&self, template: &Template) -> TvaiResult<()> { let settings = &template.settings; // 检查是否启用了任何处理模块 if !settings.enhance.active && !settings.slow_motion.active && !settings.stabilize.active && !settings.grain.active { return Err(TvaiError::ValidationError("没有激活的处理模块".to_string())); } // 如果激活,验证增强模型 if settings.enhance.active && !self.model_mappings.contains_key(&settings.enhance.model) { return Err(TvaiError::ModelMappingError(format!("未知的增强模型: {}", settings.enhance.model))); } // 如果激活,验证帧插值模型 if settings.slow_motion.active && !self.model_mappings.contains_key(&settings.slow_motion.model) { return Err(TvaiError::ModelMappingError(format!("未知的帧插值模型: {}", settings.slow_motion.model))); } Ok(()) } /// 生成带硬件加速的高级 FFmpeg 命令 pub fn generate_with_hardware_acceleration(&self, template: &Template, input_file: &str, output_file: &str, gpu_device: Option<&str>) -> TvaiResult { let mut command = self.generate(template, input_file, output_file)?; // 为 TVAI 滤镜添加 GPU 设备规范 if let Some(device) = gpu_device { command = command.replace("tvai_up=", &format!("tvai_up=device={}:", device)); command = command.replace("tvai_fi=", &format!("tvai_fi=device={}:", device)); command = command.replace("tvai_stb=", &format!("tvai_stb=device={}:", device)); } Ok(command) } /// 生成带自定义输出编解码器的命令 pub fn generate_with_codec(&self, template: &Template, input_file: &str, output_file: &str, codec: &str, quality: Option) -> TvaiResult { // 创建带自定义编解码器设置的修改模板 let mut modified_template = template.clone(); modified_template.settings.output.codec = Some(codec.to_string()); if let Some(q) = quality { // 设置质量时清除比特率 modified_template.settings.output.bitrate = None; modified_template.settings.output.crf = Some(q); } // 使用修改的模板生成命令 self.generate(&modified_template, input_file, output_file) } /// 生成批处理命令 pub fn generate_batch_commands(&self, template: &Template, input_files: &[String], output_dir: &str) -> TvaiResult> { let mut commands = Vec::new(); for input_file in input_files { let input_path = std::path::Path::new(input_file); let filename = input_path.file_stem() .ok_or_else(|| TvaiError::FfmpegError("无效的输入文件名".to_string()))? .to_string_lossy(); let output_file = format!("{}/{}_processed.mp4", output_dir, filename); let command = self.generate(template, input_file, &output_file)?; commands.push(command); } Ok(commands) } /// 获取所有可用的音频编解码器 pub fn get_available_audio_codecs(&self) -> &Vec { &self.audio_codecs } /// 获取所有可用的视频编码器 pub fn get_available_video_encoders(&self) -> &Vec { &self.video_encoders } /// 根据操作系统和GPU类型筛选视频编码器 pub fn get_compatible_video_encoders(&self, os: &str, gpu: Option<&str>) -> Vec<&VideoEncoder> { self.video_encoders.iter().filter(|encoder| { // 检查操作系统兼容性 let os_compatible = encoder.os.as_ref().map_or(true, |encoder_os| { encoder_os.split('|').any(|supported_os| supported_os == os) }); // 检查GPU兼容性 let gpu_compatible = if let Some(gpu_type) = gpu { encoder.gpu.as_ref().map_or(true, |encoder_gpu| encoder_gpu == gpu_type) } else { encoder.gpu.is_none() // 如果没有指定GPU,只返回不需要GPU的编码器 }; os_compatible && gpu_compatible }).collect() } /// 根据文件扩展名获取兼容的编码器 pub fn get_encoders_for_extension(&self, extension: &str) -> Vec<&VideoEncoder> { self.video_encoders.iter().filter(|encoder| { encoder.ext.iter().any(|ext| ext.eq_ignore_ascii_case(extension)) }).collect() } /// 生成带有自动编码器选择的命令 pub fn generate_with_auto_encoder(&self, template: &Template, input_file: &str, output_file: &str, target_os: &str, gpu: Option<&str>) -> TvaiResult { // 从输出文件获取扩展名 let output_path = std::path::Path::new(output_file); let extension = output_path.extension() .and_then(|ext| ext.to_str()) .unwrap_or("mp4"); // 获取兼容的编码器 let compatible_encoders = self.get_compatible_video_encoders(target_os, gpu); let suitable_encoders: Vec<_> = compatible_encoders.into_iter() .filter(|encoder| encoder.ext.iter().any(|ext| ext.eq_ignore_ascii_case(extension))) .collect(); if suitable_encoders.is_empty() { return Err(TvaiError::FfmpegError(format!("没有找到适用于 {} 格式和 {} 操作系统的编码器", extension, target_os))); } // 选择第一个合适的编码器 let selected_encoder = suitable_encoders[0]; // 创建修改的模板 let mut modified_template = template.clone(); modified_template.settings.output.codec = Some(selected_encoder.encoder.clone()); // 生成命令 self.generate(&modified_template, input_file, output_file) } /// 生成编码器信息报告 pub fn generate_encoder_report(&self) -> String { let mut report = String::new(); report.push_str("=== 可用编码器报告 ===\n\n"); // 按编码器类型分组 let mut h264_encoders = Vec::new(); let mut h265_encoders = Vec::new(); let mut av1_encoders = Vec::new(); let mut other_encoders = Vec::new(); for encoder in &self.video_encoders { match encoder.encoder.as_str() { "H264" => h264_encoders.push(encoder), "H265" => h265_encoders.push(encoder), "AV1" => av1_encoders.push(encoder), _ => other_encoders.push(encoder), } } // 生成各类型编码器的报告 if !h264_encoders.is_empty() { report.push_str("## H.264 编码器\n"); for encoder in h264_encoders { report.push_str(&format!("- {} ({}): {}\n", encoder.id, encoder.profile.as_deref().unwrap_or("默认"), encoder.ffmpeg_opts)); } report.push('\n'); } if !h265_encoders.is_empty() { report.push_str("## H.265 编码器\n"); for encoder in h265_encoders { report.push_str(&format!("- {} ({}): {}\n", encoder.id, encoder.profile.as_deref().unwrap_or("默认"), encoder.ffmpeg_opts)); } report.push('\n'); } if !av1_encoders.is_empty() { report.push_str("## AV1 编码器\n"); for encoder in av1_encoders { report.push_str(&format!("- {} ({}): {}\n", encoder.id, encoder.profile.as_deref().unwrap_or("默认"), encoder.ffmpeg_opts)); } report.push('\n'); } if !other_encoders.is_empty() { report.push_str("## 其他编码器\n"); for encoder in other_encoders { report.push_str(&format!("- {} {}: {}\n", encoder.encoder, encoder.id, encoder.ffmpeg_opts)); } report.push('\n'); } // 音频编解码器报告 report.push_str("## 音频编解码器\n"); for codec in &self.audio_codecs { report.push_str(&format!("- {}: {}\n", codec.name, codec.ffmpeg_opts)); } report } } /// 用于更复杂场景的 FFmpeg 命令构建器 #[derive(Debug, Default)] pub struct FfmpegCommandBuilder { input_file: String, output_file: String, filters: Vec, codec: String, quality: Option, hardware_accel: Option, custom_params: Vec, } impl FfmpegCommandBuilder { /// 创建新的命令构建器 pub fn new(input_file: &str, output_file: &str) -> Self { Self { input_file: input_file.to_string(), output_file: output_file.to_string(), filters: Vec::new(), codec: "libx264".to_string(), quality: Some(18), hardware_accel: None, custom_params: Vec::new(), } } /// 添加滤镜 pub fn add_filter(mut self, filter: &str) -> Self { self.filters.push(filter.to_string()); self } /// 设置编解码器 pub fn codec(mut self, codec: &str) -> Self { self.codec = codec.to_string(); self } /// 设置质量 pub fn quality(mut self, quality: i32) -> Self { self.quality = Some(quality); self } /// 设置硬件加速 pub fn hardware_accel(mut self, accel: &str) -> Self { self.hardware_accel = Some(accel.to_string()); self } /// 添加自定义参数 pub fn custom_param(mut self, param: &str) -> Self { self.custom_params.push(param.to_string()); self } /// 构建命令 pub fn build(self) -> String { let mut command = format!("ffmpeg -i \"{}\"", self.input_file); if let Some(accel) = &self.hardware_accel { command.push_str(&format!(" -hwaccel {}", accel)); } if !self.filters.is_empty() { command.push_str(" -vf \""); command.push_str(&self.filters.join(",")); command.push('"'); } command.push_str(&format!(" -c:v {}", self.codec)); if let Some(quality) = self.quality { match self.codec.as_str() { "hevc_nvenc" | "h264_nvenc" => { command.push_str(&format!(" -cq {}", quality)); }, "h264_amf" | "hevc_amf" => { command.push_str(&format!(" -qp {}", quality)); }, "h264_qsv" | "hevc_qsv" => { command.push_str(&format!(" -global_quality {}", quality)); }, _ => { command.push_str(&format!(" -crf {}", quality)); } } } for param in &self.custom_params { command.push_str(&format!(" {}", param)); } command.push_str(&format!(" \"{}\"", self.output_file)); command } }