-
-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathmod.rs
199 lines (189 loc) · 4.49 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
pub mod fps;
pub mod settings;
use crate::app::{AppError, AppResult};
use crate::image::Image;
use crate::record::fps::FpsClock;
use crate::record::settings::RecordSettings;
use crate::util::state::InputState;
use crate::window::Capture;
use std::io::{self, Write};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{mpsc, Arc};
use std::thread;
/* Asynchronous recording result */
#[derive(Debug)]
pub struct RecordResult<T> {
sender: mpsc::Sender<()>,
thread: thread::JoinHandle<T>,
}
impl<T> RecordResult<T> {
/**
* Create a new RecordResult object.
*
* @param sender
* @param thread
* @return RecordResult
*/
pub fn new(sender: mpsc::Sender<()>, thread: thread::JoinHandle<T>) -> Self {
Self { sender, thread }
}
/**
* Stop the thread and retrieve values.
*
* @return Option
*/
pub fn get(self) -> Option<thread::Result<T>> {
if self.sender.send(()).is_ok() {
Some(self.thread.join())
} else {
None
}
}
}
/* Recorder with FPS clock and channel */
pub struct Recorder<Window> {
window: Window,
clock: FpsClock,
channel: (mpsc::Sender<()>, mpsc::Receiver<()>),
gifski: bool,
settings: RecordSettings,
}
impl<Window> Recorder<Window>
where
Window: Capture + Send + Sync + 'static,
{
/**
* Create a new Recorder object.
*
* @param window
* @param fps
* @param gifski
* @param settings
* @return Recorder
*/
pub fn new(
window: Window,
fps: u32,
gifski: bool,
settings: RecordSettings,
) -> Self {
Self {
window,
clock: FpsClock::new(fps),
channel: mpsc::channel(),
gifski,
settings,
}
}
/**
* Get the maximum number of frames to record.
*
* @return usize
*/
fn get_max_frames(&self) -> usize {
if let Some(duration) = self.settings.time.duration {
info!(
"Recording {} FPS for {} seconds...",
self.clock.fps, duration
);
if self.gifski {
(duration * (self.clock.fps as f64)) as usize
} else {
(((duration * 100.) as u16) / (1e2 / self.clock.fps as f32) as u16)
as usize
}
} else {
info!("Recording {} FPS...", self.clock.fps);
usize::MAX
}
}
/**
* Record frames synchronously with blocking the current thread.
*
* @param input_state (Option)
* @return Vector of Image
*/
pub fn record_sync(
&mut self,
input_state: Option<&InputState>,
) -> AppResult<Vec<Image>> {
let mut frames = Vec::new();
let recording = Arc::new(AtomicBool::new(true));
let rec_state = recording.clone();
ctrlc::set_handler(move || {
rec_state.store(false, Ordering::SeqCst);
})?;
self.window.show_countdown();
let max_frames = self.get_max_frames();
while recording.load(Ordering::SeqCst) && frames.len() < max_frames {
if let Some(state) = input_state {
if state.check_cancel_keys() {
frames.clear();
debug!("\n");
warn!("User interrupt detected.");
break;
} else if state.check_action() {
break;
}
}
self.clock.tick();
frames.push(self.window.get_image().ok_or_else(|| {
AppError::FrameError(String::from("Failed to get image"))
})?);
debug!("Frames: {}\r", frames.len());
io::stdout().flush()?;
}
debug!("\n");
Ok(frames)
}
/**
* Record frames asynchronously and without blocking.
*
* @return RecordResult
*/
pub fn record_async(mut self) -> RecordResult<Vec<Image>> {
let mut frames = Vec::new();
RecordResult::new(
self.channel.0.clone(),
thread::spawn(move || {
self.window.show_countdown();
let max_frames = self.get_max_frames();
while self.channel.1.try_recv().is_err() {
self.clock.tick();
if frames.len() < max_frames {
frames.push(
self.window
.get_image()
.expect("Failed to get the image"),
);
debug!("Frames: {}\r", frames.len());
io::stdout().flush().expect("Failed to flush stdout");
}
}
debug!("\n");
frames
}),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::record::settings::RecordSettings;
use crate::window::test::TestWindow;
use pretty_assertions::assert_ne;
use std::thread;
use std::time::Duration;
#[test]
fn test_record() {
let window = TestWindow::default();
let recorder = Recorder::new(window, 10, false, RecordSettings::default());
let record = recorder.record_async();
thread::sleep(Duration::from_millis(200));
assert!(!record.get().unwrap().unwrap().is_empty());
let mut recorder =
Recorder::new(window, 10, false, RecordSettings::default());
recorder.settings.time.duration = Some(0.2);
assert_ne!(0, recorder.record_sync(None).unwrap().len());
}
}