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}