kinect_v2/
multi_source_capture.rs

1use kinect_v2_sys::{
2    ColorImageFormat, DEFAULT_FRAME_WAIT_TIMEOUT_MS, WAITABLE_HANDLE,
3    bindings::FrameSourceTypes,
4    kinect::{self, KinectSensor},
5    multi_source_frame::{MultiSourceFrame, MultiSourceFrameReader},
6};
7use windows::Win32::Foundation::{E_FAIL, WAIT_OBJECT_0, WAIT_TIMEOUT};
8use windows::Win32::System::Threading::WaitForSingleObject;
9use windows::{Win32::Foundation::WAIT_EVENT, core::Error};
10
11use crate::{
12    body_capture::BodyFrameData, body_index_capture::BodyIndexFrameData,
13    color_capture::ColorFrameData, depth_capture::DepthFrameData,
14    infrared_capture::InfraredFrameData,
15};
16
17pub struct MultiSourceCapture {
18    kinect: KinectSensor,
19}
20
21impl MultiSourceCapture {
22    /// Creates a new `MultiSourceCapture` instance.
23    ///
24    /// This function initializes the default Kinect sensor, opens it,
25    /// and sets up the multi-source frame reader.
26    ///
27    /// # Errors
28    ///
29    /// Returns an error if the Kinect sensor cannot be initialized or opened.
30    pub fn new() -> Result<Self, windows::core::Error> {
31        let kinect = kinect::get_default_kinect_sensor()?;
32        kinect.open()?;
33
34        Ok(MultiSourceCapture { kinect })
35    }
36
37    /// Returns an iterator over multi-source frames.
38    ///
39    /// The iterator will block waiting for new frames. Each item yielded by
40    /// the iterator is a `Result<MultiSourceFrameData, Error>`, allowing for error
41    /// handling during frame acquisition.
42    ///
43    /// # Errors
44    ///
45    /// Returns an error if it fails to subscribe to the frame arrived event,
46    /// which is necessary for the iterator to function.
47    pub fn iter(&self) -> Result<MultiSourceCaptureIter, Error> {
48        let enabled_frame_source_types = FrameSourceTypes::Color
49            | FrameSourceTypes::Depth
50            | FrameSourceTypes::Infrared
51            | FrameSourceTypes::BodyIndex
52            | FrameSourceTypes::Body;
53        let reader = self
54            .kinect
55            .open_multi_source_frame_reader(enabled_frame_source_types)?;
56
57        let mut waitable_handle = WAITABLE_HANDLE::default();
58        reader.subscribe_multi_source_frame_arrived(&mut waitable_handle)?;
59        Ok(MultiSourceCaptureIter {
60            reader,
61            waitable_handle,
62            timeout_ms: DEFAULT_FRAME_WAIT_TIMEOUT_MS,
63        })
64    }
65}
66
67/// An iterator that yields multi-source frames from a Kinect sensor.
68///
69/// This iterator blocks until a new frame is available or an error occurs.
70/// It is created by calling the `iter` method on `MultiSourceCapture`.
71pub struct MultiSourceCaptureIter {
72    reader: MultiSourceFrameReader,
73    waitable_handle: WAITABLE_HANDLE,
74    timeout_ms: u32,
75}
76
77impl Drop for MultiSourceCaptureIter {
78    fn drop(&mut self) {
79        // Best effort to unsubscribe from the frame arrived event.
80        // Errors in `drop` are typically logged or ignored, as panicking in drop is problematic.
81        if let Err(e) = self
82            .reader
83            .unsubscribe_multi_source_frame_arrived(self.waitable_handle)
84        {
85            log::warn!("Failed to unsubscribe multi-source frame arrived event: {e:?}");
86        }
87    }
88}
89
90impl Iterator for MultiSourceCaptureIter {
91    type Item = Result<MultiSourceFrameData, Error>;
92
93    fn next(&mut self) -> Option<Self::Item> {
94        loop {
95            let wait_status: WAIT_EVENT =
96                unsafe { WaitForSingleObject(self.waitable_handle, self.timeout_ms) };
97
98            if wait_status == WAIT_OBJECT_0 {
99                // Frame event was signaled.
100                // Use a closure and the `?` operator for cleaner error handling.
101                let result = (|| {
102                    let event_args = self
103                        .reader
104                        .get_multi_source_frame_arrived_event_data(self.waitable_handle)?;
105                    let frame_reference = event_args.get_frame_reference()?;
106                    let multi_source_frame = frame_reference.acquire_frame()?;
107                    MultiSourceFrameData::new(&multi_source_frame)
108                })(); // Immediately invoke the closure
109                return Some(result);
110            } else if wait_status == WAIT_TIMEOUT {
111                // No new frame arrived within the timeout period.
112                // Continue waiting as this is a blocking iterator.
113                continue;
114            } else {
115                return Some(Err(Error::from_hresult(E_FAIL)));
116            }
117        }
118    }
119}
120
121/// Container for multi-source frame data from the Kinect sensor.
122///
123/// This struct holds optional frame data for each enabled frame source type.
124/// Not all frame types may be available in every multi-source frame.
125#[derive(Debug)]
126pub struct MultiSourceFrameData {
127    pub color_frame: Option<ColorFrameData>,
128    pub depth_frame: Option<DepthFrameData>,
129    pub infrared_frame: Option<InfraredFrameData>,
130    pub body_index_frame: Option<BodyIndexFrameData>,
131    pub body_frame: Option<BodyFrameData>,
132}
133
134impl MultiSourceFrameData {
135    pub fn new(multi_source_frame: &MultiSourceFrame) -> Result<Self, Error> {
136        // Try to get each frame type, but don't fail if one is not available
137        let color_frame = Self::get_color_frame_data(multi_source_frame, None);
138        let depth_frame = Self::get_depth_frame_data(multi_source_frame);
139        let infrared_frame = Self::get_infrared_frame_data(multi_source_frame);
140        let body_index_frame = Self::get_body_index_frame_data(multi_source_frame);
141        let body_frame = Self::get_body_frame_data(multi_source_frame);
142
143        Ok(Self {
144            color_frame,
145            depth_frame,
146            infrared_frame,
147            body_index_frame,
148            body_frame,
149        })
150    }
151
152    fn get_color_frame_data(
153        multi_source_frame: &MultiSourceFrame,
154        format: Option<ColorImageFormat>,
155    ) -> Option<ColorFrameData> {
156        let color_frame_reference = multi_source_frame.get_color_frame_reference().ok()?;
157        let color_frame = color_frame_reference.acquire_frame().ok()?;
158        ColorFrameData::new(&color_frame, format).ok()
159    }
160
161    fn get_depth_frame_data(multi_source_frame: &MultiSourceFrame) -> Option<DepthFrameData> {
162        let depth_frame_reference = multi_source_frame.get_depth_frame_reference().ok()?;
163        let depth_frame = depth_frame_reference.acquire_frame().ok()?;
164        DepthFrameData::new(&depth_frame).ok()
165    }
166
167    fn get_infrared_frame_data(multi_source_frame: &MultiSourceFrame) -> Option<InfraredFrameData> {
168        let infrared_frame_reference = multi_source_frame.get_infrared_frame_reference().ok()?;
169        let infrared_frame = infrared_frame_reference.acquire_frame().ok()?;
170        InfraredFrameData::new(&infrared_frame).ok()
171    }
172
173    fn get_body_index_frame_data(
174        multi_source_frame: &MultiSourceFrame,
175    ) -> Option<BodyIndexFrameData> {
176        let body_index_frame_reference =
177            multi_source_frame.get_body_index_frame_reference().ok()?;
178        let body_index_frame = body_index_frame_reference.acquire_frame().ok()?;
179        BodyIndexFrameData::new(&body_index_frame).ok()
180    }
181
182    fn get_body_frame_data(multi_source_frame: &MultiSourceFrame) -> Option<BodyFrameData> {
183        let body_frame_reference = multi_source_frame.get_body_frame_reference().ok()?;
184        let body_frame = body_frame_reference.acquire_frame().ok()?;
185        BodyFrameData::new(&body_frame).ok()
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use super::*;
192    use anyhow::anyhow;
193    use std::sync::mpsc;
194
195    #[test]
196    fn multi_source_capture_test() -> anyhow::Result<()> {
197        let (tx, rx) = mpsc::channel::<MultiSourceFrameData>();
198        let max_frames_to_capture = 10;
199        let capture_thread = std::thread::spawn(move || -> anyhow::Result<()> {
200            let capture = MultiSourceCapture::new()?;
201            for (frame_count, frame) in capture.iter()?.enumerate() {
202                if frame_count >= max_frames_to_capture {
203                    break;
204                }
205                let data =
206                    frame.map_err(|e| anyhow!("Error capturing multi-source frame: {}", e))?;
207                if tx.send(data).is_err() {
208                    break;
209                }
210            }
211            Ok(())
212        });
213
214        let processing_thread = std::thread::spawn(move || -> anyhow::Result<()> {
215            for _ in 0..max_frames_to_capture {
216                let frame_data = match rx.recv() {
217                    Ok(data) => data,
218                    Err(_) => break,
219                };
220                println!("Received multi-source frame");
221                // Validate at least one frame type is present
222                anyhow::ensure!(
223                    frame_data.color_frame.is_some()
224                        || frame_data.depth_frame.is_some()
225                        || frame_data.infrared_frame.is_some()
226                        || frame_data.body_index_frame.is_some()
227                        || frame_data.body_frame.is_some(),
228                    "No frame data present in multi-source frame"
229                );
230            }
231            Ok(())
232        });
233
234        capture_thread
235            .join()
236            .map_err(|e| anyhow!("Multi-source capture thread join error: {:?}", e))??;
237        processing_thread
238            .join()
239            .map_err(|e| anyhow!("Processing thread join error: {:?}", e))??;
240        Ok(())
241    }
242}