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            if image_format != desired_format {
167                // If the image format does not match the required format, we need to convert it.
168                let desired_bpp = get_bytes_per_pixel_for_format(desired_format.clone());
169                if desired_bpp == 0 {
170                    return Err(Error::from_hresult(E_INVALIDARG));
171                }
172                let converted_buffer_size = width * height * desired_bpp;
173                let mut converted_data = vec![0u8; converted_buffer_size as usize];
174                color_frame.copy_converted_frame_data_to_array(
175                    &mut converted_data,
176                    desired_format.clone(),
177                )?;
178                return Ok(Self {
179                    width,
180                    height,
181                    fps: KINECT_DEFAULT_CAPTURE_FPS,
182                    bytes_per_pixel: desired_bpp,
183                    timestamp,
184                    image_format: desired_format,
185                    data: Arc::from(converted_data), // Convert Vec<u8> to Arc<[u8]>
186                });
187            }
188        }
189
190        let mut data = Vec::with_capacity(buffer_size as usize);
191        let raw_buffer = color_frame.access_raw_underlying_buffer()?;
192        assert!(
193            raw_buffer.len() as u32 == buffer_size,
194            "Raw buffer size does not match expected size"
195        );
196        data.extend_from_slice(raw_buffer);
197
198        Ok(Self {
199            width,
200            height,
201            fps: KINECT_DEFAULT_CAPTURE_FPS,
202            bytes_per_pixel,
203            timestamp,
204            image_format,
205            data: Arc::from(data), // Convert Vec<u8> to Arc<[u8]>
206        })
207    }
208}
209
210impl Default for ColorFrameData {
211    fn default() -> Self {
212        Self {
213            width: 0,
214            height: 0,
215            fps: KINECT_DEFAULT_CAPTURE_FPS,
216            bytes_per_pixel: 0,
217            timestamp: 0,
218            image_format: ColorImageFormat::None,
219            data: Arc::new([]), // Use an empty Arc<[u8]> for default
220        }
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use std::sync::mpsc;
227
228    use anyhow::anyhow;
229
230    use super::*;
231
232    #[test]
233    fn color_capture_test() -> anyhow::Result<()> {
234        let (color_tx, color_rx) = mpsc::channel::<ColorFrameData>();
235        let max_frames_to_capture = 10;
236        let color_capture_thread = std::thread::spawn(move || -> anyhow::Result<()> {
237            let color_capture = ColorFrameCapture::new()?;
238            for (frame_count, frame) in color_capture.iter()?.enumerate() {
239                if frame_count >= max_frames_to_capture {
240                    break;
241                }
242                let data = frame.map_err(|e| anyhow!("Error capturing color frame: {}", e))?;
243                if color_tx.send(data).is_err() {
244                    // Receiver dropped, exit thread
245                    break;
246                }
247            }
248            Ok(())
249        });
250
251        let processing_thread = std::thread::spawn(move || -> anyhow::Result<()> {
252            for _ in 0..max_frames_to_capture {
253                let frame_data = match color_rx.recv() {
254                    Ok(data) => data,
255                    Err(_) => break,
256                };
257                println!(
258                    "Received color frame: {}x{}, {} bytes, timestamp: {}, format: {:?}, bytes_per_pixel: {}",
259                    frame_data.width,
260                    frame_data.height,
261                    frame_data.data.len(),
262                    frame_data.timestamp,
263                    frame_data.image_format,
264                    frame_data.bytes_per_pixel
265                );
266
267                anyhow::ensure!(
268                    frame_data.width == 1920,
269                    "Unexpected width: {}",
270                    frame_data.width
271                );
272                anyhow::ensure!(
273                    frame_data.height == 1080,
274                    "Unexpected height: {}",
275                    frame_data.height
276                );
277                anyhow::ensure!(
278                    [2, 4].contains(&frame_data.bytes_per_pixel),
279                    "Unexpected bytes_per_pixel: {}",
280                    frame_data.bytes_per_pixel
281                );
282                anyhow::ensure!(!frame_data.data.is_empty(), "Frame data is empty");
283                anyhow::ensure!(frame_data.timestamp > 0, "Timestamp is not positive");
284                anyhow::ensure!(
285                    frame_data.fps == KINECT_DEFAULT_CAPTURE_FPS,
286                    "Unexpected FPS: {}",
287                    frame_data.fps
288                );
289            }
290            Ok(())
291        });
292
293        color_capture_thread
294            .join()
295            .map_err(|e| anyhow!("Color capture thread join error: {:?}", e))??;
296        processing_thread
297            .join()
298            .map_err(|e| anyhow!("Processing thread join error: {:?}", e))??;
299
300        Ok(())
301    }
302}