1use std::sync::Arc;
7
8use oximedia_audio::{AudioFrame, ChannelLayout};
9use oximedia_codec::VideoFrame;
10use oximedia_core::{PixelFormat, SampleFormat, Timestamp};
11
12#[derive(Clone, Debug)]
14pub enum FilterFrame {
15 Video(VideoFrame),
17 Audio(AudioFrame),
19}
20
21impl FilterFrame {
22 #[must_use]
24 pub fn timestamp(&self) -> &Timestamp {
25 match self {
26 Self::Video(f) => &f.timestamp,
27 Self::Audio(f) => &f.timestamp,
28 }
29 }
30
31 #[must_use]
33 pub fn is_video(&self) -> bool {
34 matches!(self, Self::Video(_))
35 }
36
37 #[must_use]
39 pub fn is_audio(&self) -> bool {
40 matches!(self, Self::Audio(_))
41 }
42
43 #[must_use]
45 pub fn as_video(&self) -> Option<&VideoFrame> {
46 match self {
47 Self::Video(f) => Some(f),
48 Self::Audio(_) => None,
49 }
50 }
51
52 #[must_use]
54 pub fn as_audio(&self) -> Option<&AudioFrame> {
55 match self {
56 Self::Video(_) => None,
57 Self::Audio(f) => Some(f),
58 }
59 }
60
61 pub fn as_video_mut(&mut self) -> Option<&mut VideoFrame> {
63 match self {
64 Self::Video(f) => Some(f),
65 Self::Audio(_) => None,
66 }
67 }
68
69 pub fn as_audio_mut(&mut self) -> Option<&mut AudioFrame> {
71 match self {
72 Self::Video(_) => None,
73 Self::Audio(f) => Some(f),
74 }
75 }
76}
77
78impl From<VideoFrame> for FilterFrame {
79 fn from(frame: VideoFrame) -> Self {
80 Self::Video(frame)
81 }
82}
83
84impl From<AudioFrame> for FilterFrame {
85 fn from(frame: AudioFrame) -> Self {
86 Self::Audio(frame)
87 }
88}
89
90#[derive(Clone, Debug)]
95pub struct FrameRef {
96 inner: Arc<FilterFrame>,
97}
98
99impl FrameRef {
100 pub fn new(frame: FilterFrame) -> Self {
102 Self {
103 inner: Arc::new(frame),
104 }
105 }
106
107 #[must_use]
109 pub fn frame(&self) -> &FilterFrame {
110 &self.inner
111 }
112
113 pub fn try_unwrap(self) -> Option<FilterFrame> {
117 Arc::try_unwrap(self.inner).ok()
118 }
119
120 #[must_use]
122 pub fn ref_count(&self) -> usize {
123 Arc::strong_count(&self.inner)
124 }
125
126 #[must_use]
131 pub fn make_mut(self) -> FilterFrame {
132 match Arc::try_unwrap(self.inner) {
133 Ok(frame) => frame,
134 Err(arc) => (*arc).clone(),
135 }
136 }
137}
138
139impl From<FilterFrame> for FrameRef {
140 fn from(frame: FilterFrame) -> Self {
141 Self::new(frame)
142 }
143}
144
145impl From<VideoFrame> for FrameRef {
146 fn from(frame: VideoFrame) -> Self {
147 Self::new(FilterFrame::Video(frame))
148 }
149}
150
151impl From<AudioFrame> for FrameRef {
152 fn from(frame: AudioFrame) -> Self {
153 Self::new(FilterFrame::Audio(frame))
154 }
155}
156
157#[allow(dead_code)]
161pub struct FramePool {
162 capacity: usize,
164 video_frames: Vec<VideoFrame>,
166 audio_frames: Vec<AudioFrame>,
168}
169
170impl FramePool {
171 #[must_use]
173 pub fn new(capacity: usize) -> Self {
174 Self {
175 capacity,
176 video_frames: Vec::with_capacity(capacity),
177 audio_frames: Vec::with_capacity(capacity),
178 }
179 }
180
181 #[must_use]
183 pub fn get_video_frame(&mut self, format: PixelFormat, width: u32, height: u32) -> VideoFrame {
184 if let Some(pos) = self
186 .video_frames
187 .iter()
188 .position(|f| f.format == format && f.width == width && f.height == height)
189 {
190 return self.video_frames.swap_remove(pos);
191 }
192
193 let mut frame = VideoFrame::new(format, width, height);
195 frame.allocate();
196 frame
197 }
198
199 pub fn return_video_frame(&mut self, frame: VideoFrame) {
201 if self.video_frames.len() < self.capacity {
202 self.video_frames.push(frame);
203 }
204 }
205
206 #[must_use]
208 pub fn get_audio_frame(
209 &mut self,
210 format: SampleFormat,
211 sample_rate: u32,
212 channels: ChannelLayout,
213 ) -> AudioFrame {
214 if let Some(pos) = self.audio_frames.iter().position(|f| {
216 f.format == format && f.sample_rate == sample_rate && f.channels == channels
217 }) {
218 return self.audio_frames.swap_remove(pos);
219 }
220
221 AudioFrame::new(format, sample_rate, channels)
223 }
224
225 pub fn return_audio_frame(&mut self, frame: AudioFrame) {
227 if self.audio_frames.len() < self.capacity {
228 self.audio_frames.push(frame);
229 }
230 }
231
232 pub fn clear(&mut self) {
234 self.video_frames.clear();
235 self.audio_frames.clear();
236 }
237
238 #[must_use]
240 pub fn video_frame_count(&self) -> usize {
241 self.video_frames.len()
242 }
243
244 #[must_use]
246 pub fn audio_frame_count(&self) -> usize {
247 self.audio_frames.len()
248 }
249}
250
251impl Default for FramePool {
252 fn default() -> Self {
253 Self::new(16)
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260
261 #[test]
262 fn test_filter_frame_video() {
263 let video = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
264 let frame = FilterFrame::Video(video);
265
266 assert!(frame.is_video());
267 assert!(!frame.is_audio());
268 assert!(frame.as_video().is_some());
269 assert!(frame.as_audio().is_none());
270 }
271
272 #[test]
273 fn test_filter_frame_audio() {
274 let audio = AudioFrame::new(SampleFormat::F32, 48000, ChannelLayout::Stereo);
275 let frame = FilterFrame::Audio(audio);
276
277 assert!(!frame.is_video());
278 assert!(frame.is_audio());
279 assert!(frame.as_video().is_none());
280 assert!(frame.as_audio().is_some());
281 }
282
283 #[test]
284 fn test_frame_ref() {
285 let video = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
286 let frame = FilterFrame::Video(video);
287 let frame_ref = FrameRef::new(frame);
288
289 assert_eq!(frame_ref.ref_count(), 1);
290
291 let frame_ref2 = frame_ref.clone();
292 assert_eq!(frame_ref.ref_count(), 2);
293 assert_eq!(frame_ref2.ref_count(), 2);
294 }
295
296 #[test]
297 fn test_frame_ref_try_unwrap() {
298 let video = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
299 let frame = FilterFrame::Video(video);
300 let frame_ref = FrameRef::new(frame);
301
302 let unwrapped = frame_ref.try_unwrap();
304 assert!(unwrapped.is_some());
305 }
306
307 #[test]
308 fn test_frame_ref_make_mut() {
309 let video = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
310 let frame = FilterFrame::Video(video);
311 let frame_ref = FrameRef::new(frame);
312 let frame_ref2 = frame_ref.clone();
313
314 let owned = frame_ref.make_mut();
316 assert!(owned.is_video());
317
318 assert!(frame_ref2.frame().is_video());
320 }
321
322 #[test]
323 fn test_frame_pool() {
324 let mut pool = FramePool::new(4);
325
326 let frame = pool.get_video_frame(PixelFormat::Yuv420p, 1920, 1080);
328 assert_eq!(frame.width, 1920);
329 assert_eq!(frame.height, 1080);
330
331 pool.return_video_frame(frame);
333 assert_eq!(pool.video_frame_count(), 1);
334
335 let frame2 = pool.get_video_frame(PixelFormat::Yuv420p, 1920, 1080);
337 assert_eq!(frame2.width, 1920);
338 assert_eq!(pool.video_frame_count(), 0);
339 }
340
341 #[test]
342 fn test_filter_frame_from() {
343 let video = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
344 let frame: FilterFrame = video.into();
345 assert!(frame.is_video());
346
347 let audio = AudioFrame::new(SampleFormat::F32, 48000, ChannelLayout::Stereo);
348 let frame: FilterFrame = audio.into();
349 assert!(frame.is_audio());
350 }
351
352 #[test]
353 fn test_frame_timestamp() {
354 use oximedia_core::Rational;
355
356 let mut video = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
357 video.timestamp = Timestamp::new(1000, Rational::new(1, 1000));
358 let frame = FilterFrame::Video(video);
359
360 assert_eq!(frame.timestamp().pts, 1000);
361 }
362}