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, packed(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!(CMBuffer, CMBufferRef);
165impl_CFTypeDescription!(CMBuffer);
166
167impl CMBuffer {
168    #[inline]
169    pub fn as_concrete_TypeRef(&self) -> CMBufferRef {
170        self.0
171    }
172
173    #[inline]
174    pub fn as_CFType(&self) -> CFType {
175        unsafe { CFType::wrap_under_get_rule(self.as_concrete_TypeRef()) }
176    }
177
178    #[inline]
179    pub fn as_CFTypeRef(&self) -> CFTypeRef {
180        self.as_concrete_TypeRef() as CFTypeRef
181    }
182
183    #[inline]
184    pub fn into_CFType(self) -> CFType
185    where
186        Self: Sized,
187    {
188        let reference = self.as_CFTypeRef();
189        mem::forget(self);
190        unsafe { CFType::wrap_under_create_rule(reference) }
191    }
192
193    #[inline]
194    pub unsafe fn wrap_under_create_rule(reference: CMBufferRef) -> Self {
195        CMBuffer(reference)
196    }
197
198    #[inline]
199    pub unsafe fn wrap_under_get_rule(reference: CMBufferRef) -> Self {
200        let reference = CFRetain(reference);
201        CMBuffer(reference)
202    }
203
204    #[inline]
205    pub fn type_of(&self) -> CFTypeID {
206        unsafe { CFGetTypeID(self.as_CFTypeRef()) }
207    }
208
209    #[inline]
210    pub fn instance_of<T: TCFType>(&self) -> bool {
211        self.type_of() == T::type_id()
212    }
213}
214
215declare_TCFType!(CMBufferQueue, CMBufferQueueRef);
216impl_TCFType!(CMBufferQueue, CMBufferQueueRef, CMBufferQueueGetTypeID);
217impl_CFTypeDescription!(CMBufferQueue);
218
219impl CMBufferQueue {
220    #[inline]
221    pub fn new(callbacks: &[CMBufferCallbacks]) -> Result<CMBufferQueue, OSStatus> {
222        unsafe {
223            let mut queue: CMBufferQueueRef = null_mut();
224            let status = CMBufferQueueCreate(kCFAllocatorDefault, callbacks.len() as CMItemCount, callbacks.as_ptr(), &mut queue);
225            if status == 0 {
226                Ok(TCFType::wrap_under_create_rule(queue))
227            } else {
228                Err(status)
229            }
230        }
231    }
232
233    #[inline]
234    pub fn enqueue(&self, buf: &CMBuffer) -> Result<(), OSStatus> {
235        unsafe {
236            let status = CMBufferQueueEnqueue(self.as_concrete_TypeRef(), buf.as_concrete_TypeRef());
237            if status == 0 {
238                Ok(())
239            } else {
240                Err(status)
241            }
242        }
243    }
244
245    #[inline]
246    pub fn dequeue_and_retain(&self) -> Option<CMBuffer> {
247        unsafe {
248            let buf = CMBufferQueueDequeueAndRetain(self.as_concrete_TypeRef());
249            if buf.is_null() {
250                None
251            } else {
252                Some(CMBuffer::wrap_under_create_rule(buf))
253            }
254        }
255    }
256
257    #[inline]
258    pub fn dequeue_if_data_ready_and_retain(&self) -> Option<CMBuffer> {
259        unsafe {
260            let buf = CMBufferQueueDequeueIfDataReadyAndRetain(self.as_concrete_TypeRef());
261            if buf.is_null() {
262                None
263            } else {
264                Some(CMBuffer::wrap_under_create_rule(buf))
265            }
266        }
267    }
268
269    #[inline]
270    pub fn get_head(&self) -> Option<CMBuffer> {
271        unsafe {
272            let buf = CMBufferQueueGetHead(self.as_concrete_TypeRef());
273            if buf.is_null() {
274                None
275            } else {
276                Some(CMBuffer::wrap_under_get_rule(buf))
277            }
278        }
279    }
280
281    #[inline]
282    pub fn copy_head(&self) -> Option<CMBuffer> {
283        unsafe {
284            let buf = CMBufferQueueCopyHead(self.as_concrete_TypeRef());
285            if buf.is_null() {
286                None
287            } else {
288                Some(CMBuffer::wrap_under_create_rule(buf))
289            }
290        }
291    }
292
293    #[inline]
294    pub fn is_empty(&self) -> bool {
295        unsafe { CMBufferQueueIsEmpty(self.as_concrete_TypeRef()) != 0 }
296    }
297
298    #[inline]
299    pub fn mark_end_of_data(&self) -> Result<(), OSStatus> {
300        unsafe {
301            let status = CMBufferQueueMarkEndOfData(self.as_concrete_TypeRef());
302            if status == 0 {
303                Ok(())
304            } else {
305                Err(status)
306            }
307        }
308    }
309
310    #[inline]
311    pub fn contains_end_of_data(&self) -> bool {
312        unsafe { CMBufferQueueContainsEndOfData(self.as_concrete_TypeRef()) != 0 }
313    }
314
315    #[inline]
316    pub fn is_at_end_of_data(&self) -> bool {
317        unsafe { CMBufferQueueIsAtEndOfData(self.as_concrete_TypeRef()) != 0 }
318    }
319
320    #[inline]
321    pub fn reset(&self) -> Result<(), OSStatus> {
322        unsafe {
323            let status = CMBufferQueueReset(self.as_concrete_TypeRef());
324            if status == 0 {
325                Ok(())
326            } else {
327                Err(status)
328            }
329        }
330    }
331
332    #[inline]
333    pub unsafe fn reset_with_callback(
334        &self,
335        callback: extern "C" fn(buffer: CMBufferRef, refcon: *mut c_void),
336        refcon: *mut c_void,
337    ) -> Result<(), OSStatus> {
338        unsafe {
339            let status = CMBufferQueueResetWithCallback(self.as_concrete_TypeRef(), callback, refcon);
340            if status == 0 {
341                Ok(())
342            } else {
343                Err(status)
344            }
345        }
346    }
347
348    #[inline]
349    pub fn get_buffer_count(&self) -> CMItemCount {
350        unsafe { CMBufferQueueGetBufferCount(self.as_concrete_TypeRef()) }
351    }
352
353    #[inline]
354    pub fn get_duration(&self) -> CMTime {
355        unsafe { CMBufferQueueGetDuration(self.as_concrete_TypeRef()) }
356    }
357
358    #[inline]
359    pub fn get_min_decode_time_stamp(&self) -> CMTime {
360        unsafe { CMBufferQueueGetMinDecodeTimeStamp(self.as_concrete_TypeRef()) }
361    }
362
363    #[inline]
364    pub fn get_first_decode_time_stamp(&self) -> CMTime {
365        unsafe { CMBufferQueueGetFirstDecodeTimeStamp(self.as_concrete_TypeRef()) }
366    }
367
368    #[inline]
369    pub fn get_min_presentation_time_stamp(&self) -> CMTime {
370        unsafe { CMBufferQueueGetMinPresentationTimeStamp(self.as_concrete_TypeRef()) }
371    }
372
373    #[inline]
374    pub fn get_first_presentation_time_stamp(&self) -> CMTime {
375        unsafe { CMBufferQueueGetFirstPresentationTimeStamp(self.as_concrete_TypeRef()) }
376    }
377
378    #[inline]
379    pub fn get_max_presentation_time_stamp(&self) -> CMTime {
380        unsafe { CMBufferQueueGetMaxPresentationTimeStamp(self.as_concrete_TypeRef()) }
381    }
382
383    #[inline]
384    pub fn get_end_presentation_time_stamp(&self) -> CMTime {
385        unsafe { CMBufferQueueGetEndPresentationTimeStamp(self.as_concrete_TypeRef()) }
386    }
387
388    #[inline]
389    pub fn get_total_size(&self) -> size_t {
390        unsafe { CMBufferQueueGetTotalSize(self.as_concrete_TypeRef()) }
391    }
392
393    pub unsafe fn install_trigger(
394        &self,
395        callback: CMBufferQueueTriggerCallback,
396        refcon: *mut c_void,
397        condition: CMBufferQueueTriggerCondition,
398        time: CMTime,
399    ) -> Result<CMBufferQueueTriggerToken, OSStatus> {
400        unsafe {
401            let mut token = null();
402            let status = CMBufferQueueInstallTrigger(self.as_concrete_TypeRef(), callback, refcon, condition, time, &mut token);
403            if status == 0 {
404                Ok(token)
405            } else {
406                Err(status)
407            }
408        }
409    }
410
411    pub unsafe fn install_trigger_with_integer_threshold(
412        &self,
413        callback: CMBufferQueueTriggerCallback,
414        refcon: *mut c_void,
415        condition: CMBufferQueueTriggerCondition,
416        threshold: CMItemCount,
417    ) -> Result<CMBufferQueueTriggerToken, OSStatus> {
418        unsafe {
419            let mut token = null();
420            let status =
421                CMBufferQueueInstallTriggerWithIntegerThreshold(self.as_concrete_TypeRef(), callback, refcon, condition, threshold, &mut token);
422            if status == 0 {
423                Ok(token)
424            } else {
425                Err(status)
426            }
427        }
428    }
429
430    pub fn install_trigger_closure<F>(
431        &self,
432        condition: CMBufferQueueTriggerCondition,
433        time: CMTime,
434        closure: Option<F>,
435    ) -> Result<CMBufferQueueTriggerToken, OSStatus>
436    where
437        F: Fn(CMBufferQueueTriggerToken) + 'static,
438    {
439        let mut token = null();
440        let handler = closure.map(|closure| {
441            ConcreteBlock::new(move |trigger_token: CMBufferQueueTriggerToken| {
442                closure(trigger_token);
443            })
444            .copy()
445        });
446        let status = unsafe {
447            CMBufferQueueInstallTriggerHandler(self.as_concrete_TypeRef(), condition, time, &mut token, handler.as_ref().map_or(null(), |h| &**h))
448        };
449        if status == 0 {
450            Ok(token)
451        } else {
452            Err(status)
453        }
454    }
455
456    pub fn install_trigger_with_integer_threshold_closure<F>(
457        &self,
458        condition: CMBufferQueueTriggerCondition,
459        threshold: CMItemCount,
460        closure: Option<F>,
461    ) -> Result<CMBufferQueueTriggerToken, OSStatus>
462    where
463        F: Fn(CMBufferQueueTriggerToken) + 'static,
464    {
465        let mut token = null();
466        let handler = closure.map(|closure| {
467            ConcreteBlock::new(move |trigger_token: CMBufferQueueTriggerToken| {
468                closure(trigger_token);
469            })
470            .copy()
471        });
472        let status = unsafe {
473            CMBufferQueueInstallTriggerHandlerWithIntegerThreshold(
474                self.as_concrete_TypeRef(),
475                condition,
476                threshold,
477                &mut token,
478                handler.as_ref().map_or(null(), |h| &**h),
479            )
480        };
481        if status == 0 {
482            Ok(token)
483        } else {
484            Err(status)
485        }
486    }
487
488    #[inline]
489    pub fn remove_trigger(&self, token: CMBufferQueueTriggerToken) -> Result<(), OSStatus> {
490        unsafe {
491            let status = CMBufferQueueRemoveTrigger(self.as_concrete_TypeRef(), token);
492            if status == 0 {
493                Ok(())
494            } else {
495                Err(status)
496            }
497        }
498    }
499
500    #[inline]
501    pub fn test_trigger(&self, token: CMBufferQueueTriggerToken) -> bool {
502        unsafe { CMBufferQueueTestTrigger(self.as_concrete_TypeRef(), token) != 0 }
503    }
504
505    #[inline]
506    pub unsafe fn call_for_each_buffer(
507        &self,
508        callback: extern "C" fn(buffer: CMBufferRef, refcon: *mut c_void) -> OSStatus,
509        refcon: *mut c_void,
510    ) -> Result<(), OSStatus> {
511        unsafe {
512            let status = CMBufferQueueCallForEachBuffer(self.as_concrete_TypeRef(), callback, refcon);
513            if status == 0 {
514                Ok(())
515            } else {
516                Err(status)
517            }
518        }
519    }
520
521    pub unsafe fn set_validation_callback(&self, callback: CMBufferValidationCallback, refcon: *mut c_void) -> Result<(), OSStatus> {
522        unsafe {
523            let status = CMBufferQueueSetValidationCallback(self.as_concrete_TypeRef(), callback, refcon);
524            if status == 0 {
525                Ok(())
526            } else {
527                Err(status)
528            }
529        }
530    }
531
532    pub fn set_validation_closure<F>(&self, closure: Option<F>) -> Result<(), OSStatus>
533    where
534        F: Fn(CMBufferQueue, CMBuffer) -> OSStatus + 'static,
535    {
536        let handler = closure.map(|closure| {
537            ConcreteBlock::new(move |queue: CMBufferQueueRef, buf: CMBufferRef| {
538                let queue = unsafe { CMBufferQueue::wrap_under_get_rule(queue) };
539                let buf = unsafe { CMBuffer::wrap_under_get_rule(buf) };
540                closure(queue, buf)
541            })
542            .copy()
543        });
544
545        let status = unsafe { CMBufferQueueSetValidationHandler(self.as_concrete_TypeRef(), handler.as_ref().map_or(null(), |h| &**h)) };
546        if status == 0 {
547            Ok(())
548        } else {
549            Err(status)
550        }
551    }
552}