kinect_v2/
body_capture.rs

1use kinect_v2_sys::{
2    DEFAULT_FRAME_WAIT_TIMEOUT_MS, WAITABLE_HANDLE,
3    bindings::{
4        CameraSpacePoint, DetectionResult, FrameEdges, HandState, Joint, JointOrientation,
5        JointType, PointF, TrackingConfidence, TrackingState, Vector4,
6    },
7    body::{Body, BodyFrame, BodyFrameReader},
8    kinect::{self, KinectSensor},
9};
10use windows::Win32::Foundation::{E_FAIL, WAIT_OBJECT_0, WAIT_TIMEOUT};
11use windows::Win32::System::Threading::WaitForSingleObject;
12use windows::{Win32::Foundation::WAIT_EVENT, core::Error};
13
14/// Manages the Kinect sensor and provides access to body frame data.
15///
16/// This struct is responsible for initializing and holding the necessary Kinect
17/// resources to capture body frames.
18pub struct BodyFrameCapture {
19    kinect: KinectSensor, // keep the kinect sensor instance alive.
20}
21
22impl BodyFrameCapture {
23    /// Creates a new `BodyFrameCapture` instance.
24    ///
25    /// This function initializes the default Kinect sensor, opens it,
26    /// and sets up the body frame source and reader.
27    ///
28    /// # Errors
29    ///
30    /// Returns an error if the Kinect sensor cannot be initialized,
31    /// opened, or if the body frame source is not active.
32    pub fn new() -> Result<Self, Error> {
33        let kinect = kinect::get_default_kinect_sensor()?;
34        kinect.open()?;
35
36        Ok(BodyFrameCapture { kinect })
37    }
38
39    /// Returns an iterator over body frames.
40    ///
41    /// The iterator will block waiting for new frames. Each item yielded by
42    /// the iterator is a `Result<BodyFrameData, Error>`, allowing for error
43    /// handling during frame acquisition.
44    ///
45    /// # Errors
46    ///
47    /// Returns an error if it fails to subscribe to the frame arrived event,
48    /// which is necessary for the iterator to function.
49    pub fn iter(&self) -> Result<BodyFrameCaptureIter, Error> {
50        let source = self.kinect.body_frame_source()?;
51        // Open the reader to activate the source.
52        let reader = source.open_reader()?;
53        // Ensure the body frame source is active.
54        // If not, event subscription and frame acquisition might fail.
55        if !source.get_is_active()? {
56            log::warn!("Body frame source is not active, cannot subscribe to frame arrived event.");
57            return Err(Error::from_hresult(E_FAIL));
58        }
59
60        let mut waitable_handle = WAITABLE_HANDLE::default();
61        reader.subscribe_frame_arrived(&mut waitable_handle)?;
62        Ok(BodyFrameCaptureIter {
63            reader,
64            waitable_handle,
65            timeout_ms: DEFAULT_FRAME_WAIT_TIMEOUT_MS,
66        })
67    }
68}
69
70/// An iterator that yields body frames from a Kinect sensor.
71///
72/// This iterator blocks until a new frame is available or an error occurs.
73/// It is created by calling the `iter` method on `BodyFrameCapture`.
74pub struct BodyFrameCaptureIter {
75    reader: BodyFrameReader,
76    waitable_handle: WAITABLE_HANDLE,
77    timeout_ms: u32,
78}
79
80impl Drop for BodyFrameCaptureIter {
81    fn drop(&mut self) {
82        // Best effort to unsubscribe from the frame arrived event.
83        // Errors in `drop` are typically logged or ignored, as panicking in drop is problematic.
84        if let Err(e) = self.reader.unsubscribe_frame_arrived(self.waitable_handle) {
85            log::warn!("Failed to unsubscribe body frame arrived event: {e:?}");
86        }
87    }
88}
89
90impl Iterator for BodyFrameCaptureIter {
91    type Item = Result<BodyFrameData, 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_frame_arrived_event_data(self.waitable_handle)?;
105                    let frame_reference = event_args.get_frame_reference()?;
106                    let body_frame = frame_reference.acquire_frame()?;
107                    BodyFrameData::new(&body_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#[derive(Debug, Clone)]
122pub struct BodyData {
123    pub tracking_id: u64,
124    pub is_tracked: bool,
125    pub is_restricted: bool,
126    pub hand_left_state: HandState,
127    pub hand_right_state: HandState,
128    pub hand_left_confidence: TrackingConfidence,
129    pub hand_right_confidence: TrackingConfidence,
130    pub clipped_edges: FrameEdges,
131    pub lean: PointF,
132    pub lean_tracking_state: TrackingState,
133    pub is_engaged: bool,
134    pub joints: Vec<Joint>,
135    pub joint_orientations: Vec<JointOrientation>,
136}
137
138impl BodyData {
139    pub fn new(body: Body) -> Result<Self, Error> {
140        use kinect_v2_sys::bindings::{Joint, JointOrientation};
141
142        // Get basic body properties
143        let tracking_id = body.get_tracking_id()?;
144
145        let is_tracked = body.get_is_tracked()?;
146
147        let is_restricted = body.get_is_restricted()?;
148
149        let hand_left_state = body.get_hand_left_state()?;
150
151        let hand_right_state = body.get_hand_right_state()?;
152
153        let hand_left_confidence = body.get_hand_left_confidence()?;
154
155        let hand_right_confidence = body.get_hand_right_confidence()?;
156
157        let clipped_edges = body.get_clipped_edges()?;
158
159        let is_engaged = body.get_engaged()? == DetectionResult::Yes;
160
161        // Get lean data
162        let lean = body.get_lean()?;
163        let lean_tracking_state = body.get_lean_tracking_state()?;
164
165        // Get joints (25 joints in Kinect v2)
166        const JOINT_COUNT: usize = 25;
167        let mut joints: [Joint; JOINT_COUNT] = unsafe { std::mem::zeroed() };
168        body.get_joints(joints.as_mut())?;
169        let joints: Vec<Joint> = joints.to_vec();
170
171        // Get joint orientations
172        let mut joint_orientations: [JointOrientation; JOINT_COUNT] = unsafe { std::mem::zeroed() };
173        body.get_joint_orientations(joint_orientations.as_mut())?;
174        let joint_orientations: Vec<JointOrientation> = joint_orientations.to_vec();
175
176        Ok(Self {
177            tracking_id,
178            is_tracked,
179            is_restricted,
180            hand_left_state,
181            hand_right_state,
182            hand_left_confidence,
183            hand_right_confidence,
184            clipped_edges,
185            lean,
186            lean_tracking_state,
187            is_engaged,
188            joints,
189            joint_orientations,
190        })
191    }
192
193    /// Gets the position of a specific joint by joint type.
194    pub fn get_joint_position(&self, joint_type: JointType) -> Option<CameraSpacePoint> {
195        self.joints
196            .iter()
197            .find(|joint| joint.JointType == joint_type)
198            .map(|joint| joint.Position.clone())
199    }
200
201    /// Gets the orientation of a specific joint by joint type.
202    pub fn get_joint_orientation(&self, joint_type: JointType) -> Option<Vector4> {
203        self.joint_orientations
204            .iter()
205            .find(|joint_orientation| joint_orientation.JointType == joint_type)
206            .map(|joint_orientation| joint_orientation.Orientation.clone())
207    }
208
209    /// Checks if the body is actively being tracked.
210    pub fn is_actively_tracked(&self) -> bool {
211        self.is_tracked && !self.is_restricted
212    }
213}
214
215#[derive(Debug, Clone, Default)]
216pub struct BodyFrameData {
217    pub timestamp: u64,
218    pub floor_clip_plane: Vector4,
219    pub body_count: u32,
220    pub bodies: Vec<BodyData>,
221}
222
223impl BodyFrameData {
224    pub fn new(body_frame: &BodyFrame) -> Result<Self, Error> {
225        let timestamp = body_frame.get_relative_time()? as u64;
226        // Get floor clip plane
227        let floor_clip_plane = body_frame.get_floor_clip_plane()?;
228        // Get body count from the body frame source
229        let body_frame_source = body_frame.get_body_frame_source()?;
230        let body_count = body_frame_source.get_body_count()? as u32;
231
232        // Get and refresh body data
233        let bodies = body_frame.get_and_refresh_body_data()?;
234        let bodies = bodies
235            .into_iter()
236            .map(BodyData::new)
237            .collect::<Result<Vec<BodyData>, Error>>()?;
238
239        Ok(Self {
240            timestamp,
241            floor_clip_plane,
242            body_count,
243            bodies,
244        })
245    }
246}
247
248#[cfg(test)]
249mod tests {
250    use super::*;
251    use anyhow::anyhow;
252    use std::sync::mpsc;
253
254    #[test]
255    fn body_capture_test() -> anyhow::Result<()> {
256        let (body_tx, body_rx) = mpsc::channel::<BodyFrameData>();
257        let max_frames_to_capture = 10;
258        let body_capture_thread = std::thread::spawn(move || -> anyhow::Result<()> {
259            let body_capture = BodyFrameCapture::new()?;
260            for (frame_count, frame) in body_capture.iter()?.enumerate() {
261                if frame_count >= max_frames_to_capture {
262                    break;
263                }
264                let data = frame.map_err(|e| anyhow!("Error capturing body frame: {}", e))?;
265                if body_tx.send(data).is_err() {
266                    break;
267                }
268            }
269            Ok(())
270        });
271
272        let processing_thread = std::thread::spawn(move || -> anyhow::Result<()> {
273            for _ in 0..max_frames_to_capture {
274                let frame_data = match body_rx.recv() {
275                    Ok(data) => data,
276                    Err(_) => break,
277                };
278                println!(
279                    "Received body frame: timestamp: {}, body_count: {}",
280                    frame_data.timestamp, frame_data.body_count
281                );
282                anyhow::ensure!(frame_data.body_count > 0, "No bodies detected");
283                anyhow::ensure!(frame_data.timestamp > 0, "Timestamp is not positive");
284            }
285            Ok(())
286        });
287
288        body_capture_thread
289            .join()
290            .map_err(|e| anyhow!("Body capture thread join error: {:?}", e))??;
291        processing_thread
292            .join()
293            .map_err(|e| anyhow!("Processing thread join error: {:?}", e))??;
294        Ok(())
295    }
296}