use apple_cf::cm::CMSampleBuffer;
use apple_cf_compat_04::iosurface::{IOSurface, IOSurfaceLockOptions};
use videotoolbox::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let surface =
IOSurface::create(160, 120, u32::from_be_bytes(*b"BGRA"), 4).ok_or("alloc IOSurface")?;
{
let mut g = surface
.lock(IOSurfaceLockOptions::NONE)
.map_err(|c| format!("lock: {c}"))?;
if let Some(b) = g.as_slice_mut() {
for px in b.chunks_exact_mut(4) {
px[0] = 0x40;
px[1] = 0x80;
px[2] = 0xC0;
px[3] = 0xFF;
}
}
}
let encoder = CompressionSession::builder(160, 120, Codec::H264)
.with_real_time(true)
.with_expected_frame_rate(30.0)
.build()?;
let frame = encoder.encode(&surface, (0, 30))?;
println!("Encoded {} bytes of H.264", frame.data.len());
let raw_ptr = frame.cm_sample_buffer_ptr();
let sample_buffer = unsafe { CMSampleBuffer::from_raw_retained(raw_ptr) }
.ok_or("CMSampleBuffer wrap returned None")?;
println!("CMSampleBuffer:");
println!(" is_valid: {}", sample_buffer.is_valid());
println!(" data_is_ready: {}", sample_buffer.data_is_ready());
println!(" num_samples: {}", sample_buffer.num_samples());
let pts = sample_buffer.presentation_timestamp();
println!(
" pts: {} / {} (flags=0x{:x})",
pts.value, pts.timescale, pts.flags
);
let dur = sample_buffer.duration();
println!(" duration: {} / {}", dur.value, dur.timescale);
let block_buffer = sample_buffer
.data_buffer()
.ok_or("expected a data buffer for compressed video")?;
println!(" block_buffer.len: {}", block_buffer.data_length());
let format = sample_buffer
.format_description()
.ok_or("expected a format description")?;
println!(
" format: media_type={} subtype={}",
format.media_type().display(),
format.media_subtype().display()
);
assert_eq!(
format.media_subtype().as_u32(),
u32::from_be_bytes(*b"avc1"),
"expected H.264 ('avc1') subtype"
);
Ok(())
}