1use super::ffi;
4use super::{
5 AudioBuffer, AudioBufferList, AudioBufferListRaw, CMBlockBuffer, CMFormatDescription,
6 CMSampleTimingInfo, CMTime, SCFrameStatus,
7};
8use crate::cv::CVPixelBuffer;
9use std::fmt;
10
11#[repr(transparent)]
13#[derive(Debug)]
14pub struct CMSampleBuffer(*mut std::ffi::c_void);
15
16impl PartialEq for CMSampleBuffer {
17 fn eq(&self, other: &Self) -> bool {
18 self.0 == other.0
19 }
20}
21
22impl Eq for CMSampleBuffer {}
23
24impl std::hash::Hash for CMSampleBuffer {
25 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
26 unsafe {
27 let hash_value = ffi::cm_sample_buffer_hash(self.0);
28 hash_value.hash(state);
29 }
30 }
31}
32
33impl CMSampleBuffer {
34 pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
35 if ptr.is_null() {
36 None
37 } else {
38 Some(Self(ptr))
39 }
40 }
41
42 pub unsafe fn from_ptr(ptr: *mut std::ffi::c_void) -> Self {
45 Self(ptr)
46 }
47
48 pub fn as_ptr(&self) -> *mut std::ffi::c_void {
49 self.0
50 }
51
52 pub fn create_for_image_buffer(
90 image_buffer: &CVPixelBuffer,
91 presentation_time: CMTime,
92 duration: CMTime,
93 ) -> Result<Self, i32> {
94 unsafe {
95 let mut sample_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
96 let status = ffi::cm_sample_buffer_create_for_image_buffer(
97 image_buffer.as_ptr(),
98 presentation_time.value,
99 presentation_time.timescale,
100 duration.value,
101 duration.timescale,
102 &mut sample_buffer_ptr,
103 );
104
105 if status == 0 && !sample_buffer_ptr.is_null() {
106 Ok(Self(sample_buffer_ptr))
107 } else {
108 Err(status)
109 }
110 }
111 }
112
113 pub fn image_buffer(&self) -> Option<CVPixelBuffer> {
115 unsafe {
116 let ptr = ffi::cm_sample_buffer_get_image_buffer(self.0);
117 CVPixelBuffer::from_raw(ptr)
118 }
119 }
120
121 pub fn frame_status(&self) -> Option<SCFrameStatus> {
148 unsafe {
149 let status = ffi::cm_sample_buffer_get_frame_status(self.0);
150 if status >= 0 {
151 SCFrameStatus::from_raw(status)
152 } else {
153 None
154 }
155 }
156 }
157
158 pub fn display_time(&self) -> Option<u64> {
162 unsafe {
163 let mut value: u64 = 0;
164 if ffi::cm_sample_buffer_get_display_time(self.0, &mut value) {
165 Some(value)
166 } else {
167 None
168 }
169 }
170 }
171
172 pub fn scale_factor(&self) -> Option<f64> {
176 unsafe {
177 let mut value: f64 = 0.0;
178 if ffi::cm_sample_buffer_get_scale_factor(self.0, &mut value) {
179 Some(value)
180 } else {
181 None
182 }
183 }
184 }
185
186 pub fn content_scale(&self) -> Option<f64> {
188 unsafe {
189 let mut value: f64 = 0.0;
190 if ffi::cm_sample_buffer_get_content_scale(self.0, &mut value) {
191 Some(value)
192 } else {
193 None
194 }
195 }
196 }
197
198 pub fn content_rect(&self) -> Option<crate::cg::CGRect> {
202 unsafe {
203 let mut x: f64 = 0.0;
204 let mut y: f64 = 0.0;
205 let mut width: f64 = 0.0;
206 let mut height: f64 = 0.0;
207 if ffi::cm_sample_buffer_get_content_rect(
208 self.0,
209 &mut x,
210 &mut y,
211 &mut width,
212 &mut height,
213 ) {
214 Some(crate::cg::CGRect::new(x, y, width, height))
215 } else {
216 None
217 }
218 }
219 }
220
221 pub fn bounding_rect(&self) -> Option<crate::cg::CGRect> {
225 unsafe {
226 let mut x: f64 = 0.0;
227 let mut y: f64 = 0.0;
228 let mut width: f64 = 0.0;
229 let mut height: f64 = 0.0;
230 if ffi::cm_sample_buffer_get_bounding_rect(
231 self.0,
232 &mut x,
233 &mut y,
234 &mut width,
235 &mut height,
236 ) {
237 Some(crate::cg::CGRect::new(x, y, width, height))
238 } else {
239 None
240 }
241 }
242 }
243
244 pub fn screen_rect(&self) -> Option<crate::cg::CGRect> {
248 unsafe {
249 let mut x: f64 = 0.0;
250 let mut y: f64 = 0.0;
251 let mut width: f64 = 0.0;
252 let mut height: f64 = 0.0;
253 if ffi::cm_sample_buffer_get_screen_rect(
254 self.0,
255 &mut x,
256 &mut y,
257 &mut width,
258 &mut height,
259 ) {
260 Some(crate::cg::CGRect::new(x, y, width, height))
261 } else {
262 None
263 }
264 }
265 }
266
267 pub fn dirty_rects(&self) -> Option<Vec<crate::cg::CGRect>> {
272 unsafe {
273 let mut rects_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
274 let mut count: usize = 0;
275 if ffi::cm_sample_buffer_get_dirty_rects(self.0, &mut rects_ptr, &mut count) {
276 if rects_ptr.is_null() || count == 0 {
277 return None;
278 }
279 let data = rects_ptr as *const f64;
280 let mut rects = Vec::with_capacity(count);
281 for i in 0..count {
282 let x = *data.add(i * 4);
283 let y = *data.add(i * 4 + 1);
284 let width = *data.add(i * 4 + 2);
285 let height = *data.add(i * 4 + 3);
286 rects.push(crate::cg::CGRect::new(x, y, width, height));
287 }
288 ffi::cm_sample_buffer_free_dirty_rects(rects_ptr);
289 Some(rects)
290 } else {
291 None
292 }
293 }
294 }
295
296 pub fn presentation_timestamp(&self) -> CMTime {
298 unsafe {
299 let mut value: i64 = 0;
300 let mut timescale: i32 = 0;
301 let mut flags: u32 = 0;
302 let mut epoch: i64 = 0;
303 ffi::cm_sample_buffer_get_presentation_timestamp(
304 self.0,
305 &mut value,
306 &mut timescale,
307 &mut flags,
308 &mut epoch,
309 );
310 CMTime {
311 value,
312 timescale,
313 flags,
314 epoch,
315 }
316 }
317 }
318
319 pub fn duration(&self) -> CMTime {
321 unsafe {
322 let mut value: i64 = 0;
323 let mut timescale: i32 = 0;
324 let mut flags: u32 = 0;
325 let mut epoch: i64 = 0;
326 ffi::cm_sample_buffer_get_duration(
327 self.0,
328 &mut value,
329 &mut timescale,
330 &mut flags,
331 &mut epoch,
332 );
333 CMTime {
334 value,
335 timescale,
336 flags,
337 epoch,
338 }
339 }
340 }
341
342 pub fn is_valid(&self) -> bool {
343 unsafe { ffi::cm_sample_buffer_is_valid(self.0) }
344 }
345
346 pub fn num_samples(&self) -> usize {
348 unsafe { ffi::cm_sample_buffer_get_num_samples(self.0) }
349 }
350
351 pub fn audio_buffer_list(&self) -> Option<AudioBufferList> {
353 unsafe {
354 let mut num_buffers: u32 = 0;
355 let mut buffers_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
356 let mut buffers_len: usize = 0;
357 let mut block_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
358
359 ffi::cm_sample_buffer_get_audio_buffer_list(
360 self.0,
361 &mut num_buffers,
362 &mut buffers_ptr,
363 &mut buffers_len,
364 &mut block_buffer_ptr,
365 );
366
367 if num_buffers == 0 {
368 None
369 } else {
370 Some(AudioBufferList {
371 inner: AudioBufferListRaw {
372 num_buffers,
373 buffers_ptr: buffers_ptr.cast::<AudioBuffer>(),
374 buffers_len,
375 },
376 block_buffer_ptr,
377 })
378 }
379 }
380 }
381
382 pub fn data_buffer(&self) -> Option<CMBlockBuffer> {
384 unsafe {
385 let ptr = ffi::cm_sample_buffer_get_data_buffer(self.0);
386 CMBlockBuffer::from_raw(ptr)
387 }
388 }
389
390 pub fn decode_timestamp(&self) -> CMTime {
392 unsafe {
393 let mut value: i64 = 0;
394 let mut timescale: i32 = 0;
395 let mut flags: u32 = 0;
396 let mut epoch: i64 = 0;
397 ffi::cm_sample_buffer_get_decode_timestamp(
398 self.0,
399 &mut value,
400 &mut timescale,
401 &mut flags,
402 &mut epoch,
403 );
404 CMTime {
405 value,
406 timescale,
407 flags,
408 epoch,
409 }
410 }
411 }
412
413 pub fn output_presentation_timestamp(&self) -> CMTime {
415 unsafe {
416 let mut value: i64 = 0;
417 let mut timescale: i32 = 0;
418 let mut flags: u32 = 0;
419 let mut epoch: i64 = 0;
420 ffi::cm_sample_buffer_get_output_presentation_timestamp(
421 self.0,
422 &mut value,
423 &mut timescale,
424 &mut flags,
425 &mut epoch,
426 );
427 CMTime {
428 value,
429 timescale,
430 flags,
431 epoch,
432 }
433 }
434 }
435
436 pub fn set_output_presentation_timestamp(&self, time: CMTime) -> Result<(), i32> {
442 unsafe {
443 let status = ffi::cm_sample_buffer_set_output_presentation_timestamp(
444 self.0,
445 time.value,
446 time.timescale,
447 time.flags,
448 time.epoch,
449 );
450 if status == 0 {
451 Ok(())
452 } else {
453 Err(status)
454 }
455 }
456 }
457
458 pub fn sample_size(&self, index: usize) -> usize {
460 unsafe { ffi::cm_sample_buffer_get_sample_size(self.0, index) }
461 }
462
463 pub fn total_sample_size(&self) -> usize {
465 unsafe { ffi::cm_sample_buffer_get_total_sample_size(self.0) }
466 }
467
468 pub fn is_data_ready(&self) -> bool {
470 unsafe { ffi::cm_sample_buffer_is_ready_for_data_access(self.0) }
471 }
472
473 pub fn make_data_ready(&self) -> Result<(), i32> {
479 unsafe {
480 let status = ffi::cm_sample_buffer_make_data_ready(self.0);
481 if status == 0 {
482 Ok(())
483 } else {
484 Err(status)
485 }
486 }
487 }
488
489 pub fn format_description(&self) -> Option<CMFormatDescription> {
491 unsafe {
492 let ptr = ffi::cm_sample_buffer_get_format_description(self.0);
493 CMFormatDescription::from_raw(ptr)
494 }
495 }
496
497 pub fn sample_timing_info(&self, index: usize) -> Result<CMSampleTimingInfo, i32> {
503 unsafe {
504 let mut timing_info = CMSampleTimingInfo {
505 duration: CMTime::INVALID,
506 presentation_time_stamp: CMTime::INVALID,
507 decode_time_stamp: CMTime::INVALID,
508 };
509 let status = ffi::cm_sample_buffer_get_sample_timing_info(
510 self.0,
511 index,
512 &mut timing_info.duration.value,
513 &mut timing_info.duration.timescale,
514 &mut timing_info.duration.flags,
515 &mut timing_info.duration.epoch,
516 &mut timing_info.presentation_time_stamp.value,
517 &mut timing_info.presentation_time_stamp.timescale,
518 &mut timing_info.presentation_time_stamp.flags,
519 &mut timing_info.presentation_time_stamp.epoch,
520 &mut timing_info.decode_time_stamp.value,
521 &mut timing_info.decode_time_stamp.timescale,
522 &mut timing_info.decode_time_stamp.flags,
523 &mut timing_info.decode_time_stamp.epoch,
524 );
525 if status == 0 {
526 Ok(timing_info)
527 } else {
528 Err(status)
529 }
530 }
531 }
532
533 pub fn sample_timing_info_array(&self) -> Result<Vec<CMSampleTimingInfo>, i32> {
539 let num_samples = self.num_samples();
540 let mut result = Vec::with_capacity(num_samples);
541 for i in 0..num_samples {
542 result.push(self.sample_timing_info(i)?);
543 }
544 Ok(result)
545 }
546
547 pub fn invalidate(&self) -> Result<(), i32> {
553 unsafe {
554 let status = ffi::cm_sample_buffer_invalidate(self.0);
555 if status == 0 {
556 Ok(())
557 } else {
558 Err(status)
559 }
560 }
561 }
562
563 pub fn create_copy_with_new_timing(
569 &self,
570 timing_info: &[CMSampleTimingInfo],
571 ) -> Result<Self, i32> {
572 unsafe {
573 let mut new_buffer_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
574 let status = ffi::cm_sample_buffer_create_copy_with_new_timing(
575 self.0,
576 timing_info.len(),
577 timing_info.as_ptr().cast::<std::ffi::c_void>(),
578 &mut new_buffer_ptr,
579 );
580 if status == 0 && !new_buffer_ptr.is_null() {
581 Ok(Self(new_buffer_ptr))
582 } else {
583 Err(status)
584 }
585 }
586 }
587
588 pub fn copy_pcm_data_into_audio_buffer_list(
594 &self,
595 frame_offset: i32,
596 num_frames: i32,
597 buffer_list: &mut AudioBufferList,
598 ) -> Result<(), i32> {
599 unsafe {
600 let status = ffi::cm_sample_buffer_copy_pcm_data_into_audio_buffer_list(
601 self.0,
602 frame_offset,
603 num_frames,
604 (buffer_list as *mut AudioBufferList).cast::<std::ffi::c_void>(),
605 );
606 if status == 0 {
607 Ok(())
608 } else {
609 Err(status)
610 }
611 }
612 }
613}
614
615impl Drop for CMSampleBuffer {
616 fn drop(&mut self) {
617 unsafe {
618 ffi::cm_sample_buffer_release(self.0);
619 }
620 }
621}
622
623unsafe impl Send for CMSampleBuffer {}
624unsafe impl Sync for CMSampleBuffer {}
625
626impl fmt::Display for CMSampleBuffer {
627 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
628 write!(
629 f,
630 "CMSampleBuffer(pts: {}, duration: {}, samples: {})",
631 self.presentation_timestamp(),
632 self.duration(),
633 self.num_samples()
634 )
635 }
636}