h265_transcode/
h265_transcode.rs

1use std::{io::Write, path::Path, thread};
2
3use ffmpeg_sidecar::{
4  command::FfmpegCommand,
5  event::{FfmpegEvent, LogLevel},
6};
7
8/// 1. Read an H265 source video from file
9/// 2. Decode video
10/// 3. Composite with an overlay image rendered by Rust
11/// 4. Re-encode back to H265 to file
12///
13/// ```console
14/// cargo run --example h265_transcode
15/// ```
16fn main() {
17  // Create an H265 source video as a starting point
18  let input_path = "output/h265.mp4";
19  if !Path::new(input_path).exists() {
20    create_h265_source(input_path);
21  }
22
23  // One instance decodes H265 to raw frames
24  let mut input = FfmpegCommand::new()
25    .input(input_path)
26    .rawvideo()
27    .spawn()
28    .unwrap();
29
30  // Frames can be transformed by Iterator `.map()`.
31  // This example is a no-op, with frames passed through unaltered.
32  let transformed_frames = input.iter().unwrap().filter_frames();
33
34  // You could easily add some "middleware" processing here:
35  // - overlay or composite another RGB image (or even another Ffmpeg Iterator)
36  // - apply a filter like blur or convolution
37  // Note: some of these operations are also possible with FFmpeg's (somewhat arcane)
38  // `filtergraph` API, but doing it in Rust gives you much finer-grained
39  // control, debuggability, and modularity -- you can pull in any Rust crate
40  // you need.
41
42  // A second instance encodes the updated frames back to H265
43  let mut output = FfmpegCommand::new()
44    .args([
45      "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", "600x800", "-r", "30",
46    ]) // note: should be possible to infer these params from the source input stream
47    .input("-")
48    .args(["-c:v", "libx265"])
49    .args(["-y", "output/h265_overlay.mp4"])
50    .spawn()
51    .unwrap();
52
53  // Connect the two instances
54  let mut stdin = output.take_stdin().unwrap();
55  thread::spawn(move || {
56    // `for_each` blocks through the end of the iterator,
57    // so we run it in another thread.
58    transformed_frames.for_each(|f| {
59      stdin.write_all(&f.data).ok();
60    });
61  });
62
63  // On the main thread, run the output instance to completion
64  output.iter().unwrap().for_each(|e| match e {
65    FfmpegEvent::Log(LogLevel::Error, e) => println!("Error: {}", e),
66    FfmpegEvent::Progress(p) => println!("Progress: {} / 00:00:15", p.time),
67    _ => {}
68  });
69}
70
71/// Create a H265 source video from scratch
72fn create_h265_source(path_str: &str) {
73  println!("Creating H265 source video: {}", path_str);
74  FfmpegCommand::new()
75    .args("-f lavfi -i testsrc=size=600x800:rate=30:duration=15 -c:v libx265".split(' '))
76    .arg(path_str)
77    .spawn()
78    .unwrap()
79    .iter()
80    .unwrap()
81    .for_each(|e| match e {
82      FfmpegEvent::Log(LogLevel::Error, e) => println!("Error: {}", e),
83      FfmpegEvent::Progress(p) => println!("Progress: {} / 00:00:15", p.time),
84      _ => {}
85    });
86  println!("Created H265 source video: {}", path_str);
87}