mraphics_native/
recorder.rs1use crate::Canvas;
2use mraphics_core::{Action, Camera, LogicalTimeline, Timeline};
3use std::{
4 cell::Cell,
5 io::Write,
6 process::{Command, Stdio},
7 rc::Rc,
8};
9
10pub struct Recorder<'canvas> {
11 pub timeline: LogicalTimeline<'canvas>,
12
13 pub output_path: String,
14}
15
16impl<'canvas> Recorder<'canvas> {
17 pub fn new() -> Self {
18 Self {
19 timeline: LogicalTimeline::new(),
20
21 output_path: String::from("video.mp4"),
22 }
23 }
24
25 pub fn with_fps(mut self, fps: f32) -> Self {
26 self.timeline.logical_fps = fps;
27 self
28 }
29
30 pub fn set_fps(&mut self, fps: f32) {
31 self.timeline.logical_fps = fps;
32 }
33
34 pub fn record<'res, T: Timeline<'res>, C: Camera>(
35 &mut self,
36 canvas: &'canvas mut Canvas<'res, T, C>,
37 ) {
38 let canvas_time_bak = canvas.timeline.current_time();
39
40 let is_recording = Rc::new(Cell::new(true));
41 let is_recording_clone = is_recording.clone();
42
43 let mut command = Command::new("ffmpeg");
44
45 #[rustfmt::skip]
46 command.stdin(Stdio::piped()).args([
47 "-y",
48 "-f", "rawvideo",
49 "-s", &format!("{}x{}", canvas.size.0, canvas.size.1),
50 "-pix_fmt", "rgba",
51 "-r", &self.timeline.logical_fps.to_string(),
52 "-i", "-",
53 "-vcodec", "libx264",
54 &self.output_path,
55 ]);
56
57 let mut process = command.spawn().expect("Failed to invoke FFmpeg");
58
59 let playhead = Rc::new(Cell::new(0.0));
60 let playhead_clone = playhead.clone();
61
62 let mut record_action = Action::new();
63 record_action.on_execute = Box::new(move || {
64 if canvas.timeline.all_stopped() {
65 process.stdin.as_ref().unwrap().flush().unwrap();
66
67 process.wait().unwrap();
68
69 is_recording_clone.replace(false);
70
71 canvas.timeline.seek(canvas_time_bak);
72
73 return;
74 }
75
76 canvas.timeline.seek(playhead_clone.get());
77 canvas.timeline.process();
78 canvas.render_offscreen();
79
80 let raw_img = canvas
81 .renderer
82 .as_ref()
83 .unwrap()
84 .read_texture_rgbau8(canvas.offscreen_texture.as_ref().unwrap(), canvas.size);
85
86 process.stdin.as_mut().unwrap().write_all(&raw_img).unwrap();
87 });
88
89 self.timeline.add_action(record_action);
90 self.timeline.start();
91
92 while is_recording.get() {
93 playhead.replace(self.timeline.current_time());
94 self.timeline.forward();
95 }
96 }
97}