#![allow(unsafe_code)]
use mediaway_common::Bytes;
use objc2_core_media::CMSampleBuffer;
use objc2_core_video::{
CVPixelBufferGetBaseAddressOfPlane, CVPixelBufferGetBytesPerRowOfPlane,
CVPixelBufferGetHeightOfPlane, CVPixelBufferGetPlaneCount, CVPixelBufferGetWidthOfPlane,
CVPixelBufferLockBaseAddress, CVPixelBufferLockFlags, CVPixelBufferUnlockBaseAddress,
};
pub(super) unsafe fn extract_nv12(sample_buffer: &CMSampleBuffer) -> Option<(Bytes, u32, u32)> {
let pixel_buffer = unsafe { sample_buffer.image_buffer() }?;
let status =
unsafe { CVPixelBufferLockBaseAddress(&pixel_buffer, CVPixelBufferLockFlags::ReadOnly) };
if status != 0 {
return None;
}
let result = (|| {
let plane_count = CVPixelBufferGetPlaneCount(&pixel_buffer);
if plane_count != 2 {
return None;
}
let width = u32::try_from(CVPixelBufferGetWidthOfPlane(&pixel_buffer, 0)).ok()?;
let height = u32::try_from(CVPixelBufferGetHeightOfPlane(&pixel_buffer, 0)).ok()?;
let mut out = Vec::new();
for plane in 0..2 {
let plane_width = CVPixelBufferGetWidthOfPlane(&pixel_buffer, plane);
let plane_height = CVPixelBufferGetHeightOfPlane(&pixel_buffer, plane);
let row_bytes = CVPixelBufferGetBytesPerRowOfPlane(&pixel_buffer, plane);
let row_width_bytes = if plane == 0 {
plane_width
} else {
plane_width * 2
};
let base = CVPixelBufferGetBaseAddressOfPlane(&pixel_buffer, plane);
if base.is_null() || row_bytes < row_width_bytes {
return None;
}
for row in 0..plane_height {
let row_ptr = unsafe { base.cast::<u8>().add(row * row_bytes) };
let row_slice = unsafe { std::slice::from_raw_parts(row_ptr, row_width_bytes) };
out.extend_from_slice(row_slice);
}
}
Some((Bytes::from(out), width, height))
})();
unsafe { CVPixelBufferUnlockBaseAddress(&pixel_buffer, CVPixelBufferLockFlags::ReadOnly) };
result
}