wallr_core/video/
playback.rs1use crate::video::{
9 DecoderInfo, FrameScheduler, HwAccel, VideoDecoder, VideoError, VideoFrame, VideoMetadata,
10 VideoResult,
11};
12use std::path::Path;
13use std::sync::Mutex;
14use std::time::{Duration, Instant};
15
16pub struct VideoPlayback {
18 decoder: Mutex<Option<VideoDecoder>>,
19 scheduler: Mutex<Option<FrameScheduler>>,
20 current_frame: Mutex<Option<VideoFrame>>,
21}
22
23impl VideoPlayback {
24 pub fn new() -> Self {
26 Self {
27 decoder: Mutex::new(None),
28 scheduler: Mutex::new(None),
29 current_frame: Mutex::new(None),
30 }
31 }
32
33 pub fn start(&self, path: &Path, hw_accel: HwAccel) -> VideoResult<VideoMetadata> {
35 self.stop();
38
39 let decoder = VideoDecoder::new(path, hw_accel)?;
40 let metadata = decoder.metadata().clone();
41 let scheduler = FrameScheduler::new(metadata.duration);
42
43 *self.lock_decoder() = Some(decoder);
44 *self.lock_scheduler() = Some(scheduler);
45
46 tracing::info!(
47 "Video playback started: {}x{} @ {:.2} fps, duration: {:?}",
48 metadata.width,
49 metadata.height,
50 metadata.fps,
51 metadata.duration
52 );
53
54 Ok(metadata)
55 }
56
57 pub fn stop(&self) {
59 *self.lock_decoder() = None;
60 *self.lock_scheduler() = None;
61 *self.lock_current() = None;
62 }
63
64 pub fn pause(&self) {
68 if let Some(scheduler) = self.lock_scheduler().as_mut() {
69 scheduler.pause();
70 }
71 if let Some(decoder) = self.lock_decoder().as_ref() {
72 decoder.pause();
73 }
74 }
75
76 pub fn resume(&self) {
78 if let Some(scheduler) = self.lock_scheduler().as_mut() {
79 scheduler.resume();
80 }
81 if let Some(decoder) = self.lock_decoder().as_ref() {
82 decoder.resume();
83 }
84 }
85
86 pub fn seek(&self, timestamp: Duration) -> VideoResult<()> {
89 {
90 let mut decoder = self.lock_decoder();
91 let mut scheduler = self.lock_scheduler();
92
93 let scheduler = scheduler.as_mut().ok_or_else(|| {
94 VideoError::SeekFailed(timestamp, anyhow::anyhow!("no video is playing"))
95 })?;
96 scheduler.seek(timestamp)?;
97
98 if let Some(decoder) = decoder.as_mut() {
99 decoder.seek(timestamp);
100 decoder.drain();
101 }
102 }
103 tracing::info!("Video playback seeked to {:?}", timestamp);
104 Ok(())
105 }
106
107 pub fn next_frame(&self) -> Option<VideoFrame> {
113 let mut decoder = self.lock_decoder();
114 let mut scheduler = self.lock_scheduler();
115
116 let (Some(decoder), Some(scheduler)) = (decoder.as_mut(), scheduler.as_mut()) else {
117 return None;
118 };
119
120 let mut newest: Option<VideoFrame> = None;
121 while let Some(frame) = decoder.next_frame() {
122 if scheduler.should_display(frame.pts) {
123 if scheduler.should_upload(frame.pts) {
124 *self.lock_current() = Some(frame.clone());
125 return Some(frame);
126 }
127 newest = Some(frame);
130 }
131 }
132 let _ = newest;
133 None
134 }
135
136 pub fn wait_first_frame(&self, timeout: Duration) -> Option<VideoFrame> {
141 let deadline = Instant::now() + timeout;
142 loop {
143 if let Some(frame) = self.next_frame() {
144 return Some(frame);
145 }
146 if Instant::now() >= deadline {
147 tracing::warn!("Timed out waiting for the first video frame");
148 return None;
149 }
150 thread_sleep(5);
151 }
152 }
153
154 pub fn current_frame(&self) -> Option<VideoFrame> {
156 self.lock_current().clone()
157 }
158
159 pub fn metadata(&self) -> Option<VideoMetadata> {
161 self.lock_decoder().as_ref().map(|d| d.metadata().clone())
162 }
163
164 pub fn decoder_info(&self) -> Option<DecoderInfo> {
166 self.lock_decoder().as_ref().map(|d| d.decoder_info())
167 }
168
169 pub fn hw_accel_in_use(&self) -> HwAccel {
171 self.lock_decoder()
172 .as_ref()
173 .map(VideoDecoder::hw_accel_in_use)
174 .unwrap_or(HwAccel::Software)
175 }
176
177 pub fn position(&self) -> Option<Duration> {
179 self.lock_scheduler().as_ref().map(|s| s.current_position())
180 }
181
182 pub fn is_paused(&self) -> bool {
184 self.lock_scheduler()
185 .as_ref()
186 .map(|s| s.is_paused())
187 .unwrap_or(false)
188 }
189
190 pub fn is_playing(&self) -> bool {
192 self.lock_decoder().is_some()
193 }
194
195 fn lock_decoder(&self) -> std::sync::MutexGuard<'_, Option<VideoDecoder>> {
196 self.decoder.lock().unwrap_or_else(|p| p.into_inner())
197 }
198
199 fn lock_scheduler(&self) -> std::sync::MutexGuard<'_, Option<FrameScheduler>> {
200 self.scheduler.lock().unwrap_or_else(|p| p.into_inner())
201 }
202
203 fn lock_current(&self) -> std::sync::MutexGuard<'_, Option<VideoFrame>> {
204 self.current_frame.lock().unwrap_or_else(|p| p.into_inner())
205 }
206}
207
208fn thread_sleep(ms: u64) {
211 std::thread::sleep(Duration::from_millis(ms));
212}
213
214impl Default for VideoPlayback {
215 fn default() -> Self {
216 Self::new()
217 }
218}