feat: remote unuse json

This commit is contained in:
imeepos
2025-08-15 13:59:11 +08:00
parent 247eebef9e
commit 81b80eb911
21 changed files with 866 additions and 976 deletions

View File

@@ -26,6 +26,7 @@ impl FfmpegCommandGenerator {
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());
self.model_mappings.insert("hyp-1".to_string(), "hyp-1".to_string()); // Hyperion for HDR
// Frame interpolation models
self.model_mappings.insert("apo-8".to_string(), "chr-2".to_string());
@@ -48,25 +49,31 @@ impl FfmpegCommandGenerator {
let mut filters = Vec::new();
let settings = &template.settings;
// Generate stabilization filter
if settings.stabilize.active {
let stb_filter = self.generate_stabilization_filter(&settings.stabilize)?;
filters.push(stb_filter);
}
// Generate enhancement filter
// Generate enhancement filter (usually first in chain)
if settings.enhance.active {
let up_filter = self.generate_enhancement_filter(&settings.enhance)?;
filters.push(up_filter);
}
// Generate frame interpolation filter
// Generate frame interpolation filter (after enhancement)
if settings.slow_motion.active {
let fi_filter = self.generate_frame_interpolation_filter(&settings.slow_motion, &settings.output)?;
filters.push(fi_filter);
}
// Generate grain filter (using FFmpeg's noise filter as approximation)
// Generate stabilization filter (after interpolation)
if settings.stabilize.active {
let stb_filter = self.generate_stabilization_filter(&settings.stabilize)?;
filters.push(stb_filter);
}
// Generate motion blur filter (if active)
if settings.motion_blur.active {
let mb_filter = self.generate_motion_blur_filter(&settings.motion_blur)?;
filters.push(mb_filter);
}
// Generate grain filter (usually last in chain)
if settings.grain.active {
let grain_filter = self.generate_grain_filter(&settings.grain)?;
filters.push(grain_filter);
@@ -83,7 +90,7 @@ impl FfmpegCommandGenerator {
// Add output settings
command.push_str(&self.generate_output_settings(&settings.output)?);
command.push_str(&format!(" \"{}\"", output_file));
Ok(command)
@@ -199,36 +206,143 @@ impl FfmpegCommandGenerator {
/// Generate output settings
fn generate_output_settings(&self, settings: &crate::OutputSettings) -> TvaiResult<String> {
let mut output_params = Vec::new();
// Handle output size method
match settings.out_size_method {
7 => {
// 4K output
output_params.push("-s 3840x2160".to_string());
},
6 => {
// FHD output
output_params.push("-s 1920x1080".to_string());
},
5 => {
// HD output
output_params.push("-s 1280x720".to_string());
},
_ => {
// Keep original size (method 0) or other methods
// 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));
} else {
// Handle output size method
match settings.out_size_method {
7 => {
// 4K output
output_params.push("-s 3840x2160".to_string());
},
6 => {
// FHD output
output_params.push("-s 1920x1080".to_string());
},
5 => {
// HD output
output_params.push("-s 1280x720".to_string());
},
_ => {
// Keep original size (method 0) or other methods
}
}
}
// Handle frame rate
// Frame rate
if settings.out_fps > 0.0 {
output_params.push(format!("-r {}", settings.out_fps));
}
// Add default encoding settings for quality
output_params.push("-c:v libx264".to_string());
output_params.push("-crf 18".to_string());
output_params.push("-preset slow".to_string());
// Video codec
if let Some(ref codec) = settings.codec {
output_params.push(format!("-c:v {}", codec));
} else {
output_params.push("-c:v libx264".to_string());
}
// Video quality settings
if let Some(bitrate) = settings.bitrate {
output_params.push(format!("-b:v {}k", bitrate));
} else if let Some(crf) = settings.crf {
// Use CRF for quality-based encoding
let codec = settings.codec.as_deref().unwrap_or("libx264");
match codec {
"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()); // Default quality
}
// Encoding preset
if let Some(ref preset) = settings.preset {
output_params.push(format!("-preset {}", preset));
} else {
output_params.push("-preset medium".to_string());
}
// Video profile
if let Some(ref profile) = settings.profile {
output_params.push(format!("-profile:v {}", profile));
}
// Video level
if let Some(ref level) = settings.level {
output_params.push(format!("-level {}", level));
}
// GOP size
if let Some(gop_size) = settings.gop_size {
output_params.push(format!("-g {}", gop_size));
}
// B-frames
if let Some(b_frames) = settings.b_frames {
output_params.push(format!("-bf {}", b_frames));
}
// Pixel format
if let Some(ref pix_fmt) = settings.pixel_format {
output_params.push(format!("-pix_fmt {}", pix_fmt));
}
// Color settings
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));
}
// Audio settings
if let Some(ref audio_codec) = settings.audio_codec {
if audio_codec != "copy" {
output_params.push(format!("-c:a {}", audio_codec));
if let Some(audio_bitrate) = settings.audio_bitrate {
output_params.push(format!("-b:a {}k", audio_bitrate));
}
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());
}
}
// Custom parameters
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 {
@@ -279,29 +393,18 @@ impl FfmpegCommandGenerator {
/// Generate command with custom output codec
pub fn generate_with_codec(&self, template: &Template, input_file: &str, output_file: &str, codec: &str, quality: Option<i32>) -> TvaiResult<String> {
let mut command = self.generate(template, input_file, output_file)?;
// Replace default codec settings
command = command.replace("-c:v libx264", &format!("-c:v {}", codec));
// Create a modified template with custom codec settings
let mut modified_template = template.clone();
modified_template.settings.output.codec = Some(codec.to_string());
if let Some(q) = quality {
match codec {
"hevc_nvenc" | "h264_nvenc" => {
command = command.replace("-crf 18", &format!("-cq {}", q));
},
"h264_amf" | "hevc_amf" => {
command = command.replace("-crf 18", &format!("-qp {}", q));
},
"h264_qsv" | "hevc_qsv" => {
command = command.replace("-crf 18", &format!("-global_quality {}", q));
},
_ => {
command = command.replace("-crf 18", &format!("-crf {}", q));
}
}
// Clear bitrate when setting quality
modified_template.settings.output.bitrate = None;
modified_template.settings.output.crf = Some(q);
}
Ok(command)
// Generate command with modified template
self.generate(&modified_template, input_file, output_file)
}
/// Generate batch processing commands

View File

@@ -244,6 +244,87 @@ impl TemplateBuilder {
self
}
/// Set custom resolution
pub fn resolution(mut self, width: i32, height: i32) -> Self {
self.template.settings.output.width = Some(width);
self.template.settings.output.height = Some(height);
self
}
/// Set video codec and quality
pub fn video_codec(mut self, codec: &str, crf: Option<i32>) -> Self {
self.template.settings.output.codec = Some(codec.to_string());
if let Some(quality) = crf {
self.template.settings.output.crf = Some(quality);
}
self
}
/// Set video bitrate (alternative to CRF)
pub fn video_bitrate(mut self, bitrate: i32) -> Self {
self.template.settings.output.bitrate = Some(bitrate);
self.template.settings.output.crf = None; // Clear CRF when using bitrate
self
}
/// Set encoding preset
pub fn preset(mut self, preset: &str) -> Self {
self.template.settings.output.preset = Some(preset.to_string());
self
}
/// Set video profile and level
pub fn profile_level(mut self, profile: &str, level: Option<&str>) -> Self {
self.template.settings.output.profile = Some(profile.to_string());
if let Some(l) = level {
self.template.settings.output.level = Some(l.to_string());
}
self
}
/// Set audio settings
pub fn audio_settings(mut self, codec: &str, bitrate: i32, channels: i32) -> Self {
self.template.settings.output.audio_codec = Some(codec.to_string());
self.template.settings.output.audio_bitrate = Some(bitrate);
self.template.settings.output.audio_channels = Some(channels);
self
}
/// Set hardware acceleration
pub fn hardware_acceleration(mut self, hwaccel: &str, device: Option<&str>) -> Self {
self.template.settings.output.hwaccel = Some(hwaccel.to_string());
if let Some(dev) = device {
self.template.settings.output.gpu_device = Some(dev.to_string());
}
self
}
/// Set pixel format
pub fn pixel_format(mut self, format: &str) -> Self {
self.template.settings.output.pixel_format = Some(format.to_string());
self
}
/// Set color settings for HDR content
pub fn color_settings(mut self, colorspace: &str, primaries: &str, trc: &str) -> Self {
self.template.settings.output.colorspace = Some(colorspace.to_string());
self.template.settings.output.color_primaries = Some(primaries.to_string());
self.template.settings.output.color_trc = Some(trc.to_string());
self
}
/// Add custom FFmpeg parameters
pub fn custom_params(mut self, params: Vec<String>) -> Self {
self.template.settings.output.custom_params = Some(params);
self
}
/// Enable two-pass encoding
pub fn two_pass(mut self, enabled: bool) -> Self {
self.template.settings.output.two_pass = Some(enabled);
self
}
/// Enable grain effect
pub fn enable_grain(mut self, amount: i32, size: i32) -> Self {
self.template.settings.grain.active = true;
@@ -316,6 +397,86 @@ impl TemplatePresets {
.output_settings(6, 0.0) // FHD output
.build()
}
/// Create a high-quality NVENC template
pub fn high_quality_nvenc() -> Result<Template, TvaiError> {
TemplateBuilder::new("High Quality NVENC")
.description("High quality encoding with NVIDIA NVENC")
.enable_enhancement("prob-4")
.resolution(3840, 2160) // 4K
.video_codec("hevc_nvenc", Some(18)) // H.265 with high quality
.preset("slow")
.profile_level("main10", Some("5.1"))
.pixel_format("yuv420p10le") // 10-bit
.audio_settings("aac", 256, 2) // High quality audio
.hardware_acceleration("cuda", Some("0"))
.build()
}
/// Create an HDR template
pub fn hdr_processing() -> Result<Template, TvaiError> {
TemplateBuilder::new("HDR Processing")
.description("Process HDR content with proper color settings")
.enable_enhancement("hyp-1") // Hyperion model for HDR
.resolution(3840, 2160)
.video_codec("hevc_nvenc", Some(16))
.pixel_format("yuv420p10le")
.color_settings("bt2020nc", "bt2020", "smpte2084")
.custom_params(vec![
"-color_range tv".to_string(),
"-max_muxing_queue_size 1024".to_string(),
])
.build()
}
/// Create a streaming optimized template
pub fn streaming_optimized() -> Result<Template, TvaiError> {
TemplateBuilder::new("Streaming Optimized")
.description("Optimized for live streaming and fast encoding")
.enable_enhancement("prob-4")
.enhancement_params(15, 10, 20) // Light processing for speed
.resolution(1920, 1080)
.video_codec("h264_nvenc", Some(23))
.preset("fast")
.profile_level("high", Some("4.0"))
.audio_settings("aac", 128, 2)
.custom_params(vec![
"-tune zerolatency".to_string(),
"-rc vbr".to_string(),
])
.build()
}
/// Create a mobile-optimized template
pub fn mobile_optimized() -> Result<Template, TvaiError> {
TemplateBuilder::new("Mobile Optimized")
.description("Optimized for mobile devices with small file sizes")
.enable_enhancement("prob-4")
.enhancement_params(20, 15, 5)
.resolution(1280, 720) // 720p
.video_codec("libx264", Some(28)) // Higher CRF for smaller files
.preset("fast")
.profile_level("baseline", Some("3.1")) // Compatible profile
.audio_settings("aac", 96, 2) // Lower audio bitrate
.build()
}
/// Create an archival quality template
pub fn archival_quality() -> Result<Template, TvaiError> {
TemplateBuilder::new("Archival Quality")
.description("Maximum quality for archival purposes")
.enable_enhancement("prob-4")
.enhancement_params(40, 50, 15) // High quality settings
.enable_stabilization(80, 1)
.resolution(3840, 2160)
.video_codec("libx265", Some(12)) // Very high quality
.preset("veryslow") // Maximum compression efficiency
.profile_level("main10", Some("5.1"))
.pixel_format("yuv420p10le")
.audio_settings("flac", 0, 2) // Lossless audio
.two_pass(true)
.build()
}
}
impl Default for TvaiSdk {

View File

@@ -616,14 +616,104 @@ impl OutputSettings {
self.lock_aspect_ratio = Some(true);
}
// Validate extended parameters
if let Some(width) = self.width {
if width <= 0 || width > 16384 {
return Err(TvaiError::ValidationError(format!("Invalid width: {}", width)));
}
}
if let Some(height) = self.height {
if height <= 0 || height > 16384 {
return Err(TvaiError::ValidationError(format!("Invalid height: {}", height)));
}
}
if let Some(bitrate) = self.bitrate {
if bitrate <= 0 || bitrate > 100000 {
return Err(TvaiError::ValidationError(format!("Invalid bitrate: {}", bitrate)));
}
}
if let Some(crf) = self.crf {
if crf < 0 || crf > 51 {
self.crf = Some(crf.clamp(0, 51));
}
}
if let Some(gop_size) = self.gop_size {
if gop_size <= 0 {
self.gop_size = Some(30); // Default GOP size
}
}
if let Some(b_frames) = self.b_frames {
if b_frames < 0 || b_frames > 16 {
self.b_frames = Some(b_frames.clamp(0, 16));
}
}
if let Some(audio_bitrate) = self.audio_bitrate {
// Allow 0 bitrate for lossless codecs like FLAC
let is_lossless = self.audio_codec.as_ref()
.map(|codec| codec == "flac" || codec == "alac" || codec == "pcm_s16le")
.unwrap_or(false);
if !is_lossless && (audio_bitrate <= 0 || audio_bitrate > 1024) {
return Err(TvaiError::ValidationError(format!("Invalid audio bitrate: {}", audio_bitrate)));
} else if is_lossless && audio_bitrate < 0 {
return Err(TvaiError::ValidationError(format!("Invalid audio bitrate: {}", audio_bitrate)));
}
}
if let Some(audio_channels) = self.audio_channels {
if audio_channels <= 0 || audio_channels > 8 {
self.audio_channels = Some(audio_channels.clamp(1, 8));
}
}
// Set default codec if not specified
if self.codec.is_none() {
self.codec = Some("libx264".to_string());
}
// Set default audio codec if not specified
if self.audio_codec.is_none() {
self.audio_codec = Some("aac".to_string());
}
Ok(())
}
/// Check if settings are valid
pub fn is_valid(&self) -> bool {
self.out_size_method >= 0 && self.out_size_method <= 10 &&
self.output_par >= 0 &&
self.out_fps >= 0.0
// Basic validation
if self.out_size_method < 0 || self.out_size_method > 10 ||
self.output_par < 0 ||
self.out_fps < 0.0 {
return false;
}
// Extended validation
if let Some(width) = self.width {
if width <= 0 || width > 16384 {
return false;
}
}
if let Some(height) = self.height {
if height <= 0 || height > 16384 {
return false;
}
}
if let Some(crf) = self.crf {
if crf < 0 || crf > 51 {
return false;
}
}
true
}
}
@@ -637,6 +727,31 @@ impl Default for OutputSettings {
out_fps: 0.0,
custom_resolution_priority: Some(0),
lock_aspect_ratio: Some(true),
// Extended parameters with sensible defaults
width: None,
height: None,
codec: Some("libx264".to_string()),
bitrate: None,
crf: Some(23), // Balanced quality
preset: Some("medium".to_string()),
profile: None,
level: None,
gop_size: Some(30),
b_frames: Some(3),
audio_codec: Some("aac".to_string()),
audio_bitrate: Some(128), // 128 kbps
audio_sample_rate: Some(48000), // 48 kHz
audio_channels: Some(2), // Stereo
format: Some("mp4".to_string()),
colorspace: None,
color_primaries: None,
color_trc: None,
pixel_format: Some("yuv420p".to_string()),
hwaccel: None,
gpu_device: None,
two_pass: Some(false),
custom_params: None,
}
}
}