1use super::ffi;
4use super::{
5 AudioBuffer, AudioBufferList, AudioBufferListRaw, CMBlockBuffer, CMFormatDescription,
6 CMSampleTimingInfo, CMTime, SCFrameStatus,
7};
8use crate::cv::CVPixelBuffer;
9use std::fmt;
10
11struct FrameInfoFields;
15
16impl FrameInfoFields {
17 const STATUS: u32 = 1 << 0;
18 const DISPLAY_TIME: u32 = 1 << 1;
19 const SCALE_FACTOR: u32 = 1 << 2;
20 const CONTENT_SCALE: u32 = 1 << 3;
21 const CONTENT_RECT: u32 = 1 << 4;
22 const BOUNDING_RECT: u32 = 1 << 5;
23 const SCREEN_RECT: u32 = 1 << 6;
24 const PRESENTER_OVERLAY_RECT: u32 = 1 << 7;
25}
26
27#[allow(clippy::derive_partial_eq_without_eq)]
37#[derive(Debug, Default, Clone, PartialEq)]
38pub struct FrameInfo {
39 pub frame_status: Option<SCFrameStatus>,
41 pub display_time: Option<u64>,
44 pub scale_factor: Option<f64>,
46 pub content_scale: Option<f64>,
49 pub content_rect: Option<crate::cg::CGRect>,
51 pub bounding_rect: Option<crate::cg::CGRect>,
54 pub screen_rect: Option<crate::cg::CGRect>,
56 pub presenter_overlay_content_rect: Option<crate::cg::CGRect>,
59}
60
61#[repr(transparent)]
63#[derive(Debug)]
64pub struct CMSampleBuffer(*mut std::ffi::c_void);
65
66impl PartialEq for CMSampleBuffer {
67 fn eq(&self, other: &Self) -> bool {
68 self.0 == other.0
69 }
70}
71
72impl Eq for CMSampleBuffer {}
73
74impl std::hash::Hash for CMSampleBuffer {
75 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
76 unsafe {
77 let hash_value = ffi::cm_sample_buffer_hash(self.0);
78 hash_value.hash(state);
79 }
80 }
81}
82
83impl CMSampleBuffer {
84 pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
85 if ptr.is_null() {
86 None
87 } else {
88 Some(Self(ptr))
89 }
90 }
91
92 pub unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
95 Self(ptr)
96 }
97
98 pub fn as_ptr(&self) -> *mut std::ffi::c_void {
99 self.0
100 }
101
102 pub fn create_for_image_buffer(
140 image_buffer: &CVPixelBuffer,
141 presentation_time: CMTime,
142 duration: CMTime,
143 ) -> Result<Self, i32> {
144 unsafe {
145 let mut sample_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
146 let status = ffi::cm_sample_buffer_create_for_image_buffer(
147 image_buffer.as_ptr(),
148 presentation_time.value,
149 presentation_time.timescale,
150 duration.value,
151 duration.timescale,
152 &mut sample_buffer_ptr,
153 );
154
155 if status == 0 && !sample_buffer_ptr.is_null() {
156 Ok(Self(sample_buffer_ptr))
157 } else {
158 Err(status)
159 }
160 }
161 }
162
163 pub fn image_buffer(&self) -> Option<CVPixelBuffer> {
165 unsafe {
166 let ptr = ffi::cm_sample_buffer_get_image_buffer(self.0);
167 CVPixelBuffer::from_raw(ptr)
168 }
169 }
170
171 pub fn frame_status(&self) -> Option<SCFrameStatus> {
198 unsafe {
199 let status = ffi::cm_sample_buffer_get_frame_status(self.0);
200 if status >= 0 {
201 SCFrameStatus::from_raw(status)
202 } else {
203 None
204 }
205 }
206 }
207
208 pub fn display_time(&self) -> Option<u64> {
212 unsafe {
213 let mut value: u64 = 0;
214 if ffi::cm_sample_buffer_get_display_time(self.0, &mut value) {
215 Some(value)
216 } else {
217 None
218 }
219 }
220 }
221
222 pub fn scale_factor(&self) -> Option<f64> {
226 unsafe {
227 let mut value: f64 = 0.0;
228 if ffi::cm_sample_buffer_get_scale_factor(self.0, &mut value) {
229 Some(value)
230 } else {
231 None
232 }
233 }
234 }
235
236 pub fn content_scale(&self) -> Option<f64> {
238 unsafe {
239 let mut value: f64 = 0.0;
240 if ffi::cm_sample_buffer_get_content_scale(self.0, &mut value) {
241 Some(value)
242 } else {
243 None
244 }
245 }
246 }
247
248 pub fn content_rect(&self) -> Option<crate::cg::CGRect> {
252 unsafe {
253 let mut x: f64 = 0.0;
254 let mut y: f64 = 0.0;
255 let mut width: f64 = 0.0;
256 let mut height: f64 = 0.0;
257 if ffi::cm_sample_buffer_get_content_rect(
258 self.0,
259 &mut x,
260 &mut y,
261 &mut width,
262 &mut height,
263 ) {
264 Some(crate::cg::CGRect::new(x, y, width, height))
265 } else {
266 None
267 }
268 }
269 }
270
271 pub fn bounding_rect(&self) -> Option<crate::cg::CGRect> {
275 unsafe {
276 let mut x: f64 = 0.0;
277 let mut y: f64 = 0.0;
278 let mut width: f64 = 0.0;
279 let mut height: f64 = 0.0;
280 if ffi::cm_sample_buffer_get_bounding_rect(
281 self.0,
282 &mut x,
283 &mut y,
284 &mut width,
285 &mut height,
286 ) {
287 Some(crate::cg::CGRect::new(x, y, width, height))
288 } else {
289 None
290 }
291 }
292 }
293
294 pub fn screen_rect(&self) -> Option<crate::cg::CGRect> {
298 unsafe {
299 let mut x: f64 = 0.0;
300 let mut y: f64 = 0.0;
301 let mut width: f64 = 0.0;
302 let mut height: f64 = 0.0;
303 if ffi::cm_sample_buffer_get_screen_rect(
304 self.0,
305 &mut x,
306 &mut y,
307 &mut width,
308 &mut height,
309 ) {
310 Some(crate::cg::CGRect::new(x, y, width, height))
311 } else {
312 None
313 }
314 }
315 }
316
317 #[cfg(feature = "macos_14_2")]
332 pub fn presenter_overlay_content_rect(&self) -> Option<crate::cg::CGRect> {
333 unsafe {
334 let mut x: f64 = 0.0;
335 let mut y: f64 = 0.0;
336 let mut width: f64 = 0.0;
337 let mut height: f64 = 0.0;
338 if ffi::cm_sample_buffer_get_presenter_overlay_content_rect(
339 self.0,
340 &mut x,
341 &mut y,
342 &mut width,
343 &mut height,
344 ) {
345 Some(crate::cg::CGRect::new(x, y, width, height))
346 } else {
347 None
348 }
349 }
350 }
351
352 pub fn dirty_rects(&self) -> Option<Vec<crate::cg::CGRect>> {
357 unsafe {
358 let mut rects_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
359 let mut count: usize = 0;
360 if ffi::cm_sample_buffer_get_dirty_rects(self.0, &mut rects_ptr, &mut count) {
361 if rects_ptr.is_null() || count == 0 {
362 return None;
363 }
364 let data = rects_ptr as *const f64;
365 let mut rects = Vec::with_capacity(count);
366 for i in 0..count {
367 let x = *data.add(i * 4);
368 let y = *data.add(i * 4 + 1);
369 let width = *data.add(i * 4 + 2);
370 let height = *data.add(i * 4 + 3);
371 rects.push(crate::cg::CGRect::new(x, y, width, height));
372 }
373 ffi::cm_sample_buffer_free_dirty_rects(rects_ptr);
374 Some(rects)
375 } else {
376 None
377 }
378 }
379 }
380
381 pub fn frame_info(&self) -> Option<FrameInfo> {
397 let mut fields: u32 = 0;
398 let mut status: i32 = 0;
399 let mut display_time: u64 = 0;
400 let mut scale_factor: f64 = 0.0;
401 let mut content_scale: f64 = 0.0;
402 let mut content_rect: [f64; 4] = [0.0; 4];
403 let mut bounding_rect: [f64; 4] = [0.0; 4];
404 let mut screen_rect: [f64; 4] = [0.0; 4];
405 let mut presenter_overlay_rect: [f64; 4] = [0.0; 4];
406
407 let any = unsafe {
408 ffi::cm_sample_buffer_get_frame_info(
409 self.0,
410 &mut fields,
411 &mut status,
412 &mut display_time,
413 &mut scale_factor,
414 &mut content_scale,
415 content_rect.as_mut_ptr(),
416 bounding_rect.as_mut_ptr(),
417 screen_rect.as_mut_ptr(),
418 presenter_overlay_rect.as_mut_ptr(),
419 )
420 };
421
422 if !any {
423 return None;
424 }
425
426 let to_rect = |arr: [f64; 4]| crate::cg::CGRect::new(arr[0], arr[1], arr[2], arr[3]);
427
428 Some(FrameInfo {
429 frame_status: ((fields & FrameInfoFields::STATUS) != 0)
430 .then(|| SCFrameStatus::from_raw(status))
431 .flatten(),
432 display_time: ((fields & FrameInfoFields::DISPLAY_TIME) != 0).then_some(display_time),
433 scale_factor: ((fields & FrameInfoFields::SCALE_FACTOR) != 0).then_some(scale_factor),
434 content_scale: ((fields & FrameInfoFields::CONTENT_SCALE) != 0)
435 .then_some(content_scale),
436 content_rect: ((fields & FrameInfoFields::CONTENT_RECT) != 0)
437 .then(|| to_rect(content_rect)),
438 bounding_rect: ((fields & FrameInfoFields::BOUNDING_RECT) != 0)
439 .then(|| to_rect(bounding_rect)),
440 screen_rect: ((fields & FrameInfoFields::SCREEN_RECT) != 0)
441 .then(|| to_rect(screen_rect)),
442 presenter_overlay_content_rect: ((fields & FrameInfoFields::PRESENTER_OVERLAY_RECT)
443 != 0)
444 .then(|| to_rect(presenter_overlay_rect)),
445 })
446 }
447
448 pub fn presentation_timestamp(&self) -> CMTime {
450 unsafe {
451 let mut value: i64 = 0;
452 let mut timescale: i32 = 0;
453 let mut flags: u32 = 0;
454 let mut epoch: i64 = 0;
455 ffi::cm_sample_buffer_get_presentation_timestamp(
456 self.0,
457 &mut value,
458 &mut timescale,
459 &mut flags,
460 &mut epoch,
461 );
462 CMTime {
463 value,
464 timescale,
465 flags,
466 epoch,
467 }
468 }
469 }
470
471 pub fn duration(&self) -> CMTime {
473 unsafe {
474 let mut value: i64 = 0;
475 let mut timescale: i32 = 0;
476 let mut flags: u32 = 0;
477 let mut epoch: i64 = 0;
478 ffi::cm_sample_buffer_get_duration(
479 self.0,
480 &mut value,
481 &mut timescale,
482 &mut flags,
483 &mut epoch,
484 );
485 CMTime {
486 value,
487 timescale,
488 flags,
489 epoch,
490 }
491 }
492 }
493
494 pub fn is_valid(&self) -> bool {
495 unsafe { ffi::cm_sample_buffer_is_valid(self.0) }
496 }
497
498 pub fn num_samples(&self) -> usize {
500 unsafe { ffi::cm_sample_buffer_get_num_samples(self.0) }
501 }
502
503 pub fn audio_buffer_list(&self) -> Option<AudioBufferList> {
505 unsafe {
506 let mut num_buffers: u32 = 0;
507 let mut buffers_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
508 let mut buffers_len: usize = 0;
509 let mut block_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
510
511 ffi::cm_sample_buffer_get_audio_buffer_list(
512 self.0,
513 &mut num_buffers,
514 &mut buffers_ptr,
515 &mut buffers_len,
516 &mut block_buffer_ptr,
517 );
518
519 if num_buffers == 0 {
520 None
521 } else {
522 Some(AudioBufferList {
523 inner: AudioBufferListRaw {
524 num_buffers,
525 buffers_ptr: buffers_ptr.cast::<AudioBuffer>(),
526 buffers_len,
527 },
528 block_buffer_ptr,
529 })
530 }
531 }
532 }
533
534 pub fn data_buffer(&self) -> Option<CMBlockBuffer> {
536 unsafe {
537 let ptr = ffi::cm_sample_buffer_get_data_buffer(self.0);
538 CMBlockBuffer::from_raw(ptr)
539 }
540 }
541
542 pub fn decode_timestamp(&self) -> CMTime {
544 unsafe {
545 let mut value: i64 = 0;
546 let mut timescale: i32 = 0;
547 let mut flags: u32 = 0;
548 let mut epoch: i64 = 0;
549 ffi::cm_sample_buffer_get_decode_timestamp(
550 self.0,
551 &mut value,
552 &mut timescale,
553 &mut flags,
554 &mut epoch,
555 );
556 CMTime {
557 value,
558 timescale,
559 flags,
560 epoch,
561 }
562 }
563 }
564
565 pub fn output_presentation_timestamp(&self) -> CMTime {
567 unsafe {
568 let mut value: i64 = 0;
569 let mut timescale: i32 = 0;
570 let mut flags: u32 = 0;
571 let mut epoch: i64 = 0;
572 ffi::cm_sample_buffer_get_output_presentation_timestamp(
573 self.0,
574 &mut value,
575 &mut timescale,
576 &mut flags,
577 &mut epoch,
578 );
579 CMTime {
580 value,
581 timescale,
582 flags,
583 epoch,
584 }
585 }
586 }
587
588 pub fn set_output_presentation_timestamp(&self, time: CMTime) -> Result<(), i32> {
594 unsafe {
595 let status = ffi::cm_sample_buffer_set_output_presentation_timestamp(
596 self.0,
597 time.value,
598 time.timescale,
599 time.flags,
600 time.epoch,
601 );
602 if status == 0 {
603 Ok(())
604 } else {
605 Err(status)
606 }
607 }
608 }
609
610 pub fn sample_size(&self, index: usize) -> usize {
612 unsafe { ffi::cm_sample_buffer_get_sample_size(self.0, index) }
613 }
614
615 pub fn total_sample_size(&self) -> usize {
617 unsafe { ffi::cm_sample_buffer_get_total_sample_size(self.0) }
618 }
619
620 pub fn is_data_ready(&self) -> bool {
622 unsafe { ffi::cm_sample_buffer_is_ready_for_data_access(self.0) }
623 }
624
625 pub fn make_data_ready(&self) -> Result<(), i32> {
631 unsafe {
632 let status = ffi::cm_sample_buffer_make_data_ready(self.0);
633 if status == 0 {
634 Ok(())
635 } else {
636 Err(status)
637 }
638 }
639 }
640
641 pub fn format_description(&self) -> Option<CMFormatDescription> {
643 unsafe {
644 let ptr = ffi::cm_sample_buffer_get_format_description(self.0);
645 CMFormatDescription::from_raw(ptr)
646 }
647 }
648
649 pub fn sample_timing_info(&self, index: usize) -> Result<CMSampleTimingInfo, i32> {
655 unsafe {
656 let mut timing_info = CMSampleTimingInfo {
657 duration: CMTime::INVALID,
658 presentation_time_stamp: CMTime::INVALID,
659 decode_time_stamp: CMTime::INVALID,
660 };
661 let status = ffi::cm_sample_buffer_get_sample_timing_info(
662 self.0,
663 index,
664 &mut timing_info.duration.value,
665 &mut timing_info.duration.timescale,
666 &mut timing_info.duration.flags,
667 &mut timing_info.duration.epoch,
668 &mut timing_info.presentation_time_stamp.value,
669 &mut timing_info.presentation_time_stamp.timescale,
670 &mut timing_info.presentation_time_stamp.flags,
671 &mut timing_info.presentation_time_stamp.epoch,
672 &mut timing_info.decode_time_stamp.value,
673 &mut timing_info.decode_time_stamp.timescale,
674 &mut timing_info.decode_time_stamp.flags,
675 &mut timing_info.decode_time_stamp.epoch,
676 );
677 if status == 0 {
678 Ok(timing_info)
679 } else {
680 Err(status)
681 }
682 }
683 }
684
685 pub fn sample_timing_info_array(&self) -> Result<Vec<CMSampleTimingInfo>, i32> {
691 let num_samples = self.num_samples();
692 let mut result = Vec::with_capacity(num_samples);
693 for i in 0..num_samples {
694 result.push(self.sample_timing_info(i)?);
695 }
696 Ok(result)
697 }
698
699 pub fn invalidate(&self) -> Result<(), i32> {
705 unsafe {
706 let status = ffi::cm_sample_buffer_invalidate(self.0);
707 if status == 0 {
708 Ok(())
709 } else {
710 Err(status)
711 }
712 }
713 }
714
715 pub fn create_copy_with_new_timing(
721 &self,
722 timing_info: &[CMSampleTimingInfo],
723 ) -> Result<Self, i32> {
724 unsafe {
725 let mut new_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
726 let status = ffi::cm_sample_buffer_create_copy_with_new_timing(
727 self.0,
728 timing_info.len(),
729 timing_info.as_ptr().cast::<std::ffi::c_void>(),
730 &mut new_buffer_ptr,
731 );
732 if status == 0 && !new_buffer_ptr.is_null() {
733 Ok(Self(new_buffer_ptr))
734 } else {
735 Err(status)
736 }
737 }
738 }
739
740 pub fn copy_pcm_data_into_audio_buffer_list(
746 &self,
747 frame_offset: i32,
748 num_frames: i32,
749 buffer_list: &mut AudioBufferList,
750 ) -> Result<(), i32> {
751 unsafe {
752 let status = ffi::cm_sample_buffer_copy_pcm_data_into_audio_buffer_list(
753 self.0,
754 frame_offset,
755 num_frames,
756 (buffer_list as *mut AudioBufferList).cast::<std::ffi::c_void>(),
757 );
758 if status == 0 {
759 Ok(())
760 } else {
761 Err(status)
762 }
763 }
764 }
765}
766
767impl Drop for CMSampleBuffer {
768 fn drop(&mut self) {
769 unsafe {
770 ffi::cm_sample_buffer_release(self.0);
771 }
772 }
773}
774
775unsafe impl Send for CMSampleBuffer {}
776unsafe impl Sync for CMSampleBuffer {}
777
778impl fmt::Display for CMSampleBuffer {
779 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
780 write!(
781 f,
782 "CMSampleBuffer(pts: {}, duration: {}, samples: {})",
783 self.presentation_timestamp(),
784 self.duration(),
785 self.num_samples()
786 )
787 }
788}