feat: fix bug
This commit is contained in:
@@ -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"
|
||||
|
||||
159
cargos/tvai-v2/examples/output_settings_demo.rs
Normal file
159
cargos/tvai-v2/examples/output_settings_demo.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
use tvai_sdk::*;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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(())
|
||||
}
|
||||
@@ -22,12 +22,26 @@ 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());
|
||||
@@ -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);
|
||||
|
||||
@@ -103,22 +125,21 @@ impl FfmpegCommandGenerator {
|
||||
// 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: no crop/full frame)
|
||||
let method = if settings.method == 0 { "auto_crop" } else { "no_crop" };
|
||||
params.push(format!("method={}", method));
|
||||
|
||||
// Map method (0: auto crop, 1: full frame)
|
||||
let full = if settings.method == 1 { 1 } else { 0 };
|
||||
params.push(format!("full={}", full));
|
||||
// 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(":")))
|
||||
@@ -133,7 +154,10 @@ impl FfmpegCommandGenerator {
|
||||
.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));
|
||||
@@ -164,6 +188,42 @@ impl FfmpegCommandGenerator {
|
||||
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(":")))
|
||||
}
|
||||
|
||||
@@ -186,10 +246,10 @@ impl FfmpegCommandGenerator {
|
||||
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<String> {
|
||||
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<String> {
|
||||
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<String> {
|
||||
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<String> {
|
||||
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));
|
||||
|
||||
@@ -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<Template, TvaiError> {
|
||||
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<Template, TvaiError> {
|
||||
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<Template, TvaiError> {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user