wallr_core/video/
playback.rs1use crate::video::{
2 DecoderInfo, FrameScheduler, HwAccel, VideoDecoder, VideoError, VideoFrame, VideoMetadata,
3};
4use std::path::Path;
5use std::sync::Mutex;
6use std::time::{Duration, Instant};
7
8pub struct VideoPlayback {
9 decoder: Mutex<Option<VideoDecoder>>,
10 scheduler: Mutex<Option<FrameScheduler>>,
11 pending_frame: Mutex<Option<VideoFrame>>,
12 current_frame: Mutex<Option<VideoFrame>>,
13}
14
15impl VideoPlayback {
16 pub fn new() -> Self {
17 Self {
18 decoder: Mutex::new(None),
19 scheduler: Mutex::new(None),
20 pending_frame: Mutex::new(None),
21 current_frame: Mutex::new(None),
22 }
23 }
24
25 pub fn start(
26 &self,
27 path: &Path,
28 hw_accel: HwAccel,
29 ) -> Result<VideoMetadata, crate::video::error::VideoError> {
30 self.stop();
31 let decoder = VideoDecoder::new(path, hw_accel)?;
32 let metadata = decoder.metadata().clone();
33 let scheduler = FrameScheduler::new(metadata.duration);
34 *self.lock_decoder() = Some(decoder);
35 *self.lock_scheduler() = Some(scheduler);
36 tracing::info!(
37 "Video started: {}x{} @ {:.2} fps, {:?}",
38 metadata.width,
39 metadata.height,
40 metadata.fps,
41 metadata.duration
42 );
43 Ok(metadata)
44 }
45
46 pub fn stop(&self) {
47 *self.lock_decoder() = None;
48 *self.lock_scheduler() = None;
49 *self.lock_pending() = None;
50 *self.lock_current() = None;
51 }
52
53 pub fn pause(&self) {
54 if let Some(s) = self.lock_scheduler().as_mut() {
55 s.pause();
56 }
57 if let Some(d) = self.lock_decoder().as_ref() {
58 d.pause();
59 }
60 }
61
62 pub fn resume(&self) {
63 if let Some(s) = self.lock_scheduler().as_mut() {
64 s.resume();
65 }
66 if let Some(d) = self.lock_decoder().as_ref() {
67 d.resume();
68 }
69 }
70
71 pub fn seek(&self, timestamp: Duration) -> Result<(), crate::video::error::VideoError> {
72 let mut decoder = self.lock_decoder();
73 let mut scheduler = self.lock_scheduler();
74 let mut pending = self.lock_pending();
75 let mut current = self.lock_current();
76 let scheduler = scheduler.as_mut().ok_or_else(|| {
77 VideoError::SeekFailed(timestamp, anyhow::anyhow!("no video is playing"))
78 })?;
79 scheduler.seek(timestamp)?;
80 if let Some(d) = decoder.as_mut() {
81 d.seek(timestamp);
82 d.drain();
83 }
84 *pending = None;
85 *current = None;
86 tracing::info!("Video seeked to {:?}", timestamp);
87 Ok(())
88 }
89
90 pub fn next_frame(&self) -> Option<VideoFrame> {
91 let mut decoder = self.lock_decoder();
92 let mut scheduler = self.lock_scheduler();
93 let mut pending = self.lock_pending();
94 let (Some(decoder), Some(scheduler)) = (decoder.as_mut(), scheduler.as_mut()) else {
95 return None;
96 };
97
98 let frame = take_due_frame(scheduler, &mut pending, || decoder.next_frame());
99 if let Some(frame) = &frame {
100 *self.lock_current() = Some(frame.clone());
101 }
102 frame
103 }
104
105 pub fn wait_first_frame(&self, timeout: Duration) -> Option<VideoFrame> {
106 let deadline = Instant::now() + timeout;
107 loop {
108 if let Some(frame) = self.next_frame() {
109 return Some(frame);
110 }
111 if Instant::now() >= deadline {
112 tracing::warn!("Timed out waiting for first video frame");
113 return None;
114 }
115 std::thread::sleep(Duration::from_millis(5));
116 }
117 }
118
119 pub fn current_frame(&self) -> Option<VideoFrame> {
120 self.lock_current().clone()
121 }
122
123 pub fn metadata(&self) -> Option<VideoMetadata> {
124 self.lock_decoder().as_ref().map(|d| d.metadata().clone())
125 }
126
127 pub fn decoder_info(&self) -> Option<DecoderInfo> {
128 self.lock_decoder().as_ref().map(|d| d.decoder_info())
129 }
130
131 pub fn hw_accel_in_use(&self) -> HwAccel {
132 self.lock_decoder()
133 .as_ref()
134 .map(VideoDecoder::hw_accel_in_use)
135 .unwrap_or(HwAccel::Software)
136 }
137
138 pub fn position(&self) -> Option<Duration> {
139 self.lock_scheduler().as_ref().map(|s| s.current_position())
140 }
141
142 pub fn is_paused(&self) -> bool {
143 self.lock_scheduler()
144 .as_ref()
145 .map(|s| s.is_paused())
146 .unwrap_or(false)
147 }
148
149 pub fn is_playing(&self) -> bool {
150 self.lock_decoder().is_some()
151 }
152
153 fn lock_decoder(&self) -> std::sync::MutexGuard<'_, Option<VideoDecoder>> {
154 self.decoder.lock().unwrap_or_else(|p| p.into_inner())
155 }
156
157 fn lock_scheduler(&self) -> std::sync::MutexGuard<'_, Option<FrameScheduler>> {
158 self.scheduler.lock().unwrap_or_else(|p| p.into_inner())
159 }
160
161 fn lock_pending(&self) -> std::sync::MutexGuard<'_, Option<VideoFrame>> {
162 self.pending_frame.lock().unwrap_or_else(|p| p.into_inner())
163 }
164
165 fn lock_current(&self) -> std::sync::MutexGuard<'_, Option<VideoFrame>> {
166 self.current_frame.lock().unwrap_or_else(|p| p.into_inner())
167 }
168}
169
170fn take_due_frame(
171 scheduler: &mut FrameScheduler,
172 pending: &mut Option<VideoFrame>,
173 mut next_frame: impl FnMut() -> Option<VideoFrame>,
174) -> Option<VideoFrame> {
175 let mut selected = None;
176
177 if let Some(frame) = pending.take() {
178 if !scheduler.should_display(frame.pts) {
179 *pending = Some(frame);
180 return None;
181 }
182 if scheduler.should_upload(frame.pts) {
183 selected = Some(frame);
184 }
185 }
186
187 while let Some(frame) = next_frame() {
188 if !scheduler.should_display(frame.pts) {
189 *pending = Some(frame);
190 break;
191 }
192 if scheduler.should_upload(frame.pts) {
193 selected = Some(frame);
194 }
195 }
196
197 selected
198}
199
200impl Default for VideoPlayback {
201 fn default() -> Self {
202 Self::new()
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use std::collections::VecDeque;
210
211 fn frame(pts_ms: u64) -> VideoFrame {
212 VideoFrame {
213 data: vec![pts_ms as u8],
214 width: 1,
215 height: 1,
216 pts: Duration::from_millis(pts_ms),
217 index: pts_ms,
218 }
219 }
220
221 #[test]
222 fn retains_future_frame_until_due() {
223 let mut scheduler = FrameScheduler::new(Duration::from_secs(1));
224 let mut pending = None;
225 let mut frames = VecDeque::from([frame(0), frame(100)]);
226
227 let first = take_due_frame(&mut scheduler, &mut pending, || frames.pop_front()).unwrap();
228 assert_eq!(first.pts, Duration::ZERO);
229 assert_eq!(pending.as_ref().unwrap().pts, Duration::from_millis(100));
230
231 scheduler.seek(Duration::from_millis(110)).unwrap();
232 let second = take_due_frame(&mut scheduler, &mut pending, || None).unwrap();
233 assert_eq!(second.pts, Duration::from_millis(100));
234 assert!(pending.is_none());
235 }
236
237 #[test]
238 fn selects_latest_due_frame() {
239 let mut scheduler = FrameScheduler::new(Duration::from_secs(1));
240 scheduler.seek(Duration::from_millis(50)).unwrap();
241 let mut pending = None;
242 let mut frames = VecDeque::from([frame(0), frame(16), frame(32), frame(100)]);
243
244 let selected = take_due_frame(&mut scheduler, &mut pending, || frames.pop_front()).unwrap();
245 assert_eq!(selected.pts, Duration::from_millis(32));
246 assert_eq!(pending.as_ref().unwrap().pts, Duration::from_millis(100));
247 }
248}