sreepathi-ravikumar commited on
Commit
fbb769b
·
verified ·
1 Parent(s): 7b12459

Update rust_highlight/src/lib.rs

Browse files
Files changed (1) hide show
  1. rust_highlight/src/lib.rs +62 -36
rust_highlight/src/lib.rs CHANGED
@@ -1,60 +1,86 @@
1
- use pyo3::prelude::*;
2
  use std::process::Command;
 
3
 
4
- #[pyfunction]
5
- fn render_video(
6
  id: usize,
7
  image_path: &str,
8
  audio_path: &str,
9
  duration: f64,
10
  words: Vec<(String, (u32, u32, u32, u32))>,
11
- ) -> PyResult<String> {
12
- let clip = format!("clip{}.mp4", id);
 
 
 
 
 
 
 
 
 
13
  let duration_str = duration.to_string();
 
 
14
 
15
- // --- FFmpeg drawbox filters for each word ---
16
- let mut vf = String::new();
17
- let n = words.len() as f64;
18
 
19
  for (i, (_, (x, y, w, h))) in words.iter().enumerate() {
20
- let start = (i as f64) * (duration / n);
21
- let end = start + (duration / n);
22
- vf.push_str(&format!(
23
- "drawbox=x={}:y={}:w={}:h={}:[email protected]:enable='between(t,{:.3},{:.3})',",
24
- x, y, w, h, start, end
 
 
 
 
 
 
 
 
 
25
  ));
 
26
  }
27
 
28
- // Trim trailing comma and add pixel format fix
29
- let vf_with_format = format!("{},format=yuv420p", vf.trim_end_matches(','));
30
 
31
- // --- FFmpeg command ---
32
  let status = Command::new("ffmpeg")
33
  .args(&[
34
- "-y", // Overwrite output
35
- "-loop", "1", // Loop image
36
- "-i", image_path, // Input image
37
- "-i", audio_path, // Input audio
38
- "-vf", &vf_with_format, // Video filter with highlights
39
- "-t", &duration_str, // Duration
40
- "-c:v", "libx264", // Video codec
41
- "-pix_fmt", "yuv420p", // Pixel format (for compatibility)
42
- "-c:a", "aac", // Audio codec
43
- &clip, // Output file
 
 
 
 
 
 
 
 
 
44
  ])
45
- .status()
46
- .expect("Failed to run ffmpeg");
47
-
48
- // Return result
49
- if status.success() {
50
- Ok(clip)
51
- } else {
52
- Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
53
- "FFmpeg failed",
54
- ))
55
  }
56
  }
57
 
 
58
  #[pymodule]
59
  fn rust_highlight(_py: Python, m: &PyModule) -> PyResult<()> {
60
  m.add_function(wrap_pyfunction!(render_video, m)?)?;
 
 
1
  use std::process::Command;
2
+ use std::path::Path;
3
 
4
+ pub fn render_video(
 
5
  id: usize,
6
  image_path: &str,
7
  audio_path: &str,
8
  duration: f64,
9
  words: Vec<(String, (u32, u32, u32, u32))>,
10
+ output_dir: &str,
11
+ ) -> Result<String, String> {
12
+ // Validate paths
13
+ if !Path::new(image_path).exists() {
14
+ return Err(format!("Image not found: {}", image_path));
15
+ }
16
+ if !Path::new(audio_path).exists() {
17
+ return Err(format!("Audio not found: {}", audio_path));
18
+ }
19
+
20
+ let output_path = format!("{}/clip{}.mp4", output_dir, id);
21
  let duration_str = duration.to_string();
22
+ let n_words = words.len() as f64;
23
+ let highlight_duration = duration / n_words;
24
 
25
+ // Build complex filter graph
26
+ let mut filter_complex = String::new();
27
+ let mut last_output = "0:v".to_string();
28
 
29
  for (i, (_, (x, y, w, h))) in words.iter().enumerate() {
30
+ let start = i as f64 * highlight_duration;
31
+ let end = start + highlight_duration;
32
+
33
+ // Create highlight box
34
+ filter_complex.push_str(&format!(
35
+ "[{last_out}]drawbox=x={x}:y={y}:w={w}:h={h}:[email protected]:t=fill:enable='between(t,{start:.3},{end:.3})'[v{i}];",
36
+ last_out = last_output,
37
+ x = x,
38
+ y = y,
39
+ w = w,
40
+ h = h,
41
+ start = start,
42
+ end = end,
43
+ i = i
44
  ));
45
+ last_output = format!("[v{i}]");
46
  }
47
 
48
+ // Remove trailing semicolon
49
+ filter_complex.pop();
50
 
51
+ // FFmpeg command with optimized settings
52
  let status = Command::new("ffmpeg")
53
  .args(&[
54
+ "-y", // Overwrite output
55
+ "-hwaccel", "auto", // Enable hardware acceleration
56
+ "-loop", "1",
57
+ "-i", image_path,
58
+ "-i", audio_path,
59
+ "-filter_complex", &filter_complex,
60
+ "-map", &last_output,
61
+ "-map", "1:a",
62
+ "-c:v", "libx264",
63
+ "-preset", "fast", // Balance between speed and compression
64
+ "-crf", "18", // High quality (lower = better quality)
65
+ "-pix_fmt", "yuv420p",
66
+ "-movflags", "+faststart", // For web streaming
67
+ "-c:a", "aac",
68
+ "-b:a", "192k", // Higher audio bitrate
69
+ "-r", "60", // Higher framerate for smoother animation
70
+ "-t", &duration_str,
71
+ "-vsync", "2", // Frame rate conversion method
72
+ &output_path,
73
  ])
74
+ .status();
75
+
76
+ match status {
77
+ Ok(exit_status) if exit_status.success() => Ok(output_path),
78
+ Ok(_) => Err("FFmpeg command failed".to_string()),
79
+ Err(e) => Err(format!("Failed to execute FFmpeg: {}", e)),
 
 
 
 
80
  }
81
  }
82
 
83
+
84
  #[pymodule]
85
  fn rust_highlight(_py: Python, m: &PyModule) -> PyResult<()> {
86
  m.add_function(wrap_pyfunction!(render_video, m)?)?;