Skip to main content

wallr_core/video/
playback.rs

1use 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}