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
12pub struct ColorFrameCapture {
17 kinect: KinectSensor, format: Option<ColorImageFormat>, }
20
21impl ColorFrameCapture {
22 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 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 pub fn iter(&self) -> Result<ColorFrameCaptureIter, Error> {
59 let source = self.kinect.color_frame_source()?;
60 let reader = source.open_reader()?;
62 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
82pub 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 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 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 })(); return Some(result);
123 } else if wait_status == WAIT_TIMEOUT {
124 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]>, }
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 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), });
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), })
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([]), }
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 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}