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