1use super::ffi;
15use super::{
16 AudioBuffer, AudioBufferList, AudioBufferListRaw, CMBlockBuffer, CMSampleTimingInfo, CMTime,
17 SCFrameStatus,
18};
19use crate::cv::CVPixelBuffer;
20
21pub use apple_cf::cm::CMSampleBuffer;
24
25struct FrameInfoFields;
33
34impl FrameInfoFields {
35 const STATUS: u32 = 1 << 0;
36 const DISPLAY_TIME: u32 = 1 << 1;
37 const SCALE_FACTOR: u32 = 1 << 2;
38 const CONTENT_SCALE: u32 = 1 << 3;
39 const CONTENT_RECT: u32 = 1 << 4;
40 const BOUNDING_RECT: u32 = 1 << 5;
41 const SCREEN_RECT: u32 = 1 << 6;
42 const PRESENTER_OVERLAY_RECT: u32 = 1 << 7;
43}
44
45#[allow(clippy::derive_partial_eq_without_eq)]
52#[derive(Debug, Default, Clone, PartialEq)]
53pub struct FrameInfo {
54 pub frame_status: Option<SCFrameStatus>,
56 pub display_time: Option<u64>,
59 pub scale_factor: Option<f64>,
61 pub content_scale: Option<f64>,
64 pub content_rect: Option<crate::cg::CGRect>,
66 pub bounding_rect: Option<crate::cg::CGRect>,
69 pub screen_rect: Option<crate::cg::CGRect>,
71 pub presenter_overlay_content_rect: Option<crate::cg::CGRect>,
74}
75
76pub trait CMSampleBufferSCExt {
87 fn frame_status(&self) -> Option<SCFrameStatus>;
89 fn display_time(&self) -> Option<u64>;
91 fn scale_factor(&self) -> Option<f64>;
93 fn content_scale(&self) -> Option<f64>;
95 fn content_rect(&self) -> Option<crate::cg::CGRect>;
97 fn bounding_rect(&self) -> Option<crate::cg::CGRect>;
99 fn screen_rect(&self) -> Option<crate::cg::CGRect>;
101 fn presenter_overlay_content_rect(&self) -> Option<crate::cg::CGRect>;
103 fn dirty_rects(&self) -> Option<Vec<crate::cg::CGRect>>;
105 fn frame_info(&self) -> Option<FrameInfo>;
108}
109
110impl CMSampleBufferSCExt for CMSampleBuffer {
111 fn frame_status(&self) -> Option<SCFrameStatus> {
112 unsafe {
113 let status = ffi::cm_sample_buffer_get_frame_status(self.as_ptr());
114 if status >= 0 {
115 SCFrameStatus::from_raw(status)
116 } else {
117 None
118 }
119 }
120 }
121
122 fn display_time(&self) -> Option<u64> {
123 unsafe {
124 let mut value: u64 = 0;
125 if ffi::cm_sample_buffer_get_display_time(self.as_ptr(), &mut value) {
126 Some(value)
127 } else {
128 None
129 }
130 }
131 }
132
133 fn scale_factor(&self) -> Option<f64> {
134 unsafe {
135 let mut value: f64 = 0.0;
136 if ffi::cm_sample_buffer_get_scale_factor(self.as_ptr(), &mut value) {
137 Some(value)
138 } else {
139 None
140 }
141 }
142 }
143
144 fn content_scale(&self) -> Option<f64> {
145 unsafe {
146 let mut value: f64 = 0.0;
147 if ffi::cm_sample_buffer_get_content_scale(self.as_ptr(), &mut value) {
148 Some(value)
149 } else {
150 None
151 }
152 }
153 }
154
155 fn content_rect(&self) -> Option<crate::cg::CGRect> {
156 unsafe {
157 let mut x = 0.0;
158 let mut y = 0.0;
159 let mut w = 0.0;
160 let mut h = 0.0;
161 if ffi::cm_sample_buffer_get_content_rect(self.as_ptr(), &mut x, &mut y, &mut w, &mut h)
162 {
163 Some(crate::cg::CGRect::new(x, y, w, h))
164 } else {
165 None
166 }
167 }
168 }
169
170 fn bounding_rect(&self) -> Option<crate::cg::CGRect> {
171 unsafe {
172 let mut x = 0.0;
173 let mut y = 0.0;
174 let mut w = 0.0;
175 let mut h = 0.0;
176 if ffi::cm_sample_buffer_get_bounding_rect(
177 self.as_ptr(),
178 &mut x,
179 &mut y,
180 &mut w,
181 &mut h,
182 ) {
183 Some(crate::cg::CGRect::new(x, y, w, h))
184 } else {
185 None
186 }
187 }
188 }
189
190 fn screen_rect(&self) -> Option<crate::cg::CGRect> {
191 unsafe {
192 let mut x = 0.0;
193 let mut y = 0.0;
194 let mut w = 0.0;
195 let mut h = 0.0;
196 if ffi::cm_sample_buffer_get_screen_rect(self.as_ptr(), &mut x, &mut y, &mut w, &mut h)
197 {
198 Some(crate::cg::CGRect::new(x, y, w, h))
199 } else {
200 None
201 }
202 }
203 }
204
205 fn presenter_overlay_content_rect(&self) -> Option<crate::cg::CGRect> {
206 #[cfg(feature = "macos_14_2")]
207 unsafe {
208 let mut x = 0.0;
209 let mut y = 0.0;
210 let mut w = 0.0;
211 let mut h = 0.0;
212 if ffi::cm_sample_buffer_get_presenter_overlay_content_rect(
213 self.as_ptr(),
214 &mut x,
215 &mut y,
216 &mut w,
217 &mut h,
218 ) {
219 Some(crate::cg::CGRect::new(x, y, w, h))
220 } else {
221 None
222 }
223 }
224 #[cfg(not(feature = "macos_14_2"))]
225 None
226 }
227
228 fn dirty_rects(&self) -> Option<Vec<crate::cg::CGRect>> {
229 unsafe {
230 let mut rects_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
231 let mut count: usize = 0;
232 if !ffi::cm_sample_buffer_get_dirty_rects(self.as_ptr(), &mut rects_ptr, &mut count) {
233 return None;
234 }
235 if rects_ptr.is_null() || count == 0 {
236 return None;
237 }
238 let rects_typed = rects_ptr.cast::<f64>();
239 let mut rects = Vec::with_capacity(count);
240 for i in 0..count {
241 let base = rects_typed.add(i * 4);
242 rects.push(crate::cg::CGRect::new(
243 *base,
244 *base.add(1),
245 *base.add(2),
246 *base.add(3),
247 ));
248 }
249 ffi::cm_sample_buffer_free_dirty_rects(rects_ptr);
250 Some(rects)
251 }
252 }
253
254 fn frame_info(&self) -> Option<FrameInfo> {
255 unsafe {
256 let mut fields: u32 = 0;
257 let mut status: i32 = 0;
258 let mut display_time: u64 = 0;
259 let mut scale_factor: f64 = 0.0;
260 let mut content_scale: f64 = 0.0;
261 let mut content_rect = [0.0_f64; 4];
262 let mut bounding_rect = [0.0_f64; 4];
263 let mut screen_rect = [0.0_f64; 4];
264 let mut presenter_overlay_rect = [0.0_f64; 4];
265 if !ffi::cm_sample_buffer_get_frame_info(
266 self.as_ptr(),
267 &mut fields,
268 &mut status,
269 &mut display_time,
270 &mut scale_factor,
271 &mut content_scale,
272 content_rect.as_mut_ptr(),
273 bounding_rect.as_mut_ptr(),
274 screen_rect.as_mut_ptr(),
275 presenter_overlay_rect.as_mut_ptr(),
276 ) {
277 return None;
278 }
279 let to_rect = |a: [f64; 4]| crate::cg::CGRect::new(a[0], a[1], a[2], a[3]);
280 Some(FrameInfo {
281 frame_status: ((fields & FrameInfoFields::STATUS) != 0)
282 .then(|| SCFrameStatus::from_raw(status))
283 .flatten(),
284 display_time: ((fields & FrameInfoFields::DISPLAY_TIME) != 0)
285 .then_some(display_time),
286 scale_factor: ((fields & FrameInfoFields::SCALE_FACTOR) != 0)
287 .then_some(scale_factor),
288 content_scale: ((fields & FrameInfoFields::CONTENT_SCALE) != 0)
289 .then_some(content_scale),
290 content_rect: ((fields & FrameInfoFields::CONTENT_RECT) != 0)
291 .then(|| to_rect(content_rect)),
292 bounding_rect: ((fields & FrameInfoFields::BOUNDING_RECT) != 0)
293 .then(|| to_rect(bounding_rect)),
294 screen_rect: ((fields & FrameInfoFields::SCREEN_RECT) != 0)
295 .then(|| to_rect(screen_rect)),
296 presenter_overlay_content_rect: ((fields
297 & FrameInfoFields::PRESENTER_OVERLAY_RECT)
298 != 0)
299 .then(|| to_rect(presenter_overlay_rect)),
300 })
301 }
302 }
303}
304
305pub trait CMSampleBufferExt {
313 fn create_for_image_buffer(
320 image_buffer: &CVPixelBuffer,
321 presentation_time: CMTime,
322 duration: CMTime,
323 ) -> Result<Self, i32>
324 where
325 Self: Sized;
326
327 fn image_buffer(&self) -> Option<CVPixelBuffer>;
329
330 fn audio_buffer_list(&self) -> Option<AudioBufferList>;
332
333 fn output_presentation_timestamp(&self) -> CMTime;
335
336 fn set_output_presentation_timestamp(&self, time: CMTime) -> Result<(), i32>;
342
343 fn sample_size(&self, index: usize) -> usize;
345
346 fn total_sample_size(&self) -> usize;
348
349 fn is_data_ready(&self) -> bool;
351
352 fn make_data_ready(&self) -> Result<(), i32>;
359
360 fn sample_timing_info(&self, index: usize) -> Result<CMSampleTimingInfo, i32>;
366
367 fn cg_image(&self) -> Result<apple_cf::cg::CGImage, i32>;
391}
392
393impl CMSampleBufferExt for CMSampleBuffer {
394 fn create_for_image_buffer(
395 image_buffer: &CVPixelBuffer,
396 presentation_time: CMTime,
397 duration: CMTime,
398 ) -> Result<Self, i32> {
399 unsafe {
400 let mut sample_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
401 let status = ffi::cm_sample_buffer_create_for_image_buffer(
402 image_buffer.as_ptr(),
403 presentation_time.value,
404 presentation_time.timescale,
405 duration.value,
406 duration.timescale,
407 &mut sample_buffer_ptr,
408 );
409 if status == 0 && !sample_buffer_ptr.is_null() {
410 Self::from_raw(sample_buffer_ptr).ok_or(status)
411 } else {
412 Err(status)
413 }
414 }
415 }
416
417 fn image_buffer(&self) -> Option<CVPixelBuffer> {
418 unsafe {
419 let ptr = ffi::cm_sample_buffer_get_image_buffer(self.as_ptr());
423 CVPixelBuffer::from_raw(ptr)
424 }
425 }
426
427 fn audio_buffer_list(&self) -> Option<AudioBufferList> {
428 unsafe {
429 let mut num_buffers: u32 = 0;
430 let mut buffers_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
431 let mut buffers_len: usize = 0;
432 let mut block_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
433
434 ffi::cm_sample_buffer_get_audio_buffer_list(
435 self.as_ptr(),
436 &mut num_buffers,
437 &mut buffers_ptr,
438 &mut buffers_len,
439 &mut block_buffer_ptr,
440 );
441
442 if num_buffers == 0 {
443 None
444 } else {
445 Some(AudioBufferList {
446 inner: AudioBufferListRaw {
447 num_buffers,
448 buffers_ptr: buffers_ptr.cast::<AudioBuffer>(),
449 buffers_len,
450 },
451 block_buffer_ptr,
452 })
453 }
454 }
455 }
456
457 fn output_presentation_timestamp(&self) -> CMTime {
458 unsafe {
459 let mut value: i64 = 0;
460 let mut timescale: i32 = 0;
461 let mut flags: u32 = 0;
462 let mut epoch: i64 = 0;
463 ffi::cm_sample_buffer_get_output_presentation_timestamp(
464 self.as_ptr(),
465 &mut value,
466 &mut timescale,
467 &mut flags,
468 &mut epoch,
469 );
470 CMTime {
471 value,
472 timescale,
473 flags,
474 epoch,
475 }
476 }
477 }
478
479 fn set_output_presentation_timestamp(&self, time: CMTime) -> Result<(), i32> {
480 let status = unsafe {
481 ffi::cm_sample_buffer_set_output_presentation_timestamp(
482 self.as_ptr(),
483 time.value,
484 time.timescale,
485 time.flags,
486 time.epoch,
487 )
488 };
489 if status == 0 {
490 Ok(())
491 } else {
492 Err(status)
493 }
494 }
495
496 fn sample_size(&self, index: usize) -> usize {
497 unsafe { ffi::cm_sample_buffer_get_sample_size(self.as_ptr(), index) }
498 }
499
500 fn total_sample_size(&self) -> usize {
501 unsafe { ffi::cm_sample_buffer_get_total_sample_size(self.as_ptr()) }
502 }
503
504 fn is_data_ready(&self) -> bool {
505 unsafe { ffi::cm_sample_buffer_is_ready_for_data_access(self.as_ptr()) }
506 }
507
508 fn make_data_ready(&self) -> Result<(), i32> {
509 let status = unsafe { ffi::cm_sample_buffer_make_data_ready(self.as_ptr()) };
510 if status == 0 {
511 Ok(())
512 } else {
513 Err(status)
514 }
515 }
516
517 fn sample_timing_info(&self, index: usize) -> Result<CMSampleTimingInfo, i32> {
518 unsafe {
519 let mut dur_v: i64 = 0;
520 let mut dur_s: i32 = 0;
521 let mut dur_f: u32 = 0;
522 let mut dur_e: i64 = 0;
523 let mut pts_v: i64 = 0;
524 let mut pts_s: i32 = 0;
525 let mut pts_f: u32 = 0;
526 let mut pts_e: i64 = 0;
527 let mut dts_v: i64 = 0;
528 let mut dts_s: i32 = 0;
529 let mut dts_f: u32 = 0;
530 let mut dts_e: i64 = 0;
531 let status = ffi::cm_sample_buffer_get_sample_timing_info(
532 self.as_ptr(),
533 index,
534 &mut dur_v,
535 &mut dur_s,
536 &mut dur_f,
537 &mut dur_e,
538 &mut pts_v,
539 &mut pts_s,
540 &mut pts_f,
541 &mut pts_e,
542 &mut dts_v,
543 &mut dts_s,
544 &mut dts_f,
545 &mut dts_e,
546 );
547 if status == 0 {
548 Ok(CMSampleTimingInfo {
549 duration: CMTime {
550 value: dur_v,
551 timescale: dur_s,
552 flags: dur_f,
553 epoch: dur_e,
554 },
555 presentation_time_stamp: CMTime {
556 value: pts_v,
557 timescale: pts_s,
558 flags: pts_f,
559 epoch: pts_e,
560 },
561 decode_time_stamp: CMTime {
562 value: dts_v,
563 timescale: dts_s,
564 flags: dts_f,
565 epoch: dts_e,
566 },
567 })
568 } else {
569 Err(status)
570 }
571 }
572 }
573
574 fn cg_image(&self) -> Result<apple_cf::cg::CGImage, i32> {
575 unsafe {
576 let mut status: i32 = 0;
577 let ptr = ffi::cm_sample_buffer_create_cg_image(self.as_ptr(), &mut status);
578 if !ptr.is_null() && status == 0 {
579 Ok(apple_cf::cg::CGImage::from_raw(ptr.cast_mut()))
583 } else {
584 Err(status)
585 }
586 }
587 }
588}
589
590pub trait CMSampleBufferDataBufferExt {
602 fn data_buffer_local(&self) -> Option<CMBlockBuffer>;
603}
604
605impl CMSampleBufferDataBufferExt for CMSampleBuffer {
606 fn data_buffer_local(&self) -> Option<CMBlockBuffer> {
607 unsafe {
608 let ptr = ffi::cm_sample_buffer_get_data_buffer(self.as_ptr());
609 if ptr.is_null() {
610 return None;
611 }
612 let retained = ffi::cm_block_buffer_retain(ptr);
617 CMBlockBuffer::from_raw(retained)
618 }
619 }
620}