Skip to main content

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