diff --git a/cargos/tvai-v2/Cargo.toml b/cargos/tvai-v2/Cargo.toml index fa9f888..7ec4ffa 100644 --- a/cargos/tvai-v2/Cargo.toml +++ b/cargos/tvai-v2/Cargo.toml @@ -38,3 +38,7 @@ path = "examples/ffmpeg_generation.rs" [[example]] name = "advanced_output_settings" path = "examples/advanced_output_settings.rs" + +[[example]] +name = "output_settings_demo" +path = "examples/output_settings_demo.rs" diff --git a/cargos/tvai-v2/examples/output_settings_demo.rs b/cargos/tvai-v2/examples/output_settings_demo.rs new file mode 100644 index 0000000..ce836df --- /dev/null +++ b/cargos/tvai-v2/examples/output_settings_demo.rs @@ -0,0 +1,159 @@ +use tvai_sdk::*; + +fn main() -> Result<(), Box> { + println!("Topaz Video AI SDK - Output Settings Demo"); + println!("========================================"); + + let sdk = TvaiSdk::new(); + + // Example 1: Social Media Square with Crop to Fit + println!("\n1. Social Media Square Template (Crop to Fit):"); + let social_template = TemplatePresets::social_media_square()?; + println!(" Template: {}", social_template.name); + println!(" Description: {}", social_template.description); + println!(" Settings:"); + println!(" Resolution: {}x{}", + social_template.settings.output.width.unwrap_or(0), + social_template.settings.output.height.unwrap_or(0) + ); + println!(" Crop to fit: {}", social_template.settings.output.crop_to_fit); + println!(" Lock aspect ratio: {}", social_template.settings.output.lock_aspect_ratio.unwrap_or(true)); + println!(" Custom resolution priority: {}", social_template.settings.output.custom_resolution_priority.unwrap_or(0)); + + let social_command = sdk.generate_ffmpeg_command(&social_template, "input_16x9.mp4", "output_square.mp4")?; + println!(" Command:"); + println!(" {}", social_command); + + // Example 2: Letterbox Preserve Aspect Ratio + println!("\n2. Letterbox Preserve Template (Padding):"); + let letterbox_template = TemplatePresets::letterbox_preserve()?; + println!(" Template: {}", letterbox_template.name); + println!(" Settings:"); + println!(" Resolution: {}x{}", + letterbox_template.settings.output.width.unwrap_or(0), + letterbox_template.settings.output.height.unwrap_or(0) + ); + println!(" Crop to fit: {}", letterbox_template.settings.output.crop_to_fit); + println!(" Lock aspect ratio: {}", letterbox_template.settings.output.lock_aspect_ratio.unwrap_or(true)); + + let letterbox_command = sdk.generate_ffmpeg_command(&letterbox_template, "input_4x3.mp4", "output_letterbox.mp4")?; + println!(" Command:"); + println!(" {}", letterbox_command); + + // Example 3: Minimal Processing (Output Settings Disabled) + println!("\n3. Minimal Processing Template (Output Settings Disabled):"); + let minimal_template = TemplatePresets::minimal_processing()?; + println!(" Template: {}", minimal_template.name); + println!(" Settings:"); + println!(" Output active: {}", minimal_template.settings.output.active); + + let minimal_command = sdk.generate_ffmpeg_command(&minimal_template, "input.mp4", "output_minimal.mp4")?; + println!(" Command:"); + println!(" {}", minimal_command); + + // Example 4: Custom Template with Advanced Output Settings + println!("\n4. Custom Template with Advanced Output Settings:"); + let custom_template = TemplateBuilder::new("Advanced Output Demo") + .description("Demonstrates all output settings parameters") + .enable_enhancement("prob-4") + .enhancement_params(25, 20, 10) + .resolution(1280, 720) // 720p target + .video_codec("libx264", Some(24)) + .preset("medium") + .custom_resolution_priority(1) // High priority for custom resolution + .lock_aspect_ratio(true) // Preserve aspect ratio + .crop_to_fit(false) // Use padding + .pixel_aspect_ratio(2) // 16:9 aspect ratio + .audio_settings("aac", 128, 2) + .build()?; + + println!(" Template: {}", custom_template.name); + println!(" Settings:"); + println!(" Resolution: {}x{}", + custom_template.settings.output.width.unwrap_or(0), + custom_template.settings.output.height.unwrap_or(0) + ); + println!(" Custom resolution priority: {}", custom_template.settings.output.custom_resolution_priority.unwrap_or(0)); + println!(" Lock aspect ratio: {}", custom_template.settings.output.lock_aspect_ratio.unwrap_or(true)); + println!(" Crop to fit: {}", custom_template.settings.output.crop_to_fit); + println!(" Pixel aspect ratio: {}", custom_template.settings.output.output_par); + + let custom_command = sdk.generate_ffmpeg_command(&custom_template, "input.mp4", "output_custom.mp4")?; + println!(" Command:"); + println!(" {}", custom_command); + + // Example 5: Comparison of Different Resolution Priorities + println!("\n5. Resolution Priority Comparison:"); + + // Priority 0: Normal (custom resolution used) + let priority0_template = TemplateBuilder::new("Priority 0") + .enable_enhancement("prob-4") + .resolution(1920, 1080) + .output_settings(6, 0.0) // FHD size method + .custom_resolution_priority(0) // Normal priority + .build()?; + + // Priority 2: Size method has priority + let priority2_template = TemplateBuilder::new("Priority 2") + .enable_enhancement("prob-4") + .resolution(1920, 1080) // This will be ignored + .output_settings(5, 0.0) // QHD size method takes priority + .custom_resolution_priority(2) // Size method priority + .build()?; + + let priority0_command = sdk.generate_ffmpeg_command(&priority0_template, "input.mp4", "output_p0.mp4")?; + let priority2_command = sdk.generate_ffmpeg_command(&priority2_template, "input.mp4", "output_p2.mp4")?; + + println!(" Priority 0 (Custom resolution used):"); + println!(" {}", priority0_command); + println!("\n Priority 2 (Size method used - 2560x1440):"); + println!(" {}", priority2_command); + + // Example 6: Aspect Ratio Handling Comparison + println!("\n6. Aspect Ratio Handling Comparison:"); + + // Stretch to fit (ignore aspect ratio) + let stretch_template = TemplateBuilder::new("Stretch to Fit") + .enable_enhancement("prob-4") + .resolution(1920, 1080) + .lock_aspect_ratio(false) // Allow stretching + .build()?; + + // Crop to fit (maintain aspect ratio, crop excess) + let crop_template = TemplateBuilder::new("Crop to Fit") + .enable_enhancement("prob-4") + .resolution(1920, 1080) + .lock_aspect_ratio(true) + .crop_to_fit(true) // Crop excess + .build()?; + + // Pad to fit (maintain aspect ratio, add padding) + let pad_template = TemplateBuilder::new("Pad to Fit") + .enable_enhancement("prob-4") + .resolution(1920, 1080) + .lock_aspect_ratio(true) + .crop_to_fit(false) // Add padding + .build()?; + + let stretch_command = sdk.generate_ffmpeg_command(&stretch_template, "input.mp4", "output_stretch.mp4")?; + let crop_command = sdk.generate_ffmpeg_command(&crop_template, "input.mp4", "output_crop.mp4")?; + let pad_command = sdk.generate_ffmpeg_command(&pad_template, "input.mp4", "output_pad.mp4")?; + + println!(" Stretch to fit (ignore aspect ratio):"); + println!(" {}", stretch_command); + println!("\n Crop to fit (maintain aspect ratio, crop):"); + println!(" {}", crop_command); + println!("\n Pad to fit (maintain aspect ratio, pad):"); + println!(" {}", pad_command); + + println!("\n✅ Output settings demo completed successfully!"); + println!("\nKey Features Demonstrated:"); + println!(" • Output settings active/inactive control"); + println!(" • Custom resolution priority handling"); + println!(" • Aspect ratio locking and preservation"); + println!(" • Crop vs pad behavior for aspect ratio mismatches"); + println!(" • Pixel aspect ratio configuration"); + println!(" • Resolution priority vs size method priority"); + + Ok(()) +} diff --git a/cargos/tvai-v2/src/ffmpeg.rs b/cargos/tvai-v2/src/ffmpeg.rs index 987f192..8bec8d7 100644 --- a/cargos/tvai-v2/src/ffmpeg.rs +++ b/cargos/tvai-v2/src/ffmpeg.rs @@ -22,19 +22,33 @@ impl FfmpegCommandGenerator { /// Initialize model mappings from template models to FFmpeg filter models fn init_model_mappings(&mut self) { - // Enhancement models + // Enhancement models - Proteus series (general purpose) 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()); + + // Enhancement models - Specialized series + self.model_mappings.insert("nyx-3".to_string(), "nyx-3".to_string()); // Iris series (low light/noise) self.model_mappings.insert("hyp-1".to_string(), "hyp-1".to_string()); // Hyperion for HDR - + + // Artemis series (animation/cartoon) + self.model_mappings.insert("art-1".to_string(), "art-1".to_string()); + self.model_mappings.insert("art-2".to_string(), "art-2".to_string()); + + // Gaia series (natural scenes) + self.model_mappings.insert("gai-1".to_string(), "gai-1".to_string()); + self.model_mappings.insert("gai-2".to_string(), "gai-2".to_string()); + + // Theia series (detail recovery) + self.model_mappings.insert("the-1".to_string(), "the-1".to_string()); + self.model_mappings.insert("the-2".to_string(), "the-2".to_string()); + // Frame interpolation models self.model_mappings.insert("apo-8".to_string(), "chr-2".to_string()); self.model_mappings.insert("chf-3".to_string(), "chr-2".to_string()); - + // Motion blur models self.model_mappings.insert("thm-2".to_string(), "thm-2".to_string()); - + // Stabilization models (mapped to ref-2 for tvai_stb) // Note: Template stabilization uses different approach than tvai_stb filter } @@ -79,6 +93,14 @@ impl FfmpegCommandGenerator { filters.push(grain_filter); } + // Handle second enhancement if filter manager is present + 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); + } + } + // Build the complete FFmpeg command let mut command = format!("ffmpeg -i \"{}\"", input_file); @@ -99,26 +121,25 @@ impl FfmpegCommandGenerator { /// Generate stabilization filter (tvai_stb) fn generate_stabilization_filter(&self, settings: &crate::StabilizeSettings) -> TvaiResult { let mut params = Vec::new(); - + // Use default model for stabilization params.push("model=ref-2".to_string()); - - // Map smoothness (0-100 in template to 0-16 in filter) - let smoothness = (settings.smooth as f64 / 100.0 * 16.0).round() as i32; - params.push(format!("smoothness={}", smoothness)); - - // Map method (0: auto crop, 1: full frame) - let full = if settings.method == 1 { 1 } else { 0 }; - params.push(format!("full={}", full)); - + + // Map method (0: auto crop, 1: no crop/full frame) + let method = if settings.method == 0 { "auto_crop" } else { "no_crop" }; + params.push(format!("method={}", method)); + + // Map smoothness (0-100 in template) + params.push(format!("smooth={}", settings.smooth)); + // Rolling shutter correction if settings.rsc { - params.push("roll=1".to_string()); + params.push("rsc=1".to_string()); } - + // Reduce motion if settings.reduce_motion { - params.push(format!("reduce={}", settings.reduce_motion_iteration)); + params.push(format!("reduce_motion={}", settings.reduce_motion_iteration)); } Ok(format!("tvai_stb={}", params.join(":"))) @@ -127,69 +148,108 @@ impl FfmpegCommandGenerator { /// Generate enhancement filter (tvai_up) fn generate_enhancement_filter(&self, settings: &crate::EnhanceSettings) -> TvaiResult { let mut params = Vec::new(); - + // Map model let model = self.model_mappings.get(&settings.model) .ok_or_else(|| TvaiError::ModelMappingError(format!("Unknown enhancement model: {}", settings.model)))?; params.push(format!("model={}", model)); - - // Map enhancement parameters to tvai_up parameters + + // Add scale parameter (default to no scaling unless specified) + params.push("scale=0".to_string()); + + // Map enhancement parameters to tvai_up parameters (using normalized values 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)); } + // Add video type parameter if not progressive + if settings.video_type != 1 { + match settings.video_type { + 0 => params.push("interlaced=1".to_string()), + 2 => params.push("telecine=1".to_string()), + _ => {} + } + } + + // Add field order if specified + 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()), + _ => {} + } + } + + // Add auto enhancement level if specified + if settings.auto != 0 { + let auto_level = settings.auto as f64 / 100.0; + params.push(format!("auto={:.2}", auto_level)); + } + + // Add add_noise parameter if specified + if settings.add_noise != 0 { + let add_noise = settings.add_noise as f64 / 100.0; + params.push(format!("add_noise={:.2}", add_noise)); + } + + // Add recover original detail if not default + 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)); + } + Ok(format!("tvai_up={}", params.join(":"))) } /// Generate frame interpolation filter (tvai_fi) fn generate_frame_interpolation_filter(&self, slowmo_settings: &crate::SlowMotionSettings, output_settings: &crate::OutputSettings) -> TvaiResult { let mut params = Vec::new(); - + // Map model let model = self.model_mappings.get(&slowmo_settings.model) .ok_or_else(|| TvaiError::ModelMappingError(format!("Unknown frame interpolation model: {}", slowmo_settings.model)))?; params.push(format!("model={}", model)); - + // Set slowmo factor if slowmo_settings.factor != 1.0 { params.push(format!("slowmo={}", slowmo_settings.factor)); } - + // Set output FPS if specified if output_settings.out_fps > 0.0 { params.push(format!("fps={}", output_settings.out_fps)); } - - // Duplicate frame threshold + + // Duplicate frame threshold (rdt parameter) if !slowmo_settings.duplicate { params.push("rdt=-0.01".to_string()); // Disable duplicate frame removal - } else { + } else if slowmo_settings.duplicate_threshold != 10 { // Only add if not default let threshold = slowmo_settings.duplicate_threshold as f64 / 1000.0; params.push(format!("rdt={:.3}", threshold)); } @@ -197,45 +257,186 @@ impl FfmpegCommandGenerator { Ok(format!("tvai_fi={}", params.join(":"))) } - /// Generate grain filter (approximation using FFmpeg noise filter) + /// Generate motion blur filter (tvai_mb) + fn generate_motion_blur_filter(&self, settings: &crate::MotionBlurSettings) -> TvaiResult { + let mut params = Vec::new(); + + // Map model + let model = self.model_mappings.get(&settings.model) + .ok_or_else(|| TvaiError::ModelMappingError(format!("Unknown motion blur model: {}", settings.model)))?; + params.push(format!("model={}", model)); + + Ok(format!("tvai_mb={}", params.join(":"))) + } + + /// Generate grain filter (tvai_grain or noise approximation) fn generate_grain_filter(&self, settings: &crate::GrainSettings) -> TvaiResult { - let strength = settings.grain as f64 / 100.0 * 20.0; // Scale to reasonable noise level - Ok(format!("noise=alls={}:allf=t", strength)) + // Try to use tvai_grain if available, otherwise fall back to noise filter + let mut params = Vec::new(); + + // Grain amount (0-100 scale) + let amount = settings.grain as f64 / 100.0; + params.push(format!("amount={:.2}", amount)); + + // Grain size (1-10 scale) + params.push(format!("size={}", settings.grain_size)); + + // Use tvai_grain filter + Ok(format!("tvai_grain={}", params.join(":"))) + } + + /// Generate second enhancement filter + fn generate_second_enhancement_filter(&self, enhance_settings: &crate::EnhanceSettings, filter_manager: &crate::FilterManagerSettings) -> TvaiResult { + let mut params = Vec::new(); + + // Use the same model as primary enhancement + let model = self.model_mappings.get(&enhance_settings.model) + .ok_or_else(|| TvaiError::ModelMappingError(format!("Unknown enhancement model: {}", enhance_settings.model)))?; + params.push(format!("model={}", model)); + + // Set intermediate resolution scale + let intermediate_scale = match filter_manager.second_enhancement_intermediate_resolution { + 0 => 0.5, // Half resolution + 1 => 0.75, // 3/4 resolution + 2 => 1.0, // Same resolution + 3 => 1.5, // 1.5x resolution + 4 => 2.0, // 2x resolution + _ => 1.0, // Default to same resolution + }; + params.push(format!("scale={}", intermediate_scale)); + + // Apply lighter enhancement parameters for second pass + if enhance_settings.denoise != 0 { + let noise = (enhance_settings.denoise as f64 / 100.0) * 0.5; // Reduce by half + params.push(format!("noise={:.2}", noise)); + } + + if enhance_settings.detail != 0 { + let details = (enhance_settings.detail as f64 / 100.0) * 0.5; // Reduce by half + params.push(format!("details={:.2}", details)); + } + + Ok(format!("tvai_up={}", params.join(":"))) } /// Generate output settings fn generate_output_settings(&self, settings: &crate::OutputSettings) -> TvaiResult { let mut output_params = Vec::new(); + // Check if output settings are active + if !settings.active { + // If output settings are not active, use minimal default settings + output_params.push("-c:v libx264".to_string()); + output_params.push("-c:a aac".to_string()); + return Ok(format!(" {}", output_params.join(" "))); + } + // Hardware acceleration if let Some(ref hwaccel) = settings.hwaccel { output_params.push(format!("-hwaccel {}", hwaccel)); } - // Custom resolution takes priority over size method - if let (Some(width), Some(height)) = (settings.width, settings.height) { - output_params.push(format!("-s {}x{}", width, height)); + // Handle resolution based on priority settings + let use_custom_resolution = if let (Some(_width), Some(_height)) = (settings.width, settings.height) { + // Check custom resolution priority + let priority = settings.custom_resolution_priority.unwrap_or(0); + match priority { + 0 => true, // Custom resolution has normal priority + 1 => true, // Custom resolution has high priority + 2 => false, // Size method has priority over custom resolution + _ => true, // Default to using custom resolution + } } else { - // Handle output size method + false + }; + + if use_custom_resolution { + let width = settings.width.unwrap(); + let height = settings.height.unwrap(); + + // Handle aspect ratio locking + if settings.lock_aspect_ratio.unwrap_or(true) { + // Use scale filter to maintain aspect ratio + output_params.push(format!("-vf scale={}:{}:force_original_aspect_ratio=decrease", width, height)); + + // Add padding if needed to reach exact dimensions + if settings.crop_to_fit { + // Crop to fit exact dimensions + output_params.push(format!("-vf scale={}:{}:force_original_aspect_ratio=increase,crop={}:{}", width, height, width, height)); + } else { + // Pad to fit exact dimensions + output_params.push(format!("-vf scale={}:{}:force_original_aspect_ratio=decrease,pad={}:{}:(ow-iw)/2:(oh-ih)/2", width, height, width, height)); + } + } else { + // Stretch to exact dimensions (ignore aspect ratio) + output_params.push(format!("-s {}x{}", width, height)); + } + } else { + // Handle output size method (based on Topaz Video AI size methods) match settings.out_size_method { - 7 => { - // 4K output - output_params.push("-s 3840x2160".to_string()); + 0 => { + // Original size - no scaling }, - 6 => { - // FHD output + 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 => { - // HD output - output_params.push("-s 1280x720".to_string()); + // QHD (1440p) + output_params.push("-s 2560x1440".to_string()); + }, + 6 => { + // FHD (1920x1080) - same as 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()); }, _ => { - // Keep original size (method 0) or other methods + // Unknown method - keep original size } } } + // Handle crop to fit for size method outputs + if settings.crop_to_fit && !use_custom_resolution { + // Apply crop to fit for size method outputs + if let Some(last_param) = output_params.last_mut() { + if last_param.starts_with("-s ") { + let size = &last_param[3..]; + // Replace simple scale with scale+crop + *last_param = format!("-vf scale={}:force_original_aspect_ratio=increase,crop={}", size, size); + } + } + } + + // Handle pixel aspect ratio + 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()), + _ => {} + } + } + // Frame rate if settings.out_fps > 0.0 { output_params.push(format!("-r {}", settings.out_fps)); diff --git a/cargos/tvai-v2/src/lib.rs b/cargos/tvai-v2/src/lib.rs index c9ea496..63fa8f7 100644 --- a/cargos/tvai-v2/src/lib.rs +++ b/cargos/tvai-v2/src/lib.rs @@ -325,6 +325,36 @@ impl TemplateBuilder { self } + /// Set output settings active state + pub fn output_active(mut self, active: bool) -> Self { + self.template.settings.output.active = active; + self + } + + /// Set custom resolution priority + pub fn custom_resolution_priority(mut self, priority: i32) -> Self { + self.template.settings.output.custom_resolution_priority = Some(priority); + self + } + + /// Set aspect ratio locking + pub fn lock_aspect_ratio(mut self, lock: bool) -> Self { + self.template.settings.output.lock_aspect_ratio = Some(lock); + self + } + + /// Enable crop to fit + pub fn crop_to_fit(mut self, crop: bool) -> Self { + self.template.settings.output.crop_to_fit = crop; + self + } + + /// Set pixel aspect ratio + pub fn pixel_aspect_ratio(mut self, par: i32) -> Self { + self.template.settings.output.output_par = par; + self + } + /// Enable grain effect pub fn enable_grain(mut self, amount: i32, size: i32) -> Self { self.template.settings.grain.active = true; @@ -477,6 +507,46 @@ impl TemplatePresets { .two_pass(true) .build() } + + /// Create a social media template with aspect ratio handling + pub fn social_media_square() -> Result { + TemplateBuilder::new("Social Media Square") + .description("Square format for social media with smart cropping") + .enable_enhancement("prob-4") + .enhancement_params(20, 25, 15) // Moderate enhancement + .resolution(1080, 1080) // Square format + .video_codec("libx264", Some(25)) + .preset("fast") // Quick encoding for social media + .crop_to_fit(true) // Crop to exact square + .lock_aspect_ratio(false) // Allow cropping + .custom_resolution_priority(1) // High priority for custom resolution + .pixel_aspect_ratio(2) // 16:9 source handling + .audio_settings("aac", 128, 2) + .build() + } + + /// Create a letterbox template that preserves aspect ratio + pub fn letterbox_preserve() -> Result { + TemplateBuilder::new("Letterbox Preserve") + .description("Preserve aspect ratio with letterboxing") + .enable_enhancement("prob-4") + .resolution(1920, 1080) // Target FHD + .video_codec("libx264", Some(23)) + .lock_aspect_ratio(true) // Preserve aspect ratio + .crop_to_fit(false) // Use padding instead of cropping + .custom_resolution_priority(0) // Normal priority + .build() + } + + /// Create a template with output settings disabled + pub fn minimal_processing() -> Result { + TemplateBuilder::new("Minimal Processing") + .description("Minimal processing with basic output") + .enable_enhancement("prob-4") + .enhancement_params(10, 5, 0) // Very light processing + .output_active(false) // Disable advanced output settings + .build() + } } impl Default for TvaiSdk {