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 current_frame: Mutex<Option<VideoFrame>>,
12}
13
14impl VideoPlayback {
15 pub fn new() -> Self {
16 Self {
17 decoder: Mutex::new(None),
18 scheduler: Mutex::new(None),
19 current_frame: Mutex::new(None),
20 }
21 }
22
23 pub fn start(
24 &self,
25 path: &Path,
26 hw_accel: HwAccel,
27 ) -> Result<VideoMetadata, crate::video::error::VideoError> {
28 self.stop();
29 let decoder = VideoDecoder::new(path, hw_accel)?;
30 let metadata = decoder.metadata().clone();
31 let scheduler = FrameScheduler::new(metadata.duration);
32 *self.lock_decoder() = Some(decoder);
33 *self.lock_scheduler() = Some(scheduler);
34 tracing::info!(
35 "Video started: {}x{} @ {:.2} fps, {:?}",
36 metadata.width,
37 metadata.height,
38 metadata.fps,
39 metadata.duration
40 );
41 Ok(metadata)
42 }
43
44 pub fn stop(&self) {
45 *self.lock_decoder() = None;
46 *self.lock_scheduler() = None;
47 *self.lock_current() = None;
48 }
49
50 pub fn pause(&self) {
51 if let Some(s) = self.lock_scheduler().as_mut() {
52 s.pause();
53 }
54 if let Some(d) = self.lock_decoder().as_ref() {
55 d.pause();
56 }
57 }
58
59 pub fn resume(&self) {
60 if let Some(s) = self.lock_scheduler().as_mut() {
61 s.resume();
62 }
63 if let Some(d) = self.lock_decoder().as_ref() {
64 d.resume();
65 }
66 }
67
68 pub fn seek(&self, timestamp: Duration) -> Result<(), crate::video::error::VideoError> {
69 {
70 let mut decoder = self.lock_decoder();
71 let mut scheduler = self.lock_scheduler();
72 let scheduler = scheduler.as_mut().ok_or_else(|| {
73 VideoError::SeekFailed(timestamp, anyhow::anyhow!("no video is playing"))
74 })?;
75 scheduler.seek(timestamp)?;
76 if let Some(d) = decoder.as_mut() {
77 d.seek(timestamp);
78 d.drain();
79 }
80 }
81 tracing::info!("Video seeked to {:?}", timestamp);
82 Ok(())
83 }
84
85 pub fn next_frame(&self) -> Option<VideoFrame> {
86 let mut decoder = self.lock_decoder();
87 let mut scheduler = self.lock_scheduler();
88 let (Some(decoder), Some(scheduler)) = (decoder.as_mut(), scheduler.as_mut()) else {
89 return None;
90 };
91 while let Some(frame) = decoder.next_frame() {
92 if scheduler.should_display(frame.pts) && scheduler.should_upload(frame.pts) {
93 *self.lock_current() = Some(frame.clone());
94 return Some(frame);
95 }
96 }
97 None
98 }
99
100 pub fn wait_first_frame(&self, timeout: Duration) -> Option<VideoFrame> {
101 let deadline = Instant::now() + timeout;
102 loop {
103 if let Some(frame) = self.next_frame() {
104 return Some(frame);
105 }
106 if Instant::now() >= deadline {
107 tracing::warn!("Timed out waiting for first video frame");
108 return None;
109 }
110 std::thread::sleep(Duration::from_millis(5));
111 }
112 }
113
114 pub fn current_frame(&self) -> Option<VideoFrame> {
115 self.lock_current().clone()
116 }
117
118 pub fn metadata(&self) -> Option<VideoMetadata> {
119 self.lock_decoder().as_ref().map(|d| d.metadata().clone())
120 }
121
122 pub fn decoder_info(&self) -> Option<DecoderInfo> {
123 self.lock_decoder().as_ref().map(|d| d.decoder_info())
124 }
125
126 pub fn hw_accel_in_use(&self) -> HwAccel {
127 self.lock_decoder()
128 .as_ref()
129 .map(VideoDecoder::hw_accel_in_use)
130 .unwrap_or(HwAccel::Software)
131 }
132
133 pub fn position(&self) -> Option<Duration> {
134 self.lock_scheduler().as_ref().map(|s| s.current_position())
135 }
136
137 pub fn is_paused(&self) -> bool {
138 self.lock_scheduler()
139 .as_ref()
140 .map(|s| s.is_paused())
141 .unwrap_or(false)
142 }
143
144 pub fn is_playing(&self) -> bool {
145 self.lock_decoder().is_some()
146 }
147
148 fn lock_decoder(&self) -> std::sync::MutexGuard<'_, Option<VideoDecoder>> {
149 self.decoder.lock().unwrap_or_else(|p| p.into_inner())
150 }
151
152 fn lock_scheduler(&self) -> std::sync::MutexGuard<'_, Option<FrameScheduler>> {
153 self.scheduler.lock().unwrap_or_else(|p| p.into_inner())
154 }
155
156 fn lock_current(&self) -> std::sync::MutexGuard<'_, Option<VideoFrame>> {
157 self.current_frame.lock().unwrap_or_else(|p| p.into_inner())
158 }
159}
160
161impl Default for VideoPlayback {
162 fn default() -> Self {
163 Self::new()
164 }
165}