I agree that it is probably UB, but after staring at this stuff for days, I'm running out of ideas. So please let me elaborate.
I'm testing out a depth/rgb camera (here is the API on github in C).
All the types/constants/functions prefixed with sc
or Sc
come directly from the bindgen I created from the API and are black boxes since the API libraries are closed source. There are two relevant functions with the following signatures
pub unsafe fn scGetFrameReady(device: ScDeviceHandle, waitTime: u16, pFrameReady: *mut ScFrameReady) -> ScStatus
pub unsafe fn scGetFrame(device: ScDeviceHandle, frameType: ScFrameType, pScFrame: *mut ScFrame) -> ScStatus
ScDeviceHandle: *mut ::std::os::raw::c_void
is a pointer to the device, which I wrap into a struct
pub struct Device {
pub handle: ScDeviceHandle,
}
and check it to not be NULL before returning it. It's used in many other function, for example to start up or shut down the camera. So I don't think this is the issue.
ScFrameReady
is the following struct containing a buffer storing the signal for the frame availability.
pub struct ScFrameReady {
pub _bitfield_align_1: [u8; 0],
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 4]>,
}
ScFrameType = ::std::os::raw::c_uint
is an interger deciding the type of frame to be retrieved (depth, rgb,...).
ScFrame
is the following struct, containing a pointer pFrameData
to the data
pub struct ScFrame {
pub frameIndex: u32,
pub frameType: u32,
pub pixelFormat: u32,
pub pFrameData: *mut u8,
pub dataLen: u32,
/* … */
}
So I create the following two wrapper functions
pub fn read_next_frame(device: &Device, max_wait_time_ms: u16, frame_ready: &mut ScFrameReady) {
unsafe {
scGetFrameReady(device.handle, max_wait_time_ms, frame_ready);
}
}
pub fn get_frame(
device: &Device,
frame_ready: &ScFrameReady,
frame_type: &FrameType,
frame: &mut ScFrame,
) {
let mut ft: Option<ScFrameType> = None;
match frame_type {
FrameType::Depth => {
if frame_ready.depth() == 1 {
ft = Some(ScFrameType_SC_DEPTH_FRAME);
}
}
FrameType::IR => {
if frame_ready.ir() == 1 {
ft = Some(ScFrameType_SC_IR_FRAME);
}
}
FrameType::RGB => {
if frame_ready.color() == 1 {
ft = Some(ScFrameType_SC_COLOR_FRAME);
}
}
FrameType::TransformedRGB => {
if frame_ready.transformedColor() == 1 {
ft = Some(ScFrameType_SC_TRANSFORM_COLOR_IMG_TO_DEPTH_SENSOR_FRAME);
}
}
}
if let Some(ft) = ft {
unsafe {
let status = scGetFrame(device.handle, ft, frame);
if status != ScStatus_SC_OK {
panic!("get_frame failded with status {}", status);
}
}
if frame.pFrameData.is_null() {
panic!("frame pointer is NULL!");
}
}
}
and then use these functions in the main loop of the program, for example, to receive a depth frame:
let frame_ready = &mut FrameReady::default();
let frame = &mut Frame::default();
loop {
read_next_frame(&device, 1200, frame_ready);
get_frame(&device, frame_ready, &FrameType::Depth, frame);
// do something with the frame data...
}
This works fine for all FrameType
s except for FrameType::TransformedRGB
in release mode, where frame.dataLen
is zero and there is no data in frame.pFrameData
(that's the only weird thing happening).
There is no other exceptional thing going on, frame_ready.transformedColor() == 1
is true, saying that "yes, there is a frame ready for that type", the return status of scGetFrame()
is ok too, and the frame.pFrameData
pointer is not NULL.
Sorry for the lengthy description, but is there anything wrong with my wrapping code that could create UB?