core_media/
buffer_queue.rs

1use std::{
2    mem,
3    ptr::{null, null_mut},
4};
5
6use block::{Block, ConcreteBlock};
7use core_foundation::{
8    base::{kCFAllocatorDefault, Boolean, CFAllocatorRef, CFComparisonResult, CFGetTypeID, CFRetain, CFType, CFTypeID, CFTypeRef, OSStatus, TCFType},
9    string::CFStringRef,
10};
11use libc::{c_void, size_t};
12
13use crate::{base::CMItemCount, time::CMTime};
14
15pub const kCMBufferQueueError_AllocationFailed: OSStatus = -12760;
16pub const kCMBufferQueueError_RequiredParameterMissing: OSStatus = -12761;
17pub const kCMBufferQueueError_InvalidCMBufferCallbacksStruct: OSStatus = -12762;
18pub const kCMBufferQueueError_EnqueueAfterEndOfData: OSStatus = -12763;
19pub const kCMBufferQueueError_QueueIsFull: OSStatus = -12764;
20pub const kCMBufferQueueError_BadTriggerDuration: OSStatus = -12765;
21pub const kCMBufferQueueError_CannotModifyQueueFromTriggerCallback: OSStatus = -12766;
22pub const kCMBufferQueueError_InvalidTriggerCondition: OSStatus = -12767;
23pub const kCMBufferQueueError_InvalidTriggerToken: OSStatus = -12768;
24pub const kCMBufferQueueError_InvalidBuffer: OSStatus = -12769;
25
26#[repr(C)]
27pub struct OpaqueCMBufferQueue(c_void);
28
29pub type CMBufferQueueRef = *mut OpaqueCMBufferQueue;
30
31pub type CMBufferRef = CFTypeRef;
32
33pub type CMBufferGetTimeCallback = extern "C" fn(buf: CMBufferRef, refcon: *mut c_void) -> CMTime;
34pub type CMBufferGetTimeHandler = *const Block<CMBufferRef, CMTime>;
35pub type CMBufferGetBooleanCallback = extern "C" fn(buf: CMBufferRef, refcon: *mut c_void) -> Boolean;
36pub type CMBufferGetBooleanHandler = *const Block<CMBufferRef, Boolean>;
37pub type CMBufferCompareCallback = extern "C" fn(buf1: CMBufferRef, buf2: CMBufferRef, refcon: *mut c_void) -> CFComparisonResult;
38pub type CMBufferCompareHandler = *const Block<(CMBufferRef, CMBufferRef), CFComparisonResult>;
39pub type CMBufferGetSizeCallback = extern "C" fn(buf: CMBufferRef, refcon: *mut c_void) -> size_t;
40pub type CMBufferGetSizeHandler = *const Block<CMBufferRef, size_t>;
41
42#[repr(C, align(4))]
43#[derive(Clone, Copy, Debug)]
44pub struct CMBufferCallbacks {
45    pub version: u32,
46    pub refcon: *mut c_void,
47    pub getDecodeTimeStamp: CMBufferGetTimeCallback,
48    pub getPresentationTimeStamp: CMBufferGetTimeCallback,
49    pub getDuration: CMBufferGetTimeCallback,
50    pub isDataReady: CMBufferGetBooleanCallback,
51    pub compare: CMBufferCompareCallback,
52    pub dataBecameReadyNotification: CFStringRef,
53    pub getSize: CMBufferGetSizeCallback,
54}
55
56extern "C" {
57    pub fn CMBufferQueueGetCallbacksForUnsortedSampleBuffers() -> *const CMBufferCallbacks;
58    pub fn CMBufferQueueGetCallbacksForSampleBuffersSortedByOutputPTS() -> *const CMBufferCallbacks;
59    pub fn CMBufferQueueCreate(
60        allocator: CFAllocatorRef,
61        capacity: CMItemCount,
62        callbacks: *const CMBufferCallbacks,
63        queueOut: *mut CMBufferQueueRef,
64    ) -> OSStatus;
65    pub fn CMBufferQueueGetTypeID() -> CFTypeID;
66    pub fn CMBufferQueueEnqueue(queue: CMBufferQueueRef, buf: CMBufferRef) -> OSStatus;
67    pub fn CMBufferQueueDequeueAndRetain(queue: CMBufferQueueRef) -> CMBufferRef;
68    pub fn CMBufferQueueDequeueIfDataReadyAndRetain(queue: CMBufferQueueRef) -> CMBufferRef;
69    pub fn CMBufferQueueGetHead(queue: CMBufferQueueRef) -> CMBufferRef;
70    pub fn CMBufferQueueCopyHead(queue: CMBufferQueueRef) -> CMBufferRef;
71    pub fn CMBufferQueueIsEmpty(queue: CMBufferQueueRef) -> Boolean;
72    pub fn CMBufferQueueMarkEndOfData(queue: CMBufferQueueRef) -> OSStatus;
73    pub fn CMBufferQueueContainsEndOfData(queue: CMBufferQueueRef) -> Boolean;
74    pub fn CMBufferQueueIsAtEndOfData(queue: CMBufferQueueRef) -> Boolean;
75    pub fn CMBufferQueueReset(queue: CMBufferQueueRef) -> OSStatus;
76    pub fn CMBufferQueueResetWithCallback(
77        queue: CMBufferQueueRef,
78        callback: extern "C" fn(buffer: CMBufferRef, refcon: *mut c_void),
79        refcon: *mut c_void,
80    ) -> OSStatus;
81    pub fn CMBufferQueueGetBufferCount(queue: CMBufferQueueRef) -> CMItemCount;
82    pub fn CMBufferQueueGetDuration(queue: CMBufferQueueRef) -> CMTime;
83    pub fn CMBufferQueueGetMinDecodeTimeStamp(queue: CMBufferQueueRef) -> CMTime;
84    pub fn CMBufferQueueGetFirstDecodeTimeStamp(queue: CMBufferQueueRef) -> CMTime;
85    pub fn CMBufferQueueGetMinPresentationTimeStamp(queue: CMBufferQueueRef) -> CMTime;
86    pub fn CMBufferQueueGetFirstPresentationTimeStamp(queue: CMBufferQueueRef) -> CMTime;
87    pub fn CMBufferQueueGetMaxPresentationTimeStamp(queue: CMBufferQueueRef) -> CMTime;
88    pub fn CMBufferQueueGetEndPresentationTimeStamp(queue: CMBufferQueueRef) -> CMTime;
89    pub fn CMBufferQueueGetTotalSize(queue: CMBufferQueueRef) -> size_t;
90}
91
92#[repr(C)]
93pub struct opaqueCMBufferQueueTriggerToken(c_void);
94
95pub type CMBufferQueueTriggerToken = *const opaqueCMBufferQueueTriggerToken;
96
97pub type CMBufferQueueTriggerCallback = extern "C" fn(triggerRefcon: *mut c_void, triggerToken: CMBufferQueueTriggerToken);
98pub type CMBufferQueueTriggerHandler = *const Block<(CMBufferQueueTriggerToken,), ()>;
99
100pub type CMBufferQueueTriggerCondition = i32;
101
102pub const kCMBufferQueueTrigger_WhenDurationBecomesLessThan: CMBufferQueueTriggerCondition = 1;
103pub const kCMBufferQueueTrigger_WhenDurationBecomesLessThanOrEqualTo: CMBufferQueueTriggerCondition = 2;
104pub const kCMBufferQueueTrigger_WhenDurationBecomesGreaterThan: CMBufferQueueTriggerCondition = 3;
105pub const kCMBufferQueueTrigger_WhenDurationBecomesGreaterThanOrEqualTo: CMBufferQueueTriggerCondition = 4;
106pub const kCMBufferQueueTrigger_WhenMinPresentationTimeStampChanges: CMBufferQueueTriggerCondition = 5;
107pub const kCMBufferQueueTrigger_WhenMaxPresentationTimeStampChanges: CMBufferQueueTriggerCondition = 6;
108pub const kCMBufferQueueTrigger_WhenDataBecomesReady: CMBufferQueueTriggerCondition = 7;
109pub const kCMBufferQueueTrigger_WhenEndOfDataReached: CMBufferQueueTriggerCondition = 8;
110pub const kCMBufferQueueTrigger_WhenReset: CMBufferQueueTriggerCondition = 9;
111pub const kCMBufferQueueTrigger_WhenBufferCountBecomesLessThan: CMBufferQueueTriggerCondition = 10;
112pub const kCMBufferQueueTrigger_WhenBufferCountBecomesGreaterThan: CMBufferQueueTriggerCondition = 11;
113pub const kCMBufferQueueTrigger_WhenDurationBecomesGreaterThanOrEqualToAndBufferCountBecomesGreaterThan: CMBufferQueueTriggerCondition = 12;
114
115extern "C" {
116    pub fn CMBufferQueueInstallTrigger(
117        queue: CMBufferQueueRef,
118        callback: CMBufferQueueTriggerCallback,
119        refcon: *mut c_void,
120        condition: CMBufferQueueTriggerCondition,
121        time: CMTime,
122        triggerTokenOut: *mut CMBufferQueueTriggerToken,
123    ) -> OSStatus;
124    pub fn CMBufferQueueInstallTriggerWithIntegerThreshold(
125        queue: CMBufferQueueRef,
126        callback: CMBufferQueueTriggerCallback,
127        refcon: *mut c_void,
128        condition: CMBufferQueueTriggerCondition,
129        threshold: CMItemCount,
130        triggerTokenOut: *mut CMBufferQueueTriggerToken,
131    ) -> OSStatus;
132    pub fn CMBufferQueueInstallTriggerHandler(
133        queue: CMBufferQueueRef,
134        condition: CMBufferQueueTriggerCondition,
135        time: CMTime,
136        triggerTokenOut: *mut CMBufferQueueTriggerToken,
137        handler: CMBufferQueueTriggerHandler,
138    ) -> OSStatus;
139    pub fn CMBufferQueueInstallTriggerHandlerWithIntegerThreshold(
140        queue: CMBufferQueueRef,
141        condition: CMBufferQueueTriggerCondition,
142        threshold: CMItemCount,
143        triggerTokenOut: *mut CMBufferQueueTriggerToken,
144        handler: CMBufferQueueTriggerHandler,
145    ) -> OSStatus;
146    pub fn CMBufferQueueRemoveTrigger(queue: CMBufferQueueRef, triggerToken: CMBufferQueueTriggerToken) -> OSStatus;
147    pub fn CMBufferQueueTestTrigger(queue: CMBufferQueueRef, triggerToken: CMBufferQueueTriggerToken) -> Boolean;
148    pub fn CMBufferQueueCallForEachBuffer(
149        queue: CMBufferQueueRef,
150        callback: extern "C" fn(buffer: CMBufferRef, refcon: *mut c_void) -> OSStatus,
151        refcon: *mut c_void,
152    ) -> OSStatus;
153}
154
155pub type CMBufferValidationCallback = extern "C" fn(queue: CMBufferQueueRef, buf: CMBufferRef, validationRefCon: *mut c_void) -> OSStatus;
156pub type CMBufferValidationHandler = *const Block<(CMBufferQueueRef, CMBufferRef), OSStatus>;
157
158extern "C" {
159    pub fn CMBufferQueueSetValidationCallback(queue: CMBufferQueueRef, callback: CMBufferValidationCallback, refCon: *mut c_void) -> OSStatus;
160    pub fn CMBufferQueueSetValidationHandler(queue: CMBufferQueueRef, handler: CMBufferValidationHandler) -> OSStatus;
161}
162
163declare_TCFType! {
164    CMBuffer, CMBufferRef
165}
166impl_CFTypeDescription!(CMBuffer);
167
168impl CMBuffer {
169    #[inline]
170    pub fn as_concrete_TypeRef(&self) -> CMBufferRef {
171        self.0
172    }
173
174    #[inline]
175    pub fn as_CFType(&self) -> CFType {
176        unsafe { CFType::wrap_under_get_rule(self.as_concrete_TypeRef()) }
177    }
178
179    #[inline]
180    pub fn as_CFTypeRef(&self) -> CFTypeRef {
181        self.as_concrete_TypeRef() as CFTypeRef
182    }
183
184    #[inline]
185    pub fn into_CFType(self) -> CFType
186    where
187        Self: Sized,
188    {
189        let reference = self.as_CFTypeRef();
190        mem::forget(self);
191        unsafe { CFType::wrap_under_create_rule(reference) }
192    }
193
194    #[inline]
195    pub unsafe fn wrap_under_create_rule(reference: CMBufferRef) -> Self {
196        CMBuffer(reference)
197    }
198
199    #[inline]
200    pub unsafe fn wrap_under_get_rule(reference: CMBufferRef) -> Self {
201        let reference = CFRetain(reference);
202        CMBuffer(reference)
203    }
204
205    #[inline]
206    pub fn type_of(&self) -> CFTypeID {
207        unsafe { CFGetTypeID(self.as_CFTypeRef()) }
208    }
209
210    #[inline]
211    pub fn instance_of<T: TCFType>(&self) -> bool {
212        self.type_of() == T::type_id()
213    }
214}
215
216declare_TCFType! {
217    CMBufferQueue, CMBufferQueueRef
218}
219impl_TCFType!(CMBufferQueue, CMBufferQueueRef, CMBufferQueueGetTypeID);
220impl_CFTypeDescription!(CMBufferQueue);
221
222impl CMBufferQueue {
223    #[inline]
224    pub fn new(callbacks: &[CMBufferCallbacks]) -> Result<CMBufferQueue, OSStatus> {
225        unsafe {
226            let mut queue: CMBufferQueueRef = null_mut();
227            let status = CMBufferQueueCreate(kCFAllocatorDefault, callbacks.len() as CMItemCount, callbacks.as_ptr(), &mut queue);
228            if status == 0 {
229                Ok(TCFType::wrap_under_create_rule(queue))
230            } else {
231                Err(status)
232            }
233        }
234    }
235
236    #[inline]
237    pub fn enqueue(&self, buf: &CMBuffer) -> Result<(), OSStatus> {
238        unsafe {
239            let status = CMBufferQueueEnqueue(self.as_concrete_TypeRef(), buf.as_concrete_TypeRef());
240            if status == 0 {
241                Ok(())
242            } else {
243                Err(status)
244            }
245        }
246    }
247
248    #[inline]
249    pub fn dequeue_and_retain(&self) -> Option<CMBuffer> {
250        unsafe {
251            let buf = CMBufferQueueDequeueAndRetain(self.as_concrete_TypeRef());
252            if buf.is_null() {
253                None
254            } else {
255                Some(CMBuffer::wrap_under_create_rule(buf))
256            }
257        }
258    }
259
260    #[inline]
261    pub fn dequeue_if_data_ready_and_retain(&self) -> Option<CMBuffer> {
262        unsafe {
263            let buf = CMBufferQueueDequeueIfDataReadyAndRetain(self.as_concrete_TypeRef());
264            if buf.is_null() {
265                None
266            } else {
267                Some(CMBuffer::wrap_under_create_rule(buf))
268            }
269        }
270    }
271
272    #[inline]
273    pub fn get_head(&self) -> Option<CMBuffer> {
274        unsafe {
275            let buf = CMBufferQueueGetHead(self.as_concrete_TypeRef());
276            if buf.is_null() {
277                None
278            } else {
279                Some(CMBuffer::wrap_under_get_rule(buf))
280            }
281        }
282    }
283
284    #[inline]
285    pub fn copy_head(&self) -> Option<CMBuffer> {
286        unsafe {
287            let buf = CMBufferQueueCopyHead(self.as_concrete_TypeRef());
288            if buf.is_null() {
289                None
290            } else {
291                Some(CMBuffer::wrap_under_create_rule(buf))
292            }
293        }
294    }
295
296    #[inline]
297    pub fn is_empty(&self) -> bool {
298        unsafe { CMBufferQueueIsEmpty(self.as_concrete_TypeRef()) != 0 }
299    }
300
301    #[inline]
302    pub fn mark_end_of_data(&self) -> Result<(), OSStatus> {
303        unsafe {
304            let status = CMBufferQueueMarkEndOfData(self.as_concrete_TypeRef());
305            if status == 0 {
306                Ok(())
307            } else {
308                Err(status)
309            }
310        }
311    }
312
313    #[inline]
314    pub fn contains_end_of_data(&self) -> bool {
315        unsafe { CMBufferQueueContainsEndOfData(self.as_concrete_TypeRef()) != 0 }
316    }
317
318    #[inline]
319    pub fn is_at_end_of_data(&self) -> bool {
320        unsafe { CMBufferQueueIsAtEndOfData(self.as_concrete_TypeRef()) != 0 }
321    }
322
323    #[inline]
324    pub fn reset(&self) -> Result<(), OSStatus> {
325        unsafe {
326            let status = CMBufferQueueReset(self.as_concrete_TypeRef());
327            if status == 0 {
328                Ok(())
329            } else {
330                Err(status)
331            }
332        }
333    }
334
335    #[inline]
336    pub unsafe fn reset_with_callback(
337        &self,
338        callback: extern "C" fn(buffer: CMBufferRef, refcon: *mut c_void),
339        refcon: *mut c_void,
340    ) -> Result<(), OSStatus> {
341        unsafe {
342            let status = CMBufferQueueResetWithCallback(self.as_concrete_TypeRef(), callback, refcon);
343            if status == 0 {
344                Ok(())
345            } else {
346                Err(status)
347            }
348        }
349    }
350
351    #[inline]
352    pub fn get_buffer_count(&self) -> CMItemCount {
353        unsafe { CMBufferQueueGetBufferCount(self.as_concrete_TypeRef()) }
354    }
355
356    #[inline]
357    pub fn get_duration(&self) -> CMTime {
358        unsafe { CMBufferQueueGetDuration(self.as_concrete_TypeRef()) }
359    }
360
361    #[inline]
362    pub fn get_min_decode_time_stamp(&self) -> CMTime {
363        unsafe { CMBufferQueueGetMinDecodeTimeStamp(self.as_concrete_TypeRef()) }
364    }
365
366    #[inline]
367    pub fn get_first_decode_time_stamp(&self) -> CMTime {
368        unsafe { CMBufferQueueGetFirstDecodeTimeStamp(self.as_concrete_TypeRef()) }
369    }
370
371    #[inline]
372    pub fn get_min_presentation_time_stamp(&self) -> CMTime {
373        unsafe { CMBufferQueueGetMinPresentationTimeStamp(self.as_concrete_TypeRef()) }
374    }
375
376    #[inline]
377    pub fn get_first_presentation_time_stamp(&self) -> CMTime {
378        unsafe { CMBufferQueueGetFirstPresentationTimeStamp(self.as_concrete_TypeRef()) }
379    }
380
381    #[inline]
382    pub fn get_max_presentation_time_stamp(&self) -> CMTime {
383        unsafe { CMBufferQueueGetMaxPresentationTimeStamp(self.as_concrete_TypeRef()) }
384    }
385
386    #[inline]
387    pub fn get_end_presentation_time_stamp(&self) -> CMTime {
388        unsafe { CMBufferQueueGetEndPresentationTimeStamp(self.as_concrete_TypeRef()) }
389    }
390
391    #[inline]
392    pub fn get_total_size(&self) -> size_t {
393        unsafe { CMBufferQueueGetTotalSize(self.as_concrete_TypeRef()) }
394    }
395
396    pub unsafe fn install_trigger(
397        &self,
398        callback: CMBufferQueueTriggerCallback,
399        refcon: *mut c_void,
400        condition: CMBufferQueueTriggerCondition,
401        time: CMTime,
402    ) -> Result<CMBufferQueueTriggerToken, OSStatus> {
403        unsafe {
404            let mut token = null();
405            let status = CMBufferQueueInstallTrigger(self.as_concrete_TypeRef(), callback, refcon, condition, time, &mut token);
406            if status == 0 {
407                Ok(token)
408            } else {
409                Err(status)
410            }
411        }
412    }
413
414    pub unsafe fn install_trigger_with_integer_threshold(
415        &self,
416        callback: CMBufferQueueTriggerCallback,
417        refcon: *mut c_void,
418        condition: CMBufferQueueTriggerCondition,
419        threshold: CMItemCount,
420    ) -> Result<CMBufferQueueTriggerToken, OSStatus> {
421        unsafe {
422            let mut token = null();
423            let status =
424                CMBufferQueueInstallTriggerWithIntegerThreshold(self.as_concrete_TypeRef(), callback, refcon, condition, threshold, &mut token);
425            if status == 0 {
426                Ok(token)
427            } else {
428                Err(status)
429            }
430        }
431    }
432
433    pub fn install_trigger_closure<F>(
434        &self,
435        condition: CMBufferQueueTriggerCondition,
436        time: CMTime,
437        closure: Option<F>,
438    ) -> Result<CMBufferQueueTriggerToken, OSStatus>
439    where
440        F: Fn(CMBufferQueueTriggerToken) + 'static,
441    {
442        let mut token = null();
443        let handler = closure.map(|closure| {
444            ConcreteBlock::new(move |trigger_token: CMBufferQueueTriggerToken| {
445                closure(trigger_token);
446            })
447            .copy()
448        });
449        let status = unsafe {
450            CMBufferQueueInstallTriggerHandler(self.as_concrete_TypeRef(), condition, time, &mut token, handler.as_ref().map_or(null(), |h| &**h))
451        };
452        if status == 0 {
453            Ok(token)
454        } else {
455            Err(status)
456        }
457    }
458
459    pub fn install_trigger_with_integer_threshold_closure<F>(
460        &self,
461        condition: CMBufferQueueTriggerCondition,
462        threshold: CMItemCount,
463        closure: Option<F>,
464    ) -> Result<CMBufferQueueTriggerToken, OSStatus>
465    where
466        F: Fn(CMBufferQueueTriggerToken) + 'static,
467    {
468        let mut token = null();
469        let handler = closure.map(|closure| {
470            ConcreteBlock::new(move |trigger_token: CMBufferQueueTriggerToken| {
471                closure(trigger_token);
472            })
473            .copy()
474        });
475        let status = unsafe {
476            CMBufferQueueInstallTriggerHandlerWithIntegerThreshold(
477                self.as_concrete_TypeRef(),
478                condition,
479                threshold,
480                &mut token,
481                handler.as_ref().map_or(null(), |h| &**h),
482            )
483        };
484        if status == 0 {
485            Ok(token)
486        } else {
487            Err(status)
488        }
489    }
490
491    #[inline]
492    pub fn remove_trigger(&self, token: CMBufferQueueTriggerToken) -> Result<(), OSStatus> {
493        unsafe {
494            let status = CMBufferQueueRemoveTrigger(self.as_concrete_TypeRef(), token);
495            if status == 0 {
496                Ok(())
497            } else {
498                Err(status)
499            }
500        }
501    }
502
503    #[inline]
504    pub fn test_trigger(&self, token: CMBufferQueueTriggerToken) -> bool {
505        unsafe { CMBufferQueueTestTrigger(self.as_concrete_TypeRef(), token) != 0 }
506    }
507
508    #[inline]
509    pub unsafe fn call_for_each_buffer(
510        &self,
511        callback: extern "C" fn(buffer: CMBufferRef, refcon: *mut c_void) -> OSStatus,
512        refcon: *mut c_void,
513    ) -> Result<(), OSStatus> {
514        unsafe {
515            let status = CMBufferQueueCallForEachBuffer(self.as_concrete_TypeRef(), callback, refcon);
516            if status == 0 {
517                Ok(())
518            } else {
519                Err(status)
520            }
521        }
522    }
523
524    pub unsafe fn set_validation_callback(&self, callback: CMBufferValidationCallback, refcon: *mut c_void) -> Result<(), OSStatus> {
525        unsafe {
526            let status = CMBufferQueueSetValidationCallback(self.as_concrete_TypeRef(), callback, refcon);
527            if status == 0 {
528                Ok(())
529            } else {
530                Err(status)
531            }
532        }
533    }
534
535    pub fn set_validation_closure<F>(&self, closure: Option<F>) -> Result<(), OSStatus>
536    where
537        F: Fn(CMBufferQueue, CMBuffer) -> OSStatus + 'static,
538    {
539        let handler = closure.map(|closure| {
540            ConcreteBlock::new(move |queue: CMBufferQueueRef, buf: CMBufferRef| {
541                let queue = unsafe { CMBufferQueue::wrap_under_get_rule(queue) };
542                let buf = unsafe { CMBuffer::wrap_under_get_rule(buf) };
543                closure(queue, buf)
544            })
545            .copy()
546        });
547
548        let status = unsafe { CMBufferQueueSetValidationHandler(self.as_concrete_TypeRef(), handler.as_ref().map_or(null(), |h| &**h)) };
549        if status == 0 {
550            Ok(())
551        } else {
552            Err(status)
553        }
554    }
555}