kinect_v2/
color_capture.rs

1use std::sync::Arc;
2
3use kinect_v2_sys::{
4    ColorImageFormat, DEFAULT_FRAME_WAIT_TIMEOUT_MS, KINECT_DEFAULT_CAPTURE_FPS, WAITABLE_HANDLE,
5    color::{ColorFrame, ColorFrameReader},
6    kinect::{self, KinectSensor},
7};
8use windows::Win32::Foundation::{E_FAIL, E_INVALIDARG, WAIT_OBJECT_0, WAIT_TIMEOUT};
9use windows::Win32::System::Threading::WaitForSingleObject;
10use windows::{Win32::Foundation::WAIT_EVENT, core::Error};
11
12/// Manages the Kinect sensor and provides access to color frame data.
13///
14/// This struct is responsible for initializing and holding the necessary Kinect
15/// resources to capture color frames.
16pub struct ColorFrameCapture {
17    kinect: KinectSensor,             // keep the kinect sensor instance alive.
18    format: Option<ColorImageFormat>, // The desired color image format.
19}
20
21impl ColorFrameCapture {
22    /// Creates a new `ColorFrameCapture` instance.
23    ///
24    /// This function initializes the default Kinect sensor, opens it,
25    /// and sets up the color frame source and reader.
26    ///
27    /// # Errors
28    ///
29    /// Returns an error if the Kinect sensor cannot be initialized,
30    /// opened, or if the color frame source is not active.
31    pub fn new() -> Result<Self, Error> {
32        let kinect = kinect::get_default_kinect_sensor()?;
33        kinect.open()?;
34
35        Ok(ColorFrameCapture {
36            kinect,
37            format: None,
38        })
39    }
40
41    /// Creates a new `ColorFrameCapture` instance with the specified color image format.
42    pub fn new_with_format(color_image_format: ColorImageFormat) -> Result<Self, Error> {
43        let mut capture = Self::new()?;
44        capture.format = Some(color_image_format);
45        Ok(capture)
46    }
47
48    /// Returns an iterator over color frames.
49    ///
50    /// The iterator will block waiting for new frames. Each item yielded by
51    /// the iterator is a `Result<ColorFrameData, Error>`, allowing for error
52    /// handling during frame acquisition.
53    ///
54    /// # Errors
55    ///
56    /// Returns an error if it fails to subscribe to the frame arrived event,
57    /// which is necessary for the iterator to function.
58    pub fn iter(&self) -> Result<ColorFrameCaptureIter, Error> {
59        let source = self.kinect.color_frame_source()?;
60        // Open the reader to active the source.
61        let reader = source.open_reader()?;
62        // Ensure the color frame source is active.
63        // If not, event subscription and frame acquisition might fail.
64        if !source.get_is_active()? {
65            log::warn!(
66                "Color frame source is not active, cannot subscribe to frame arrived event."
67            );
68            return Err(Error::from_hresult(E_FAIL));
69        }
70        let mut waitable_handle = WAITABLE_HANDLE::default();
71
72        reader.subscribe_frame_arrived(&mut waitable_handle)?;
73        Ok(ColorFrameCaptureIter {
74            reader,
75            waitable_handle,
76            timeout_ms: DEFAULT_FRAME_WAIT_TIMEOUT_MS,
77            format: self.format.clone(),
78        })
79    }
80}
81
82/// An iterator that yields color frames from a Kinect sensor.
83///
84/// This iterator blocks until a new frame is available or an error occurs.
85/// It is created by calling the `iter` method on `ColorFrameCapture`.
86pub struct ColorFrameCaptureIter {
87    reader: ColorFrameReader,
88    waitable_handle: WAITABLE_HANDLE,
89    timeout_ms: u32,
90    format: Option<ColorImageFormat>,
91}
92
93impl Drop for ColorFrameCaptureIter {
94    fn drop(&mut self) {
95        // Best effort to unsubscribe from the frame arrived event.
96        // Errors in `drop` are typically logged or ignored, as panicking in drop is problematic.
97        if let Err(e) = self.reader.unsubscribe_frame_arrived(self.waitable_handle) {
98            log::warn!("Failed to unsubscribe color frame arrived event: {e:?}");
99        }
100    }
101}
102
103impl Iterator for ColorFrameCaptureIter {
104    type Item = Result<ColorFrameData, Error>;
105
106    fn next(&mut self) -> Option<Self::Item> {
107        loop {
108            let wait_status: WAIT_EVENT =
109                unsafe { WaitForSingleObject(self.waitable_handle, self.timeout_ms) };
110
111            if wait_status == WAIT_OBJECT_0 {
112                // Frame event was signaled.
113                // Use a closure and the `?` operator for cleaner error handling.
114                let result = (|| {
115                    let event_args = self
116                        .reader
117                        .get_frame_arrived_event_data(self.waitable_handle)?;
118                    let frame_reference = event_args.get_frame_reference()?;
119                    let color_frame = frame_reference.acquire_frame()?;
120                    ColorFrameData::new(&color_frame, self.format.clone())
121                })(); // Immediately invoke the closure
122                return Some(result);
123            } else if wait_status == WAIT_TIMEOUT {
124                // No new frame arrived within the timeout period.
125                // Continue waiting as this is a blocking iterator.
126                continue;
127            } else {
128                return Some(Err(Error::from_hresult(E_FAIL)));
129            }
130        }
131    }
132}
133
134#[derive(Debug, Clone)]
135pub struct ColorFrameData {
136    pub width: u32,
137    pub height: u32,
138    pub fps: u32,
139    pub bytes_per_pixel: u32,
140    pub timestamp: u64,
141    pub image_format: ColorImageFormat,
142    pub data: Arc<[u8]>, //Use Arc<[u8]> instead of Vec<u8> to avoid expensive cloning
143}
144
145fn get_bytes_per_pixel_for_format(format: ColorImageFormat) -> u32 {
146    match format {
147        ColorImageFormat::Rgba | ColorImageFormat::Bgra | ColorImageFormat::Bayer => 4,
148        ColorImageFormat::Yuy2 | ColorImageFormat::Yuv => 2,
149        ColorImageFormat::None => 0,
150    }
151}
152
153impl ColorFrameData {
154    pub fn new(
155        color_frame: &ColorFrame,
156        format_opt: Option<ColorImageFormat>,
157    ) -> Result<Self, Error> {
158        let frame_description = color_frame.get_frame_description()?;
159        let width = frame_description.get_width()? as u32;
160        let height = frame_description.get_height()? as u32;
161        let bytes_per_pixel = frame_description.get_bytes_per_pixel()?;
162        let buffer_size = width * height * bytes_per_pixel;
163        let timestamp = color_frame.get_relative_time()? as u64;
164        let image_format = color_frame.get_raw_color_image_format()?;
165        if let Some(desired_format) = format_opt
166            && image_format != desired_format
167        {
168            // If the image format does not match the required format, we need to convert it.
169            let desired_bpp = get_bytes_per_pixel_for_format(desired_format.clone());
170            if desired_bpp == 0 {
171                return Err(Error::from_hresult(E_INVALIDARG));
172            }
173            let converted_buffer_size = width * height * desired_bpp;
174            let mut converted_data = vec![0u8; converted_buffer_size as usize];
175            color_frame
176                .copy_converted_frame_data_to_array(&mut converted_data, desired_format.clone())?;
177            return Ok(Self {
178                width,
179                height,
180                fps: KINECT_DEFAULT_CAPTURE_FPS,
181                bytes_per_pixel: desired_bpp,
182                timestamp,
183                image_format: desired_format,
184                data: Arc::from(converted_data), // Convert Vec<u8> to Arc<[u8]>
185            });
186        }
187
188        let mut data = Vec::with_capacity(buffer_size as usize);
189        let raw_buffer = color_frame.access_raw_underlying_buffer()?;
190        assert!(
191            raw_buffer.len() as u32 == buffer_size,
192            "Raw buffer size does not match expected size"
193        );
194        data.extend_from_slice(raw_buffer);
195
196        Ok(Self {
197            width,
198            height,
199            fps: KINECT_DEFAULT_CAPTURE_FPS,
200            bytes_per_pixel,
201            timestamp,
202            image_format,
203            data: Arc::from(data), // Convert Vec<u8> to Arc<[u8]>
204        })
205    }
206}
207
208impl Default for ColorFrameData {
209    fn default() -> Self {
210        Self {
211            width: 0,
212            height: 0,
213            fps: KINECT_DEFAULT_CAPTURE_FPS,
214            bytes_per_pixel: 0,
215            timestamp: 0,
216            image_format: ColorImageFormat::None,
217            data: Arc::new([]), // Use an empty Arc<[u8]> for default
218        }
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use std::sync::mpsc;
225
226    use anyhow::anyhow;
227
228    use super::*;
229
230    #[test]
231    fn color_capture_test() -> anyhow::Result<()> {
232        let (color_tx, color_rx) = mpsc::channel::<ColorFrameData>();
233        let max_frames_to_capture = 10;
234        let color_capture_thread = std::thread::spawn(move || -> anyhow::Result<()> {
235            let color_capture = ColorFrameCapture::new()?;
236            for (frame_count, frame) in color_capture.iter()?.enumerate() {
237                if frame_count >= max_frames_to_capture {
238                    break;
239                }
240                let data = frame.map_err(|e| anyhow!("Error capturing color frame: {}", e))?;
241                if color_tx.send(data).is_err() {
242                    // Receiver dropped, exit thread
243                    break;
244                }
245            }
246            Ok(())
247        });
248
249        let processing_thread = std::thread::spawn(move || -> anyhow::Result<()> {
250            for _ in 0..max_frames_to_capture {
251                let frame_data = match color_rx.recv() {
252                    Ok(data) => data,
253                    Err(_) => break,
254                };
255                println!(
256                    "Received color frame: {}x{}, {} bytes, timestamp: {}, format: {:?}, bytes_per_pixel: {}",
257                    frame_data.width,
258                    frame_data.height,
259                    frame_data.data.len(),
260                    frame_data.timestamp,
261                    frame_data.image_format,
262                    frame_data.bytes_per_pixel
263                );
264
265                anyhow::ensure!(
266                    frame_data.width == 1920,
267                    "Unexpected width: {}",
268                    frame_data.width
269                );
270                anyhow::ensure!(
271                    frame_data.height == 1080,
272                    "Unexpected height: {}",
273                    frame_data.height
274                );
275                anyhow::ensure!(
276                    [2, 4].contains(&frame_data.bytes_per_pixel),
277                    "Unexpected bytes_per_pixel: {}",
278                    frame_data.bytes_per_pixel
279                );
280                anyhow::ensure!(!frame_data.data.is_empty(), "Frame data is empty");
281                anyhow::ensure!(frame_data.timestamp > 0, "Timestamp is not positive");
282                anyhow::ensure!(
283                    frame_data.fps == KINECT_DEFAULT_CAPTURE_FPS,
284                    "Unexpected FPS: {}",
285                    frame_data.fps
286                );
287            }
288            Ok(())
289        });
290
291        color_capture_thread
292            .join()
293            .map_err(|e| anyhow!("Color capture thread join error: {:?}", e))??;
294        processing_thread
295            .join()
296            .map_err(|e| anyhow!("Processing thread join error: {:?}", e))??;
297
298        Ok(())
299    }
300}