1use crate::frame::Frame;
14
15pub struct FrameCallback {
21 inner: Box<dyn for<'a> Fn(&Frame<'a>) + Send + Sync + 'static>,
22}
23
24impl FrameCallback {
25 pub fn new<F>(f: F) -> Self
27 where
28 F: for<'a> Fn(&Frame<'a>) + Send + Sync + 'static,
29 {
30 Self { inner: Box::new(f) }
31 }
32
33 pub fn invoke(&self, frame: &Frame<'_>) {
35 (self.inner)(frame);
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42 use crate::frame::PixelFormat;
43 use std::sync::atomic::{AtomicUsize, Ordering};
44 use std::sync::Arc;
45
46 #[test]
47 fn frame_callback_dispatches_to_closure() {
48 let counter = Arc::new(AtomicUsize::new(0));
49 let counter_clone = counter.clone();
50 let cb = FrameCallback::new(move |_frame| {
51 counter_clone.fetch_add(1, Ordering::SeqCst);
52 });
53
54 let data = vec![0u8; 16];
55 let frame = Frame::new(&data, 4, 4, PixelFormat::Mono8, 0, 0);
56
57 cb.invoke(&frame);
58 cb.invoke(&frame);
59 assert_eq!(counter.load(Ordering::SeqCst), 2);
60 }
61
62 #[test]
63 fn frame_callback_receives_frame_metadata() {
64 let seen = Arc::new(std::sync::Mutex::new(None));
65 let seen_clone = seen.clone();
66 let cb = FrameCallback::new(move |frame: &Frame<'_>| {
67 *seen_clone.lock().unwrap() = Some((
68 frame.width,
69 frame.height,
70 frame.pixel_format,
71 frame.frame_id,
72 frame.timestamp_ns,
73 frame.data().to_vec(),
74 ));
75 });
76
77 let data = vec![9u8, 8, 7];
78 cb.invoke(&Frame::new(&data, 3, 1, PixelFormat::Bgr8, 42, 11));
79
80 let got = seen.lock().unwrap().clone().expect("callback not called");
81 assert_eq!(got.0, 3);
82 assert_eq!(got.1, 1);
83 assert_eq!(got.2, PixelFormat::Bgr8);
84 assert_eq!(got.3, 11);
85 assert_eq!(got.4, 42);
86 assert_eq!(got.5, vec![9, 8, 7]);
87 }
88}