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}